Пример #1
0
def home(request):
    project_name = request.matchdict['project_name']
    session = DBSession()
    try:
        project = session.query(Project).filter_by(name=project_name).one()
    except NoResultFound:
        raise HTTPNotFound()
    if not has_permission(request, PERM_VIEW_PROJECT, project):
        raise HTTPForbidden()
    can_participate = has_permission(
        request, PERM_PARTICIPATE_IN_PROJECT, project)
    can_manage_project = has_permission(request, PERM_MANAGE_PROJECT, project)
    issues = session.query(Issue.id).filter_by(project_id=project.id).\
        filter(Issue.status!=ISSUE_STATUS_CLOSED)
    n_assigned = issues.filter_by(assignee=request.user.id).count()
    n_watching = 0  # FIXME
    n_not_assigned = issues.filter_by(assignee=None).count()
    search_form = make_simplified_search_form(project, session)
    recent_activity = get_recent_activity(session, project, request)
    return {'api': TemplateAPI(request, project.title),
            'project': project,
            'can_participate': can_participate,
            'can_manage_project': can_manage_project,
            'n_assigned': n_assigned,
            'n_watching': n_watching,
            'n_not_assigned': n_not_assigned,
            'search_form': search_form,
            'recent_activity': recent_activity}
Пример #2
0
def view(request, form=None):
    project_name = request.matchdict['project_name']
    try:
        issue_ref = int(request.matchdict['issue_ref'])
    except ValueError:
        # 'issue_ref' is not an integer. The route connected to this
        # view matched because no other route matched. This could be a
        # typo, for example: '/p/project-foo/confgure'.
        raise HTTPNotFound()
    session = DBSession()
    try:
        project = session.query(Project).filter_by(name=project_name).one()
    except NoResultFound:
        raise HTTPNotFound()
    if not has_permission(request, PERM_VIEW_PROJECT, project):
        raise HTTPForbidden()
    try:
        issue = session.query(Issue).filter_by(
            project_id=project.id, ref=issue_ref).one()
    except NoResultFound:
        raise HTTPNotFound()
    if form is None:
        data = {'assignee': issue.assignee,
                'deadline': issue.deadline,
                'kind': issue.kind,
                'priority': issue.priority,
                'status': issue.status,
                'time_estimated': issue.time_estimated,
                'time_billed': issue.time_billed,
                'title': issue.title}
        form = make_add_change_form(project, session, **data)
    can_see_priv = has_permission(
        request, PERM_SEE_PRIVATE_TIMING_INFO, project)
    can_participate = has_permission(
        request, PERM_PARTICIPATE_IN_PROJECT, project)
    can_manage_project = has_permission(request, PERM_MANAGE_PROJECT, project)
    time_info = issue.get_time_info(include_private_info=can_see_priv)
    return {'api': TemplateAPI(request, issue.title),
            'project': project,
            'issue': issue,
            'time_info': time_info,
            'form': form,
            'can_participate': can_participate,
            'can_manage_project': can_manage_project,
            'can_see_private_time_info': can_see_priv}
Пример #3
0
def recent_activity(request):
    project_name = request.matchdict['project_name']
    session = DBSession()
    try:
        project = session.query(Project).filter_by(name=project_name).one()
    except NoResultFound:
        raise HTTPNotFound()
    if not has_permission(request, PERM_VIEW_PROJECT, project):
        raise HTTPForbidden()
    can_participate = has_permission(
        request, PERM_PARTICIPATE_IN_PROJECT, project)
    can_manage_project = has_permission(request, PERM_MANAGE_PROJECT, project)
    recent_activity = get_recent_activity(session, project, request)
    return {'api': TemplateAPI(request, project.title),
            'project': project,
            'can_participate': can_participate,
            'can_manage_project': can_manage_project,
            'recent_activity': recent_activity}
Пример #4
0
def add_form(request, form=None):
    session = DBSession()
    project_name = request.matchdict['project_name']
    try:
        project = session.query(Project).filter_by(name=project_name).one()
    except NoResultFound:
        raise HTTPNotFound()
    if not has_permission(request, PERM_PARTICIPATE_IN_PROJECT, project):
        raise HTTPForbidden()
    if form is None:
        form = make_add_issue_form(project, session)
    can_see_priv = has_permission(
        request, PERM_SEE_PRIVATE_TIMING_INFO, project)
    can_manage_project = has_permission(request, PERM_MANAGE_PROJECT, project)
    return {'api': TemplateAPI(request, _(u'Add issue')),
            'project': project,
            'form': form,
            'can_see_private_time_info': can_see_priv,
            'can_manage_project': can_manage_project}
