Skip to content

Commit

Permalink
Load MComunnity details on admin user page
Browse files Browse the repository at this point in the history
This isn't the fastest query, so we load it via Ajax.

AFAICT, there isn't much more interesting information we can pull
down without asking ITS for more permissions.

Closes #218.
  • Loading branch information
ppannuto committed Mar 8, 2016
1 parent 2f10ce9 commit f023780
Show file tree
Hide file tree
Showing 7 changed files with 103 additions and 17 deletions.
1 change: 1 addition & 0 deletions chezbetty/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,7 @@ def debug(request):
config.add_route('admin_users_email_oneperson', '/admin/users/email/oneperson')
config.add_route('admin_users_email_all', '/admin/users/email/all')
config.add_route('admin_user', '/admin/user/{user_id}')
config.add_route('admin_user_details', '/admin/user/{user_id}/details')
config.add_route('admin_user_search_json', '/admin/user/search/{search}/json')
config.add_route('admin_user_balance_edit', '/admin/user/balance/edit')
config.add_route('admin_user_balance_edit_submit', '/admin/user/balance/edit/submit')
Expand Down
49 changes: 42 additions & 7 deletions chezbetty/models/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,19 @@ class LDAPLookup(object):
BASE_DN = "ou=People,dc=umich,dc=edu"
PASSWORD = None
ATTRIBUTES = ["uid", "entityid", "displayName"]
# http://www.itcs.umich.edu/itcsdocs/r1463/attributes-for-ldap.html
DETAILS_ATTRIBUTES = [
# Could be interesting but require extra perm's from ITS
#"umichInstRoles",
#"umichAlumStatus",
#"umichAAAcadProgram",
#"umichAATermStatus",
#"umichHR",
"notice",
"ou",
"umichDescription",
"umichTitle",
]

def __init__(self):
self.__conn = None
Expand All @@ -38,14 +51,14 @@ def __connect(self):
)


def __lookup(self, k, v):
def __do_lookup(self, k, v, attributes, full_dict=False):
self.__connect()
query = "(%s=%s)" % (k, v)
try:
self.__conn.search(self.BASE_DN,
query,
ldap3.SEARCH_SCOPE_WHOLE_SUBTREE,
attributes=self.ATTRIBUTES
attributes=attributes
)
except:
# sometimes our connections time out
Expand All @@ -54,22 +67,41 @@ def __lookup(self, k, v):
self.__conn.search(self.BASE_DN,
query,
ldap3.SEARCH_SCOPE_WHOLE_SUBTREE,
attributes=self.ATTRIBUTES
attributes=attributes
)

if len(self.__conn.response) == 0:
raise InvalidUserException()

if full_dict:
return self.__conn.response[0]["attributes"]

return {
"umid":self.__conn.response[0]["attributes"]["entityid"],
"uniqname":self.__conn.response[0]["attributes"]["uid"][0],
"name":self.__conn.response[0]["attributes"]["displayName"][0]
}

def lookup_umid(self, umid):
return self.__lookup("entityid", umid)
def __lookup(self, k, v):
return self.__do_lookup(k, v, self.ATTRIBUTES)

def lookup_uniqname(self, uniqname):
return self.__lookup("uid", uniqname)
def __detail_lookup(self, k, v):
return self.__do_lookup(k, v,
self.ATTRIBUTES + self.DETAILS_ATTRIBUTES,
full_dict=True,
)

def lookup_umid(self, umid, details=False):
if details:
return self.__detail_lookup("entityid", umid)
else:
return self.__lookup("entityid", umid)

def lookup_uniqname(self, uniqname, details=False):
if details:
return self.__detail_lookup("uid", uniqname)
else:
return self.__lookup("uid", uniqname)


class User(account.Account):
Expand Down Expand Up @@ -104,6 +136,9 @@ def __str__(self):
def from_id(cls, id):
return DBSession.query(cls).filter(cls.id == id).one()

def get_details(self):
return self.__ldap.lookup_uniqname(self.uniqname, details=True)

@classmethod
def from_uniqname(cls, uniqname, local_only=False):
u = DBSession.query(cls).filter(cls.uniqname == uniqname).first()
Expand Down
6 changes: 6 additions & 0 deletions chezbetty/static/js/chezbetty-common-onload.js
Original file line number Diff line number Diff line change
Expand Up @@ -92,3 +92,9 @@ $(".rotate-divs").each(function () {
}, parseInt(showing_div.attr('data-rotate-div-timeout')));
});

$(".ajax-fill").each(function () {
console.log($(this));
$(this).removeClass("ajax-fill");
$(this).load($(this).attr("data-path"));
});

