www/members/nominate_member.cgi (212 lines of code) (raw):

#!/usr/bin/env ruby PAGETITLE = "Nominate someone for ASF Member" # Wvisible:meeting $LOAD_PATH.unshift '/srv/whimsy/lib' require 'time' require 'wunderbar' require 'wunderbar/bootstrap' require 'whimsy/asf' require 'whimsy/asf/forms' require 'whimsy/asf/member-files' # See for nomination file parsing require 'whimsy/asf/wunderbar_updates' require 'whimsy/asf/meeting-util' require 'whimsy/asf/time-utils' require 'mail' MAILING_LIST = 'members@apache.org' def emit_form(title, prev_data) _whimsy_panel(title, style: 'panel-success') do _form.form_horizontal method: 'post' do field = 'availid' _whimsy_forms_input(label: 'Nominee availid', name: field, value: prev_data[field], helptext: 'Enter the availid of the committer you are nominating for ASF Membership' ) _whimsy_forms_input(label: 'Nominated by', name: 'nomby', readonly: true, value: $USER ) _whimsy_forms_input( label: 'Seconded by', name: 'secby', helptext: 'Optional comma-separated list of seconds; ONLY if you have confirmed with the seconds directly' ) field = 'statement' _whimsy_forms_input(label: 'Nomination Statement', name: field, rows: 10, value: prev_data[field], helptext: 'Explain why you believe this person would make a good ASF Member, and what projects/communities they work on at the ASF' ) _whimsy_forms_submitwrap( noicon: true, label: 'submit', name: 'submit', value: 'submit', helptext: 'Checkin this nomination and send email to members@' ) end end end # Validation as needed within the script # Returns: 'OK' or a text message describing the problem def validate_form(formdata: {}) uid = formdata['availid'].downcase return "You MUST provide a nomination statement for Candidate #{uid}; blank was provided!" if formdata['statement'].empty? chk = ASF::Person[uid]&.asf_member? chk.nil? and return "Invalid availid supplied: (#{uid})\n\nStatement:\n#{formdata['statement']}" # Allow renomination of Emeritus pubname = ASF::Person[uid].public_name chk && !chk.to_s.start_with?('Emeritus') and return "Your nominee #{pubname} (#{uid}) is already an ASF member!" already = ASF::MemberFiles.member_nominees return "Candidate (#{uid}) has already been nominated by #{already[uid]['Nominated by']}" if already.include? uid return 'OK' end # Handle submission (checkout user's apacheid.json, write form data, checkin file) # @return true if we think it succeeded; false in all other cases def process_form(formdata: {}, wunderbar: {}) _h3 "Transcript of update to nomination file #{ASF::MemberFiles::NOMINATED_MEMBERS}" entry = ASF::MemberFiles.make_member_nomination({ availid: formdata['availid'].downcase, nomby: formdata['nomby'], secby: formdata['secby'], statement: formdata['statement'] }) environ = Struct.new(:user, :password).new($USER, $PASSWORD) ASF::MemberFiles.update_member_nominees(environ, wunderbar, [entry], "+= #{formdata['availid'].downcase}") return true end # Send email to members@ with this nomination's data # Reports status to user in a _div def send_nomination_mail(formdata: {}) uid = formdata['availid'].downcase nomby = formdata['nomby'] public_name = ASF::Person.new(uid).public_name secby = formdata.fetch('secby', nil) secby.nil? || secby.empty? ? nomseconds = '' : nomseconds = "Nomination seconded by: #{secby}" mail_body = <<-MAILBODY This nomination for #{public_name} (#{uid}) as a New Member Candidate has been added: #{formdata['statement']} #{nomseconds} -- - #{ASF::Person[nomby].public_name} Email generated by Whimsy (#{File.basename(__FILE__)}) MAILBODY # See check_membernoms.cgi which parses this in list archives mailsubject = "[MEMBER NOMINATION] #{ASF::Person.new(uid).public_name} (#{uid})" ASF::Mail.configure mail = Mail.new do to MAILING_LIST bcc 'notifications@whimsical.apache.org' from "#{ASF::Person[nomby].public_name} <#{nomby}@apache.org>" subject mailsubject text_part do body mail_body end end begin mail.deliver! rescue StandardError => e _div.alert.alert_danger role: 'alert' do _p.strong "ERROR: email was NOT sent due to: #{e.message} #{e.backtrace[0]}" _p do _ "To: #{MAILING_LIST}" _br _ "Subject: #{mailsubject}" _br _ "#{mail_body}" end end return end _div.alert.alert_success role: 'alert' do _p "The following email was sent:" _p do _ "To: #{MAILING_LIST}" _br _ "Subject: #{mailsubject}" _br _ "#{mail_body}" end end return end # Produce HTML _html do _body? do # Countdown until nominations for current meeting close latest_meeting_dir = ASF::MeetingUtil.latest_meeting_dir timelines = ASF::MeetingUtil.get_timeline(latest_meeting_dir) t_now = Time.now.to_i t_end = Time.parse(timelines['nominations_close_iso']).to_i nomclosed = t_now > t_end _whimsy_body( title: PAGETITLE, subtitle: 'About This Script', related: { 'meeting.cgi' => 'Member Meeting FAQ and info', '/roster/committer/' => 'Lookup any committer availID', 'memberless-pmcs.cgi' => 'PMCs with no/few ASF Members', 'watch.cgi' => 'Watch list for potential Member candidates', 'check_membernoms.cgi' => 'Cross-check existing Member nominations', ASF::SVN.svnpath!('Meetings') => 'Official Meeting Agenda Directory' }, helpblock: -> { _b "For: #{timelines['meeting_type']} Meeting on: #{timelines['meeting_iso']}" _p do _ %Q{ Use this form to nominate any Committer the new ASF Membership election. It automatically adds a properly formatted nomination to the #{ASF::MemberFiles::NOMINATED_MEMBERS} file, and will then } _strong "send an email to the #{MAILING_LIST} list" _ ' from you with the nomination, ' _a 'as is tradition.', href: 'https://lists.apache.org/list?members@apache.org:2023-2:%22MEMBER%20NOMINATION%22' _ ' This form only supports adding new nominations of existing committers; to add seconds or comments, please use SVN. To nominate a non-committer, add them manually and use use n/a for the id. ' _a 'Lookup committer availIDs', href: '/roster/committer/' end } ) do if nomclosed _h1 'Nominations are now closed!' _p 'Sorry, no further nominations will be accepted for ballots at this meeting.' else _h3 "Nominations close in #{ASFTime.secs2text(t_end - t_now)} at #{Time.at(t_end).utc} for Meeting: #{timelines['meeting_iso']}" end _div id: 'nomination-form' do if _.post? unless nomclosed submission = _whimsy_params2formdata(params) valid = validate_form(formdata: submission) end if nomclosed _div.alert.alert_warning role: 'alert' do _p "Nominations have closed" end elsif valid == 'OK' if process_form(formdata: submission, wunderbar: _) _div.alert.alert_success role: 'alert' do _p "Your nomination was submitted to svn; now sending email to #{MAILING_LIST}." end mailval = send_nomination_mail(formdata: submission) _pre mailval else _div.alert.alert_danger role: 'alert' do _p do _span.strong "ERROR: Form data invalid in process_form(), update was NOT submitted!" _br _ "#{submission}" end end end else _div.alert.alert_danger role: 'alert' do _p do _span.strong "ERROR: Form data invalid in validate_form(), update was NOT submitted!" _br _p valid end end end else # if _.post? if nomclosed _p do _ 'Sorry, no further nominations will be accepted for ballots at this meeting.' _br _a 'See existing nominations.', href: '/members/check_membernoms.cgi' end else emit_form('Enter your New Member nomination', {}) end end end end end end