Пример #5
0
def issues(request):
    """A list of issues that correspond to the filter requested in the
    GET parameters.
    """
    project_name = request.matchdict['project_name']
    session = DBSession()
    try:
        project = session.query(Project).filter_by(name=project_name).one()
    except NoResultFound:
        raise HTTPNotFound()
    if not has_permission(request, PERM_VIEW_PROJECT, project):
        raise HTTPForbidden()
    can_participate = has_permission(
        request, PERM_PARTICIPATE_IN_PROJECT, project)
    can_manage_project = has_permission(request, PERM_MANAGE_PROJECT, project)
    issues = session.query(Issue).filter_by(project_id=project.id).\
        filter(Issue.status!=ISSUE_STATUS_CLOSED)
    filter = request.GET.get('filter')
    if filter == 'assigned-to-me':
        issues = issues.filter_by(assignee=request.user.id)
        filter_label = _('Assigned to me')
    elif filter == 'watching':
        # FIXME: not implemented yet
        filter_label = None
    elif filter == 'not-assigned':
        filter_label = _('Not assigned')
        issues = issues.filter_by(assignee=None)
    else:
        filter_label = None
        issues = ()
    if issues:
        issues = issues.all()
    return {'api': TemplateAPI(request, project.title),
            'project': project,
            'can_participate': can_participate,
            'can_manage_project': can_manage_project,
            'filter': filter,
            'filter_label': filter_label,
            'issues': issues,
            'count': len(issues)}
Пример #6
0
def configure_statuses(request):
    project_name = request.matchdict['project_name']
    session = DBSession()
    try:
        project = session.query(Project).filter_by(name=project_name).one()
    except NoResultFound:
        raise HTTPNotFound()
    if not has_permission(request, PERM_MANAGE_PROJECT, project):
        raise HTTPForbidden()
    posted_statuses = map(int, request.POST.getall('statuses'))
    # The UI should not allow to remove default statuses, but let's
    # enforce it here.
    for default_status in DEFAULT_STATUSES:
        if default_status['id'] not in posted_statuses:
            msg = _('You cannot remove this status.')
            request.session.flash(msg, 'error')
            return configure_statuses_form(request)
    # The UI should not allow to remove statuses that are being used,
    # but let's enforce it here.
    # FIXME: to do
    current_statuses = {}
    for status in project.statuses:
        current_statuses[status.id] = status
    statuses = zip(posted_statuses, request.POST.getall('labels'))
    # Change existing statuses and add new ones
    new_id = session.execute(
        'SELECT MAX(id) FROM statuses '
        'WHERE project_id=%d' % project.id).fetchone()[0]
    for position, (status_id, label) in enumerate(statuses, 1):
        if not status_id:
            new_id += 1
            status = Status(id=new_id, project_id=project.id,
                            label=label, position=position)
            session.add(status)
        else:
            status = current_statuses[status_id]
            if label != status.label:
                status.label = label
            if position != status.position:
                status.position = position
    # Remove statuses
    for status in project.statuses:
        if status.id not in posted_statuses:
            session.delete(status)
    msg = _('Your changes have been saved.')
    request.session.flash(msg, 'success')
    url = request.route_url('project_configure_statuses',
                            project_name=project.name)
    return HTTPSeeOther(location=url)
Пример #7
0
def configure_statuses_form(request):
    project_name = request.matchdict['project_name']
    session = DBSession()
    try:
        project = session.query(Project).filter_by(name=project_name).one()
    except NoResultFound:
        raise HTTPNotFound()
    if not has_permission(request, PERM_MANAGE_PROJECT, project):
        raise HTTPForbidden()
    used = [s[0] for s in session.query(distinct(Issue.status)).\
        filter_by(project_id=project.id).all()]
    # FIXME: We may want to include default statuses in 'used' as well.
    return {'api': TemplateAPI(request, project.title),
            'project': project,
            'can_manage_project': True,
            'used': used}
