| class Invite < Vue |
| def initialize |
| @disabled = true |
| @alert = nil |
| |
| # initialize form fields |
| @iclaname = '' |
| @iclaemail = '' |
| @pmc = '' |
| @votelink = '' |
| @noticelink = '' |
| @phase = '' |
| @role = '' |
| @roleText = ' to submit ICLA for ' |
| @subject = '' |
| @subjectPhase = '' |
| @previewMessage = 'Preview' |
| @pmcOrPpmc = '' |
| @phasePrefix = '' |
| @member = Server.data.member |
| @user = Server.data.user |
| |
| # initialize conditional text |
| @showPMCVoteLink = false; |
| @showPPMCVoteLink = false; |
| @voteErrorMessage = ''; |
| @showVoteErrorMessage = false; |
| @showPMCNoticeLink = false; |
| @showPPMCNoticeLink = false; |
| @noticeErrorMessage = ''; |
| @showNoticeErrorMessage = false; |
| @showDiscussFrame = false; |
| @showVoteFrame = false; |
| @showPhaseFrame = false; |
| @showRoleFrame = false; |
| @discussComment = '' |
| @voteComment = '' |
| @proposalText = '' |
| @voteProposalText = '' |
| end |
| |
| def render |
| _p %{ |
| This application allows PMC and PPMC members to |
| discuss contributors to achieve consensus; |
| vote on contributors to become a committer or a PMC/PPMC member; or |
| simply invite them to submit an ICLA. |
| } |
| _p do |
| _b '** NOTE: only new contributors are currently supported, i.e. existing ASF committers are excluded. **' |
| end |
| _p %{ |
| If you would like to discuss the candidate, go to the Discuss tab |
| after filling the contributor and PMC/PPMC fields. |
| } |
| _p %{ |
| If you have discussed the candidate and would like to conduct a vote, |
| go to the Vote tab after filling the contributor and PMC/PPMC fields. |
| } |
| _p %{ |
| If you have already achieved consensus, you can go to the Invite tab |
| after filling the contributor and PMC/PPMC fields. |
| } |
| |
| |
| # error messages |
| if @alert |
| _div.alert.alert_danger do |
| _b 'Error: ' |
| _span @alert |
| end |
| end |
| |
| # |
| # Form fields |
| # |
| |
| _div.form_group do |
| _label "Contributor's name (required):", :for => 'iclaname' |
| _input.form_control.iclaname! placeholder: 'GivenName FamilyName', |
| required: true, value: @iclaname |
| end |
| _div.form_group do |
| _label "Contributor's E-Mail address (required):", :for => 'iclaemail' |
| _input.form_control.iclaemail! type: 'email', required: true, |
| placeholder: 'user@example.com', onChange: self.setIclaEmail, |
| value: @iclaemail |
| end |
| |
| _div.form_group do |
| _label 'PMC/PPMC (required)', :for => 'pmc' |
| _select.form_control.pmc! required: true, onChange: self.setPMC, value: @pmc do |
| _option '' |
| Server.data.allData.each_key do |pmc| |
| _option pmc if Server.data.allData[pmc]['pmc'] |
| end |
| _option '---', disabled: true # No point letting it be chosen |
| Server.data.allData.each_key do |ppmc| |
| _option ppmc unless Server.data.allData[ppmc]['pmc'] |
| end |
| end |
| end |
| if @showPhaseFrame |
| _ul.nav.nav_tabs do |
| _li :class => ('active' if @phase == :discuss) do |
| _a 'Discuss', onClick: self.selectDiscuss |
| end |
| _li :class => ('active' if @phase == :vote) do |
| _a 'Vote', onClick: self.selectVote |
| end |
| _li :class => ('active' if @phase == :invite) do |
| _a 'Invite', onClick: self.selectInvite |
| end |
| end |
| end |
| if @showPMCVoteLink |
| _p %{ |
| Fill the following field only if the person was voted by the PMC |
| to become a committer. |
| Link to the [RESULT][VOTE] message in the mail archives. |
| } |
| end |
| if @showPPMCVoteLink |
| _p %{ |
| Fill the following field only if the person is an initial |
| committer on a new project accepted for incubation, or the person |
| has been voted as a committer on a podling. |
| For new incubator projects use the |
| http://wiki.apache.org/incubator/XXXProposal link; for existing |
| podlings link to the [RESULT][VOTE] message in the mail archives. |
| } |
| end |
| if @showPMCVoteLink or @showPPMCVoteLink |
| _ 'Navigate to ' |
| _a 'Ponymail', href: "https://lists.apache.org/list.html?private@#{@mail_list}.apache.org:lte=1M:[VOTE][RESULT]", target: _blank |
| _ ', select the appropriate message, right-click PermaLink, copy link' |
| _ ' to the clip-board, and paste the link here.' |
| _p |
| |
| _div.form_group do |
| _label 'VOTE link', :for => 'votelink' |
| _input.form_control.votelink! type: 'url', onChange: self.setVoteLink, |
| value: @votelink |
| end |
| if @showVoteErrorMessage |
| _div.alert.alert_danger do |
| _span @voteErrorMessage |
| end |
| end |
| end |
| if @showPMCNoticeLink |
| _p %{ |
| Fill the following field only if the person was voted by the PMC |
| to become a PMC member. |
| Link to the [NOTICE] message sent to the board. |
| The message must have been in the archives for at least 72 hours. |
| } |
| end |
| if @showPPMCNoticeLink |
| _p %{ |
| Fill the following field only if the person was voted by the |
| PPMC to be a PPMC member. |
| Link to the [NOTICE] message sent to the incubator PMC. |
| The message must have been in the archives for at least 72 hours. |
| } |
| end |
| if @showPMCNoticeLink or @showPPMCNoticeLink |
| _ 'Navigate to ' |
| if @showPMCNoticeLink |
| _a 'Ponymail', href: "https://lists.apache.org/list.html?board@apache.org:lte=1M:NOTICE%20for%20#{@display_name}", target: _blank |
| else |
| _a 'Ponymail', href: "https://lists.apache.org/list.html?private@incubator.apache.org:lte=1M:NOTICE%20for%20#{@display_name}", target: _blank |
| end |
| _ ', select the appropriate message, right-click PermaLink, copy link' |
| _ ' to the clip-board, and paste the link here.' |
| _p |
| |
| _div.form_group do |
| _label 'NOTICE link', :for => 'noticelink' |
| _input.form_control.noticelink! type: 'url', onChange: self.setNoticeLink, |
| value: @noticelink |
| end |
| end |
| if @showNoticeErrorMessage |
| _div.alert.alert_danger do |
| _span @noticeErrorMessage |
| end |
| end |
| if @showRoleFrame |
| _div.form_check do |
| _label do |
| _input type: :radio, name: :role, value: :committer, id: 'role_committer', |
| onClick: lambda {@role = :committer; |
| @disabled = false |
| @subject = @subjectPhase + ' Invite ' + @iclaname + |
| ' to become a committer for ' + @display_name |
| @proposalText = 'I propose we invite ' + @iclaname + |
| ' to become a committer.' |
| @voteProposalText = @proposalText + "\nHere is my +1." |
| } |
| _span @phasePrefix + |
| ' invite to become a committer' |
| end |
| _br |
| _label do |
| _input type: :radio, name: :role, value: :pmc, id: 'role_pmc', |
| onClick: lambda {@role = :pmc |
| @disabled = false |
| @subject = @subjectPhase + ' Invite ' + @iclaname + |
| ' to become committer and ' + @pmcOrPPMC + ' member for ' + @display_name |
| @proposalText = 'I propose we invite ' + @iclaname + |
| ' to become a committer and ' + @pmcOrPPMC + ' member.' |
| @voteProposalText = @proposalText + ' Here is my +1.' |
| } |
| _span @phasePrefix + |
| ' invite to become a committer and ' + @pmcOrPPMC + ' member' |
| end |
| if @showDiscussFrame |
| _br |
| _label do |
| _input type: :radio, name: :role, value: :invite, id: 'role_invite', |
| onClick: lambda {@role = :invite |
| @disabled = false |
| @subject = @subjectPhase + ' Invite ' + @iclaname + |
| ' to submit an ICLA for ' + @display_name |
| @proposalText = 'I propose we invite ' + @iclaname + |
| ' to submit an ICLA.' |
| } |
| _span @phasePrefix + |
| ' invite to submit an ICLA' |
| end |
| end |
| _p |
| end |
| end |
| if @showDiscussFrame |
| _div 'From: ' + @member |
| _div 'To: private@' + @mail_list + '.apache.org' |
| _div 'Subject: ' + @subject |
| _p |
| _span @proposalText |
| _p |
| _textarea.form_control rows: 4, |
| placeholder: 'Here are my reasons:', |
| name: 'discussComment', value: @discussComment, |
| onChange: self.setdiscussComment |
| end |
| if @showVoteFrame |
| _div 'From: ' + @member |
| _div 'To: private@' + @mail_list + '.apache.org' |
| _div 'Subject: ' + @subject |
| _p |
| _span @voteProposalText |
| _p |
| _textarea.form_control rows: 4, |
| placeholder: 'Here are my reasons:', |
| name: 'voteComment', value: @voteComment, |
| onChange: self.setvoteComment |
| end |
| |
| # |
| # Submission buttons |
| # |
| _p do |
| _button.btn.btn_primary @previewMessage, disabled: @disabled, |
| onClick: self.preview |
| end |
| # |
| # Hidden form: preview invite email |
| # |
| _div.modal.fade.invitation_preview! do |
| _div.modal_dialog do |
| _div.modal_content do |
| _div.modal_header do |
| _button.close "\u00d7", type: 'button', data_dismiss: 'modal' |
| _h4 'Preview Invitation Email' |
| end |
| |
| _div.modal_body do |
| # headers |
| _div do |
| _b 'From: ' |
| _span @userEmail |
| end |
| _div do |
| _b 'To: ' |
| _span "#{@iclaname} <#{@iclaemail}>" |
| end |
| _div do |
| _b 'cc: ' |
| _span @pmcEmail |
| end |
| |
| # draft invitation email |
| _div.form_group do |
| _label :for => 'invitation' |
| _textarea.form_control.invitation! value: @invitation, rows: 12, |
| onChange: self.setInvitation |
| end |
| end |
| |
| _div.modal_footer do |
| _button.btn.btn_default 'Cancel', data_dismiss: 'modal' |
| _button.btn.btn_primary 'Mock Send', onClick: self.mockSend |
| end |
| end |
| end |
| end |
| _p |
| |
| # |
| # Hidden form: preview discussion email |
| # |
| _div.modal.fade.discussion_preview! do |
| _div.modal_dialog do |
| _div.modal_content do |
| _div.modal_header do |
| _button.close "\u00d7", type: 'button', data_dismiss: 'modal' |
| _h4 'Discussion Email' |
| end |
| |
| _div.modal_body do |
| # headers |
| _div do _b 'From: ' |
| _span @member |
| end |
| _div do _b 'To: ' |
| _span @pmcEmail |
| end |
| _div do _b 'Subject: ' |
| _span @subject |
| end |
| _div do _b |
| _pre @message |
| end |
| end |
| |
| _div.modal_footer do |
| _button.btn.btn_default 'Close', data_dismiss: 'modal' |
| end |
| end |
| end |
| end |
| _p |
| |
| # |
| # Hidden form: preview vote email |
| # |
| _div.modal.fade.vote_preview! do |
| _div.modal_dialog do |
| _div.modal_content do |
| _div.modal_header do |
| _button.close "\u00d7", type: 'button', data_dismiss: 'modal' |
| _h4 'Vote Email' |
| end |
| |
| _div.modal_body do |
| # headers |
| _div do _b 'From: ' |
| _span @member |
| end |
| _div do _b 'To: ' |
| _span @pmcEmail |
| end |
| _div do _b 'Subject: ' |
| _span @subject |
| end |
| _div do _b |
| _pre @message |
| end |
| end |
| |
| _div.modal_footer do |
| _button.btn.btn_default 'Close', data_dismiss: 'modal' |
| end |
| end |
| end |
| end |
| _p |
| |
| end |
| # when the form is initially loaded, set the focus on the iclaname field |
| def mounted() |
| document.getElementById('iclaname').focus() |
| end |
| |
| # |
| # field setters |
| # |
| |
| def resetCheckBoxes() |
| document.getElementById('role_pmc').checked = false if document.getElementById('role_pmc') |
| document.getElementById('role_committer').checked = false if document.getElementById('role_committer') |
| document.getElementById('role_invite').checked = false if document.getElementById('role_invite') |
| end |
| |
| def setIclaName(event) |
| @iclaname = event.target.value |
| self.checkValidity() |
| end |
| |
| def setIclaEmail(event) |
| @iclaemail = event.target.value |
| self.checkValidity() |
| end |
| |
| def setPMC(event) |
| @pmc = event.target.value |
| if Server.data.allData[@pmc] |
| @isPMC = Server.data.allData[@pmc]['pmc'] |
| @pmcOrPPMC = @isPMC ? 'PMC' : 'PPMC' |
| @phase = :discuss |
| @subject = '' |
| @showPhaseFrame = true |
| @showRoleFrame = true |
| @mail_list = Server.data.allData[@pmc]['mail_list'] |
| @display_name = Server.data.allData[@pmc]['display_name'] |
| else |
| @isPMC = false # true, but not the whole story! |
| @pmcOrPPMC = '---' |
| @phase = :discuss |
| @subject = '' |
| @showPhaseFrame = false |
| @showRoleFrame = false |
| @mail_list = '---' |
| @display_name = '---' |
| end |
| self.checkValidity() |
| selectDiscuss() |
| end |
| |
| def selectDiscuss(event) |
| @phase = :discuss |
| @subject = '' |
| @subjectPhase = '[DISCUSS]' |
| @previewMessage = 'Start the Discussion' |
| @phasePrefix = ' Start the discussion to' |
| @showDiscussFrame = true; |
| @showRoleFrame = true; |
| @showVoteFrame = false; |
| @showPMCVoteLink = false |
| @showPPMCVoteLink = false |
| @showPMCNoticeLink = false |
| @showPPMCNoticeLink = false |
| @showVoteErrorMessage = false; |
| @showNoticeErrorMessage = false; |
| self.checkValidity() |
| @disabled = true; |
| self.resetCheckBoxes() |
| end |
| |
| def setdiscussComment(event) |
| @discussComment = event.target.value |
| end |
| |
| def selectVote(event) |
| @phase = :vote |
| @subject = '' |
| @subjectPhase = '[VOTE]' |
| @previewMessage = 'Start the Vote' |
| @phasePrefix = ' Start the vote to' |
| @showVoteFrame = true; |
| @showRoleFrame = true; |
| @showDiscussFrame = false; |
| @showPMCVoteLink = false |
| @showPPMCVoteLink = false |
| @showPMCNoticeLink = false |
| @showPPMCNoticeLink = false |
| @showVoteErrorMessage = false; |
| @showNoticeErrorMessage = false; |
| self.checkValidity() |
| @disabled = true; |
| self.resetCheckBoxes() |
| end |
| |
| def setvoteComment(event) |
| @voteComment = event.target.value |
| end |
| |
| def selectInvite(event) |
| @phase = :invite |
| @previewMessage = 'Preview Invitation' |
| @showDiscussFrame = false; |
| @showVoteFrame = false; |
| @showRoleFrame = false; |
| @showPMCVoteLink = @isPMC |
| @showPPMCVoteLink = ! @isPMC |
| @showPMCNoticeLink = @isPMC |
| @showPPMCNoticeLink = ! @isPMC |
| @showVoteErrorMessage = false; |
| @showNoticeErrorMessage = false; |
| checkVoteLink() if document.getElementById('votelink'); |
| checkNoticeLink() if document.getElementById('noticelink'); |
| self.checkValidity() |
| end |
| |
| def setVoteLink(event) |
| @votelink = event.target.value |
| @showVoteErrorMessage = false |
| checkVoteLink() |
| self.checkValidity() |
| end |
| |
| def checkVoteLink() |
| document.getElementById('votelink').setCustomValidity(''); |
| if (@votelink) |
| # verify that the link refers to lists.apache.org message on the project list |
| if not @votelink=~ /^https:\/\/lists\.apache\.org\// |
| @voteErrorMessage = "Error: Please link to\ |
| a message via https://lists.apache.org/" |
| @showVoteErrorMessage = true; |
| end |
| if not @votelink=~ /private@#{@mail_list}(\.incubator)?\.apache\.org/ |
| @voteErrorMessage = "Error: Please link to\ |
| the [RESULT][VOTE] message sent to the private list." |
| @showVoteErrorMessage = true; |
| end |
| if @showVoteErrorMessage |
| document.getElementById('votelink').setCustomValidity(@voteErrorMessage); |
| end |
| end |
| end |
| |
| def setNoticeLink(event) |
| @noticelink = event.target.value |
| @showNoticeErrorMessage = false; |
| checkNoticeLink() |
| self.checkValidity() |
| end |
| |
| def checkNoticeLink() |
| document.getElementById('noticelink').setCustomValidity(''); |
| # verify that the link refers to lists.apache.org message on the proper list |
| if (@noticelink) |
| if not @noticelink=~ /^https:\/\/lists\.apache\.org\// |
| @noticeErrorMessage = "Error: please link to\ |
| a message via https://lists.apache.org/" |
| @showNoticeErrorMessage = true; |
| end |
| if @showPMCNoticeLink and not @noticelink=~ /board@apache\.org/ |
| @noticeErrorMessage = "Error: please link to\ |
| the NOTICE message sent to the board list." |
| @showNoticeErrorMessage = true; |
| end |
| if @showPPMCNoticeLink and not @noticelink=~ /private@incubator\.apache\.org/ |
| @noticeErrorMessage = "Error: please link to\ |
| the NOTICE message sent to the incubator private list." |
| @showNoticeErrorMessage = true; |
| end |
| if @showNoticeErrorMessage |
| document.getElementById('noticelink').setCustomValidity(@noticeErrorMessage); |
| end |
| end |
| end |
| |
| def setInvitation(event) |
| @invitation = event.target.value |
| self.checkValidity() |
| end |
| |
| # |
| # validation and processing |
| # |
| |
| # client side field validations |
| def checkValidity() |
| @disabled = !%w(iclaname iclaemail pmc votelink noticelink).all? do |id| |
| element = document.getElementById(id) |
| (not element) or element.checkValidity() |
| end |
| end |
| |
| # server side field validations |
| def preview() |
| if @phase == :invite |
| previewInvitation() |
| elsif @phase == :discuss |
| previewDiscussion() |
| elsif @phase == :vote |
| previewVote() |
| end |
| end |
| |
| def previewInvitation() |
| data = { |
| iclaname: @iclaname, |
| iclaemail: @iclaemail, |
| pmc: @pmc, |
| votelink: @votelink, |
| noticelink: @noticelink |
| } |
| |
| @disabled = true |
| @alert = nil |
| console.log('>previewInvitation: ' + data.inspect) |
| post 'validate', data do |response| |
| console.log('<previewInvitation: ' + response.inspect) |
| @disabled = false |
| @alert = response.error |
| @memberEmail = response.memberEmail |
| @userEmail = response.userEmail |
| @pmcEmail = response.pmcEmail |
| @invitation = response.invitation |
| @token = response.token |
| document.getElementById(response.focus).focus() if response.focus |
| jQuery('#invitation-preview').modal(:show) unless @alert |
| end |
| end |
| def previewDiscussion() |
| data = { |
| iclaname: @iclaname, |
| iclaemail: @iclaemail, |
| pmc: @pmc, |
| proposer: @member, |
| subject: @subject, |
| proposalText: @proposalText, |
| discussComment: @discussComment |
| } |
| |
| @disabled = true |
| @alert = nil |
| console.log('>previewDiscussion: ' + data.inspect) |
| post 'discuss', data do |response| |
| console.log('<previewDiscussion: ' + response.inspect) |
| @disabled = false |
| @alert = response.error |
| @memberEmail = response.memberEmail |
| @userEmail = response.userEmail |
| @pmcEmail = response.pmcEmail |
| @discussion = response.discussion |
| @token = response.token |
| @message = response.message |
| document.getElementById(response.focus).focus() if response.focus |
| jQuery('#discussion-preview').modal(:show) unless @alert |
| end |
| end |
| def previewVote() |
| data = { |
| user: @user, |
| iclaname: @iclaname, |
| iclaemail: @iclaemail, |
| pmc: @pmc, |
| proposer: @member, |
| subject: @subject, |
| proposalText: @voteProposalText, |
| voteComment: @voteComment, |
| voteComment: @voteComment |
| } |
| |
| @disabled = true |
| @alert = nil |
| console.log('>previewVote: ' + data.inspect) |
| post 'vote', data do |response| |
| console.log('<previewVote: ' + response.inspect) |
| @disabled = false |
| @alert = response.error |
| @memberEmail = response.memberEmail |
| @userEmail = response.userEmail |
| @pmcEmail = response.pmcEmail |
| @token = response.token |
| @message = response.message |
| document.getElementById(response.focus).focus() if response.focus |
| jQuery('#vote-preview').modal(:show) unless @alert |
| end |
| end |
| |
| # pretend to send an invitation |
| def mockSend() |
| # dismiss modal dialog |
| jQuery('#invitation-preview').modal(:hide) |
| |
| # save information for later use (for demo purposes, this is client only) |
| FormData.token = @token |
| FormData.fullname = @iclaname |
| FormData.email = @iclaemail |
| FormData.pmc = @pmc |
| FormData.votelink = @votelink |
| FormData.noticelink = @noticelink |
| |
| # for demo purposes advance to the interview. Note: the below line |
| # updates the URL in a way that breaks the back button. |
| history.replaceState({}, nil, "form?token=#@token") |
| |
| # change the view |
| Main.navigate(Interview) |
| end |
| end |