www/members/subscriptions.cgi (167 lines of code) (raw):

#!/usr/bin/env ruby PAGETITLE = "Apache members@ Subscription Crosscheck" # Wvisible:members $LOAD_PATH.unshift '/srv/whimsy/lib' require 'wunderbar' require 'whimsy/asf' require 'whimsy/asf/mlist' require 'wunderbar/bootstrap' require 'wunderbar/jquery/stupidtable' subscribers, modtime = ASF::MLIST.members_subscribers(false) # excluding archivers _html do _body? do _whimsy_body( title: PAGETITLE, related: { '/roster/members' => 'Listing Of All Members', '/committers/subscribe' => 'Committers Self-Subscribe Tool', 'https://lists.apache.org' => 'Apache Ponymail List Archives' }, helpblock: -> { _p! do _ "This process starts with the list of subscribers (updated #{modtime}) to " _a 'members@apache.org', href: 'https://mail-search.apache.org/members/private-arch/members/' _br _ 'These are matched against ' _a 'members.txt', href: ASF::SVN.svnpath!('foundation', 'members.txt') _ ', ' _a 'iclas.txt', href: ASF::SVN.svnpath!('officers', 'iclas.txt') _ ', and ' _code 'ldapsearch mail' _ ' to attempt to match the email address to an Apache ID.' _br _ 'Those that are not found are listed as ' _code.text_danger '*missing*' _ '. Emeritus members are ' _em 'listed in italics' _ '. Non ASF members are ' _span.text_danger 'listed in red' _ '.' end _p! do _ 'The resulting list is then cross-checked against ' _code 'ldapsearch cn=member' _ '. Membership that is only listed in one of these two sources is also ' _span.text_danger 'listed in red' _ '.' end _p! do _ 'Separate tables below show ' _a 'Members not subscribed to the list', href: "#unsub" _ ', and ' _a 'Copyable list of Members not subscribed', href: "#unsublist" _ ', and ' _a 'Entries in LDAP but not members.txt', href: "#ldap" _ '.' end } ) do ldap = ASF.members members = ASF::Member.new.map {|id, text| ASF::Person.find(id)} ASF::Person.preload('cn', members) maillist = ASF::Mail.list subscriptions = [] subscribers.each do |line| person = maillist[line.downcase] person ||= maillist[line.downcase.sub(/[-+]\w+@/,'@')] # allow for trailing +- suffix if person id = person.id id = '*notinavail*' if id == 'notinavail' else person = ASF::Person.find('notinavail') id = '*missing*' end subscriptions << [id, person, line] end _table.table do _thead_ do _tr do _th 'id', data_sort: 'string' _th 'email', data_sort: 'string' _th 'name', data_sort: 'string' end end _tbody do subscriptions.sort.each do |id, person, email| _tr_ do if id.include? '*' _td.text_danger id elsif not person.asf_member? _td.text_danger id, title: 'Non Member', data_sort_value: '1' elsif person.asf_member? != true _td(data_sort_value: '2') {_em {_a id, href: "/roster/committer/#{id}", title: 'Emeritus'}} elsif not ldap.include? person _td(data_sort_value: '3') {_strong.text_danger id, title: 'Not in LDAP'} else _td {_a id, href: "/roster/committer/#{id}"} end _td email if id.include? '*' _td '' else _td person.public_name end end end end end missing = members - (subscriptions.map {|id,person,email| person}) missing.delete_if {|person| person.asf_member? != true} # remove emeritus unless missing.empty? _h3_.unsub! 'Not subscribed to the list' _table.table do _tr_ do _th 'id' _th 'name' end missing.sort_by(&:name).each do |person| id = person.id _tr do if not ldap.include? person _td {_strong.text_danger {_a id, href: "/roster/committer/#{id}", title: 'Not in LDAP'}} else _td {_a id, href: "/roster/committer/#{id}"} end if person.public_name _td person.public_name else _td.text_danger '*notinavail*' end end end end _h3_.unsublist! 'Handy List of Unsubscribed Emails' _p do missing.each do |person| _ "#{person.id}@apache.org, " end end end extras = ldap - members unless extras.empty? _h3_.ldap! 'In LDAP but not in members.txt' _table.table do _tr_ do _th 'id' _th 'name' end extras.sort.each do |person| _tr do _td person.id _td person.public_name end end end end _script %{ var table = $(".table").stupidtable(); table.on("aftertablesort", function (event, data) { var th = $(this).find("th"); th.find(".arrow").remove(); var dir = $.fn.stupidtable.dir; var arrow = data.direction === dir.ASC ? "&uarr;" : "&darr;"; th.eq(data.column).append('<span class="arrow">' + arrow +'</span>'); }); } end end end