This is an automated email from the ASF dual-hosted git repository. paul_a pushed a commit to branch dynamicwholist in repository https://gitbox.apache.org/repos/asf/cloudstack-www.git
commit 5f2123527fc45f66cb17256ad5f27a1da72abedf Author: PaulAngus <paul.an...@shapeblue.com> AuthorDate: Fri Aug 16 15:27:08 2019 +0100 Dynamic who page using Apache Phonebook javascript link to Whimsy --- source/javascripts/phonebook.js | 917 ++++++++++++++++++++++++++++++++++++++++ source/who.html.markdown | 222 +--------- 2 files changed, 934 insertions(+), 205 deletions(-) diff --git a/source/javascripts/phonebook.js b/source/javascripts/phonebook.js new file mode 100644 index 0000000..74a4b31 --- /dev/null +++ b/source/javascripts/phonebook.js @@ -0,0 +1,917 @@ +var pmcs = [] // array of PMC names (excludes non-PMC committees) +var people = {} // public_ldap_people.json +var ldapauth = {} // public_ldap_authgroups.json +// TODO don't rely on ldap_groups containing PMC :members groups +var ldapgroups = {} // public_ldap_groups.json +var ldapservices = {} // public_ldap_services.json +var ldapprojects = {} // public_ldap_projects.json + +var members = {} // copy of member-info.json +var committees = {} // copy of committee-info.json (plus details for 'member' dummy PMC) +var iclainfo = {} // copy of icla-info.json (committers only) +var podlings = {} // public_ldap_projects.json where podling is 'current' + +var info = {} // copies of json info + +// Constants for query types. +// Do NOT change the values once established, as they are part of the public API +// For example they may be used in projects.a.o and reporter.a.o +// The values are used for matching HTTP queries and linkifying lists (to generate a valid HTML link) + +var Q_USER = 'user' // search users +var Q_PROJECT = 'project' // search PMC names +var Q_UID = 'uid' // availid, exact match +var Q_PMC = 'pmc' // PMC, exact match +var Q_UNIX = 'unix' // LDAP group +var Q_CTTE = 'ctte' // LDAP group +var Q_SERVICE = 'service' // LDAP group +var Q_PODLING = 'podling' // podling (non-LDAP group) +var Q_AUTH = 'auth' // podling (LDAP auth group) + + +// Not intended for general use; may change at any time +var Q_DEBUG = 'debug' // print some debug info + +// compatibility shim for IE8 and other older browsers +if (!Date.now) { + Date.now = function() { + return new Date().getTime(); + } +} + +// This is faster than parseInt, and it's more obvious why it is being done +function toInt(number) { + return number | 0 // +} + +var fetchCount = 0; +// Fetch an array of URLs, each with their description and own callback plus a final callback +// Used to fetch everything before rendering a page that relies on multiple JSON sources. + + +function getAsyncJSONArray(urls, finalCallback) { + var obj = document.getElementById('progress'); + if (fetchCount == 0) { + fetchCount = urls.length; + } + + if (urls.length > 0) { + var a = urls.shift(); + var URL = a[0]; + var desc = a[1]; + var cb = a[2]; + var xmlHttp = null; + if (window.XMLHttpRequest) { + xmlHttp = new XMLHttpRequest(); + } else { + xmlHttp = new ActiveXObject("Microsoft.XMLHTTP"); + } + + if (obj) { + obj.innerHTML = "loading file #" + (fetchCount - urls.length) + " / " + fetchCount + "<br>" + desc + } + var start = Date.now() + xmlHttp.open("GET", URL, true); + xmlHttp.onreadystatechange = function(state) { + if (xmlHttp.readyState == 4) { + if (cb) { + if (xmlHttp.status == 200) { + elapsed = Date.now() - start + cb(JSON.parse(xmlHttp.responseText)); + // must be done after as cb creates the hash + info[desc]['elapsed'] = elapsed + } else { + cb({}); + alert("Error: '" + xmlHttp.statusText + "' while loading " + URL) + } + } + getAsyncJSONArray(urls, finalCallback); + } + } + xmlHttp.send(null); + } else { + if (obj) { + obj.innerHTML = "building page content..." + } + finalCallback(); + } +} + +// get list of projects on which uid is a committer +function getProjectCommittership(uid) { + var cl = [] + for (var i in ldapprojects) { + if (ldapprojects[i].pmc && ldapprojects[i].members.indexOf(uid) > -1) { + cl.push(i) + } + } + return cl +} + +//get list of projects on which uid is an owner (member karma) +function getProjectOwnership(uid) { + var cl = [] + for (var i in ldapprojects) { + if (ldapprojects[i].pmc && ldapprojects[i].owners.indexOf(uid) > -1) { + cl.push(i) + } + } + return cl +} + +// Get the roster from a json group +// returns: all the keys where the uid is [not] a member +function getRoster(json, uid, notIn) { + var cl = [] + for (var i in json) { + if (json[i].roster.indexOf(uid) > -1) { + if (typeof notIn === 'undefined') { + cl.push(i) + } else { + if (notIn.indexOf(i) == -1) { + cl.push(i) + } + } + } + } + return cl +} + +// get data from committee-info for a person +// return [list of pmcs, list of chairs] +function getCommitteeRoles(uid) { + var pl = [] + var ch = [] + for (var i in committees) { + // Only list actual PMCs + if (committees[i].pmc && uid in committees[i].roster) { + pl.push(i) + } + //var chair = committees[i].chair // might not be one (eg members) + //if (chair && uid in committees[i].chair) { + ch.push(i) + //} + } + return [pl, ch] +} + +function getCommitterName(uid) { + var noicla = { + 'andrei': '(Andrei Zmievski)', + 'pcs': '(Paul Sutton)', + 'rasmus': '(Rasmus Lerdorf)' + } + var name + if (uid in people) { // it's possible for a list to contain a uid that is not in people (e.g. andrei in member) + name = people[uid].name + } + if (!name) { + name = iclainfo[uid] + } + if (!name) { // try the backup specials + name = noicla[uid] + } + return name +} + +// Linkify list of group names by adding the appropriate ?type= href + +function linkifyList(type, names) { + var text = '' + var index, len, i + names.sort() + for (i = 0, len = names.length; i < len; ++i) { + if (i > 0) { + text = text + ", " + } + text = text + "<a href='?" + type + "=" + names[i] + "'>" + names[i] + "</a>" + } + return text +} + +// Linkify user ids + +function userList(ua) { + var text = '' + var index, len + ua.sort() + for (index = 0, len = ua.length; index < len; ++index) { + if (index > 0) { + text = text + ", " + } + text = text + hiliteMember(ua[index]) + } + return text +} + +//Linkify URLs + +function linkifyURLs(ua) { + var text = '' + var index, len + ua.sort() + for (index = 0, len = ua.length; index < len; ++index) { + if (index > 0) { + text = text + ", " + } + text = text + "<a target='_blank' href='" + ua[index] + "'>" + ua[index] + "</a>" + } + return text +} + +function showCommitter(obj, uid) { + var details = document.getElementById('details_committer_' + uid) + if (!details) { + details = document.createElement('p') + details.setAttribute("id", 'details_committer_' + uid) + var cl = getProjectCommittership(uid) // committer(in :members) of these LDAP PMC projects + var roles = getCommitteeRoles(uid) + var cttees = getProjectOwnership(uid) // member(in :owners) of these LDAP PMC projects + var pl = roles[0] // pmc membership + var ch = roles[1] // chairs + if (isNologin(uid)) { + details.innerHTML += "<b>Login is currently disabled</b><br/><br/>" + } + if (isMember(uid)) { + details.innerHTML += "<i>Foundation member</i><br/><br/>" + } + if (ch.length > 0) { + details.innerHTML += "<b>Chair of:</b> " + linkifyList(Q_PMC, ch) + if (!isChair(uid)) { + details.innerHTML += " <b>Not a member of pmc-chairs!</b>" + } + details.innerHTML += "<br/><br/>" + } + var purls = urls(uid) + if (purls.length > 0) { + details.innerHTML += "<b>Personal URLs:</b> " + linkifyURLs(purls) + "<br/><br/>" + } + if (cl.length > 0) { + details.innerHTML += "<b>Committer on:</b> " + linkifyList(Q_UNIX, cl) + "<br/><br/>" + } + var nc = [] // On PMC but not in LDAP unix + var nl = [] // On PMC but not in LDAP committee + var np = [] // Not in PMC even though in LDAP committee + var nu = [] // In LDAP committee but not in LDAP unix + var pn; + if (pl.length > 0) { + details.innerHTML += "<b>PMC member of:</b> " + linkifyList(Q_PMC, pl) + "<br/><br/>" + for (p in pl) { + pn = pl[p] + // There is an LDAP PMC group but the uid is not in the committer(:members) group + if (isProjectPMC(pn) && cl.indexOf(pn) < 0) { + nc.push(pn) + } + // There is an LDAP PMC group but the uid is not in the committee(:owners) group + if (isProjectPMC(pn) && cttees.indexOf(pn) < 0) { + nl.push(pn) + } + } + } + + if (cttees.length > 0) { + for (p in cttees) { + pn = cttees[p] + // name is a PMC but uid is not on the PMC + if (isPMC(pn) && pl.indexOf(pn) < 0) { + np.push(pn) + } + // name has LDAP project entry but uid is not in the committer (member) list + if (isProjectPMC(pn) && cl.indexOf(pn) < 0) { + nu.push(pn) + } + } + details.innerHTML += "<b>LDAP committee group membership:</b> " + linkifyList(Q_CTTE, cttees) + "<br/><br/>" + } + + var services = getRoster(ldapservices, uid) + if (services.length > 0) { + details.innerHTML += "<b>Service group membership:</b> " + linkifyList(Q_SERVICE, services) + "<br/><br/>" + } + var auths = getRoster(ldapauth, uid) + if (auths.length > 0) { + details.innerHTML += "<b>Auth group membership:</b> " + linkifyList(Q_AUTH, auths) + "<br/><br/>" + } + var pods = getRoster(podlings, uid) + if (pods.length > 0) { + details.innerHTML += "<b>Podling membership:</b> " + linkifyList(Q_PODLING, pods) + "<br/><br/>" + } + + // Note any discrepancies + if (np.length > 0) { + details.innerHTML += "<span class='error'>In LDAP committee group, but <b>not a PMC member</b>:</span> " + linkifyList(Q_CTTE, np) + "<br/><br/>" + } + if (nc.length > 0) { + details.innerHTML += "<span class='error'>On PMC, but not a member of the committer group:</span> " + linkifyList(Q_PMC, nc) + "<br/><br/>" + } + if (nl.length > 0) { + details.innerHTML += "<span class='error'>On PMC, but not member of the LDAP committee group:</span> " + linkifyList(Q_CTTE, nl) + "<br/><br/>" + } + if (nu.length > 0) { + details.innerHTML += "<span class='error'>In LDAP committee group but not a member of the committer group:</span> " + linkifyList(Q_UNIX, nu) + "<br/><br/>" + } + obj.appendChild(details) + } else { + obj.removeChild(details) + } +} + +function hoverCommitter(parent, uid) { + var div = document.getElementById('hoverbar') + + // If the datepicker object doesn't exist, spawn it + if (!div) { + div = document.createElement('div') + document.body.appendChild(div) + div.setAttribute("id", "hoverbar") + div.style.position = "fixed" + div.style.width = "400px" + div.style.background = "linear-gradient(to bottom, rgba(254,255,232,1) 0%,rgba(214,219,191,1) 100%)" + div.style.borderRadius = "4px" + div.style.border = "1px solid #333" + div.style.zIndex = "9999" + } + + // Reset the contents of the datepicker object + div.innerHTML = "" + + var bb = parent.getBoundingClientRect() + div.style.top = (bb.bottom + 24) + "px" + div.style.left = (bb.left + 32) + "px" + + if (uid) { + div.style.display = "block" + div.innerHTML = "<h4>" + getCommitterName(uid) + "</h4>" + var cl = getProjectCommittership(uid) + var roles = getCommitteeRoles(uid) + var pl = roles[0] + var ch = roles[1] + if (isMember(uid) == true) { + div.innerHTML += "<i>Foundation member</i><br/><br/>" + } + if (isNologin(uid)) { + div.innerHTML += "<b>Login is currently disabled</b><br/><br/>" + } + if (ch.length > 0) { + ch.sort() + div.innerHTML += "<b>Chair of:</b> " + ch.join(", ") + if (!isChair(uid)) { + div.innerHTML += " <b>Not a member of pmc-chairs!</b>" + } + div.innerHTML += "<br/><br/>" + } + if (cl.length > 0) { + cl.sort() + div.innerHTML += "<b>Committer on:</b> " + cl.join(", ") + "<br/><br/>" + } + var nc = [] + if (pl.length > 0) { + pl.sort() + div.innerHTML += "<b>PMC member of:</b> " + pl.join(", ") + "<br/><br/>" + for (p in pl) { + var pn = pl[p] + if (pn != 'member' && cl.indexOf(pn) < 0) { + nc.push(pn) + } + } + } + if (nc.length > 0) { + div.innerHTML += "<i>On PMC, but not a Committer on:</i> " + nc.join(", ") + "<br/><br/>" + } + + + } else { + div.style.display = "none" + } +} + +function isNologin(uid) { + return !(uid in people) || people[uid].noLogin +} + +function isMember(uid) { + return members['members'].indexOf(uid) > -1 +} + +function isChair(uid) { + return ldapservices['pmc-chairs'].roster.indexOf(uid) > -1 +} + +function urls(uid) { + return people[uid].urls || [] +} + +// Is a PMC according to committee-info +function isPMC(name) { + return pmcs.indexOf(name) >= 0; +} + +//Is a PMC according to ldap_projects +function isProjectPMC(name) { + return name in ldapprojects && ldapprojects[name].pmc +} + +function linkifyUid(uid) { + if (isNologin(uid)) { + return uid + } + return uid +} + +function hiliteMember(uid) { + if (isMember(uid)) { + return linkifyUid(uid) + } else { + return linkifyUid(uid) + } +} + +function getChair(uid) { + var chair = committees[uid].chair + if (chair) { + for (var x in chair) { + return chair[x].name + } + } + return null +} + +function showProject(obj, prj) { + var details = document.getElementById('details_project_' + prj) + if (!details) { + details = document.createElement('p') + details.setAttribute("id", 'details_project_' + prj) + var desc = committees[prj].description + if (!desc) { + desc = 'TBA (please ensure that <a href="http://www.apache.org/index.html#projects-list">the projects list</a> is updated)' + } + var chair = getChair(prj) + if (chair) { + details.innerHTML += "<b>Chair:</b> " + chair + "<br/><br/>" + } + var url = committees[prj].site + var cl + var clExists = false // Does the unix group exist? + try { + cl = ldapgroups[prj].roster.slice() + clExists = true + } catch (err) { // Allow for missing Unix group + cl = [] + } + var pl = [] + var pmc = committees[prj] + + var pmcnoctte = [] // on pmc but not in LDAP committee + var cttenopmc = [] // In LDAP ctte but not on PMC + var ldappmc = [] + var ctteeExists = false + if (isProjectPMC(prj)) { // may not exist, e.g. for 'member' PMC and if group has yet to be created + ldappmc = ldapprojects[prj].owners + ctteeExists = true + } + var pmcnounix = [] // on PMC but not in LDAP unix group + var cttenounix = [] // In LDAP ctte but not in LDAP unix + if (pmc) { + for (var c in pmc.roster) { + pl.push(c) + } + for (var i in ldappmc) { + if (!(ldappmc[i] in pmc.roster)) { + cttenopmc.push(ldappmc[i]) + } + } + } + cl.sort() + pl.sort() + + // Must use cl before it is re-used to hold the entries + if (clExists && prj != 'member') { // does not exist for 'member' PMC + for (var i in ldappmc) { + var id = ldappmc[i] + if (cl.indexOf(id) < 0) { // in LDAP cttee but not in LDAP unix + cttenounix.push(id) + } + } + } + + for (var i in pl) { + var id = pl[i] + pl[i] = "<tr><td><b>" + getCommitterName(pl[i]) + "</b></td><td>(" + hiliteMember(pl[i]) + ")</td></tr>" + if (clExists && cl.indexOf(id) < 0) { // On PMC but not in LDAP unix group + pmcnounix.push(id) + } + if (prj != 'member' && ldappmc && ldappmc.indexOf(id) < 0) { // in PMC but not in LDAP committee (does not apply to member) + pmcnoctte.push(id) + } + } + + for (var i in cl) { + cl[i] = "<tr><td><b>" + getCommitterName(cl[i]) + "</b></td><td>(" + hiliteMember(cl[i]) + ")</td></tr>" + } + + if (pl.length > 0) { + if (prj == 'member') { + details.innerHTML += "<b>ASF members</b><br><br><table>" + pl.join("\n") + "</table><br/>" + } else { + details.innerHTML += "<h4>PMC members (also in the committer group): " + pl.length + "</h4><table>" + pl.join("\n") + "</table><br/>" + } + } + + if (cl && cl.length > 0) { + details.innerHTML += "<h4>Committers: " + cl.length + "</h4><table>" + cl.join("\n") + "</table>" + if (podlings[prj]) { + details.innerHTML += "<span class='error'>WARNING: <a href='?podling=" + prj + "'>" + prj + " podling group</a> also exists - this can cause authentication issues</span><br/><br/>" + } + } else { + if (!clExists) { + details.innerHTML += "<span class='error'>LDAP unix group not present!</span><br/><br/>" + } + } + + if (pmcnoctte.length) { + if (ctteeExists) { + details.innerHTML += "<span class='error'>PMC members not in LDAP committee group:</span> " + userList(pmcnoctte) + "<br/><br/>" + } else { + details.innerHTML += "<span class='error'>LDAP committee group not present!</span><br/><br/>" + } + } + if (pmcnounix.length) { + if (prj == 'member') { + details.innerHTML += "<span class='error'>ASF members not in committers(unix) group:</span> " + userList(pmcnounix) + "<br/><br/>" + } else { + details.innerHTML += "<span class='error'>PMC members not in committers(unix) group:</span> " + userList(pmcnounix) + "<br/><br/>" + } + } + if (cttenounix.length) { + details.innerHTML += "<span class='error'>LDAP committee group members not in committers(unix) group:</span> " + userList(cttenounix) + "<br/><br/>" + } + if (cttenopmc.length) { + details.innerHTML += "<span class='error'>LDAP committee group members not on PMC:</span> " + userList(cttenopmc) + "<br/><br/>" + } + + + obj.appendChild(details) + } else { + obj.removeChild(details) + } +} + +// Generic group display function +// attr is either missing or 'owners' or 'members' +function showJsonRoster(obj, type, json, name, attr, checkUnix) { + var id = 'details_' + type + '_' + name + var details = document.getElementById(id) + if (!details) { + details = document.createElement('p') + details.setAttribute("id", id) + var podtype = json[name]['podling'] + if (podtype) { + details.innerHTML += "<b>podling:</b> " + podtype + "<br><br>" + } + var cl; + if (attr == 'owners') { + cl = json[name].owners.slice() + } else if (attr == 'members') { + cl = json[name].members.slice() + } else { + cl = json[name].roster.slice() + } + cl.sort() + for (var i in cl) { + var uid = cl[i] + cl[i] = "<tr><td onmouseover='hoverCommitter(this, \"" + uid + "\");' onmouseout='hoverCommitter(this, null);'><kbd>" + hiliteMember(uid) + "</kbd></td><td>" + getCommitterName(uid) + "</td>" + if (checkUnix) { // check against Unix group + if (ldapgroups[name]) { // make sure group exists! + if (ldapgroups[name].roster.indexOf(uid) > -1) { + cl[i] += "<td> </td>" + } else { + cl[i] += "<td> N.B. not found in corresponding Unix group</td>" + } + } + } + cl[i] += "</tr>" + } + + if (cl && cl.length > 0) { + details.innerHTML += "<b>Roster:</b><br><br><table>" + cl.join("\n") + "</table><br/>" + } + obj.appendChild(details) + } else { + obj.removeChild(details) + } +} + +// Show a single Service group +function showServiceRoster(obj, name) { + showJsonRoster(obj, 'service', ldapservices, name) +} + +// Show a single Auth group +function showAuthRoster(obj, name) { + showJsonRoster(obj, 'auth', ldapauth, name) +} + +function showPodlingRoster(obj, name) { + showJsonRoster(obj, 'podling', podlings, name) +} + +// Show an LDAP Unix group + +function showGroup(obj, name) { + showJsonRoster(obj, 'group', ldapgroups, name) +} + +// Show an LDAP Commiteee group + +function showCommittee(obj, name) { + showJsonRoster(obj, 'ctte', ldapprojects, name, 'owners', true) +} + +function searchProjects(keyword, open) { + var obj = document.getElementById('phonebook') + if (keyword != '') { + obj.innerHTML = "" + } else { + obj.innerHTML = '' + } + for (var i in pmcs) { + var pmc = pmcs[i] + if (pmc.search(keyword.toLowerCase()) != -1) { + var ppmc = committees[pmc].display_name + obj.innerHTML += "<div id='project_" + pmc + "' class='group'><h3>Apache " + ppmc + "</h3></div>" + if (open) { + showProject(document.getElementById('project_' + pmc), pmc) + } + } + } +} + +function searchService(keyword, open) { + var obj = document.getElementById('phonebook') + if (keyword != '') { + obj.innerHTML = "<h3>Search results:</h3><hr/>" + } else { + obj.innerHTML = '' + } + for (var srv in ldapservices) { + if (srv.search(keyword.toLowerCase()) != -1) { + obj.innerHTML += "<div id='service_" + srv + "' class='group'><h3 onclick=\"showServiceRoster(this.parentNode, '" + srv + "');\">" + srv + "</h3></div>" + if (open) { + showServiceRoster(document.getElementById('service_' + srv), srv) + } + } + } +} + +function searchAuth(keyword, open) { + var obj = document.getElementById('phonebook') + if (keyword != '') { + obj.innerHTML = "<h3>Search results:</h3><hr/>" + } else { + obj.innerHTML = '' + } + for (var auth in ldapauth) { + if (auth.search(keyword.toLowerCase()) != -1) { + obj.innerHTML += "<div id='auth_" + auth + "' class='group'><h3 onclick=\"showAuthRoster(this.parentNode, '" + auth + "');\">" + auth + "</h3></div>" + if (open) { + showauthRoster(document.getElementById('auth_' + auth), auth) + } + } + } +} + +// Show a single PMC + +function showPMC(pmc) { + var obj = document.getElementById('phonebook') + if (pmc in committees) { + var ppmc = committees[pmc].display_name + obj.innerHTML = "<div id='project_" + pmc + "' class='group'><h3 onclick=\"showProject(this.parentNode, '" + pmc + "');\">Apache " + ppmc + "</h3></div>" + showProject(document.getElementById('project_' + pmc), pmc) + } else { + obj.innerHTML = "<h3>Could not find PMC: '" + pmc + "'</h3>" + } +} + +// Show a single Unix Group + +function showUNIX(unix) { + var obj = document.getElementById('phonebook') + var id = 'group_' + unix + if (unix in ldapgroups) { + obj.innerHTML = "<div id='" + id + "' class='group'><h3 onclick=\"showGroup(this.parentNode, '" + unix + "');\">" + unix + " (LDAP unix group)</h3></div>" + showGroup(document.getElementById(id), unix) + } else { + obj.innerHTML = "<h3>Could not find unix group: '" + unix + "'</h3>" + } +} + +// Show a single Committee group + +function showCTTE(ctte) { + var obj = document.getElementById('phonebook') + var id = 'ctte_' + ctte + if (ctte in ldapprojects && ldapprojects[ctte].pmc) { + obj.innerHTML = "<div id='" + id + "' class='group'><h3 onclick=\"showCommittee(this.parentNode, '" + ctte + "');\">" + ctte + " (LDAP committee group)</h3></div>" + showCommittee(document.getElementById(id), ctte) + } else { + obj.innerHTML = "<h3>Could not find committee group: '" + ctte + "'</h3>" + } +} + +function showSVC(name) { + var obj = document.getElementById('phonebook') + var id = 'service_' + name + if (name in ldapservices) { + obj.innerHTML = "<div id='" + id + "' class='group'><h3 onclick=\"showServiceRoster(this.parentNode, '" + name + "');\">" + name + " (LDAP service group)</h3></div>" + showServiceRoster(document.getElementById(id), name) + } else { + obj.innerHTML = "<h3>Could not find the service group: '" + name + "'</h3>" + } +} + +function showAUTH(name) { + var obj = document.getElementById('phonebook') + var id = 'auth_' + name + if (name in ldapauth) { + obj.innerHTML = "<div id='" + id + "' class='group'><h3 onclick=\"showAuthRoster(this.parentNode, '" + name + "');\">" + name + " (LDAP auth group)</h3></div>" + showAuthRoster(document.getElementById(id), name) + } else { + obj.innerHTML = "<h3>Could not find the auth group: '" + name + "'</h3>" + } +} + +function showPOD(name) { + var obj = document.getElementById('phonebook') + var id = 'podling_' + name + if (name in podlings) { + obj.innerHTML = "<div id='" + id + "' class='group'><h3 onclick=\"showPodlingRoster(this.parentNode, '" + name + "');\">" + name + " (podling)</h3></div>" + showPodlingRoster(document.getElementById(id), name) + } else { + obj.innerHTML = "<h3>Could not find the podling: '" + name + "'</h3>" + } +} + +function searchPodlings(keyword, open) { + var obj = document.getElementById('phonebook') + obj.innerHTML = "<h3>Search results:</h3><hr/>" + for (var name in podlings) { + if (name.search(keyword.toLowerCase()) != -1) { + var id = 'podling_' + name + obj.innerHTML += "<div id='" + id + "' class='group'><h3 onclick=\"showPodlingRoster(this.parentNode, '" + name + "');\">" + name + " (podling)</h3></div>" + } + } +} + +function showDBG(name) { + var obj = document.getElementById('phonebook') + if (name == 'info') { + obj.innerHTML = "<h3>info</h3>" + obj.innerHTML += "<pre>" + JSON.stringify(info, null, 1) + "</pre>" + + } else { + obj.innerHTML = "<h3>Unknown debug name: '" + name + "'</h3>" + } +} + +// Show a single User + +function showUid(uid) { + var obj = document.getElementById('phonebook') + if (uid in people) { + var name = getCommitterName(uid) + obj.innerHTML = "<div class='group' id='committer_" + uid + "'><h4 onclick=\"showCommitter(this.parentNode, '" + uid + "');\">" + name + " (<kbd>" + uid + "</kbd>)</h4></div>" + showCommitter(document.getElementById('committer_' + uid), uid) + } else { + obj.innerHTML = "<h3>Could not find user id: '" + uid + "'</h3>" + } +} + +function showError(error) { + var obj = document.getElementById('phonebook') + if (typeof(error) === 'string') { + obj.innerHTML = "<h3>Error detected</h3>" + obj.innerHTML += error + } else { // assume it's an error object + obj.innerHTML = "<h3>Javascript Error detected</h3>" + obj.innerHTML += "<hr/>" + obj.innerHTML += "<pre>" + error.message + "</pre>" + obj.innerHTML += "<pre>" + error.stack + "</pre>" + obj.innerHTML += "<hr/>" + } +} + +function searchCommitters(keyword, open) { + if (keyword.length < 2) { + return + } + var n = 0 + var obj = document.getElementById('phonebook') + obj.innerHTML = "" + for (var uid in people) { + if (!people[uid].noLogin) { // don't display disabled logins + var name = getCommitterName(uid) + if (uid.search(keyword.toLowerCase()) != -1 || name.toLowerCase().search(keyword.toLowerCase()) != -1) { + n++ + if (n > 50) { + return; + } + obj.innerHTML += "<div class='group' id='committer_" + uid + "'><h4 onclick=\"showCommitter(this.parentNode, '" + uid + "');\">" + name + + " (<kbd>" + uid + "</kbd>) <a title='Link to committer details' href='phonebook.html?uid=" + uid + "'>•</a></h4></div>" + if (open) { + showCommitter(document.getElementById('committer_' + uid), uid) + } + } + } + } +} + +function saveInfo(json, name) { + info[name] = {} + try { + info[name]['lastTimestamp'] = json.lastTimestamp + } catch (err) {} // ignored + try { + info[name]['lastCreateTimestamp'] = json.lastCreateTimestamp + } catch (err) {} // ignored + try { + info[name]['last_updated'] = json.last_updated + } catch (err) {} // ignored +} + +function preRender() { + getAsyncJSONArray([ + ['https://whimsy.apache.org/public/public_ldap_projects.json', "projects", function(json) { + ldapprojects = json.projects; + saveInfo(json, 'projects'); + }], + ['https://whimsy.apache.org/public/member-info.json', "members", function(json) { + members = json; + saveInfo(json, 'members'); + }], + ['https://whimsy.apache.org/public/public_ldap_people.json', "people", function(json) { + people = json.people; + saveInfo(json, 'people'); + }], + ['https://whimsy.apache.org/public/committee-info.json', "committees", function(json) { + committees = json.committees; + saveInfo(json, 'committees'); + }], + ['https://whimsy.apache.org/public/icla-info.json', "iclainfo", function(json) { + iclainfo = json.committers; + saveInfo(json, 'iclainfo'); + }], + ['https://whimsy.apache.org/public/public_ldap_groups.json', "ldapgroups", function(json) { + ldapgroups = json.groups; + saveInfo(json, 'ldapgroups'); + }], + ['https://whimsy.apache.org/public/public_ldap_authgroups.json', "ldapauth", function(json) { + ldapauth = json.auth; + saveInfo(json, 'ldapauth'); + }], + ['https://whimsy.apache.org/public/public_ldap_services.json', "services", function(json) { + ldapservices = json.services; + saveInfo(json, 'services'); + }], + ], + allDone); +} + +// Called when all the async GETs have been completed + +function allDone() { + try { + pmcs = [] + for (var k in committees) { // actual committees, not LDAP committee groups + if (committees[k].pmc) { // skip non-PMCs + pmcs.push(k) + } + } + for (var g in ldapprojects) { + // get podlings from projects + if (ldapprojects[g]['podling'] == 'current') { + podlings[g] = {} + podlings[g].roster = ldapprojects[g].members + } + } + pmcs.push('member') + pmcs.sort() + var mMap = {} + for (var m in members.members) { + mMap[members.members[m]] = {} + } + // copy across the members info + committees['member'] = { + 'roster': mMap, + 'display_name': 'Foundation Members', + 'description': "Current ASF members (Committers == those with member karma)", + 'site': 'http://www.apache.org/foundation/' + } + + // Match ?type=name + searchProjects('cloudstack', true) + + } catch (error) { + showError(error) + } +} \ No newline at end of file diff --git a/source/who.html.markdown b/source/who.html.markdown index bc10c5e..e44b827 100644 --- a/source/who.html.markdown +++ b/source/who.html.markdown @@ -15,208 +15,20 @@ title: Apache CloudStack Project Membership </div> </div> - -This page includes the Apache CloudStack Project Management Committee (PMC) members and committers. - -## PMC - -Active Project Management Committee contains (in alphabetical order of their usernames): - -{:.table-bordered} - - -| Username | Name | -|----------|------| -|akarasulu|Alex Karasulu| -|alena1108|Alena Prokharchyk| -|animesh|Animesh| -|andrijapanic|Andrija Panic| -|boris|Boris Schrijver| -|bstoyanov|Boris Stoyanov| -|chipchilders|Chip Childers| -|chiradeep|Chiradeep Vittal| -|dahn|Daan| -|ekho|Wilder Rodrigues| -|erikw|Erik Weber| -|giles|Giles Sirett| -|gochiba|Go Chiba| -|gabriel|Gabriel Beims Bräscher| -|hogstrom|Matt Richard Hogstrom| -|hugo|Hugo Trippaers| -|ilya|Ilya Musayev| -|jburwell|John Burwell| -|jlk|John Kinsella| -|jzb|Joe Brockmeier| -|karenv|Karen Vuong| -|ke4qqq|David Nalley| -|kluge|Kevin Kluge| -|milamber|Bruno Demion| -|mlsorensen|Marcus Sorensen| -|mnour|Mohammad Nour El-Din| -|mrhinkle|Mark R. Hinkle| -|mtutkowski|Mike Tutkowski| -|nslater|Naomi Slater| -|nux|Nux| -|nathanejohnson|Nathan Johnson| -|paul_a|Paul Angus| -|pdion891|Pierre-Luc Dion| -|rafael|Rafael Weingärtner| -|rajani|Rajani Karuturi| -|remi|Remi Bergsma| -|resmo|Rene Moser| -|rohit|Rohit Yadav| -|sebgoa|Sebastien Goasguen| -|sweller|Simon Weller| -|swill|Will Stevens| -|syed|Syed Ahmed| -|svogel|Sven Vogel| -|tsp|Prasanna| -|weizhou|Wei Zhou| -|widodh|Wido den Hollander| -|willchan|William Chan| - - - -## Emeritus PMC Members - -PMC members who are no longer active include: - -+ Alex Huang (ahuang) -+ Disheng Su (edison) -+ Ian Duffy (duffy) -+ Olivier Lamy (olamy) - - - -## Committers - -Active list of committers (in alphabetical order of their usernames): - -{:.table-bordered} - - -| Username | Name | -|----------|------| -|ahmad|Ahmad Emneina| -|akarasulu|Alex Karasulu| -|alena1108|Alena Prokharchyk| -|amoghvk|Amogh Vasekar| -|andrijapanic|Andrija Panic| -|animesh|Animesh| -|anthonyxu|Anthony Xu| -|aprateek|Abhinandan Prateek| -|bfederle|Brian Federle| -|boris|Boris Schrijver| -|brett|Brett Porter| -|bstoyanov|Boris Stoyanov| -|chipchilders|Chip Childers| -|chiradeep|Chiradeep Vittal| -|claytonweise|Clayton Weise| -|csuich2|Chris Suich| -|dahn|Daan| -|darren|Darren Shepherd| -|dcahill|Dave Cahill| -|demetriust|Demetrius Tsitrelis| -|devdeep|Devdeep Singh| -|dkonrad|Dennis Konrad| -|dsonstebo|Dag Sonstebo| -|ekho|Wilder Rodrigues| -|erikw|Erik Weber| -|fmaximus|Frank Maximus| -|frankzhang|Xin Zhang| -|gabriel|Gabriel Beims Bräscher| -|gaurav|Gaurav Nandkumar Aradhye| -|gavinlee|Gavin Lee| -|giles|Giles Sirett| -|girish|Girish Prabhakar Shilamkar| -|gochiba|Go Chiba| -|haeena|Toshiaki Hatano| -|harikrishna|Harikrishna Patnala| -|higster|Geoff Higginbottom| -|hogstrom|Matt Richard Hogstrom| -|hugo|Hugo Trippaers| -|ilya|Ilya Musayev| -|isaacchiang|Isaac Chiang| -|jayapal|Jayapal| -|jbausewein|Jason Bausewein| -|jburwell|John Burwell| -|jessicawang|Jessica Wang| -|jim|Jim Jagielski| -|jlk|John Kinsella| -|jtomechak|Jessica Tomechak| -|jzb|Joe Brockmeier| -|karenv|Karen Vuong| -|kawai|Hiroaki Kawai| -|kdamage|Kelcey Damage| -|ke4qqq|David Nalley| -|kelveny|Kelven Yang| -|kirk|Kirk Kosinski| -|kishan|Kishan| -|kluge|Kevin Kluge| -|kocka|Laszlo Hornyak| -|koushik|Koushik Das| -|likithas|Likitha Shetty| -|marcaurele|Marc-Aurèle Brothier| -|mchen|Min Chen| -|mice|Mice Xia| -|milamber|Bruno Demion| -|mlsorensen|Marcus Sorensen| -|mnour|Mohammad Nour El-Din| -|mrhinkle|Mark R. Hinkle| -|msinhore|Marco Sinhoreli| -|mtutkowski|Mike Tutkowski| -|muralireddy|Murali Mohan Reddy| -|nathanejohnson|Nathan Johnson| -|nitin|Nitin| -|noa|Noa Resare| -|nslater|Naomi Slater| -|nux|Nux| -|nvazquez|Nicolás Vázquez| -|olgasmola|Olga Smola| -|paul_a|Paul Angus| -|pdion891|Pierre-Luc Dion| -|pnguyen|Phong Nguyen| -|prachidamle|Prachi Damle| -|pranavs|Pranav Saxena| -|pyr|Pierre-Yves Ritschard| -|radhika|Radhika Nair| -|rafael|Rafael Weingärtner| -|rajani|Rajani Karuturi| -|rajeshbattala|Rajesh Battala| -|remi|Remi Bergsma| -|resmo|Rene Moser| -|rohit|Rohit Yadav| -|sailajamada|Sailaja Mada| -|saksham|Saksham Srivastava| -|sangeethah|Sangeetha Hariharan| -|sanjaytripathi|Sanjay Tripathi| -|sanjeev|Sanjeev Neelarapu| -|santhoshedukulla|Santhosh| -|sateesh|Sateesh Chodapuneedi| -|schhen|Sonny Heng Chhen| -|sebgoa|Sebastien Goasguen| -|serg38|Sergey Levitskiy| -|slriv|Sam Robertson| -|snuf|Funs Kessen| -|sowmya|Sowmya Krishnan| -|sudhap|Sudhap| -|svogel|Sven Vogel| -|swamy|Venkata Swamy| -|sweller|Simon Weller| -|swill|Will Stevens| -|syed|Syed Ahmed| -|talluri|Srikanteswararao Talluri| -|tsp|Prasanna| -|tuna|Anh Tu Nguyen| -|vijayendrabvs|Vijayendra Bhamidipati| -|weizhou|Wei Zhou| -|widodh|Wido den Hollander| -|willchan|William Chan| -|yasker|Sheng Yang| -|ynojima|Yoshikazu Nojima| - - - -## Emeritus Committers - -Committers who are no longer active include: + <div id="phonebook"> + + <p> + Loading data, please wait...<br/> + <img src="images/loader.gif" alt=""/> + </p> + <p id="progress"></p> + <noscript> + <h2>Notice!</h2> + <p> + This site relies heavily on JavaScript. + Please enable it or get a browser that supports it. + </p> + </noscript> + + </div> + <script type='text/javascript' src="javascripts/phonebook.js" onload="preRender()"></script>