зеркало из https://github.com/mozilla/phonebook.git
First round of massive refactoring
git-svn-id: http://svn.mozilla.org/projects/phonebook/trunk@50675 4eb1ac78-321c-0410-a911-ec516a8615a5
This commit is contained in:
Родитель
26709f8724
Коммит
fff2cad47f
|
@ -0,0 +1,42 @@
|
|||
<?php
|
||||
|
||||
$tree_config = array(
|
||||
"ldap" => array(
|
||||
"search_base" => "o=com, dc=mozilla",
|
||||
"search_filter" => "mail=*",
|
||||
"search_attributes" => array(
|
||||
"cn", "manager", "title", "mail", "employeeType"
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
function tree_view_process_entry($person) {
|
||||
return array(
|
||||
"title" => !empty($person["title"][0]) ? $person["title"][0] : null,
|
||||
"name" => !empty($person["cn"][0]) ? $person["cn"][0] : null,
|
||||
"disabled" => isset($person["employeetype"]) ?
|
||||
strpos($person["employeetype"][0], 'D') !== FALSE:
|
||||
FALSE
|
||||
);
|
||||
}
|
||||
|
||||
function tree_view_roots() {
|
||||
return array(
|
||||
"mitchell@mozilla.com", "lilly@mozilla.com"
|
||||
);
|
||||
}
|
||||
|
||||
function tree_view_item($email, $leaf=FALSE) {
|
||||
global $everyone;
|
||||
$email = htmlspecialchars($email);
|
||||
$id = str_replace('@', "-at-", $email);
|
||||
$name = htmlspecialchars($everyone[$email]["name"]);
|
||||
$title = htmlspecialchars($everyone[$email]["title"]);
|
||||
$leaf = $leaf ? " leaf" : '';
|
||||
$disabled = $everyone[$email]["disabled"] ? " disabled" : '';
|
||||
return "<li id=\"$id\" class=\"hr-node expanded$leaf$disabled\">".
|
||||
"<a href=\"#search/$email\" class=\"hr-link\">$name</a> ".
|
||||
"<span class=\"title\">$title</span>".
|
||||
"</li>";
|
||||
}
|
||||
|
|
@ -178,17 +178,16 @@ a:hover {
|
|||
/* Card view */
|
||||
|
||||
#results {
|
||||
overflow: auto;
|
||||
width: 62em;
|
||||
margin: 0 auto;
|
||||
margin: 0 1em 1em;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.vcard {
|
||||
display: block;
|
||||
text-align: left;
|
||||
display: inline-block;
|
||||
width: 31em;
|
||||
vertical-align: top;
|
||||
margin-top: 1em;
|
||||
margin: 1em 1em 0 1em;
|
||||
}
|
||||
.vcard .header,
|
||||
.vcard .body,
|
||||
|
@ -345,14 +344,6 @@ a:hover {
|
|||
/* Editing view */
|
||||
|
||||
form#edit {
|
||||
/*width: 85%;
|
||||
margin: 0 auto 1em auto;
|
||||
padding: 1em;
|
||||
background-color: #FFF;
|
||||
border-radius: 0.5em;
|
||||
-moz-border-radius: 0.5em;
|
||||
-webkit-border-radius: 0.5em;
|
||||
font-size: 0.75em;*/
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
|
|
2
edit.php
2
edit.php
|
@ -97,6 +97,6 @@ if (!empty($_POST)) {
|
|||
}
|
||||
|
||||
$user_data = clean_userdata($user_data);
|
||||
$managerlist = manager_list($ldapconn);
|
||||
$managerlist = everyone_list($ldapconn);
|
||||
|
||||
require_once 'templates/edit.php';
|
||||
|
|
148
face.php
148
face.php
|
@ -1,148 +0,0 @@
|
|||
<?php
|
||||
require_once('templates/header.php'); ?>
|
||||
<div id="results">
|
||||
</div>
|
||||
|
||||
<div id="overlay">
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
Function.prototype.lazify = function() {
|
||||
var cache = {};
|
||||
return this.wrap(function(original, x) {
|
||||
return cache[x] ? cache[x] : (cache[x] = original(x));
|
||||
});
|
||||
};
|
||||
|
||||
String.toInt = parseInt.methodize();
|
||||
|
||||
Number.emToPx = function(x) {
|
||||
var div = new Element("div").setStyle({
|
||||
position: "absolute", margin: "0", padding: "0", visibility: "none",
|
||||
width: x + "em", height: "9px", top: "-100000em", left: "-100000em"
|
||||
});
|
||||
$(document.body).insert(div);
|
||||
var px = div.getWidth();
|
||||
div.remove();
|
||||
return px;
|
||||
}.lazify();
|
||||
|
||||
$(document).observe("keypress", function(e) {
|
||||
if ((e.charCode || e.keyCode) == 47) { // KEY_SLASH
|
||||
$("text").focus(); e.stop();
|
||||
}
|
||||
});
|
||||
|
||||
$(document).observe("dom:loaded", function() {
|
||||
$("phonebook-search").addClassName("large");
|
||||
|
||||
var adjustLayout = function() {
|
||||
var img = $$("img.wall-photo");
|
||||
img = img.length ? img[0] : null;
|
||||
var width = 140;
|
||||
if (img) {
|
||||
width = img.width +
|
||||
$w("paddingLeft marginLeft paddingRight marginRight").map(function(x) {
|
||||
return img.getStyle(x).replace("px", '').toInt();
|
||||
}).inject(function(x, y) { return x + y; });
|
||||
} else {
|
||||
width += Number.emToPx(5);
|
||||
}
|
||||
var photos = 4;
|
||||
Event.observe(Prototype.resizeTarget, "resize", readjust);
|
||||
function readjust() {
|
||||
var c = Math.floor(document.viewport.getWidth() / width);
|
||||
console.log(c);
|
||||
if (photos != c && c != 0) {
|
||||
$("results").setStyle({width: ((photos = c) * width) + "px"});
|
||||
}
|
||||
}
|
||||
};
|
||||
adjustLayout();
|
||||
$("results").setStyle({width: "880px"});
|
||||
$("menu").down("a.wall").addClassName("selected");
|
||||
|
||||
$(document).observe("hash:changed", function(e) {
|
||||
$("text").value = e.memo.hash.replace("search/", '');
|
||||
startSearch();
|
||||
});
|
||||
|
||||
$("phonebook-search").observe("submit", function(e) {
|
||||
window.location.hash = "#search/" + $F("text");
|
||||
e.stop();
|
||||
});
|
||||
|
||||
$("overlay").observe("click", function(e) {
|
||||
if (e.element() == this) {
|
||||
$(document.body).removeClassName("lightbox");
|
||||
}
|
||||
});
|
||||
Event.observe(Prototype.resizeTarget, "resize", function() {
|
||||
$$("div.vcard").invoke("verticallyCenter");
|
||||
});
|
||||
|
||||
var stoppedResizing = false;
|
||||
if (window.location.hash.startsWith("#search/")) {
|
||||
$(document).fire("hash:changed", {hash: window.location.hash.substring(1)});
|
||||
} else {
|
||||
$("header").verticallyCenter();
|
||||
Event.observe(Prototype.resizeTarget, "resize", centralize);
|
||||
$("text").focus();
|
||||
}
|
||||
|
||||
function centralize() {
|
||||
if (!arguments.callee.stop) {
|
||||
$("header").verticallyCenter();
|
||||
}
|
||||
}
|
||||
|
||||
function startSearch() {
|
||||
if (!stoppedResizing) {
|
||||
centralize.stop = true;
|
||||
stoppedResizing = true;
|
||||
$("header").stopVerticallyCentering();
|
||||
$("phonebook-search").removeClassName("large");
|
||||
}
|
||||
$("phonebook-search").request({
|
||||
parameters: {format: "json"},
|
||||
onSuccess: function onSuccess(r) {
|
||||
$("text").releaseFocus();
|
||||
$("results").update('');
|
||||
r.responseText.evalJSON().each(function(person) {
|
||||
var container = new Element("div").addClassName("photo-frame");
|
||||
var face = new Element("img", {
|
||||
src: person.picture + "&type=thumb", "class": "wall-photo"
|
||||
}).observe("click", function() { showPerson(person.dn); });
|
||||
var name = new Element("span").update(person.cn);
|
||||
container.insert(name).insert(face);
|
||||
if (person.employeetype.indexOf("DISABLED") != -1) {
|
||||
container.addClassName("disabled");
|
||||
}
|
||||
$("results").insert(container);
|
||||
});
|
||||
adjustLayout();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function showPerson(dn) {
|
||||
$("phonebook-search").request({
|
||||
parameters: {format: "html", query: dn.dnToEmail()},
|
||||
onSuccess: function onSuccess(r) {
|
||||
$(document.body).addClassName("lightbox");
|
||||
var close = new Element("div").observe("click", function(e) {
|
||||
$(document.body).removeClassName("lightbox");
|
||||
}).addClassName("close-button").writeAttribute("title", "Close");
|
||||
$("overlay").update('').update(r.responseText).
|
||||
down("div.vcard").verticallyCenter().
|
||||
down("div.header").insert(close);
|
||||
$("overlay").down(".vcard p.manager a").observe("click", function() {
|
||||
$(document.body).removeClassName("lightbox");
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<?php require_once('templates/footer.php'); ?>
|
|
@ -0,0 +1,11 @@
|
|||
<?php
|
||||
require_once('templates/header.php'); ?>
|
||||
<div id="results">
|
||||
</div>
|
||||
|
||||
<div id="overlay">
|
||||
</div>
|
||||
|
||||
<script type="text/javascript" src="js/view-faces.js"></script>
|
||||
|
||||
<?php require_once('templates/footer.php'); ?>
|
|
@ -0,0 +1,92 @@
|
|||
<?php
|
||||
|
||||
function get_filters() {
|
||||
return array(
|
||||
"employeetype" => "employee_status",
|
||||
"manager" => "get_manager",
|
||||
"physicaldeliveryofficename" => "location_formatter",
|
||||
"mobile" => "mobile_normalizer",
|
||||
"im" => "mobile_normalizer",
|
||||
"description" => "wikilinks",
|
||||
"other" => "wikilinks"
|
||||
);
|
||||
}
|
||||
|
||||
function employee_status($status) {
|
||||
global $orgs, $emp_type;
|
||||
$current_org = $current_emp_type = "";
|
||||
if ($status != "") {
|
||||
$current_org = $status[0];
|
||||
$current_emp_type = $status[1];
|
||||
}
|
||||
if ($status == "DISABLED") {
|
||||
return array('DISABLED');
|
||||
} else {
|
||||
if (array_key_exists($current_org, $orgs) &&
|
||||
array_key_exists($current_emp_type, $emp_type)) {
|
||||
return array($orgs[$current_org], $emp_type[$current_emp_type]);
|
||||
} else {
|
||||
return array('Unknown');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function get_manager($manager_dn) {
|
||||
global $ldapconn, $memcache_on, $memcache;
|
||||
if ($memcache_on && ($manager = $memcache->get($manager_dn))) {
|
||||
return $manager;
|
||||
}
|
||||
$manager_search = @ldap_search($ldapconn, $manager_dn, '(mail=*)', array('cn','mail'));
|
||||
if (ldap_errno($ldapconn) == 32) { // No manager found
|
||||
return null;
|
||||
}
|
||||
if ($manager_search) {
|
||||
$entry = ldap_first_entry($ldapconn, $manager_search);
|
||||
if ($entry) {
|
||||
$attrs = ldap_get_attributes($ldapconn, $entry);
|
||||
$manager = array(
|
||||
"cn" => $attrs['cn'][0],
|
||||
"dn" => $manager_dn
|
||||
);
|
||||
} else {
|
||||
$manager = null;
|
||||
}
|
||||
if ($memcache_on) {
|
||||
$memcache->set($manager_dn, $manager);
|
||||
}
|
||||
return $manager;
|
||||
}
|
||||
}
|
||||
|
||||
function location_formatter($location) {
|
||||
return str_replace(":::", "/", $location);
|
||||
}
|
||||
|
||||
function mobile_normalizer($m) {
|
||||
if (!is_array($m)) {
|
||||
$m = array($m);
|
||||
}
|
||||
return array_map("wikilinks", $m);
|
||||
}
|
||||
|
||||
function wikilinks($string) {
|
||||
$matches = array();
|
||||
$string = nl2br(htmlspecialchars($string));
|
||||
if (preg_match_all('/\[(.+?)(?:\s(.+?))?\]/', $string, $matches)) {
|
||||
foreach ($matches[1] as $key => $value) {
|
||||
if (!empty($matches[2][$key])) {
|
||||
$title = $matches[2][$key];
|
||||
} else {
|
||||
$title = $value;
|
||||
}
|
||||
$string = str_replace(
|
||||
$matches[0][$key],
|
||||
'<a href="'. $value .'">'. $title .'</a>',
|
||||
$string
|
||||
);
|
||||
}
|
||||
}
|
||||
return $string;
|
||||
}
|
||||
|
||||
|
|
@ -57,83 +57,6 @@ function search_users($ldapconn, $search) {
|
|||
return ldap_get_entries($ldapconn, $search);
|
||||
}
|
||||
|
||||
function employee_status($status) {
|
||||
global $orgs, $emp_type;
|
||||
$current_org = $current_emp_type = "";
|
||||
if ($status != "") {
|
||||
$current_org = $status[0];
|
||||
$current_emp_type = $status[1];
|
||||
}
|
||||
if ($status == "DISABLED") {
|
||||
return array('DISABLED');
|
||||
} else {
|
||||
if (array_key_exists($current_org, $orgs) &&
|
||||
array_key_exists($current_emp_type, $emp_type)) {
|
||||
return array($orgs[$current_org], $emp_type[$current_emp_type]);
|
||||
} else {
|
||||
return array('Unknown');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function get_manager($manager_dn) {
|
||||
global $ldapconn, $memcache_on, $memcache;
|
||||
if ($memcache_on && ($manager = $memcache->get($manager_dn))) {
|
||||
return $manager;
|
||||
}
|
||||
$manager_search = @ldap_search($ldapconn, $manager_dn, '(mail=*)', array('cn','mail'));
|
||||
if (ldap_errno($ldapconn) == 32) { // No manager found
|
||||
return null;
|
||||
}
|
||||
if ($manager_search) {
|
||||
$entry = ldap_first_entry($ldapconn, $manager_search);
|
||||
if ($entry) {
|
||||
$attrs = ldap_get_attributes($ldapconn, $entry);
|
||||
$manager = array(
|
||||
"cn" => $attrs['cn'][0],
|
||||
"dn" => $manager_dn
|
||||
);
|
||||
} else {
|
||||
$manager = null;
|
||||
}
|
||||
if ($memcache_on) {
|
||||
$memcache->set($manager_dn, $manager);
|
||||
}
|
||||
return $manager;
|
||||
}
|
||||
}
|
||||
|
||||
function wikilinks($string) {
|
||||
$matches = array();
|
||||
$string = nl2br(htmlspecialchars($string));
|
||||
if (preg_match_all('/\[(.+?)(?:\s(.+?))?\]/', $string, $matches)) {
|
||||
foreach ($matches[1] as $key => $value) {
|
||||
if (!empty($matches[2][$key])) {
|
||||
$title = $matches[2][$key];
|
||||
} else {
|
||||
$title = $value;
|
||||
}
|
||||
$string = str_replace(
|
||||
$matches[0][$key],
|
||||
'<a href="'. $value .'">'. $title .'</a>',
|
||||
$string
|
||||
);
|
||||
}
|
||||
}
|
||||
return $string;
|
||||
}
|
||||
|
||||
function location_formatter($location) {
|
||||
return str_replace(":::", "/", $location);
|
||||
}
|
||||
|
||||
function mobile_normalizer($m) {
|
||||
if (!is_array($m)) {
|
||||
$m = array($m);
|
||||
}
|
||||
return array_map("wikilinks", $m);
|
||||
}
|
||||
|
||||
// The logic here is that failure to find out who has permissions to edit
|
||||
// someone else's entry implies that you aren't one of them.
|
||||
function is_phonebook_admin($ldapconn, $mail) {
|
||||
|
@ -176,7 +99,7 @@ function clean_userdata($user_data) {
|
|||
return $user_data;
|
||||
}
|
||||
|
||||
function manager_list($ldapconn) {
|
||||
function everyone_list($ldapconn) {
|
||||
$search = ldap_search($ldapconn, 'o=com,dc=mozilla', 'objectClass=mozComPerson');
|
||||
ldap_sort($ldapconn, $search, 'cn');
|
||||
return ldap_get_entries($ldapconn, $search);
|
||||
|
|
191
index-json.html
191
index-json.html
|
@ -1,191 +0,0 @@
|
|||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="en-US" dir="ltr">
|
||||
<head>
|
||||
<title>Phonebook</title>
|
||||
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
|
||||
<script src="./js/prototype.js" type="text/javascript"></script>
|
||||
<!--<script src="./js/jquery-1.3.2.min.js" type="text/javascript"></script>-->
|
||||
<link rel="stylesheet" type="text/css" href="./css/style.css"/>
|
||||
<link rel="shortcut icon" type="image/x-icon" href="./favicon.ico" />
|
||||
|
||||
<script type='text/javascript'>
|
||||
// jQuery.noConflict();
|
||||
if (!window.console) {
|
||||
window.console = {};
|
||||
window.console.log = Prototype.emptyFunction;
|
||||
}
|
||||
|
||||
// Convert a dn to an email address
|
||||
String.prototype.toEmail = function String_toEmail() {
|
||||
var m = this.match(/mail=(\w+@mozilla.*),o=/);
|
||||
return (m ? m[1] : null);
|
||||
};
|
||||
|
||||
// Implement onhashchange support
|
||||
(function() {
|
||||
var hash = window.location.hash;
|
||||
var fire = function(str) {
|
||||
$(document).fire("hash:changed", { hash: str.substring(1) });
|
||||
};
|
||||
var pe = new PeriodicalExecuter(function() {
|
||||
var newHash = window.location.hash;
|
||||
if (newHash != hash) {
|
||||
fire(newHash);
|
||||
hash = newHash;
|
||||
}
|
||||
}, 1);
|
||||
})();
|
||||
|
||||
$(document).observe("keypress", function(e) {
|
||||
if ((e.charCode || e.keyCode) == 47) { // KEY_SLASH
|
||||
$("text").focus();
|
||||
e.stop();
|
||||
}
|
||||
});
|
||||
|
||||
$(document).observe("dom:loaded", function() {
|
||||
Prototype.Browser.WebKit && $("text").writeAttribute({
|
||||
type: "search", results: 5
|
||||
}) && $("search").hide(); // Just this one special treatment, Safari
|
||||
|
||||
var fillAndSearch = function() {
|
||||
$("text").value = window.location.hash.replace("#search/", '');
|
||||
startSearch();
|
||||
};
|
||||
|
||||
if (window.location.hash.startsWith("#search/")) {
|
||||
fillAndSearch();
|
||||
} else {
|
||||
$("text").focus();
|
||||
}
|
||||
|
||||
$(document).observe("hash:changed", fillAndSearch);
|
||||
|
||||
$("phonebook-search").observe("submit", function(e) {
|
||||
window.location.hash = "#search/" + $F("text");
|
||||
startSearch(); e.stop();
|
||||
});
|
||||
|
||||
var listify = function(a) {
|
||||
return $A(a).map(function(x) {
|
||||
return "<li>" + x + "</li>";
|
||||
}).join('');
|
||||
};
|
||||
var emailLinkify = function(s) {
|
||||
return '<li><a class="value" href="mailto:#{s}">#{s}</a></li>'.interpolate({s: s});
|
||||
};
|
||||
var processors = $H({
|
||||
"email": emailLinkify,
|
||||
"emailalias": emailLinkify.wrap(function(original, email) {
|
||||
email = Object.isString(email) ? [email] : $A(email);
|
||||
return email.map(original);
|
||||
}),
|
||||
"employeetype": function(l) { return l.join(", "); },
|
||||
"im": listify.wrap(function(original, im) {
|
||||
return im ? ('<ul class="im">' + original(im) + '</ul>') : '';
|
||||
}),
|
||||
"mobile": listify.wrap(function(original, list) {
|
||||
return '<ul class="telecommunications">' +
|
||||
original(list).replace(/<li>/, '<li class="tel">') + '</ul>';
|
||||
}),
|
||||
"description": function(s) {
|
||||
return '<div class="note">I work on: #{s}</div>'.interpolate({s: s});
|
||||
},
|
||||
"other": function(s) {
|
||||
return '<div class="other">' + s + '</div>';
|
||||
},
|
||||
"manager": function(m) {
|
||||
return '<p class="manager">Manager: <a href="#search/#{email}">#{name}</a></p>'.interpolate({
|
||||
email: m.dn.toEmail(),
|
||||
name: m.cn
|
||||
});
|
||||
},
|
||||
"telephonenumber": function(x) { return "ext. " + x; },
|
||||
"bugzillaemail": function(s) {
|
||||
return '<ul class="bugmail"><li><a title="Bugmail">#{s}</a></li></ul>'.interpolate({s: s});
|
||||
}
|
||||
});
|
||||
|
||||
function startSearch() {
|
||||
$("phonebook-search").request({onSuccess: function onSuccess(r) {
|
||||
$("results").update('');
|
||||
var results = r.responseText.evalJSON().each(function entryEach(e) {
|
||||
var code = process(e);
|
||||
var vcard = new Element("div", {"class":"vcard"}).update(code);
|
||||
$("results").insert(vcard);
|
||||
});
|
||||
$("text").blur();
|
||||
if (results.length == 0) {
|
||||
$("results").update(
|
||||
'<div style="text-align: center; margin-top: 5em;">' +
|
||||
'<img src="./img/ohnoes.jpg" />' +
|
||||
'<h2>OH NOES! No ones were foundz.</h2>' +
|
||||
'</div>'
|
||||
);
|
||||
}
|
||||
}});
|
||||
};
|
||||
|
||||
function process(entry) {
|
||||
entry.email = [entry.dn.toEmail()];
|
||||
entry.picture = '<a href="#{url}" class="photo" target="_blank"><img class="photo" src="#{url}&type=thumb" alt="Photo of #{name}" /></a>'.interpolate({
|
||||
url: entry.picture,
|
||||
name: entry.cn
|
||||
});
|
||||
console.log("JSON preprocessing done");
|
||||
|
||||
processors.each(function processorEach(pair) {
|
||||
if (entry[pair.key])
|
||||
entry[pair.key] = pair.value(entry[pair.key]);
|
||||
});
|
||||
console.log("JSON postprocessing done");
|
||||
return template(entry);
|
||||
}
|
||||
|
||||
function template(person) {
|
||||
return [
|
||||
'<div class="header"><h2 class="fn">#{cn}</h2></div>',
|
||||
'<div class="body">#{picture}',
|
||||
'<div class="employee">',
|
||||
'<p class="title">#{title}</p>',
|
||||
'<p class="employee-type">#{employeetype}</p>',
|
||||
'#{manager}',
|
||||
'</div>',
|
||||
'<ul class="adr"><li>#{telephonenumber} @ ',
|
||||
'<span class="locality">#{physicaldeliveryofficename}</span>',
|
||||
'</li></ul>',
|
||||
'#{mobile}',
|
||||
'<ul class="email">#{email}#{emailalias}</ul>',
|
||||
'#{bugzillaemail}',
|
||||
'#{im}',
|
||||
'#{description}',
|
||||
'#{other}',
|
||||
'</div><div class="footer"></div>'
|
||||
].join('').interpolate(person);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="header">
|
||||
<form action="search.php" method="get" id="phonebook-search">
|
||||
<h1>Phonebook</h1>
|
||||
<div id="search-region">
|
||||
<input type="text" name="query" id="text" />
|
||||
<input type="submit" value="Search" id="search" />
|
||||
</div>
|
||||
<ul id="menu">
|
||||
<li><a href="./#search/*">Everyone</a></li>
|
||||
<li><a href="https://intranet.mozilla.org/">Intranet</a></li>
|
||||
<li><a href="https://intranet.mozilla.org/OfficeLocations">Offices</a></li>
|
||||
<li class="edit"><a href="edit.php" id="edit-entry">Edit My Entry</a></li>
|
||||
</ul>
|
||||
</form>
|
||||
</div>
|
||||
<div id="results">
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
86
index.php
86
index.php
|
@ -6,89 +6,5 @@ require_once('templates/header.php');
|
|||
<div id="results">
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
Function.prototype.lazify = function() {
|
||||
var cache = {};
|
||||
return this.wrap(function(original, x) {
|
||||
return cache[x] ? cache[x] : (cache[x] = original(x));
|
||||
});
|
||||
};
|
||||
|
||||
Number.emToPx = function(x) {
|
||||
var div = new Element("div").setStyle({
|
||||
position: "absolute", margin: "0", padding: "0", visibility: "none",
|
||||
width: x + "em", height: "9px", top: "-100000em", left: "-100000em"
|
||||
});
|
||||
$(document.body).insert(div);
|
||||
var px = div.getWidth();
|
||||
div.remove();
|
||||
return px;
|
||||
}.lazify();
|
||||
|
||||
(function() {
|
||||
var cards = 2;
|
||||
Event.observe(Prototype.resizeTarget, "resize", function() {
|
||||
var c = Math.floor(document.viewport.getWidth() / Number.emToPx(1) / 31);
|
||||
if (cards != c && c != 0) {
|
||||
$("results").setStyle({width: ((cards = c) * 31) + "em"});
|
||||
}
|
||||
});
|
||||
})();
|
||||
|
||||
$(document).observe("keypress", function(e) {
|
||||
if ((e.charCode || e.keyCode) == 47) { // KEY_SLASH
|
||||
$("text").focus(); e.stop();
|
||||
}
|
||||
});
|
||||
|
||||
$(document).observe("dom:loaded", function() {
|
||||
$("menu").down("a.card").addClassName("selected");
|
||||
$(document).observe("hash:changed", function(e) {
|
||||
$("text").value = e.memo.hash.replace("search/", '');
|
||||
startSearch();
|
||||
});
|
||||
|
||||
$("phonebook-search").observe("submit", function(e) {
|
||||
e.stop();
|
||||
window.location.hash = "#search/" + $F("text");
|
||||
});
|
||||
|
||||
var stoppedResizing = false;
|
||||
if (window.location.hash.startsWith("#search/")) {
|
||||
$(document).fire("hash:changed", {hash: window.location.hash.substring(1)});
|
||||
} else {
|
||||
$("phonebook-search").addClassName("large");
|
||||
$("header").verticallyCenter();
|
||||
Event.observe(Prototype.resizeTarget, "resize", centralize);
|
||||
$("text").focus();
|
||||
}
|
||||
|
||||
function centralize() {
|
||||
if (!arguments.callee.stop) {
|
||||
$("header").verticallyCenter();
|
||||
}
|
||||
}
|
||||
|
||||
function startSearch() {
|
||||
if (!$F("text").strip()) {
|
||||
window.location = "./";
|
||||
}
|
||||
if (!stoppedResizing) {
|
||||
centralize.stop = true;
|
||||
stoppedResizing = true;
|
||||
$("header").stopVerticallyCentering();
|
||||
$("phonebook-search").removeClassName("large");
|
||||
}
|
||||
$("phonebook-search").request({onSuccess: function onSuccess(r) {
|
||||
$("results").update('').update(r.responseText ||
|
||||
'<div style="text-align: center; margin-top: 5em;">' +
|
||||
'<img src="./img/ohnoes.jpg" />' +
|
||||
'<h2>OH NOES! No ones were foundz.</h2>' +
|
||||
'</div>'
|
||||
);
|
||||
$("text").releaseFocus();
|
||||
}});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<script type="text/javascript" src="js/view-cards.js"></script>
|
||||
<?php require_once('templates/footer.php'); ?>
|
||||
|
|
|
@ -0,0 +1,232 @@
|
|||
|
||||
if (!window.console) {
|
||||
window.console = {log: Prototype.emptyFunction};
|
||||
}
|
||||
|
||||
// Implement onhashchange support
|
||||
(function() {
|
||||
var hash = window.location.hash;
|
||||
var fire = function(str) {
|
||||
$(document).fire("hash:changed", { hash: str.substring(1) });
|
||||
};
|
||||
var pe = new PeriodicalExecuter(function() {
|
||||
var newHash = window.location.hash;
|
||||
if (newHash != hash) {
|
||||
fire(newHash);
|
||||
hash = newHash;
|
||||
}
|
||||
}, 1);
|
||||
})();
|
||||
|
||||
Element.addMethods({
|
||||
verticallyCenter: function(element, percentage) {
|
||||
element = $(element);
|
||||
if (!element.retrieve("originalMarginTop")) {
|
||||
element.store("originalMarginTop", element.getStyle("marginTop"));
|
||||
}
|
||||
var offset = $(document).viewport.getHeight() - element.getHeight();
|
||||
return element.setStyle({marginTop: (offset * (percentage || 0.4)) + "px"});
|
||||
},
|
||||
|
||||
stopVerticallyCentering: function(element) {
|
||||
element = $(element);
|
||||
var original = element.getStorage().unset("originalMarginTop");
|
||||
return element.setStyle({marginTop: original});
|
||||
}
|
||||
});
|
||||
|
||||
// Force Gecko to redraw the blinking cursor
|
||||
Element.addMethods("input", {
|
||||
releaseFocus: function(element) {
|
||||
$w("blur focus blur").each(function(method) { $(element)[method](); });
|
||||
return element;
|
||||
}
|
||||
});
|
||||
|
||||
Prototype.resizeTarget = document.onresize ? document : window;
|
||||
|
||||
Array.prototype.find = function find(selector) {
|
||||
return this.filter(function(x) {
|
||||
return x.match ? x.match(selector) : false;
|
||||
});
|
||||
};
|
||||
|
||||
// Show / hide throbbers accoring to Ajax requests
|
||||
Ajax.Responders.register({
|
||||
onCreate: function() { $("throbber").addClassName("loading"); },
|
||||
onComplete: function() { $("throbber").removeClassName("loading"); }
|
||||
});
|
||||
|
||||
// Search keyword persistence between different views
|
||||
$(document).observe("dom:loaded", function() {
|
||||
$$("#menu a.persist").each(function(link) {
|
||||
link.store("originalLink", link.readAttribute("href"));
|
||||
});
|
||||
});
|
||||
$(document).observe("hash:changed", function(e) {
|
||||
$$("#menu a.persist").each(function(link) {
|
||||
link.writeAttribute("href", link.retrieve("originalLink") + "#" + e.memo.hash);
|
||||
});
|
||||
});
|
||||
|
||||
Function.prototype.lazify = function() {
|
||||
var cache = {};
|
||||
return this.wrap(function(original, x) {
|
||||
return cache[x] ? cache[x] : (cache[x] = original(x));
|
||||
});
|
||||
};
|
||||
|
||||
Number.emToPx = function(x) {
|
||||
var div = new Element("div").setStyle({
|
||||
position: "absolute", margin: "0", padding: "0", visibility: "none",
|
||||
width: x + "em", height: "9px", top: "-100000em", left: "-100000em"
|
||||
});
|
||||
$(document.body).insert(div);
|
||||
var px = div.getWidth();
|
||||
div.remove();
|
||||
return px;
|
||||
}.lazify();
|
||||
|
||||
Number.prototype.emToPx = Number.emToPx.methodize();
|
||||
String.prototype.toInt = window.parseInt.methodize();
|
||||
|
||||
// BehaviorManager allows encapsulation of event handlers that frequently need
|
||||
// to be enabled or disabled due to change of state. Behaviors must be first
|
||||
// registered with BehaviorManager.register().
|
||||
var BehaviorManager = {
|
||||
behaviors: {},
|
||||
|
||||
// `behaviors' is an object that must support three methods at minimal:
|
||||
// `enable', `disable', and `fire'.
|
||||
// * `enable' is called when a behavior is switched on.
|
||||
// * `disable' is called when a behavior is switched off.
|
||||
// * `fire' is called when the behavior must be executed immediately.
|
||||
// If `fire' is not present, BehaviorManager assumes that a function called
|
||||
// `observer' is present and calls it.
|
||||
// Although not enforced by BehaviorManager, it is customary to name the
|
||||
// event handler `observer".
|
||||
// The `behaviors' object must not manipulate `_enabled', as it is used
|
||||
// internally by BehaviorManager.
|
||||
register: function(name, behaviors) {
|
||||
this.behaviors[name] = behaviors;
|
||||
this.behaviors[name]._enabled = false;
|
||||
},
|
||||
|
||||
enable: function(behavior, immediatelyFire) {
|
||||
var name = behavior;
|
||||
behavior = this.behaviors[behavior];
|
||||
(behavior._enabled = true) && behavior.enable();
|
||||
if (immediatelyFire) {
|
||||
this.fire(name);
|
||||
}
|
||||
},
|
||||
|
||||
// Returns false if the behavior has already been disabled, otherwise true.
|
||||
disable: function(behavior) {
|
||||
behavior = this.behaviors[behavior];
|
||||
if (behavior._enabled) {
|
||||
behavior.disable();
|
||||
behavior._enabled = false;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
fire: function(behavior) {
|
||||
behavior = this.behaviors[behavior];
|
||||
(behavior.fire || behavior.observer).bind(behavior)();
|
||||
}
|
||||
};
|
||||
|
||||
// If o is a string, it will be treated as an id.
|
||||
// If o is an element, make sure the DOM is loaded.
|
||||
// If o is a Selector, it will be a live set of nodes.
|
||||
Function.prototype.toBehavior = function(o, event) {
|
||||
return {
|
||||
observer: this,
|
||||
enable: o instanceof Selector ? function() {
|
||||
o.findElements().uniq().invoke("observe", event, this.observer);
|
||||
} : function() { Event.observe(o, event, this.observer); },
|
||||
disable: o instanceof Selector ? function() {
|
||||
o.findElements().uniq().invoke("stopObserving", event, this.observer);
|
||||
} : function() { Event.stopObserving(o, event, this.observer); }
|
||||
};
|
||||
};
|
||||
|
||||
BehaviorManager.register("slashSearch", function(e) {
|
||||
if (e.findElement().identify() == "text") {
|
||||
return;
|
||||
}
|
||||
if ((e.which || e.keyCode) == 47) { // KEY_SLASH
|
||||
$("text").focus(); e.stop();
|
||||
}
|
||||
}.toBehavior(document, "keypress"));
|
||||
|
||||
BehaviorManager.register("submitOnEnter", function(e) {
|
||||
e.stop();
|
||||
window.location.hash = "#search/" + $F("text");
|
||||
}.toBehavior("phonebook-search", "submit"));
|
||||
|
||||
BehaviorManager.register("centerHeader", {
|
||||
attached: false, fire: function() {
|
||||
this.observer.adjust = true;
|
||||
this.observer();
|
||||
},
|
||||
|
||||
observer: function(e) {
|
||||
if (arguments.callee.adjust) { $("header").verticallyCenter(); }
|
||||
},
|
||||
|
||||
enable: function() {
|
||||
this.observer.adjust = true;
|
||||
if (!this.attached) {
|
||||
Event.observe(Prototype.resizeTarget, "resize", this.observer);
|
||||
this.attached = true;
|
||||
}
|
||||
},
|
||||
|
||||
disable: function() {
|
||||
this.observer.adjust = false;
|
||||
$("header").stopVerticallyCentering();
|
||||
}
|
||||
});
|
||||
|
||||
// Normally, you should simply mix in a method called `startSearch' and then
|
||||
// call SearchManager.initialize() when DOM is loaded. For advanced examples,
|
||||
// take a look at tree.js, which overrides everything but `initialize'.
|
||||
var SearchManager = {
|
||||
enabledBehaviorsOnInit: ["submitOnEnter"],
|
||||
initialize: function() {
|
||||
$(document).observe("hash:changed", this.onHashChange.bind(this));
|
||||
this.onLoad();
|
||||
this.enabledBehaviorsOnInit.each(function(b) {
|
||||
BehaviorManager.enable(b);
|
||||
});
|
||||
},
|
||||
|
||||
onHashChange: function(e) {
|
||||
console.log("hash");
|
||||
console.log(this);
|
||||
$("text").value = e.memo.hash.replace("search/", '');
|
||||
this.startSearch.bind(this)();
|
||||
},
|
||||
|
||||
onLoad: function() {
|
||||
if (window.location.hash.startsWith("#search/")) {
|
||||
$(document).fire("hash:changed", {
|
||||
hash: window.location.hash.substring(1)
|
||||
});
|
||||
} else {
|
||||
$("phonebook-search").addClassName("large");
|
||||
BehaviorManager.enable("centerHeader", true);
|
||||
$("text").focus();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
String.prototype.dnToEmail = function() {
|
||||
var m = this.match(/mail=(\w+@mozilla.*),o=/);
|
||||
return (m ? m[1] : null);
|
||||
};
|
||||
|
|
@ -0,0 +1,76 @@
|
|||
|
||||
BehaviorManager.enable("slashSearch");
|
||||
|
||||
$(document).observe("dom:loaded", function() {
|
||||
|
||||
$("edit-entry").addClassName("selected").removeAttribute("href");
|
||||
if (window.location.search.toQueryParams().edit_mail) {
|
||||
$("edit-entry").update("Edit Entry");
|
||||
}
|
||||
|
||||
$("phonebook-search").observe("submit", function(e) {
|
||||
e.stop();
|
||||
window.location = "./#search/" + $F("text");
|
||||
});
|
||||
|
||||
var countryMap = {
|
||||
'Mountain View': 'US',
|
||||
'Auckland': 'NZ',
|
||||
'Beijing': 'CN',
|
||||
'Denmark': 'DK',
|
||||
'Paris': 'FR',
|
||||
'Toronto': 'CA',
|
||||
'Tokyo': 'JP'
|
||||
};
|
||||
$("office-city-select").observe("change", function(e) {
|
||||
var city = $F(this);
|
||||
$("office-city-text")[city == "Other" ? "show" : "hide"]();
|
||||
if (countryMap[city]) {
|
||||
$("office-country-select").value = countryMap[city];
|
||||
}
|
||||
});
|
||||
|
||||
var remover = function(e) {
|
||||
e.element().up().remove();
|
||||
e.stop();
|
||||
};
|
||||
var adder = function(name, title) {
|
||||
title = "Remove " + title;
|
||||
return function(e) {
|
||||
var div = new Element("div");
|
||||
var input = new Element("input", {type: "text", name: name});
|
||||
var a = new Element("a", {href: '#', title: title});
|
||||
div.insert(input).insert(a);
|
||||
a.observe("click", remover).addClassName("remove-link");
|
||||
e.element().insert({before: div}); e.stop();
|
||||
input.focus();
|
||||
};
|
||||
};
|
||||
|
||||
$("email-alias-add").observe("click", adder("emailAlias[]", "e-mail"));
|
||||
$("phone-number-add").observe("click", adder("mobile[]", "number"));
|
||||
$("im-add").observe("click", adder("im[]", "account"));
|
||||
|
||||
$w("email-aliases phone-numbers im-accounts").map(function(x) {
|
||||
return $(x).descendants().find("input + a");
|
||||
}).flatten().compact().invoke("observe", "click", remover).each(function(x) {
|
||||
x.writeAttribute("title", x.innerHTML).update('');
|
||||
});
|
||||
|
||||
// Replace dumb combobox with an autocomplete textbox
|
||||
var manager = new Element("input", {type: "text", id: "manager-text"});
|
||||
$("select-manager").hide().insert({before: manager});
|
||||
manager.value = $$("option[value='#{dn}']".interpolate({
|
||||
dn: $F("select-manager")
|
||||
}))[0].innerHTML;
|
||||
|
||||
new Autocomplete(manager, {
|
||||
serviceUrl: "./search.php?format=autocomplete",
|
||||
minChars: 2,
|
||||
onSelect: function(value, data) {
|
||||
$("select-manager").value = data;
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
|
||||
BehaviorManager.enable("slashSearch");
|
||||
SearchManager.startSearch = function() {
|
||||
if (!$F("text").strip()) {
|
||||
window.location = "./";
|
||||
}
|
||||
BehaviorManager.disable("centerHeader") &&
|
||||
$("phonebook-search").removeClassName("large");
|
||||
$("phonebook-search").request({onSuccess: function onSuccess(r) {
|
||||
$("results").update('').update(r.responseText ||
|
||||
'<div style="text-align: center; margin-top: 5em;">' +
|
||||
'<img src="./img/ohnoes.jpg" />' +
|
||||
'<h2>OH NOES! No ones were foundz.</h2>' +
|
||||
'</div>'
|
||||
);
|
||||
$("text").releaseFocus();
|
||||
}});
|
||||
};
|
||||
|
||||
$(document).observe("dom:loaded", function() {
|
||||
$("menu").down("a.card").addClassName("selected");
|
||||
SearchManager.initialize();
|
||||
});
|
|
@ -0,0 +1,72 @@
|
|||
|
||||
BehaviorManager.register("centerVCard", {
|
||||
observer: function() { $$("div.vcard").invoke("verticallyCenter"); },
|
||||
enable: function() {
|
||||
Event.observe(Prototype.resizeTarget, "resize", this.observer);
|
||||
},
|
||||
disable: Prototype.emptyFunction
|
||||
});
|
||||
|
||||
Object.extend(SearchManager, {
|
||||
startSearch: function() {
|
||||
BehaviorManager.disable("centerHeader") &&
|
||||
$("phonebook-search").removeClassName("large");
|
||||
$("phonebook-search").request({
|
||||
parameters: {format: "json"},
|
||||
onSuccess: function onSuccess(r) {
|
||||
$("text").releaseFocus();
|
||||
$("results").update('');
|
||||
r.responseText.evalJSON().each(function(person) {
|
||||
var container = new Element("div").addClassName("photo-frame");
|
||||
var face = new Element("img", {
|
||||
src: person.picture + "&type=thumb", "class": "wall-photo"
|
||||
}).observe("click", function() {
|
||||
this.showPerson(person.dn);
|
||||
}.bind(this));
|
||||
var name = new Element("span").update(person.cn);
|
||||
container.insert(name).insert(face);
|
||||
if (person.employeetype.indexOf("DISABLED") != -1) {
|
||||
container.addClassName("disabled");
|
||||
}
|
||||
$("results").insert(container);
|
||||
}.bind(this));
|
||||
}.bind(this)
|
||||
});
|
||||
},
|
||||
|
||||
showPerson: function(dn) {
|
||||
$("phonebook-search").request({
|
||||
parameters: {format: "html", query: dn.dnToEmail()},
|
||||
onSuccess: function onSuccess(r) {
|
||||
$(document.body).addClassName("lightbox");
|
||||
var close = new Element("div").observe("click", function(e) {
|
||||
$(document.body).removeClassName("lightbox");
|
||||
}).addClassName("close-button").writeAttribute("title", "Close");
|
||||
$("overlay").update('').update(r.responseText).
|
||||
down("div.vcard").verticallyCenter().
|
||||
down("div.header").insert(close);
|
||||
$("overlay").down(".vcard p.manager a").observe("click", function() {
|
||||
$(document.body).removeClassName("lightbox");
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
BehaviorManager.enable("slashSearch");
|
||||
|
||||
$(document).observe("dom:loaded", function() {
|
||||
$("menu").down("a.wall").addClassName("selected");
|
||||
|
||||
BehaviorManager.enable("centerVCard");
|
||||
BehaviorManager.enable("submitOnEnter");
|
||||
|
||||
$("overlay").observe("click", function(e) {
|
||||
if (e.element() == this) {
|
||||
$(document.body).removeClassName("lightbox");
|
||||
}
|
||||
});
|
||||
|
||||
SearchManager.initialize();
|
||||
});
|
||||
|
|
@ -0,0 +1,168 @@
|
|||
|
||||
Element.addMethods("li", {
|
||||
childTree: function(element) {
|
||||
var next = $(element).next();
|
||||
return (next && next.match("ul")) ? next : undefined;
|
||||
},
|
||||
|
||||
collapse: function(element) {
|
||||
var child = $(element).childTree();
|
||||
child && child.hide();
|
||||
return $(element).addClassName("collapsed").removeClassName("expanded");
|
||||
},
|
||||
|
||||
expand: function(element) {
|
||||
var child = $(element).childTree();
|
||||
child && child.show();
|
||||
return $(element).addClassName("expanded").removeClassName("collapsed");
|
||||
},
|
||||
|
||||
collapsed: function(element) {
|
||||
return $(element).hasClassName("collapsed");
|
||||
},
|
||||
|
||||
expanded: function(element) {
|
||||
return !$(element).collapsed();
|
||||
},
|
||||
|
||||
toggleTree: function(element) {
|
||||
element = $(element);
|
||||
return element[element.collapsed() ? "expand" : "collapse"]();
|
||||
}
|
||||
});
|
||||
|
||||
BehaviorManager.register("scrollSnap", function() {
|
||||
var card = $("person").down("div.vcard");
|
||||
var tree = $("orgchart");
|
||||
if (!card || !tree) { return; }
|
||||
if (!card.retrieve("originalTop")) {
|
||||
card.store("originalTop", card.getStyle("marginTop"));
|
||||
}
|
||||
var refTop = tree.viewportOffset().top;
|
||||
card[refTop >= 5 ? "removeClassName" : "addClassName"]("snap-to-top");
|
||||
}.toBehavior(document, "scroll"));
|
||||
|
||||
BehaviorManager.register("treeNodeToggling", function(e) {
|
||||
!e.element().match("a") && $(this).toggleTree();
|
||||
Tree.select(this);
|
||||
e.stop();
|
||||
}.toBehavior(new Selector("div li.hr-node"), "click"));
|
||||
|
||||
BehaviorManager.register("treeNodeSelecting", function(e) {
|
||||
Tree.select($(this).up().addClassName("selected"));
|
||||
e.stop();
|
||||
}.toBehavior(new Selector("div li.hr-node span.title"), "click"));
|
||||
|
||||
BehaviorManager.register("stopFilteringOnEsc", function(e) {
|
||||
console.log(e.which || e.keyCode);
|
||||
if ((e.which || e.keyCode) == Event.KEY_ESC) {
|
||||
(function() { $(this).clear(); }).bind(this).defer();
|
||||
Tree.stopFiltering();
|
||||
}
|
||||
}.toBehavior("text", "keyup"));
|
||||
|
||||
BehaviorManager.register("filterOnSubmit", function(e) {
|
||||
e.stop();
|
||||
Tree.stopFiltering();
|
||||
$("text").blur();
|
||||
if (!$F("text").strip()) {
|
||||
$$("#orgchart li:not(.leaf)").invoke("expand");
|
||||
return;
|
||||
}
|
||||
Tree.filter();
|
||||
}.toBehavior("phonebook-search", "submit"));
|
||||
|
||||
var Tree = {
|
||||
selected: null,
|
||||
select: function(node, changeHash) {
|
||||
this.selected && this.selected.removeClassName("selected");
|
||||
this.selected = $(node).addClassName("selected");
|
||||
window.location.hash = "#search/" + this.selected.id.replace("-at-", '@');
|
||||
},
|
||||
|
||||
stopFiltering: function() {
|
||||
$("orgchart").removeClassName("filter-view");
|
||||
$$("#orgchart li.highlighted").invoke("removeClassName", "highlighted");
|
||||
$$("#orgchart li:not(.leaf)").invoke("expand");
|
||||
},
|
||||
|
||||
showPerson: function(email) {
|
||||
new Ajax.Request("search.php", {
|
||||
method: "get",
|
||||
parameters: {query: email, format: "html"},
|
||||
onSuccess: function(r) {
|
||||
$("person").update(r.responseText).down(".vcard");
|
||||
BehaviorManager.fire("scrollSnap");
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
filter: function() {
|
||||
$("phonebook-search").request({
|
||||
parameters: {format: "json"},
|
||||
onSuccess: function onSuccess(r) {
|
||||
var people = r.responseText.evalJSON();
|
||||
people = people.pluck("dn").map(this.dnToEmail).compact();
|
||||
people.sort(function(a, b) {
|
||||
return a.cumulativeOffset().top - b.cumulativeOffset().top;
|
||||
});
|
||||
console.log("Post-eval");
|
||||
|
||||
var allowedToShow = people.map(function(x) {
|
||||
console.log(" " + x.id);
|
||||
var rootwards = x.ancestors().find("ul").compact().invoke("previous", "li");
|
||||
console.log(" Post rootwards");
|
||||
var leafwards = [];
|
||||
console.log(" Post leafwards");
|
||||
console.log(" Post leafing");
|
||||
return [x].concat(rootwards).concat(leafwards).compact();
|
||||
});
|
||||
|
||||
$$("#orgchart li:not(.leaf)").invoke("collapse");
|
||||
console.log("Post-collapsing");
|
||||
allowedToShow.flatten().uniq().find(":not(.leaf)").invoke("expand");
|
||||
console.log("Post-expansion");
|
||||
people.invoke("addClassName", "highlighted").first().scrollTo();
|
||||
$("orgchart").addClassName("filter-view");
|
||||
}.bind(this)
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
Object.extend(SearchManager, {
|
||||
enabledBehaviorsOnInit: ["filterOnSubmit"],
|
||||
onHashChange: function(e) {
|
||||
var email = e.memo.hash.replace("search/", '');
|
||||
Tree.showPerson(email);
|
||||
Tree.select($(email.replace('@', "-at-")));
|
||||
},
|
||||
|
||||
onLoad: function() {
|
||||
var hash = window.location.hash;
|
||||
if (hash.startsWith("#search/")) {
|
||||
$(document).fire("hash:changed", {hash: hash.substring(1)});
|
||||
var search = $("text").value = hash.replace(/^#search\//, '');
|
||||
if (!search.strip()) { return; }
|
||||
Tree.filter();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
$(document).observe("dom:loaded", function() {
|
||||
$("search").update("Filter");
|
||||
$("menu").down("a.tree").addClassName("selected");
|
||||
|
||||
BehaviorManager.enable("scrollSnap");
|
||||
BehaviorManager.enable("treeNodeToggling");
|
||||
BehaviorManager.enable("slashSearch");
|
||||
BehaviorManager.enable("stopFilteringOnEsc");
|
||||
|
||||
SearchManager.initialize();
|
||||
});
|
||||
|
||||
Object.extend(Tree, {
|
||||
dnToEmail: function(x) {
|
||||
var m = x.match(/mail=(\w+@mozilla.*),o=/);
|
||||
return m ? $(m[1].replace('@', "-at-")) : null;
|
||||
}
|
||||
});
|
14
search.php
14
search.php
|
@ -1,23 +1,15 @@
|
|||
<?php
|
||||
require_once("init.php");
|
||||
require_once("filters.inc");
|
||||
|
||||
$keyword = isset($_GET["query"]) ? $_GET["query"] : '*';
|
||||
$entries = normalize(search_users($ldapconn, $keyword));
|
||||
$filters = array(
|
||||
"description" => "wikilinks",
|
||||
"other" => "wikilinks",
|
||||
"employeetype" => "employee_status",
|
||||
"physicaldeliveryofficename" => "location_formatter",
|
||||
"mobile" => "mobile_normalizer",
|
||||
"im" => "mobile_normalizer",
|
||||
"manager" => "get_manager"
|
||||
);
|
||||
$filters = get_filters();
|
||||
|
||||
foreach ($entries as &$entry) {
|
||||
foreach ($entry as $name => $attribute) {
|
||||
if (isset($filters[$name]) && function_exists($filter = $filters[$name])) {
|
||||
$entry[$name] = call_user_func($filter, $attribute);
|
||||
} else {
|
||||
# $attribute = htmlspecialchars($entry);
|
||||
}
|
||||
}
|
||||
if (preg_match("/mail=(\w+@mozilla.*),o=/", $entry["dn"], $m)) {
|
||||
|
|
|
@ -63,7 +63,7 @@
|
|||
<tr>
|
||||
<td><label>Office Country</label></td>
|
||||
<td>
|
||||
<select id="office_country_select" name="office_country">
|
||||
<select id="office-country-select" name="office_country">
|
||||
<option value=""></option>
|
||||
<?php
|
||||
foreach($country_codes as $country_name => $code) {
|
||||
|
@ -170,86 +170,7 @@
|
|||
|
||||
</div>
|
||||
|
||||
<script src="js/autocomplete.js" type="text/javascript"></script>
|
||||
<script type="text/javascript">
|
||||
var countryMap = {
|
||||
'Mountain View': 'US',
|
||||
'Auckland': 'NZ',
|
||||
'Beijing': 'CN',
|
||||
'Denmark': 'DK',
|
||||
'Paris': 'FR',
|
||||
'Toronto': 'CA',
|
||||
'Tokyo': 'JP'
|
||||
};
|
||||
|
||||
$(document).observe("keypress", function(e) {
|
||||
if ((e.charCode || e.keyCode) == 47) { // KEY_SLASH
|
||||
$("text").focus();
|
||||
e.stop();
|
||||
}
|
||||
});
|
||||
|
||||
$(document).observe("dom:loaded", function() {
|
||||
|
||||
$("phonebook-search").observe("submit", function(e) {
|
||||
e.stop();
|
||||
window.location = "./#search/" + $F("text");
|
||||
});
|
||||
|
||||
$("edit-entry").addClassName("selected").removeAttribute("href");
|
||||
if (window.location.search.toQueryParams().edit_mail) {
|
||||
$("edit-entry").update("Edit Entry");
|
||||
}
|
||||
|
||||
$("office-city-select").observe("change", function(e) {
|
||||
var city = $F("office-city-select");
|
||||
$("office-city-text")[city == "Other" ? "show" : "hide"]();
|
||||
if (countryMap[city]) {
|
||||
$("office-country-select").value = countryMap[city];
|
||||
}
|
||||
});
|
||||
|
||||
var remover = function(e) {
|
||||
e.element().up().remove();
|
||||
e.stop();
|
||||
};
|
||||
var adder = function(name, title) {
|
||||
title = "Remove " + title;
|
||||
return function(e) {
|
||||
var div = new Element("div");
|
||||
var input = new Element("input", {type: "text", name: name});
|
||||
var a = new Element("a", {href: '#', title: title});
|
||||
div.insert(input).insert(a);
|
||||
a.observe("click", remover).addClassName("remove-link");
|
||||
e.element().insert({before: div}); e.stop();
|
||||
input.focus();
|
||||
};
|
||||
};
|
||||
|
||||
$("email-alias-add").observe("click", adder("emailAlias[]", "e-mail"));
|
||||
$("phone-number-add").observe("click", adder("mobile[]", "number"));
|
||||
$("im-add").observe("click", adder("im[]", "account"));
|
||||
|
||||
$w("email-aliases phone-numbers im-accounts").map(function(x) {
|
||||
return $(x).descendants().find("input + a");
|
||||
}).flatten().compact().invoke("observe", "click", remover).each(function(x) {
|
||||
x.writeAttribute("title", x.innerHTML).update('');
|
||||
});
|
||||
|
||||
var manager = new Element("input", {type: "text", id: "manager-text"});
|
||||
$("select-manager").hide().insert({before: manager});
|
||||
manager.value = $$("option[value='#{dn}']".interpolate({
|
||||
dn: $F("select-manager")
|
||||
}))[0].innerHTML;
|
||||
new Autocomplete(manager, {
|
||||
serviceUrl: "./search.php?format=autocomplete",
|
||||
minChars: 2,
|
||||
onSelect: function(value, data) {
|
||||
$("select-manager").value = data;
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
</script>
|
||||
<script type="text/javascript" src="js/autocomplete.js"></script>
|
||||
<script type="text/javascript" src="js/edit.js"></script>
|
||||
|
||||
<?php require_once('templates/footer.php'); ?>
|
||||
|
|
|
@ -5,85 +5,8 @@
|
|||
<title>Mozilla Phonebook</title>
|
||||
<link href="css/style.css" rel="stylesheet" type="text/css" />
|
||||
<link rel="shortcut icon" type="image/x-icon" href="./favicon.ico" />
|
||||
<script src="js/prototype.js" type="text/javascript"></script>
|
||||
<script type="text/javascript">
|
||||
if (!window.console) {
|
||||
window.console = {};
|
||||
window.console.log = Prototype.emptyFunction;
|
||||
}
|
||||
|
||||
// Implement onhashchange support
|
||||
(function() {
|
||||
var hash = window.location.hash;
|
||||
var fire = function(str) {
|
||||
$(document).fire("hash:changed", { hash: str.substring(1) });
|
||||
};
|
||||
var pe = new PeriodicalExecuter(function() {
|
||||
var newHash = window.location.hash;
|
||||
if (newHash != hash) {
|
||||
fire(newHash);
|
||||
hash = newHash;
|
||||
}
|
||||
}, 1);
|
||||
})();
|
||||
|
||||
Element.addMethods({
|
||||
verticallyCenter: function(element, percentage) {
|
||||
element = $(element);
|
||||
if (!element.retrieve("originalMarginTop")) {
|
||||
element.store("originalMarginTop", element.getStyle("marginTop"));
|
||||
}
|
||||
var offset = $(document).viewport.getHeight() - element.getHeight();
|
||||
return element.setStyle({marginTop: (offset * (percentage || 0.4)) + "px"});
|
||||
},
|
||||
|
||||
stopVerticallyCentering: function(element) {
|
||||
element = $(element);
|
||||
var original = element.getStorage().unset("originalMarginTop");
|
||||
return element.setStyle({marginTop: original});
|
||||
},
|
||||
|
||||
searchify: function(element, results) {
|
||||
return $(element).writeAttribute({type: "search", results: results || 5});
|
||||
}
|
||||
});
|
||||
|
||||
Element.addMethods("input", {
|
||||
releaseFocus: function(element) {
|
||||
$w("blur focus blur").each(function(method) { $(element)[method](); });
|
||||
return element;
|
||||
}
|
||||
});
|
||||
|
||||
Prototype.resizeTarget = document.onresize ? document : window;
|
||||
|
||||
Array.prototype.find = function find(selector) {
|
||||
return this.filter(function(x) {
|
||||
return x.match ? x.match(selector) : false;
|
||||
});
|
||||
};
|
||||
|
||||
String.prototype.dnToEmail = function() {
|
||||
var m = this.match(/mail=(\w+@mozilla.*),o=/);
|
||||
return (m ? m[1] : null);
|
||||
};
|
||||
|
||||
Ajax.Responders.register({
|
||||
onCreate: function() { $("throbber").addClassName("loading"); },
|
||||
onComplete: function() { $("throbber").removeClassName("loading"); }
|
||||
});
|
||||
|
||||
$(document).observe("dom:loaded", function() {
|
||||
$$("#menu a.persist").each(function(link) {
|
||||
link.store("originalLink", link.readAttribute("href"));
|
||||
});
|
||||
});
|
||||
$(document).observe("hash:changed", function(e) {
|
||||
$$("#menu a.persist").each(function(link) {
|
||||
link.writeAttribute("href", link.retrieve("originalLink") + "#" + e.memo.hash);
|
||||
});
|
||||
});
|
||||
</script>
|
||||
<script type="text/javascript" src="js/prototype.js"></script>
|
||||
<script type="text/javascript" src="js/common.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
@ -98,7 +21,7 @@
|
|||
<div id="throbber"></div>
|
||||
<ul id="menu">
|
||||
<li><a class="card persist" href="./">Cards</a></li>
|
||||
<li><a class="wall persist" href="./face.php">Faces</a></li>
|
||||
<li><a class="wall persist" href="./faces.php">Faces</a></li>
|
||||
<li><a class="tree" href="./tree.php">Org Chart</a></li>
|
||||
<li class="edit"><a class="edit" href="./edit.php" id="edit-entry">Edit My Entry</a></li>
|
||||
</ul>
|
||||
|
|
213
tree.php
213
tree.php
|
@ -1,11 +1,12 @@
|
|||
<?php
|
||||
require_once "init.php";
|
||||
require_once "config.php";
|
||||
|
||||
$search = ldap_search(
|
||||
$ldapconn,
|
||||
"o=com, dc=mozilla",
|
||||
"mail=*",
|
||||
array("cn", "manager", "title", "mail", "employeeType")
|
||||
$tree_config["ldap"]["search_base"],
|
||||
$tree_config["ldap"]["search_filter"],
|
||||
$tree_config["ldap"]["search_attributes"]
|
||||
);
|
||||
$data = ldap_get_entries($ldapconn, $search);
|
||||
|
||||
|
@ -13,15 +14,11 @@ $people = array();
|
|||
$orphans = array();
|
||||
$everyone = array();
|
||||
|
||||
$tree_view_roots = tree_view_roots();
|
||||
|
||||
foreach ($data as $person) {
|
||||
$mail = $person['mail'][0];
|
||||
$everyone[$mail] = array(
|
||||
"title" => !empty($person["title"][0]) ? $person["title"][0] : null,
|
||||
"name" => !empty($person["cn"][0]) ? $person["cn"][0] : null,
|
||||
"disabled" => isset($person["employeetype"]) ?
|
||||
strpos($person["employeetype"][0], 'D') !== FALSE:
|
||||
FALSE
|
||||
);
|
||||
$everyone[$mail] = tree_view_process_entry($person);
|
||||
|
||||
// If a user has a manager, try to find their place in the tree.
|
||||
if (!empty($person["manager"][0])) {
|
||||
|
@ -34,34 +31,17 @@ foreach ($data as $person) {
|
|||
} else {
|
||||
$people[$manager][] = $mail;
|
||||
}
|
||||
} elseif (!empty($mail) &&
|
||||
strpos("lilly@mozilla.com", $mail) === FALSE &&
|
||||
strpos("mitchell@mozilla.com", $mail) === FALSE) {
|
||||
} elseif (!empty($mail) && !in_array($mail, $tree_view_roots)) {
|
||||
// Person is an orphan.
|
||||
$orphans[] = $mail;
|
||||
}
|
||||
}
|
||||
|
||||
function item($email, $leaf=FALSE) {
|
||||
global $everyone;
|
||||
$email = htmlspecialchars($email);
|
||||
$id = str_replace('@', "-at-", $email);
|
||||
$name = htmlspecialchars($everyone[$email]["name"]);
|
||||
$title = htmlspecialchars($everyone[$email]["title"]);
|
||||
$leaf = $leaf ? " leaf" : '';
|
||||
$disabled = $everyone[$email]["disabled"] ? " disabled" : '';
|
||||
return "<li id=\"$id\" class=\"hr-node expanded$leaf$disabled\">".
|
||||
"<a href=\"#search/$email\" class=\"hr-link\">$name</a> ".
|
||||
"<span class=\"title\">$title</span>".
|
||||
"</li>";
|
||||
}
|
||||
|
||||
|
||||
function make_tree($level, $root, $nodes=null) {
|
||||
global $people;
|
||||
global $everyone;
|
||||
|
||||
print "\n". item($root, ($nodes == null));
|
||||
print "\n". tree_view_item($root, ($nodes == null));
|
||||
|
||||
if (is_array($nodes)) {
|
||||
print "\n<ul>";
|
||||
|
@ -85,8 +65,15 @@ require_once "templates/header.php";
|
|||
|
||||
<div id="orgchart" class="tree">
|
||||
<ul>
|
||||
<?= make_tree(0, 'mitchell@mozilla.com'); ?>
|
||||
<?= make_tree(0, 'lilly@mozilla.com', $people['lilly@mozilla.com']); ?>
|
||||
<?php
|
||||
foreach ($tree_view_roots as $root) {
|
||||
if (!isset($people[$root])) {
|
||||
make_tree(0, $root);
|
||||
} else {
|
||||
make_tree(0, $root, $people[$root]);
|
||||
}
|
||||
}
|
||||
?>
|
||||
</ul>
|
||||
</div>
|
||||
<br />
|
||||
|
@ -96,7 +83,7 @@ require_once "templates/header.php";
|
|||
<ul>
|
||||
<?php
|
||||
foreach ($orphans as $orphan) {
|
||||
print "\n". item($orphan, TRUE);
|
||||
print "\n". tree_view_item($orphan, TRUE);
|
||||
}
|
||||
?>
|
||||
</ul>
|
||||
|
@ -108,166 +95,6 @@ print "\n". item($orphan, TRUE);
|
|||
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
Element.addMethods("li", {
|
||||
childTree: function(element) {
|
||||
var next = $(element).next();
|
||||
return (next && next.match("ul")) ? next : undefined;
|
||||
},
|
||||
|
||||
collapse: function(element) {
|
||||
var child = $(element).childTree();
|
||||
child && child.hide();
|
||||
return $(element).addClassName("collapsed").removeClassName("expanded");
|
||||
},
|
||||
|
||||
expand: function(element) {
|
||||
var child = $(element).childTree();
|
||||
child && child.show();
|
||||
return $(element).addClassName("expanded").removeClassName("collapsed");
|
||||
},
|
||||
|
||||
collapsed: function(element) {
|
||||
return $(element).hasClassName("collapsed");
|
||||
},
|
||||
|
||||
expanded: function(element) {
|
||||
return !$(element).collapsed();
|
||||
},
|
||||
|
||||
toggleTree: function(element) {
|
||||
element = $(element);
|
||||
return element[element.collapsed() ? "expand" : "collapse"]();
|
||||
}
|
||||
});
|
||||
|
||||
$(document).observe("dom:loaded", function() {
|
||||
$("search").update("Filter");
|
||||
$("menu").down("a.tree").addClassName("selected");
|
||||
var region = $("person");
|
||||
|
||||
$(document).observe("scroll", scrollSnap);
|
||||
|
||||
$$("div li.hr-node").invoke("observe", "click", function(e) {
|
||||
!e.element().match("a") && $(this).toggleTree();
|
||||
select(this);
|
||||
e.stop();
|
||||
});
|
||||
|
||||
$$("div li.hr-node span.title").invoke("observe", "click", function(e) {
|
||||
select($(this).up().addClassName("selected"));
|
||||
e.stop();
|
||||
});
|
||||
|
||||
$(document).observe("keypress", function(e) {
|
||||
if ((e.which || e.keyCode) == 47) { // KEY_SLASH
|
||||
$("text").focus(); e.stop();
|
||||
}
|
||||
});
|
||||
|
||||
$("text").observe("keypress", function(e) {
|
||||
if ((e.which || e.keyCode) == Event.KEY_ESC) {
|
||||
(function() { $(this).clear(); }).bind(this).defer();
|
||||
stopFiltering();
|
||||
}
|
||||
if ((e.which || e.keyCode) == 47) {
|
||||
e.stop();
|
||||
}
|
||||
});
|
||||
|
||||
$("phonebook-search").observe("submit", function(e) {
|
||||
e.stop();
|
||||
stopFiltering();
|
||||
$("text").blur();
|
||||
if (!$F("text").strip()) {
|
||||
$$("#orgchart li:not(.leaf)").invoke("expand");
|
||||
return;
|
||||
}
|
||||
// window.location.hash = "#search/" + $F("text");
|
||||
filter();
|
||||
});
|
||||
|
||||
function scrollSnap() {
|
||||
var card = region.down("div.vcard");
|
||||
var tree = $("orgchart");
|
||||
if (!card || !tree) { return; }
|
||||
if (!card.retrieve("originalTop")) {
|
||||
card.store("originalTop", card.getStyle("marginTop"));
|
||||
}
|
||||
var refTop = tree.viewportOffset().top;
|
||||
card[refTop >= 5 ? "removeClassName" : "addClassName"]("snap-to-top");
|
||||
}
|
||||
|
||||
function filter() {
|
||||
$("phonebook-search").request({
|
||||
parameters: {format: "json"},
|
||||
onSuccess: function onSuccess(r) {
|
||||
var people = r.responseText.evalJSON().pluck("dn").map(function(x) {
|
||||
var m = x.match(/mail=(\w+@mozilla.*),o=/);
|
||||
return m ? $(m[1].replace('@', "-at-")) : null;
|
||||
}).compact();
|
||||
people.sort(function(a, b) {
|
||||
return a.cumulativeOffset().top - b.cumulativeOffset().top;
|
||||
});
|
||||
console.log("Post-eval");
|
||||
|
||||
var allowedToShow = people.map(function(x) {
|
||||
console.log(" " + x.id);
|
||||
var rootwards = x.ancestors().find("ul").compact().invoke("previous", "li");
|
||||
console.log(" Post rootwards");
|
||||
var leafwards = [];
|
||||
console.log(" Post leafwards");
|
||||
// leafwards = leafwards && leafwards.match("ul") ? leafwards.select("li") : [];
|
||||
console.log(" Post leafing");
|
||||
return [x].concat(rootwards).concat(leafwards).compact();
|
||||
});
|
||||
|
||||
$$("#orgchart li:not(.leaf)").invoke("collapse");
|
||||
console.log("Post-collapsing");
|
||||
allowedToShow.flatten().uniq().find(":not(.leaf)").invoke("expand");
|
||||
console.log("Post-expansion");
|
||||
people.invoke("addClassName", "highlighted").first().scrollTo();
|
||||
$("orgchart").addClassName("filter-view");
|
||||
// allowedToShow.each(function(x) console.log(x));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function stopFiltering() {
|
||||
$("orgchart").removeClassName("filter-view");
|
||||
$$("#orgchart li.highlighted").invoke("removeClassName", "highlighted");
|
||||
$$("#orgchart li:not(.leaf)").invoke("expand");
|
||||
}
|
||||
|
||||
var selected = null;
|
||||
function select(node) {
|
||||
selected && selected.removeClassName("selected");
|
||||
selected = $(node).addClassName("selected");
|
||||
window.location.hash = "#search/" + selected.id.replace("-at-", '@');
|
||||
}
|
||||
|
||||
$(document).observe("hash:changed", function(e) {
|
||||
showPerson(e.memo.hash.replace("search/", ''));
|
||||
});
|
||||
var hash = window.location.hash;
|
||||
if (hash.startsWith("#search/")) {
|
||||
$(document).fire("hash:changed", {hash: hash.substring(1)});
|
||||
var search = $("text").value = hash.replace(/^#search\//, '');
|
||||
if (!search.strip()) { return; }
|
||||
filter();
|
||||
}
|
||||
|
||||
function showPerson(email) {
|
||||
new Ajax.Request("search.php", {
|
||||
method: "get",
|
||||
parameters: {query: email, format: "html"},
|
||||
onSuccess: function(r) {
|
||||
$("person").update(r.responseText).down(".vcard");
|
||||
scrollSnap();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<script type="text/javascript" src="js/view-tree.js"></script>
|
||||
|
||||
<?php require_once "templates/footer.php"; ?>
|
||||
|
|
Загрузка…
Ссылка в новой задаче