blob: 91afff2d899f976dfddadba13196a729db9b4b3c [file] [log] [blame]
class Committee
def self.serialize(id, env)
response = {}
pmc = ASF::Committee.find(id)
return unless pmc.pmc? # Only show PMCs
members = pmc.owners
committers = pmc.committers
return if members.empty? and committers.empty?
ASF::Committee.load_committee_info
# We'll be needing the mail data later
people = ASF::Person.preload(['cn', 'mail', 'asf-altEmail', 'githubUsername'], (members + committers).uniq)
lists = ASF::Mail.lists(true).select do |list, mode|
list =~ /^#{pmc.mail_list}\b/
end
comdev = ASF::SVN['comdev-foundation']
info = JSON.parse(File.read(File.join(comdev, 'projects.json')))[id]
image = ASF::SiteImage.find(id)
moderators = nil
modtime = nil
subscribers = nil # we get the counts only here
subtime = nil
pSubs = Array.new # private@ subscribers
unMatchedSubs = [] # unknown private@ subscribers
unMatchedSecSubs = [] # unknown security@ subscribers
currentUser = ASF::Person.find(env.user)
analysePrivateSubs = false # whether to show missing private@ subscriptions
if pmc.roster.include? env.user or currentUser.asf_member?
require 'whimsy/asf/mlist'
moderators, modtime = ASF::MLIST.list_moderators(pmc.mail_list)
subscribers, subtime = ASF::MLIST.list_subs(pmc.mail_list) # counts only, no archivers
analysePrivateSubs = currentUser.asf_member?
unless analysePrivateSubs # check for private moderator if not already allowed access
# TODO match using canonical emails
user_mail = currentUser.all_mail || []
pMods = moderators["private@#{pmc.mail_list}.apache.org"] || []
analysePrivateSubs = !(pMods & user_mail).empty?
end
if analysePrivateSubs
pSubs = ASF::MLIST.private_subscribers(pmc.mail_list)[0]||[]
unMatchedSubs=Set.new(pSubs) # init ready to remove matched mails
pSubs.map!{|m| m.downcase} # for matching
sSubs = ASF::MLIST.security_subscribers(pmc.mail_list)[0]||[]
unMatchedSecSubs=Set.new(sSubs) # init ready to remove matched mails
end
else
lists = lists.select {|list, mode| mode == 'public'}
end
roster = pmc.roster.dup # from committee-info
# ensure PMC members are all processed even they don't belong to the owner group
roster.each do |key, info|
info[:role] = 'PMC member'
next if pmc.ownerids.include?(key) # skip the rest (expensive) if person is in the owner group
person = ASF::Person[key]
if analysePrivateSubs
# Analyse the subscriptions, matching against canonicalised personal emails
allMail = person.all_mail.map{|m| ASF::Mail.to_canonical(m.downcase)}
# pSubs is already downcased
# TODO should it be canonicalised as well above?
roster[key]['notSubbed'] = (allMail & pSubs.map{|m| ASF::Mail.to_canonical(m)}).empty?
unMatchedSubs.delete_if {|k| allMail.include? ASF::Mail.to_canonical(k.downcase)}
unMatchedSecSubs.delete_if {|k| allMail.include? ASF::Mail.to_canonical(k.downcase)}
end
roster[key]['githubUsername'] = (person.attrs['githubUsername'] || []).join(', ')
end
members.each do |person| # process the owners
roster[person.id] ||= {
name: person.public_name,
role: 'PMC member' # TODO not strictly true, as CI is the canonical source
}
if analysePrivateSubs
# Analyse the subscriptions, matching against canonicalised personal emails
allMail = person.all_mail.map{|m| ASF::Mail.to_canonical(m.downcase)}
# pSubs is already downcased
# TODO should it be canonicalised as well above?
roster[person.id]['notSubbed'] = (allMail & pSubs.map{|m| ASF::Mail.to_canonical(m)}).empty?
unMatchedSubs.delete_if {|k| allMail.include? ASF::Mail.to_canonical(k.downcase)}
unMatchedSecSubs.delete_if {|k| allMail.include? ASF::Mail.to_canonical(k.downcase)}
end
roster[person.id]['ldap'] = true
roster[person.id]['githubUsername'] = (person.attrs['githubUsername'] || []).join(', ')
end
committers.each do |person|
roster[person.id] ||= {
name: person.public_name,
role: 'Committer'
}
roster[person.id]['githubUsername'] = (person.attrs['githubUsername'] || []).join(', ')
end
roster.each {|id, info| info[:member] = ASF::Person.find(id).asf_member?}
if pmc.chair and roster[pmc.chair.id]
roster[pmc.chair.id]['role'] = 'PMC 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=Hash.new
if moderators
moderators.each { |list,mods| mods.each {|m| nonASFmails[m]='' unless m.end_with? '@apache.org'} }
end
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
pmc_chair = false
if pmc.chair
pmcchairs = ASF::Service.find('pmc-chairs')
pmc_chair = pmcchairs.members.include? pmc.chair
end
response = {
id: id,
chair: pmc.chair && pmc.chair.id,
pmc_chair: pmc_chair,
display_name: pmc.display_name,
description: pmc.description,
schedule: pmc.schedule,
report: pmc.report,
site: pmc.site,
established: pmc.established,
ldap: members.map(&:id),
members: pmc.roster.keys,
committers: committers.map(&:id),
roster: roster,
mail: Hash[lists.sort],
moderators: moderators,
modtime: modtime,
subscribers: subscribers,
subtime: subtime,
nonASFmails: nonASFmails,
project_info: info,
image: image,
analysePrivateSubs: analysePrivateSubs,
unknownSubs: unknownSubs,
asfMembers: asfMembers,
unknownSecSubs: unknownSecSubs,
}
response
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
if not @people
# bulk loading the mail information makes things go faster
mail = Hash[ASF::Mail.list.group_by(&:last).
map {|person, list| [person, 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