www/roster/models/nonpmc.rb (175 lines of code) (raw):
class NonPMC
def self.serialize(id, env)
cttee = ASF::Committee.find(id)
return unless cttee.nonpmc?
hasLDAP = !ASF::Project[cttee.name].nil? # TODO this check perhaps belongs in the library code
members = cttee.owners
committers = cttee.committers
# Hack to fix unusual mail_list values e.g. press@apache.org
mail_list = cttee.mail_list.sub(/@apache\.org/,'')
mail_list = 'legal' if mail_list =~ /^legal-/ && cttee.name != 'dataprivacy'
mail_list = 'fundraising' if mail_list =~ /^fundraising-/
ASF::Committee.load_committee_info
# We'll be needing the mail data later
ASF::Person.preload(['cn', 'mail', 'asf-altEmail', 'githubUsername'], (members + committers).uniq)
image = ASF::SiteImage.find(id)
moderators = nil
modtime = nil
subscribers = nil # we get the counts only here
subtime = nil
pSubs = [] # private@ subscribers
unMatchedSubs = [] # unknown private@ subscribers
unMatchedSecSubs = [] # unknown security@ subscribers
currentUser = ASF::Person.find(env.user)
# Might make sense for non-PMCs - remove the code later if not
analysePrivateSubs = false # whether to show missing private@ subscriptions
if cttee.roster.include? env.user or currentUser.asf_member?
require 'whimsy/asf/mlist'
moderators, modtime = ASF::MLIST.list_moderators(mail_list)
subscribers, subtime = ASF::MLIST.list_subs(mail_list) # counts only, no archivers
if mail_list == 'press' # SPECIAL
%w{markpub announce}.each do |alt_list|
mods, _ = ASF::MLIST.list_moderators(alt_list)
moderators.merge! mods
subs, _ = ASF::MLIST.list_subs(alt_list)
subscribers.merge! subs
end
end
# TODO: do any non-PMCs have private lists?
analysePrivateSubs = currentUser.asf_member?
unless analysePrivateSubs # check for private moderator if not already allowed access
user_mail = currentUser.all_mail || []
pMods = moderators["private@#{mail_list}.apache.org"] || []
analysePrivateSubs = !(pMods & user_mail).empty?
end
if analysePrivateSubs
pSubs = ASF::MLIST.private_subscribers(mail_list)[0]||[]
unMatchedSubs=Set.new(pSubs) # init ready to remove matched mails
pSubs.map!(&:downcase) # for matching
sSubs = ASF::MLIST.security_subscribers(mail_list)[0]||[]
unMatchedSecSubs = Set.new(sSubs) # init ready to remove matched mails
end
lists = ASF::MLIST.domain_lists(mail_list, true)
else
lists = ASF::MLIST.domain_lists(mail_list, false)
end
roster = ASF.dup(cttee.roster)
# if the roster is empty, then add the chair(s)
if roster.empty?
cttee.chairs.each do |ch|
roster[ch[:id]] = {name: ch[:name], date: 'unknown'} # it is used to flag CI data so must be true in JavaScript
end
end
cttee_members = roster.keys # get the potentially updated list
# now add the status info
roster.each {|_, info| info[:role] = 'Committee member'}
members.each do |person|
roster[person.id] ||= {
name: person.public_name,
role: 'Committee member'
}
if analysePrivateSubs
allMail = person.all_mail.map(&:downcase)
roster[person.id]['notSubbed'] = true if (allMail & pSubs).empty?
unMatchedSubs.delete_if {|k| allMail.include? k.downcase}
unMatchedSecSubs.delete_if {|k| allMail.include? k.downcase}
end
roster[person.id]['ldap'] = true
end
committers.each do |person|
roster[person.id] ||= {
name: person.public_name,
role: 'Committer'
}
end
roster.each do |k, v|
person = ASF::Person.find(k)
v[:member] = person.asf_member?
v['githubUsername'] = (person.attrs['githubUsername'] || []).join(', ')
end
if cttee.chair and roster[cttee.chair.id]
roster[cttee.chair.id]['role'] = 'Committee chair'
end
# separate out the known ASF members and extract any matching committer details
unknownSubs = [] # unknown private@ subscribers: not PMC or ASF
asfMembers = []
unknownSecSubs = [] # unknown security@ subscribers: not PMC or ASF
# Also look for non-ASF mod emails
nonASFmails = {}
moderators&.each { |_, mods| mods.each {|m| nonASFmails[m] = '' unless m.end_with? '@apache.org'} }
if unMatchedSubs.length > 0 or nonASFmails.length > 0 or unMatchedSecSubs.length > 0
load_emails # set up @people
unMatchedSubs.each { |addr|
who = nil
@people.each do |person|
if person[:mail].any? {|mail| mail.downcase == addr.downcase}
who = person
end
end
if who
if who[:member]
asfMembers << { addr: addr, person: who }
else
unknownSubs << { addr: addr, person: who }
end
else
unknownSubs << { addr: addr, person: nil }
end
}
nonASFmails.each {|k, v|
@people.each do |person|
if person[:mail].any? {|mail| ASF::Mail.to_canonical(mail.downcase) == ASF::Mail.to_canonical(k.downcase)}
nonASFmails[k] = person[:id]
end
end
}
unMatchedSecSubs.each { |addr|
who = nil
@people.each do |person|
if person[:mail].any? {|mail| mail.downcase == addr.downcase}
who = person
end
end
if who
unless who[:member]
unknownSecSubs << { addr: addr, person: who }
end
else
unknownSecSubs << { addr: addr, person: nil }
end
}
end
return {
id: id,
chair: cttee.chair&.id,
display_name: cttee.display_name,
description: cttee.description,
schedule: cttee.schedule,
report: cttee.report,
site: cttee.site,
established: cttee.established,
hasLDAP: hasLDAP,
ldap: members.map(&:id),
members: cttee_members,
committers: committers.map(&:id),
roster: roster,
mail: lists.sort.to_h,
moderators: moderators,
modtime: modtime,
subscribers: subscribers,
subtime: subtime,
nonASFmails: nonASFmails,
image: image,
analysePrivateSubs: analysePrivateSubs,
unknownSubs: unknownSubs,
asfMembers: asfMembers,
unknownSecSubs: unknownSecSubs,
}
end
private
def self.load_emails
# recompute index if the data is 5 minutes old or older
@people = nil if not @people_time or Time.now-@people_time >= 300
unless @people
# bulk loading the mail information makes things go faster
mail = ASF::Mail.list.group_by(&:last).transform_values {|list| list.map(&:first)}
# build a list of people, their public-names, and email addresses
@people = ASF::Person.list.map {|person|
result = {id: person.id, name: person.public_name, mail: mail[person]}
result[:member] = true if person.asf_member?
result
}
# cache
@people_time = Time.now
end
@people
end
end