Merge branch 'ldaptest'
commit
31b6d4ace3
|
@ -47,6 +47,50 @@ def _setup_ldap():
|
||||||
def _destroy_ldap(exception=None):
|
def _destroy_ldap(exception=None):
|
||||||
g.ldap.unbind_s()
|
g.ldap.unbind_s()
|
||||||
|
|
||||||
|
def get_ldap_group_diff(members):
|
||||||
|
active_members = filter(lambda m: m['judgement'], members)
|
||||||
|
fatty = set([member['username'] for member in active_members if member['type'] in ['fatty', 'supporting']])
|
||||||
|
starving = set([member['username'] for member in active_members if member['type'] in ['starving']])
|
||||||
|
|
||||||
|
ldap_fatty = set(get_group_members(g.ldap, 'fatty'))
|
||||||
|
ldap_starving = set(get_group_members(g.ldap, 'starving'))
|
||||||
|
ldap_potato = set(get_group_members(g.ldap, 'potato'))
|
||||||
|
|
||||||
|
result = {}
|
||||||
|
result['fatty_to_remove'] = list(ldap_fatty - fatty)
|
||||||
|
result['fatty_to_add'] = list(fatty - ldap_fatty)
|
||||||
|
result['starving_to_remove'] = list(ldap_starving - starving)
|
||||||
|
result['starving_to_add'] = list(starving - ldap_starving)
|
||||||
|
if sum([len(result[k]) for k in result]) == 0:
|
||||||
|
return None
|
||||||
|
return result
|
||||||
|
|
||||||
|
def update_member_groups(c, changes):
|
||||||
|
ops = {'add': ldap.MOD_ADD, 'remove': ldap.MOD_DELETE}
|
||||||
|
for group in changes:
|
||||||
|
modlist = []
|
||||||
|
for op in changes[group]:
|
||||||
|
for username in changes[group][op]:
|
||||||
|
modlist.append((ops[op],'uniqueMember','uid={},{}'.format(username.encode('utf-8'),app.config['LDAP_USER_BASE'])))
|
||||||
|
c.modify_s('cn={},{}'.format(group.encode('utf-8'),app.config['LDAP_GROUP_BASE']), modlist)
|
||||||
|
#print group, modlist
|
||||||
|
|
||||||
|
def get_group_members(c, group):
|
||||||
|
lfilter = '(&(cn={}){})'.format(group, app.config['LDAP_GROUP_FILTER'])
|
||||||
|
data = c.search_s(app.config['LDAP_GROUP_BASE'], ldap.SCOPE_SUBTREE,
|
||||||
|
lfilter, tuple(['uniqueMember',]))
|
||||||
|
|
||||||
|
members = []
|
||||||
|
for dn, obj in data:
|
||||||
|
for k, v in obj.iteritems():
|
||||||
|
if k == "uniqueMember":
|
||||||
|
for iv in v:
|
||||||
|
part,uid,index = ldap.dn.str2dn(iv)[0][0]
|
||||||
|
if not part == 'uid' or not index == 1:
|
||||||
|
raise ValueError("First part type {} or index {} seem wrong for DN {}".format(part,index,iv))
|
||||||
|
members.append(uid.decode('utf-8'))
|
||||||
|
return members
|
||||||
|
|
||||||
def get_member_fields(c, member, fields):
|
def get_member_fields(c, member, fields):
|
||||||
if isinstance(fields, str):
|
if isinstance(fields, str):
|
||||||
fields = [fields,]
|
fields = [fields,]
|
||||||
|
|
|
@ -22,8 +22,11 @@
|
||||||
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||||
# POSSIBILITY OF SUCH DAMAGE.
|
# POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
from wtforms import Form, TextField, PasswordField, validators
|
from wtforms import Form, TextField, PasswordField, SelectMultipleField, FormField, validators, widgets
|
||||||
|
|
||||||
|
class MultiCheckboxField(SelectMultipleField):
|
||||||
|
widget = widgets.ListWidget(prefix_label=False)
|
||||||
|
option_widget = widgets.CheckboxInput()
|
||||||
|
|
||||||
class LoginForm(Form):
|
class LoginForm(Form):
|
||||||
username = TextField('Username', [validators.Required()])
|
username = TextField('Username', [validators.Required()])
|
||||||
|
@ -33,3 +36,10 @@ class LoginForm(Form):
|
||||||
class BREFetchForm(Form):
|
class BREFetchForm(Form):
|
||||||
identifier = TextField("Identifier", [validators.Required()])
|
identifier = TextField("Identifier", [validators.Required()])
|
||||||
token = PasswordField("Identifier", [validators.Required()])
|
token = PasswordField("Identifier", [validators.Required()])
|
||||||
|
|
||||||
|
class LDAPSyncForm(Form):
|
||||||
|
fatty_to_add = MultiCheckboxField("Fatty to add", choices=[])
|
||||||
|
fatty_to_remove = MultiCheckboxField("Fatty to remove", choices=[])
|
||||||
|
starving_to_add = MultiCheckboxField("Starving to add", choices=[])
|
||||||
|
starving_to_remove = MultiCheckboxField("Starving to remove", choices=[])
|
||||||
|
|
||||||
|
|
|
@ -32,6 +32,7 @@ import json
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from sqlalchemy.orm import subqueryload_all
|
from sqlalchemy.orm import subqueryload_all
|
||||||
|
from sqlalchemy.sql.expression import or_
|
||||||
from flask import g
|
from flask import g
|
||||||
|
|
||||||
from webapp import app, db, mc, cache_enabled
|
from webapp import app, db, mc, cache_enabled
|
||||||
|
@ -88,7 +89,7 @@ class Member(db.Model):
|
||||||
api_keys = db.relationship("APIKey")
|
api_keys = db.relationship("APIKey")
|
||||||
join_year = db.Column(db.Integer)
|
join_year = db.Column(db.Integer)
|
||||||
join_month = db.Column(db.Integer)
|
join_month = db.Column(db.Integer)
|
||||||
ldap_username = db.Column(db.String(64), unique=True)
|
alias = db.Column(db.String(64))
|
||||||
# Normal - standard 3 months grace period
|
# Normal - standard 3 months grace period
|
||||||
# Extended Grace Period - do not shut off account after grace period
|
# Extended Grace Period - do not shut off account after grace period
|
||||||
# Potato - do not ever shut off account, report falsified payment status
|
# Potato - do not ever shut off account, report falsified payment status
|
||||||
|
@ -148,8 +149,8 @@ class Member(db.Model):
|
||||||
del now_date
|
del now_date
|
||||||
|
|
||||||
status = {}
|
status = {}
|
||||||
status['ldap_username'] = self.ldap_username
|
|
||||||
status['username'] = self.username
|
status['username'] = self.username
|
||||||
|
status['alias'] = self.alias
|
||||||
status['type'] = self.type
|
status['type'] = self.type
|
||||||
status['payment_policy'] = self.payment_policy
|
status['payment_policy'] = self.payment_policy
|
||||||
# First check - did we actually get any transfers?
|
# First check - did we actually get any transfers?
|
||||||
|
@ -236,19 +237,22 @@ class Member(db.Model):
|
||||||
def get_list_email(self):
|
def get_list_email(self):
|
||||||
if self.preferred_email:
|
if self.preferred_email:
|
||||||
return self.preferred_email
|
return self.preferred_email
|
||||||
return '{}@hackerspace.pl'.format(self.ldap_username)
|
return '{}@hackerspace.pl'.format(self.username)
|
||||||
|
|
||||||
def get_contact_email(self):
|
def get_contact_email(self):
|
||||||
if self.preferred_email:
|
if self.preferred_email:
|
||||||
return self.preferred_email
|
return self.preferred_email
|
||||||
mra = directory.get_member_fields(g.ldap, self.ldap_username,
|
mra = directory.get_member_fields(g.ldap, self.username,
|
||||||
'mailRoutingAddress')
|
'mailRoutingAddress')
|
||||||
mra = mra['mailRoutingAddress']
|
mra = mra['mailRoutingAddress']
|
||||||
if mra:
|
if mra:
|
||||||
return mra
|
return mra
|
||||||
else:
|
else:
|
||||||
return '{}@hackerspace.pl'.format(self.ldap_username)
|
return '{}@hackerspace.pl'.format(self.username)
|
||||||
|
|
||||||
|
def invalidate_cache(self):
|
||||||
|
cache_key = 'kasownik-payment_status-{}'.format(self.username)
|
||||||
|
mc.delete(cache_key)
|
||||||
|
|
||||||
def get_status(self):
|
def get_status(self):
|
||||||
"""It's better to call this after doing a full select of data."""
|
"""It's better to call this after doing a full select of data."""
|
||||||
|
@ -302,7 +306,6 @@ class Member(db.Model):
|
||||||
now_date = datetime.datetime.now()
|
now_date = datetime.datetime.now()
|
||||||
self.join_year = now_date.year
|
self.join_year = now_date.year
|
||||||
self.join_month = now_date.month
|
self.join_month = now_date.month
|
||||||
self.ldap_username = _username
|
|
||||||
self.payment_policy = PaymentPolicy.normal.value
|
self.payment_policy = PaymentPolicy.normal.value
|
||||||
|
|
||||||
|
|
||||||
|
@ -330,7 +333,7 @@ class Transfer(db.Model):
|
||||||
return self.uid[:16]
|
return self.uid[:16]
|
||||||
|
|
||||||
def parse_title(self):
|
def parse_title(self):
|
||||||
m = re.match(ur"^([a-z0-9\-_\.]+) *\- *(fatty|starving|superfatty|supporting|supporter) *\- *([0-9a-z\-_ąężźćóżłśń \(\),/\.]+$)", self.title.strip().lower())
|
m = re.match(ur"^([a-z0-9ąężźćóżłśń\-_\.]+) *\- *(fatty|starving|superfatty|supporting|supporter) *\- *([0-9a-z\-_ąężźćóżłśń \(\),/\.]+$)", self.title.strip().lower())
|
||||||
if not m:
|
if not m:
|
||||||
return (None, None, None)
|
return (None, None, None)
|
||||||
member, _type, title = m.group(1), m.group(2), m.group(3)
|
member, _type, title = m.group(1), m.group(2), m.group(3)
|
||||||
|
@ -343,16 +346,19 @@ class Transfer(db.Model):
|
||||||
title = self.parse_title()
|
title = self.parse_title()
|
||||||
if not title[0]:
|
if not title[0]:
|
||||||
return self.MATCH_UNPARSEABLE, self.title
|
return self.MATCH_UNPARSEABLE, self.title
|
||||||
|
|
||||||
member_name = title[0]
|
member_name = title[0]
|
||||||
member = Member.query.filter_by(username=member_name).first()
|
member = Member.query.filter(or_(Member.username==member_name, Member.alias==member_name)).first()
|
||||||
if not member:
|
if not member:
|
||||||
return self.MATCH_NO_USER, member_name
|
return self.MATCH_NO_USER, member_name, 0
|
||||||
|
|
||||||
if (title[1] == 'starving' and self.amount > 50) or (title[1] == 'fatty' and self.amount > 100):
|
|
||||||
return self.MATCH_WRONG_TYPE, member
|
|
||||||
|
|
||||||
if title[2]:
|
if title[2]:
|
||||||
return self.MATCH_WRONG_TYPE, member
|
return self.MATCH_WRONG_TYPE, member, 0
|
||||||
|
|
||||||
return self.MATCH_OK, member
|
if title[1] == 'starving' and self.amount >= (50*100) and (self.amount % (50*100)) == 0:
|
||||||
|
return self.MATCH_OK, member, (self.amount/(50*100))
|
||||||
|
|
||||||
|
if title[1] == 'fatty' and self.amount >= (100*100) and (self.amount % (100*100)) == 0:
|
||||||
|
return self.MATCH_OK, member, (self.amount/(100*100))
|
||||||
|
|
||||||
|
|
||||||
|
return self.MATCH_WRONG_TYPE, member, 0
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
body {
|
body {
|
||||||
padding-top: 50px;
|
padding-top: 75px;
|
||||||
|
padding-bottom: 50px;
|
||||||
}
|
}
|
||||||
.logincontainer {
|
.logincontainer {
|
||||||
padding: 40px 15px;
|
padding: 40px 15px;
|
||||||
|
@ -41,10 +42,10 @@ body {
|
||||||
}
|
}
|
||||||
|
|
||||||
.flashes {
|
.flashes {
|
||||||
position: absolute;
|
/*position: absolute;*/
|
||||||
z-index: 100;
|
z-index: 100;
|
||||||
width: 90%;
|
width: 70%;
|
||||||
margin-left: 5%;
|
margin-left: 15%;
|
||||||
padding-top: 10px;
|
padding-top: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,79 +1,2 @@
|
||||||
{% extends "root.html" %}
|
<pre>{% for member in active_members %}{{loop.index}},{{member.username}},{{member.cn}}
|
||||||
{% set active_page = "admin" %}
|
{% endfor %}</pre>
|
||||||
{% block title %}Admin Member List{% endblock %}
|
|
||||||
{% block content %}
|
|
||||||
|
|
||||||
<div class="container">
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-2 operations">
|
|
||||||
<h4>Active operations:</h4>
|
|
||||||
<h4>Available operations:</h4>
|
|
||||||
<p>
|
|
||||||
<!--<form action="/fetch" method="post">
|
|
||||||
<button type="button" class="btn btn-primary">Fetch transfer data</button>
|
|
||||||
</form>
|
|
||||||
<form action="/spam" method="post">
|
|
||||||
<button type="button" class="btn btn-primary">Send reminders</button>
|
|
||||||
</form>-->
|
|
||||||
<a href="/admin/fetch"><b>Fetch transfer data</b></a>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
{% for group in active_members|groupby("type") %}
|
|
||||||
<div class="col-md-5">
|
|
||||||
<h2>Active members, {{ group.grouper }}:</h2>
|
|
||||||
<table class="table table-striped">
|
|
||||||
<tr>
|
|
||||||
<th>#</th>
|
|
||||||
<th>LDAP Username</th>
|
|
||||||
<th>Months Due</th>
|
|
||||||
<th>Payment Policy</th>
|
|
||||||
</tr>
|
|
||||||
{% for member in group.list %}
|
|
||||||
<tr>
|
|
||||||
<td>{{loop.index}}.</td>
|
|
||||||
<td>
|
|
||||||
<a href="/admin/member/{{member.ldap_username}}">
|
|
||||||
<b>{{member.ldap_username}}</b>
|
|
||||||
</a>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<span class="badge" style="background-color: #{{member.color}}">
|
|
||||||
{{member.months_due}}
|
|
||||||
</span>
|
|
||||||
</td>
|
|
||||||
<td>{% include "button_payment_policy.html" %}</td>
|
|
||||||
</tr>
|
|
||||||
{% endfor %}
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
{% endfor %}
|
|
||||||
<div class="col-md-5">
|
|
||||||
<h2>Inactive members:</h2>
|
|
||||||
<table class="table table-striped">
|
|
||||||
<tr>
|
|
||||||
<th>#</th>
|
|
||||||
<th>LDAP Username</th>
|
|
||||||
<th>Months Due</th>
|
|
||||||
<th>Payment Policy</th>
|
|
||||||
</tr>
|
|
||||||
{% for member in inactive_members %}
|
|
||||||
<tr>
|
|
||||||
<td>{{loop.index}}.</td>
|
|
||||||
<td>
|
|
||||||
<a href="/admin/member/{{member.ldap_username}}">
|
|
||||||
<b>{{member.ldap_username}}</b>
|
|
||||||
</a>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<span class="badge" style="background-color: #{{member.color}}">
|
|
||||||
{{member.months_due}}
|
|
||||||
</span>
|
|
||||||
</td>
|
|
||||||
<td>{% include "button_payment_policy.html" %}</td>
|
|
||||||
</tr>
|
|
||||||
{% endfor %}
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
|
||||||
|
|
|
@ -16,64 +16,69 @@
|
||||||
<button type="button" class="btn btn-primary">Send reminders</button>
|
<button type="button" class="btn btn-primary">Send reminders</button>
|
||||||
</form>-->
|
</form>-->
|
||||||
<a href="/admin/fetch"><b>Fetch transfer data</b></a>
|
<a href="/admin/fetch"><b>Fetch transfer data</b></a>
|
||||||
|
<a href="/admin/ldapsync"><b>Synchronize LDAP groups</b></a>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
{% for group in active_members|groupby("type") %}
|
<div class="col-md-10">
|
||||||
<div class="col-md-5">
|
<div class="row">
|
||||||
<h2>Active members, {{ group.grouper }}:</h2>
|
{% for group in active_members|groupby("type") %}
|
||||||
<table class="table table-striped">
|
<div class="col-md-6">
|
||||||
<tr>
|
<h2>Active members, {{ group.grouper }}:</h2>
|
||||||
<th>#</th>
|
<table class="table table-striped">
|
||||||
<th>LDAP Username</th>
|
<tr>
|
||||||
<th>Months Due</th>
|
<th>#</th>
|
||||||
<th>Payment Policy</th>
|
<th>LDAP Username</th>
|
||||||
</tr>
|
<th>Months Due</th>
|
||||||
{% for member in group.list %}
|
<th>Payment Policy</th>
|
||||||
<tr>
|
</tr>
|
||||||
<td>{{loop.index}}.</td>
|
{% for member in group.list %}
|
||||||
<td>
|
<tr>
|
||||||
<a href="/admin/member/{{member.ldap_username}}">
|
<td>{{loop.index}}.</td>
|
||||||
<b>{{member.ldap_username}}</b>
|
<td>
|
||||||
</a>
|
<a href="/admin/member/{{member.username}}">
|
||||||
</td>
|
<b>{{member.username}}</b>
|
||||||
<td>
|
</a>
|
||||||
<span class="badge" style="background-color: #{{member.color}}">
|
</td>
|
||||||
{{member.months_due}}
|
<td>
|
||||||
</span>
|
<span class="badge" style="background-color: #{{member.color}}">
|
||||||
</td>
|
{{member.months_due}}
|
||||||
<td>{% include "button_payment_policy.html" %}</td>
|
</span>
|
||||||
</tr>
|
</td>
|
||||||
{% endfor %}
|
<td>{% include "button_payment_policy.html" %}</td>
|
||||||
</table>
|
</tr>
|
||||||
</div>
|
{% endfor %}
|
||||||
{% endfor %}
|
</table>
|
||||||
<div class="col-md-5">
|
</div>
|
||||||
<h2>Inactive members:</h2>
|
{% endfor %}
|
||||||
<table class="table table-striped">
|
<div class="col-md-6">
|
||||||
<tr>
|
<h2>Inactive members:</h2>
|
||||||
<th>#</th>
|
<table class="table table-striped">
|
||||||
<th>LDAP Username</th>
|
<tr>
|
||||||
<th>Months Due</th>
|
<th>#</th>
|
||||||
<th>Payment Policy</th>
|
<th>LDAP Username</th>
|
||||||
</tr>
|
<th>Months Due</th>
|
||||||
{% for member in inactive_members %}
|
<th>Payment Policy</th>
|
||||||
<tr>
|
</tr>
|
||||||
<td>{{loop.index}}.</td>
|
{% for member in inactive_members %}
|
||||||
<td>
|
<tr>
|
||||||
<a href="/admin/member/{{member.ldap_username}}">
|
<td>{{loop.index}}.</td>
|
||||||
<b>{{member.ldap_username}}</b>
|
<td>
|
||||||
</a>
|
<a href="/admin/member/{{member.username}}">
|
||||||
</td>
|
<b>{{member.username}}</b>
|
||||||
<td>
|
</a>
|
||||||
<span class="badge" style="background-color: #{{member.color}}">
|
</td>
|
||||||
{{member.months_due}}
|
<td>
|
||||||
</span>
|
<span class="badge" style="background-color: #{{member.color}}">
|
||||||
</td>
|
{{member.months_due}}
|
||||||
<td>{% include "button_payment_policy.html" %}</td>
|
</span>
|
||||||
</tr>
|
</td>
|
||||||
{% endfor %}
|
<td>{% include "button_payment_policy.html" %}</td>
|
||||||
</table>
|
</tr>
|
||||||
</div>
|
{% endfor %}
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -0,0 +1,58 @@
|
||||||
|
{% extends "root.html" %}
|
||||||
|
{% set active_page = "admin" %}
|
||||||
|
{% block title %}Admin LDAP Sync{% endblock %}
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-2 operations">
|
||||||
|
<h4>Active operations:</h4>
|
||||||
|
<h4>Available operations:</h4>
|
||||||
|
<p>
|
||||||
|
<!--<form action="/fetch" method="post">
|
||||||
|
<button type="button" class="btn btn-primary">Fetch transfer data</button>
|
||||||
|
</form>
|
||||||
|
<form action="/spam" method="post">
|
||||||
|
<button type="button" class="btn btn-primary">Send reminders</button>
|
||||||
|
</form>-->
|
||||||
|
<a href="/admin/fetch"><b>Fetch transfer data</b></a>
|
||||||
|
<a href="/admin/ldapsync"><b>Synchronize LDAP groups</b></a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-10">
|
||||||
|
<div class="row">
|
||||||
|
{% if not form %}
|
||||||
|
<div class="col-md-12">
|
||||||
|
<h2>No sync required - groups are up to date.</h2>
|
||||||
|
</div>
|
||||||
|
{% else %}
|
||||||
|
<form method="post" action="/admin/ldapsync">
|
||||||
|
<div class="col-md-12">
|
||||||
|
<input type="submit" value="Sync" />
|
||||||
|
</div>
|
||||||
|
<div class="col-md-3">
|
||||||
|
<h2>Fatty to add:</h2>
|
||||||
|
{{ form.fatty_to_add() }}
|
||||||
|
</div>
|
||||||
|
<div class="col-md-3">
|
||||||
|
<h2>Fatty to remove:</h2>
|
||||||
|
{{ form.fatty_to_remove() }}
|
||||||
|
</div>
|
||||||
|
<div class="col-md-3">
|
||||||
|
<h2>Starving to add:</h2>
|
||||||
|
{{ form.starving_to_add() }}
|
||||||
|
</div>
|
||||||
|
<div class="col-md-3">
|
||||||
|
<h2>Starving to remove:</h2>
|
||||||
|
{{ form.starving_to_remove() }}
|
||||||
|
</div>
|
||||||
|
<div class="col-md-12">
|
||||||
|
<input type="submit" value="Sync" />
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
|
@ -5,13 +5,13 @@
|
||||||
{% set active_page = "profile" %}
|
{% set active_page = "profile" %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% block title %}{% if admin %}{{member.ldap_username}}{%else%}Profile{%endif%}{% endblock %}
|
{% block title %}{% if admin %}{{member.username}}{%else%}Profile{%endif%}{% endblock %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-3">
|
<div class="col-md-3">
|
||||||
<img src="{{ member.get_contact_email() | gravatar }}" alt="gravatar" style="width: 70%; margin-top: 10px;" />
|
<img src="{{ member.get_contact_email() | gravatar }}" alt="gravatar" style="width: 70%; margin-top: 10px;" />
|
||||||
<h1>{{member.ldap_username}}{%if cn %}<br /><small>{{cn}}</small>{% endif%}</h1>
|
<h1>{{member.username}}{%if cn %}<br /><small>{{cn}}</small>{% endif%}</h1>
|
||||||
<h5>{{member.get_contact_email()}}</h5>
|
<h5>{{member.get_contact_email()}}</h5>
|
||||||
<p>
|
<p>
|
||||||
{% if status.judgement %}
|
{% if status.judgement %}
|
||||||
|
@ -62,7 +62,7 @@
|
||||||
(which is <b>overdue</b>), so is <b>INACTIVE</b>.
|
(which is <b>overdue</b>), so is <b>INACTIVE</b>.
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% else %}
|
{% else %}
|
||||||
. {{member.ldap_username}} Is in Extended Grace Period, so is <b>ACTIVE</b>.
|
. {{member.username}} Is in Extended Grace Period, so is <b>ACTIVE</b>.
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
|
@ -7,8 +7,8 @@
|
||||||
{{member.type|capitalize}} <span class="caret"></span>
|
{{member.type|capitalize}} <span class="caret"></span>
|
||||||
</button>
|
</button>
|
||||||
<ul class="dropdown-menu" role="menu">
|
<ul class="dropdown-menu" role="menu">
|
||||||
<li><a href="/admin/member/{{member.ldap_username}}/membership:fatty">Fatty (100PLN p/m)</a></li>
|
<li><a href="/admin/member/{{member.username}}/membership:fatty">Fatty (100PLN p/m)</a></li>
|
||||||
<li><a href="/admin/member/{{member.ldap_username}}/membership:starving">Starving (50PLN p/m)</a></li>
|
<li><a href="/admin/member/{{member.username}}/membership:starving">Starving (50PLN p/m)</a></li>
|
||||||
<li><a href="/admin/member/{{member.ldap_username}}/membership:supporting">Supporting</a></li>
|
<li><a href="/admin/member/{{member.username}}/membership:supporting">Supporting</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -9,9 +9,9 @@
|
||||||
{{member.payment_policy}} <span class="caret"></span>
|
{{member.payment_policy}} <span class="caret"></span>
|
||||||
</button>
|
</button>
|
||||||
<ul class="dropdown-menu" role="menu">
|
<ul class="dropdown-menu" role="menu">
|
||||||
<li><a href="/admin/member/{{member.ldap_username}}/policy:normal">Normal</a></li>
|
<li><a href="/admin/member/{{member.username}}/policy:normal">Normal</a></li>
|
||||||
<li><a href="/admin/member/{{member.ldap_username}}/policy:extended">Extended Grace Period</a></li>
|
<li><a href="/admin/member/{{member.username}}/policy:extended">Extended Grace Period</a></li>
|
||||||
<li><a href="/admin/member/{{member.ldap_username}}/policy:potato">Potato</a></li>
|
<li><a href="/admin/member/{{member.username}}/policy:potato">Potato</a></li>
|
||||||
<li><a href="/admin/member/{{member.ldap_username}}/policy:disabled">Disabled</a></li>
|
<li><a href="/admin/member/{{member.username}}/policy:disabled">Disabled</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
</tr>
|
</tr>
|
||||||
{% for member in active_members %}
|
{% for member in active_members %}
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{member['ldap_username']}}</td>
|
<td>{{member['username']}}</td>
|
||||||
<td>{{member['type']}}</td>
|
<td>{{member['type']}}</td>
|
||||||
<td>{{member['joined'][0]}}/{{"%02i" |format(member['joined'][1])}}</td>
|
<td>{{member['joined'][0]}}/{{"%02i" |format(member['joined'][1])}}</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
|
@ -34,11 +34,11 @@ from subprocess import Popen, PIPE
|
||||||
|
|
||||||
from webapp import app, forms, User, db, models, mc, cache_enabled, admin_required
|
from webapp import app, forms, User, db, models, mc, cache_enabled, admin_required
|
||||||
from flask.ext.login import login_user, login_required, logout_user, current_user
|
from flask.ext.login import login_user, login_required, logout_user, current_user
|
||||||
from flask import request, redirect, flash, render_template, url_for, abort, g
|
from flask import Response, request, redirect, flash, render_template, url_for, abort, g
|
||||||
import banking
|
import banking
|
||||||
import logic
|
import logic
|
||||||
import directory
|
import directory
|
||||||
|
import traceback
|
||||||
|
|
||||||
@app.route('/')
|
@app.route('/')
|
||||||
def stats():
|
def stats():
|
||||||
|
@ -64,11 +64,11 @@ def memberlist():
|
||||||
@app.route('/profile')
|
@app.route('/profile')
|
||||||
@login_required
|
@login_required
|
||||||
def self_profile():
|
def self_profile():
|
||||||
member = models.Member.get_members(True).filter_by(ldap_username=current_user.username).first()
|
member = models.Member.get_members(True).filter_by(username=current_user.username).first()
|
||||||
if not member:
|
if not member:
|
||||||
abort(404)
|
abort(404)
|
||||||
status = member.get_status()
|
status = member.get_status()
|
||||||
cn = directory.get_member_fields(g.ldap, member.ldap_username, 'cn')['cn']
|
cn = directory.get_member_fields(g.ldap, member.username, 'cn')['cn']
|
||||||
return render_template("admin_member.html", member=member, status=status,
|
return render_template("admin_member.html", member=member, status=status,
|
||||||
cn=cn, admin=False)
|
cn=cn, admin=False)
|
||||||
|
|
||||||
|
@ -88,33 +88,70 @@ def admin_index():
|
||||||
|
|
||||||
active_members = filter(lambda m: m['judgement'], members)
|
active_members = filter(lambda m: m['judgement'], members)
|
||||||
inactive_members = filter(lambda m: not m['judgement'], members)
|
inactive_members = filter(lambda m: not m['judgement'], members)
|
||||||
|
diff = directory.get_ldap_group_diff(members)
|
||||||
|
if diff is not None:
|
||||||
|
flash("LDAP sync required")
|
||||||
return render_template("admin_index.html",
|
return render_template("admin_index.html",
|
||||||
active_members=active_members,
|
active_members=active_members,
|
||||||
inactive_members=inactive_members)
|
inactive_members=inactive_members)
|
||||||
|
|
||||||
|
@app.route("/admin/ldapsync", methods=["POST", "GET"])
|
||||||
|
@admin_required
|
||||||
|
@login_required
|
||||||
|
def admin_ldap_sync():
|
||||||
|
members = [m.get_status() for m in models.Member.get_members(True)]
|
||||||
|
diff = directory.get_ldap_group_diff(members)
|
||||||
|
if diff is None:
|
||||||
|
return render_template("admin_ldap_sync.html", form=False)
|
||||||
|
|
||||||
|
form = forms.LDAPSyncForm(request.form)
|
||||||
|
|
||||||
|
form.fatty_to_add.choices = zip(diff['fatty_to_add'],diff['fatty_to_add'])
|
||||||
|
form.fatty_to_add.default = diff['fatty_to_add']
|
||||||
|
|
||||||
|
form.fatty_to_remove.choices = zip(diff['fatty_to_remove'],diff['fatty_to_remove'])
|
||||||
|
form.fatty_to_remove.default = diff['fatty_to_remove']
|
||||||
|
|
||||||
|
form.starving_to_add.choices = zip(diff['starving_to_add'],diff['starving_to_add'])
|
||||||
|
form.starving_to_add.default = diff['starving_to_add']
|
||||||
|
|
||||||
|
form.starving_to_remove.choices = zip(diff['starving_to_remove'],diff['starving_to_remove'])
|
||||||
|
form.starving_to_remove.default = diff['starving_to_remove']
|
||||||
|
|
||||||
|
form.process(request.form)
|
||||||
|
if request.method == "POST" and form.validate():
|
||||||
|
changes = {'fatty': {}, 'starving': {}}
|
||||||
|
changes['fatty']['add'] = form.fatty_to_add.data
|
||||||
|
changes['fatty']['remove'] = form.fatty_to_remove.data
|
||||||
|
changes['starving']['add'] = form.starving_to_add.data
|
||||||
|
changes['starving']['remove'] = form.starving_to_remove.data
|
||||||
|
|
||||||
|
directory.update_member_groups(g.ldap, changes)
|
||||||
|
|
||||||
|
return render_template("admin_ldap_sync.html", form=form)
|
||||||
|
|
||||||
|
|
||||||
@app.route("/admin/csv")
|
@app.route("/admin/csv")
|
||||||
@admin_required
|
@admin_required
|
||||||
@login_required
|
@login_required
|
||||||
def admin_csv():
|
def admin_csv():
|
||||||
members = [m.get_status() for m in models.Member.get_members(True)]
|
members = [m.get_status() for m in models.Member.get_members(True)]
|
||||||
for member in members:
|
for member in members:
|
||||||
member["cn"] = directory.get_member_fields(g.ldap, member.ldap_username, 'cn')['cn']
|
member["cn"] = directory.get_member_fields(g.ldap, member['username'], 'cn')['cn']
|
||||||
|
|
||||||
active_members = filter(lambda m: m['judgement'] and not m['type'] == 'supporting', members)
|
active_members = filter(lambda m: m['judgement'] and not m['type'] == 'supporting', members)
|
||||||
|
output = render_template("admin_csv.html", active_members=active_members)
|
||||||
return render_template("admin_csv.html",
|
return Response(output)
|
||||||
active_members=active_members)
|
|
||||||
|
|
||||||
@app.route('/admin/member/<membername>')
|
@app.route('/admin/member/<membername>')
|
||||||
@login_required
|
@login_required
|
||||||
@admin_required
|
@admin_required
|
||||||
def admin_member(membername):
|
def admin_member(membername):
|
||||||
member = models.Member.get_members(True).filter_by(ldap_username=membername).first()
|
member = models.Member.get_members(True).filter_by(username=membername).first()
|
||||||
if not member:
|
if not member:
|
||||||
abort(404)
|
abort(404)
|
||||||
status = member.get_status()
|
status = member.get_status()
|
||||||
cn = directory.get_member_fields(g.ldap, member.ldap_username, 'cn')['cn']
|
cn = directory.get_member_fields(g.ldap, member.username, 'cn')['cn']
|
||||||
return render_template("admin_member.html", member=member, status=status,
|
return render_template("admin_member.html", member=member, status=status,
|
||||||
cn=cn, admin=True)
|
cn=cn, admin=True)
|
||||||
|
|
||||||
|
@ -122,7 +159,7 @@ def admin_member(membername):
|
||||||
@login_required
|
@login_required
|
||||||
@admin_required
|
@admin_required
|
||||||
def admin_member_set_policy(membername,policy):
|
def admin_member_set_policy(membername,policy):
|
||||||
member = models.Member.query.filter_by(ldap_username=membername).first()
|
member = models.Member.query.filter_by(username=membername).first()
|
||||||
member.payment_policy = models.PaymentPolicy[policy].value
|
member.payment_policy = models.PaymentPolicy[policy].value
|
||||||
db.session.add(member)
|
db.session.add(member)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
@ -132,7 +169,7 @@ def admin_member_set_policy(membername,policy):
|
||||||
@login_required
|
@login_required
|
||||||
@admin_required
|
@admin_required
|
||||||
def admin_member_set_membership(membername,membershiptype):
|
def admin_member_set_membership(membername,membershiptype):
|
||||||
member = models.Member.query.filter_by(ldap_username=membername).first()
|
member = models.Member.query.filter_by(username=membername).first()
|
||||||
member.type = models.MembershipType[membershiptype].name
|
member.type = models.MembershipType[membershiptype].name
|
||||||
db.session.add(member)
|
db.session.add(member)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
@ -168,7 +205,7 @@ def admin_fetch():
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
exc_type, exc_value, exc_traceback = sys.exc_info()
|
exc_type, exc_value, exc_traceback = sys.exc_info()
|
||||||
|
|
||||||
flash("Error when fetching data. %s" % traceback.format_exception(exc_type, exc_value,exc_traceback))
|
flash("Error when fetching data. <pre>%s</pre>" % traceback.format_exception(exc_type, exc_value,exc_traceback))
|
||||||
return redirect(url_for("admin_fetch"))
|
return redirect(url_for("admin_fetch"))
|
||||||
|
|
||||||
logic.update_transfer_rows()
|
logic.update_transfer_rows()
|
||||||
|
@ -185,17 +222,20 @@ def admin_match_auto():
|
||||||
left = 0
|
left = 0
|
||||||
transfers_unmatched = logic.get_unmatched_transfers()
|
transfers_unmatched = logic.get_unmatched_transfers()
|
||||||
for transfer in transfers_unmatched:
|
for transfer in transfers_unmatched:
|
||||||
matchability, extra = transfer.get_matchability()
|
matchability, member, months = transfer.get_matchability()
|
||||||
if matchability == models.Transfer.MATCH_OK:
|
if matchability == models.Transfer.MATCH_OK:
|
||||||
member = extra
|
|
||||||
if len(member.transfers) > 0:
|
if len(member.transfers) > 0:
|
||||||
year, month = member.get_next_unpaid()
|
year, month = member.get_next_unpaid()
|
||||||
else:
|
else:
|
||||||
year, month = transfer.date.year, transfer.date.month
|
year, month = transfer.date.year, transfer.date.month
|
||||||
mt = models.MemberTransfer(None, year, month, transfer)
|
for m in range(months):
|
||||||
member.transfers.append(mt)
|
mt = models.MemberTransfer(None, year, month, transfer)
|
||||||
db.session.add(mt)
|
member.transfers.append(mt)
|
||||||
|
db.session.add(mt)
|
||||||
|
flash("Matched transfer {} to member {} for month {}-{}".format(transfer.id, member.username, year, month))
|
||||||
|
year, month = member._yearmonth_increment((year,month))
|
||||||
matched += 1
|
matched += 1
|
||||||
|
member.invalidate_cache()
|
||||||
else:
|
else:
|
||||||
left += 1
|
left += 1
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
Loading…
Reference in New Issue