Пример #8
0
def configure_roles_form(request):
    project_name = request.matchdict['project_name']
    session = DBSession()
    try:
        project = session.query(Project).filter_by(name=project_name).one()
    except NoResultFound:
        raise HTTPNotFound()
    if not has_permission(request, PERM_MANAGE_PROJECT, project):
        raise HTTPForbidden()
    roles = []
    for role_id, label in ROLE_LABELS.items():
        roles.append({'id': role_id, 'label': label})
    user_roles = []
    for role, user in session.query(Role, User).\
            filter(Role.project_id==project.id).\
            filter(Role.user_id==User.id).\
            order_by(User.fullname):
        user_roles.append({'user_id': user.id,
                           'fullname': user.fullname,
                           'role': role.role})
    if request.user.is_admin:
        ids_with_role = [u['user_id'] for u in user_roles]
        users_with_no_role = [{'id': 0, 'fullname': _(u'Select a user...')}]
        # 'ids_with_role' may be empty. SQLAlchemy informs us in this
        # case that
        #     the IN-predicate on "users.id" was invoked with an empty
        #     sequence. This results in a contradiction, which
        #     nonetheless can be expensive to evaluate.
        # Hence the construction of a tuple that contains at least -1 (an
        # impossible user id).
        for user in session.query(User).\
                filter(~User.id.in_((-1, ) + tuple(ids_with_role))).\
                filter_by(is_admin=False).\
                order_by(User.fullname):
            users_with_no_role.append({'id': user.id,
                                       'fullname': user.fullname})
    else:
        # Project managers are not allowed to grant a role to users
        # who do not have a prior role in the project.
        users_with_no_role = ()
    return {'api': TemplateAPI(request, project.title),
            'can_manage_project': True,
            'project': project,
            'roles': roles,
            'user_roles': user_roles,
            'users_with_no_role': users_with_no_role}
Пример #9
0
def configure(request):
    project_name = request.matchdict['project_name']
    session = DBSession()
    try:
        project = session.query(Project).filter_by(name=project_name).one()
    except NoResultFound:
        raise HTTPNotFound()
    if not has_permission(request, PERM_MANAGE_PROJECT, project):
        raise HTTPForbidden()
    form = make_edit_project_form(request.POST)
    if not form.validate():
        return configure_form(request, form)
    project.update(**form.data)
    msg = _(u'Changes have been saved.')
    request.session.flash(msg, 'success')
    url = request.route_url('project_configure', project_name=project.name)
    return HTTPSeeOther(url)
Пример #10
0
def configure_form(request, form=None):
    project_name = request.matchdict['project_name']
    session = DBSession()
    try:
        project = session.query(Project).filter_by(name=project_name).one()
    except NoResultFound:
        raise HTTPNotFound()
    if not has_permission(request, PERM_MANAGE_PROJECT, project):
        raise HTTPForbidden()
    if form is None:
        data = {'title': project.title,
                'public': project.public}
        form = make_edit_project_form(**data)
    return {'api': TemplateAPI(request, project.title),
            'project': project,
            'can_manage_project': True,
            'form': form}
Пример #11
0
def configure_roles(request):
    project_name = request.matchdict['project_name']
    session = DBSession()
    try:
        project = session.query(Project).filter_by(name=project_name).one()
    except NoResultFound:
        raise HTTPNotFound()
    if not has_permission(request, PERM_MANAGE_PROJECT, project):
        raise HTTPForbidden()
    current_roles = {}
    for role in session.query(Role).filter_by(project_id=project.id):
        current_roles[role.user_id] = role.role
    updated_roles = []
    for field, role_id in request.POST.items():
        if not field.startswith('role_'):  # pragma: no cover
            continue
        user_id = int(field[1 + field.rfind('_'):])
        if not request.user.is_admin:
            if user_id == request.user.id:
                # Manager cannot revoke his/her own manager role. This
                # is not allowed by the UI but we handle the case
                # anyway.
                msg = _(u'You cannot revoke your own manager role.')
                request.session.flash(msg, 'error')
                return configure_roles_form(request)
            if user_id not in current_roles:
                # A non-administrator cannot grant a role to a user
                # who has no prior role in this project. This is not
                # allowed by the UI but we handle the case anyway.
                msg = _(u'Granting a role to a new user is not allowed '
                        u'because you are not an administrator.')
                request.session.flash(msg, 'error')
                return configure_roles_form(request)
        role_id = int(role_id)
        if role_id:
            updated_roles.append(Role(project_id=project.id,
                                      user_id=user_id,
                                      role=role_id))
    session.query(Role).filter_by(project_id=project.id).delete()
    session.add_all(updated_roles)
    request.session.flash(_(u'Roles have been updated'), 'success')
    location = request.route_url('project_configure_roles',
                                 project_name=project.name)
    return HTTPSeeOther(location=location)