7 changes: 7 additions & 0 deletions chezbetty/templates/admin/macro_ajax.jinja2
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{% macro fill(path, load_text=None) %}
<div class="ajax-fill" data-path="{{path}}">
{% if load_text %}
<p>{{ load_text }}</p>
{% endif %}
</div>
{% endmacro %}
28 changes: 18 additions & 10 deletions chezbetty/templates/admin/user.jinja2
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{% extends "base.jinja2" %}
{% import "macro_graph.jinja2" as graph %}
{% import "macro_ajax.jinja2" as ajax %}
{% set active_page = 'users' %}
{% block title %}User{% endblock %}

Expand All @@ -14,16 +15,23 @@
<h3 class="panel-title">User Details</h3>
</div>
<div class="panel-body">
<dl class="dl-horizontal">
<dt>Name</dt><dd>{{ user.name }}</dd>
<dt>uniqname</dt><dd><a href="https://mcommunity.umich.edu/#search:{{ user.uniqname }}">{{ user.uniqname }}</a></dd>
<dt>UMID</dt><dd>{{ user.umid }}</dd>
<dt>Balance</dt><dd>{{ user.balance|format_currency|safe }}</dd>
<dt>Lifetime Discounts</dt><dd>{{ user.lifetime_discounts|format_currency|safe }}</dd>
<dt>Lifetime Fees</dt><dd>{{ user.lifetime_fees|format_currency|safe }}</dd>
<dt>Joined</dt><dd>{{ user.created_at|pretty_date|safe }}</dd>
<dt>Enabled</dt><dd><td>{{ button.onoff_switch("user", "enabled", user.id, user.enabled) }}</td></dd>
</dl>
<div class="row">
<div class="col-lg-4 col-md-4 col-sm-12">
<dl class="dl-horizontal">
<dt>Name</dt><dd>{{ user.name }}</dd>
<dt>uniqname</dt><dd><a href="https://mcommunity.umich.edu/#search:{{ user.uniqname }}">{{ user.uniqname }}</a></dd>
<dt>UMID</dt><dd>{{ user.umid }}</dd>
<dt>Balance</dt><dd>{{ user.balance|format_currency|safe }}</dd>
<dt>Lifetime Discounts</dt><dd>{{ user.lifetime_discounts|format_currency|safe }}</dd>
<dt>Lifetime Fees</dt><dd>{{ user.lifetime_fees|format_currency|safe }}</dd>
<dt>Joined</dt><dd>{{ user.created_at|pretty_date|safe }}</dd>
<dt>Enabled</dt><dd><td>{{ button.onoff_switch("user", "enabled", user.id, user.enabled) }}</td></dd>
</dl>
</div>
<div class="col-lg-8 col-md-8 col-sm-12">
{{ ajax.fill("/admin/user/" + user.id|string + "/details", "Loading user details from LDAP...") }}
</div>
</div>
<hr />
{% if user.has_password == False %}
{{ button.ajax_singleuse_button("Create and Email Password", "/admin/user/"~user.id~"/password/create") }}
Expand Down
18 changes: 18 additions & 0 deletions chezbetty/templates/admin/user_details.jinja2
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<dl class="dl-horizontal">
{% if umichTitle %}
<dt>Title</dt><dd>{{ umichTitle|join(", ") }}</dd>
{% endif %}
{% if ou %}
<dt>Affiliations</dt><dd>{{ ou|join(", ") }}</dd>
{% endif %}
{# This is text that the user enters in the MCommunity Directory. They might
indicate the best way to contact them, who their administrative assistant is,
or any other sort of notice that they want. #}
{% if notice %}
<dt>Notice</dt><dd>{{ notice }}</dd>
{% endif %}
{% if umichDescription %}
<dt>About Me</dt><dd>{{ umichDescription }}</dd>
{% endif %}
</dl>

11 changes: 11 additions & 0 deletions chezbetty/views_admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -1984,6 +1984,17 @@ def admin_user(request):
request.session.flash('Invalid user?', 'error')
return HTTPFound(location=request.route_url('admin_index'))

@view_config(route_name='admin_user_details',
renderer='templates/admin/user_details.jinja2',
permission='admin')
def admin_user_details(request):
try:
user = User.from_id(request.matchdict['user_id'])
details = user.get_details()
return details
except Exception as e:
if request.debug: raise(e)
return '<p>Unknown error loading user detail.</p>'

@view_config(route_name='admin_user_purchase_add',
renderer='templates/admin/user_purchase_add.jinja2',
Expand Down

0 comments on commit f023780

Please sign in to comment.