group.py 6.82 KB
from pyramid.view import view_config
from pyramid.httpexceptions import HTTPFound
from pyramid.i18n import TranslationStringFactory
import colander
from deform import (
    Form,
    Button,
    ValidationFailure,
    )
from deform.widget import (
    TextAreaWidget,
    HiddenWidget,
    CheckboxChoiceWidget,
    )
from ..models import DBSession
from ..models.ziggurat import (
    Group,
    GroupPermission,
    )


_ = TranslationStringFactory('user')


########
# List #
########
@view_config(
    route_name='group', renderer='templates/group/list.pt',
    permission='user-edit')
def view_list(request):
    q = DBSession.query(Group).order_by(Group.group_name)
    return dict(groups=q, title=_('Groups'))


#######
# Add #
#######
def clean_name(s):
    s = s.strip()
    while s.find('  ') > -1:
        s = s.replace('  ', ' ')
    return s


class GroupNameValidator:
    def __init__(self, group):
        self.group = group

    def __call__(self, node, value):
        group_name = clean_name(value)
        if self.group and self.group.group_name.lower() == group_name.lower():
            return
        q = DBSession.query(Group).filter(Group.group_name.ilike(group_name))
        found = q.first()
        if not found:
            return
        data = dict(group_name=group_name, gid=found.id)
        ts = _(
                'group-name-already-used',
                default='Group name ${group_name} already used by ID ${gid}',
                mapping=data)
        raise colander.Invalid(node, ts)


@colander.deferred
def deferred_group_name_validator(node, kw):
    return GroupNameValidator(kw['group'])


@colander.deferred
def deferred_permissions(node, kw):
    values = kw.get('permission_list', [])
    return CheckboxChoiceWidget(values=values)


class AddSchema(colander.Schema):
    group_name = colander.SchemaNode(
            colander.String(), title=_('Group name'),
            validator=deferred_group_name_validator)
    description = colander.SchemaNode(
                    colander.String(),
                    missing=colander.drop,
                    widget=TextAreaWidget(rows=5),
                    title=_('Description'))
    permissions = colander.SchemaNode(
            colander.Set(), widget=deferred_permissions,
            title=_('Permissions'))


class EditSchema(AddSchema):
    id = colander.SchemaNode(
            colander.String(), widget=HiddenWidget(readonly=True))


PERMISSIONS = [
    ('edit-user', _('User management'))
    ]


def get_form(request, class_form, group=None):
    schema = class_form()
    schema = schema.bind(permission_list=PERMISSIONS, group=group)
    btn_save = Button('save', _('Save'))
    btn_cancel = Button('cancel', _('Cance'))
    buttons = (btn_save, btn_cancel)
    return Form(schema, buttons=buttons)


def insert(values):
    group = Group()
    group.group_name = clean_name(values['group_name'])
    group.description = values['description']
    DBSession.add(group)
    DBSession.flush()
    for perm_name in values['permissions']:
        gp = GroupPermission()
        gp.group_id = group.id
        gp.perm_name = perm_name
        DBSession.add(gp)
    return group


@view_config(
    route_name='group-add', renderer='templates/group/add.pt',
    permission='user-edit')
def view_add(request):
    form = get_form(request, AddSchema)
    resp = dict(title=_('Add goup'))
    if not request.POST:
        resp['form'] = form.render()
        return resp
    if 'save' not in request.POST:
        return HTTPFound(location=request.route_url('group'))
    items = request.POST.items()
    try:
        c = form.validate(items)
    except ValidationFailure:
        resp['form'] = form.render()
        return resp
    group = insert(dict(c.items()))
    data = dict(group_name=group.group_name)
    ts = _(
            'group-added',
            default='${group_name} group has been added.',
            mapping=data)
    request.session.flash(ts)
    return HTTPFound(location=request.route_url('group'))


########
# Edit #
########
def group_permission_set(group):
    q = DBSession.query(GroupPermission).filter_by(group_id=group.id)
    r = []
    for gp in q:
        r.append(gp.perm_name)
    return set(r)


def update(group, values):
    group.group_name = clean_name(values['group_name'])
    group.description = values['description']
    DBSession.add(group)
    existing = group_permission_set(group)
    unused = existing - values['permissions']
    if unused:
        q = DBSession.query(GroupPermission).filter_by(group_id=group.id).\
                filter(GroupPermission.perm_name.in_(unused))
        q.delete(synchronize_session=False)
    new = values['permissions'] - existing
    for perm_name in new:
        gp = GroupPermission()
        gp.group_id = group.id
        gp.perm_name = perm_name
        DBSession.add(gp)


@view_config(
    route_name='group-edit', renderer='templates/group/edit.pt',
    permission='user-edit')
def view_edit(request):
    q = DBSession.query(Group).filter_by(id=request.matchdict['id'])
    group = q.first()
    if not group:
        return HTTPNotFound()
    form = get_form(request, EditSchema, group)
    resp = dict(title=_('Edit group'))
    if not request.POST:
        d = group.to_dict_without_none()
        d['permissions'] = group_permission_set(group)
        resp['form'] = form.render(appstruct=d)
        return resp
    if 'save' not in request.POST:
        return HTTPFound(location=request.route_url('group'))
        resp['form'] = form.render()
    items = request.POST.items()
    try:
        c = form.validate(items)
    except ValidationFailure:
        resp['form'] = form.render()
        return resp
    update(group, dict(c.items()))
    data = dict(group_name=group.group_name)
    ts = _(
            'group-updated', default='${group_name} group profile updated',
            mapping=data)
    request.session.flash(ts)
    return HTTPFound(location=request.route_url('group'))


##########
# Delete #
##########
@view_config(
    route_name='group-delete', renderer='templates/group/delete.pt',
    permission='user-edit')
def view_delete(request):
    q = DBSession.query(Group).filter_by(id=request.matchdict['id'])
    group = q.first()
    if not group:
        return HTTPNotFound()
    if not request.POST:
        btn_delete = Button('delete', _('Delete'))
        btn_cancel = Button('cancel', _('Cancel'))
        buttons = (btn_delete, btn_cancel)
        form = Form(colander.Schema(), buttons=buttons)
        return dict(title=_('Delete group'), form=form.render(), group=group)
    if 'delete' not in request.POST:
        return HTTPFound(location=request.route_url('group'))
    data = dict(group_name=group.group_name)
    ts = _(
            'group-deleted',
            default='{group_name} group has been deleted.',
            mapping=data)
    q.delete()
    request.session.flash(ts)
    return HTTPFound(location=request.route_url('group'))