Пример #12
0
def add(request):
    session = DBSession()
    project_name = request.matchdict['project_name']
    try:
        project = session.query(Project).filter_by(name=project_name).one()
    except NoResultFound:
        raise HTTPNotFound()
    if not has_permission(request, PERM_PARTICIPATE_IN_PROJECT, project):
        raise HTTPForbidden()
    form = make_add_issue_form(project, session, request.POST)
    if not form.validate():
        return add_form(request, form)

    last_ref = session.execute(
        'SELECT MAX(ref) FROM issues '
        'WHERE project_id=%d' % project.id).fetchone()[0]
    if last_ref is None:
        last_ref = 0
    ref = last_ref + 1
    reporter = request.user.id
    now = datetime.utcnow()
    issue = Issue(project_id=project.id,
                  date_created=now,
                  date_edited=now,
                  reporter=reporter,
                  ref=ref)
    form.populate_obj(issue)
    session.add(issue)
    session.flush()
    change = Change(project_id=project.id,
                    issue_id=issue.id,
                    type=CHANGE_TYPE_OPENING,
                    author=reporter,
                    date=now,
                    changes={})
    form.populate_obj(change)
    session.add(change)
    route_args = {'project_name': project_name, 'issue_ref': issue.ref}
    url = request.route_url('issue_view', **route_args)
    return HTTPSeeOther(location=url)
Пример #13
0
 def _call_fut(self, *args, **kwargs):
     from yait.auth import has_permission
     return has_permission(*args, **kwargs)
Пример #14
0
def update(request):
    project_name = request.matchdict['project_name']
    issue_ref = int(request.matchdict['issue_ref'])
    session = DBSession()
    try:
        project = session.query(Project).filter_by(name=project_name).one()
    except NoResultFound:
        raise HTTPNotFound()
    if not has_permission(request, PERM_PARTICIPATE_IN_PROJECT, project):
        raise HTTPForbidden()
    try:
        issue = session.query(Issue).filter_by(
            project_id=project.id, ref=issue_ref).one()
    except NoResultFound:
        raise HTTPNotFound()

    # FIXME: move logic outside so that it can be more easily tested.
    form = make_add_change_form(project, session, request.POST)
    if not form.validate():
        return view(request, form)
    now = datetime.utcnow()
    userid = request.user.id
    changes = {}
    for attr in (
        'title', 'status', 'assignee', 'deadline', 'priority', 'kind',
        'time_estimated', 'time_billed'):
        old_v = getattr(issue, attr)
        new_v = getattr(form, attr).data
        if old_v != new_v:
            changes[attr] = (old_v, new_v)
            setattr(issue, attr, new_v)
    if form.time_spent_real.data and \
            has_permission(request, PERM_SEE_PRIVATE_TIMING_INFO, project):
        changes['time_spent_real'] = (None, form.time_spent_real.data)
    if form.time_spent_public.data:
        changes['time_spent_public'] = (None, form.time_spent_public.data)

    if not changes and not form.text.data:
        error = _(u'You did not provide any comment or update.')
        form.errors['form'] = [error]
        return view(request, form)

    if form.status.data == ISSUE_STATUS_OPEN and \
            issue.status != ISSUE_STATUS_OPEN:
        change_type = CHANGE_TYPE_REOPENING
    elif form.status.data == ISSUE_STATUS_CLOSED:
        change_type = CHANGE_TYPE_CLOSING
    else:
        change_type = CHANGE_TYPE_UPDATE
    change = Change(project_id=project.id,
                    issue_id=issue.id,
                    type=change_type,
                    author=userid,
                    date=now,
                    text=form.text.data,
                    text_renderer=form.text_renderer.data,
                    changes=changes)
    if 'time_spent_real' in changes:
        change.time_spent_real = form.time_spent_real.data
    if 'time_spent_public' in changes:
        change.time_spent_public = form.time_spent_public.data
    session.add(change)
    route_args = {'project_name': project_name,
                  'issue_ref': issue.ref,
                  '_query': {'issue_updated': 1},
                  '_anchor': 'issue_updated'}
    url = request.route_url('issue_view', **route_args)
    return HTTPSeeOther(location=url)