Example #1
0
def delete_extract(request):
    user_id = request.authenticated_userid
    discussion_id = int(request.matchdict['discussion_id'])

    if not user_id:
        # Straight from annotator
        token = request.headers.get('X-Annotator-Auth-Token')
        if token:
            token = decode_token(token,
                                 request.registry.settings['session.secret'])
            if token:
                user_id = token['userId']
    user_id = user_id or Everyone

    extract_id = request.matchdict['id']
    extract = Extract.get_instance(extract_id)

    if not (user_has_permission(discussion_id, user_id, P_EDIT_EXTRACT) or
            (user_has_permission(discussion_id, user_id, P_EDIT_MY_EXTRACT)
             and user_id == extract.owner_id)):
        raise HTTPForbidden()

    if not extract:
        return HTTPNoContent()

    # TODO: Tombstonable extracts???
    extract.delete()
    return HTTPNoContent()
Example #2
0
def delete_extract(request):
    user_id = authenticated_userid(request)
    discussion_id = int(request.matchdict['discussion_id'])

    if not user_id:
        # Straight from annotator
        token = request.headers.get('X-Annotator-Auth-Token')
        if token:
            token = decode_token(
                token, request.registry.settings['session.secret'])
            if token:
                user_id = token['userId']
    user_id = user_id or Everyone

    extract_id = request.matchdict['id']
    extract = Extract.get_instance(extract_id)

    if not (user_has_permission(discussion_id, user_id, P_EDIT_EXTRACT)
        or (user_has_permission(discussion_id, user_id, P_EDIT_MY_EXTRACT)
            and user_id == extract.owner_id)):
        raise HTTPForbidden()

    if not extract:
        return HTTPNoContent()

    with transaction.manager:
        # TODO: Tombstonable extracts???
        Extract.default_db.delete(extract)
    request.response.status = HTTPNoContent.code
    return HTTPNoContent()
Example #3
0
def put_extract(request):
    """
    Updating an Extract
    """
    extract_id = request.matchdict['id']
    user_id = authenticated_userid(request)
    discussion = request.context

    if not user_id:
        # Straight from annotator
        token = request.headers.get('X-Annotator-Auth-Token')
        if token:
            token = decode_token(token,
                                 request.registry.settings['session.secret'])
            if token:
                user_id = token['userId']
    user_id = user_id or Everyone

    updated_extract_data = json.loads(request.body)
    extract = Extract.get_instance(extract_id)
    if not extract:
        raise HTTPNotFound("Extract with id '%s' not found." % extract_id)

    if not (user_has_permission(discussion.id, user_id, P_EDIT_EXTRACT) or
            (user_has_permission(discussion.id, user_id, P_EDIT_MY_EXTRACT)
             and user_id == extract.owner_id)):
        raise HTTPForbidden()

    extract.owner_id = user_id or AgentProfile.get_database_id(
        extract.owner_id)
    extract.order = updated_extract_data.get('order', extract.order)
    extract.important = updated_extract_data.get('important',
                                                 extract.important)
    idea_id = updated_extract_data.get('idIdea', None)
    if idea_id:
        idea = Idea.get_instance(idea_id)
        if (idea.discussion != extract.discussion):
            raise HTTPBadRequest(
                "Extract from discussion %s cannot be associated with an idea from a different discussion."
                % extract.get_discussion_id())
        extract.idea = idea
    else:
        extract.idea = None

    Extract.default_db.add(extract)
    #TODO: Merge ranges. Sigh.

    return {'ok': True}
Example #4
0
def discussion_list_view(request):
    request.session.pop('discussion')
    user_id = authenticated_userid(request) or Everyone
    user = None
    if user_id != Everyone:
        user = User.get(user_id)
    roles = get_roles(user_id)
    context = get_default_context(request)
    context['discussions'] = []
    # Show even anonymous users every discussion one has access to if
    # authenticated, so they can login and read them
    discussions = discussions_with_access(Authenticated if user_id ==
                                          Everyone else user_id)
    for discussion in discussions:
        discussionFrontendUrls = FrontendUrls(discussion)
        discussion_context = {
            'topic': discussion.topic,
            'slug': discussion.slug,
            'url': discussionFrontendUrls.get_discussion_url()
        }
        if user_has_permission(discussion.id, user_id, P_ADMIN_DISC):
            discussion_context[
                'admin_url'] = discussionFrontendUrls.get_discussion_edition_url(
                )
            discussion_context['permissions_url'] = request.route_url(
                'discussion_permissions', discussion_id=discussion.id)
        context['discussions'].append(discussion_context)
    if R_SYSADMIN in roles:
        context['discussions_admin_url'] = request.route_url(
            'discussion_admin')
        context['permissions_admin_url'] = request.route_url(
            'general_permissions')
    context['user'] = user
    return context
Example #5
0
def discussion_list_view(request):
    user_id = authenticated_userid(request) or Everyone
    user = None
    if user_id != Everyone:
        user = User.get(user_id)
    roles = get_roles(user_id)
    context = get_default_context(request)
    context['discussions'] = []
    #Show even anonymous users every discussion one has access to if 
    #authenticated, so they can login and read them
    discussions = discussions_with_access(Authenticated if user_id == Everyone else user_id)
    for discussion in discussions:
        discussionFrontendUrls = FrontendUrls(discussion)
        discussion_context = {
            'topic': discussion.topic,
            'slug': discussion.slug,
            'url': discussionFrontendUrls.get_discussion_url()
        }
        if user_has_permission(discussion.id, user_id, P_ADMIN_DISC):
            discussion_context['admin_url'] = discussionFrontendUrls.get_discussion_edition_url()
            discussion_context['permissions_url'] = request.route_url(
                'discussion_permissions', discussion_id=discussion.id)
        context['discussions'].append(discussion_context)
    if R_SYSADMIN in roles:
        context['discussions_admin_url'] = request.route_url('discussion_admin')
        context['permissions_admin_url'] = request.route_url('general_permissions')
    context['user'] = user
    return context
Example #6
0
def do_search_extracts(request):
    uri = request.GET.get('uri', None)
    if not uri:
        raise HTTPClientError("Please specify a URI")
    view_def = request.GET.get('view') or 'default'
    discussion = request.context
    user_id = authenticated_userid(request)
    if not user_id:
        # Straight from annotator
        token = request.headers.get('X-Annotator-Auth-Token')
        if token:
            token = decode_token(token,
                                 request.registry.settings['session.secret'])
            if token:
                user_id = token['userId']
    user_id = user_id or Everyone
    if not user_has_permission(discussion.id, user_id, P_READ):
        raise HTTPForbidden()
    permissions = [P_READ]

    if not uri:
        raise HTTPBadRequest("Please specify a search uri")
    content = Webpage.get_by(url=uri)
    if content:
        extracts = Extract.default_db.query(Extract).filter_by(
            content=content).all()
        rows = [
            extract.generic_json(view_def, user_id, permissions)
            for extract in extracts
        ]
        return {"total": len(extracts), "rows": rows}
    return {"total": 0, "rows": []}
Example #7
0
def put_extract(request):
    """
    Updating an Extract
    """
    extract_id = request.matchdict['id']
    user_id = authenticated_userid(request)
    discussion_id = int(request.matchdict['discussion_id'])

    if not user_id:
        # Straight from annotator
        token = request.headers.get('X-Annotator-Auth-Token')
        if token:
            token = decode_token(
                token, request.registry.settings['session.secret'])
            if token:
                user_id = token['userId']
    if not user_id:
        user_id = Everyone

    updated_extract_data = json.loads(request.body)
    extract = Extract.get_instance(extract_id)
    if not extract:
        raise HTTPNotFound("Extract with id '%s' not found." % extract_id)

    if not (user_has_permission(discussion_id, user_id, P_EDIT_EXTRACT)
        or (user_has_permission(discussion_id, user_id, P_EDIT_MY_EXTRACT)
            and user_id == extract.owner_id)):
        return HTTPForbidden()

    extract.owner_id = user_id or get_database_id("User", extract.owner_id)
    extract.order = updated_extract_data.get('order', extract.order)
    extract.important = updated_extract_data.get('important', extract.important)
    idea_id = updated_extract_data.get('idIdea', None)
    if idea_id:
        idea = Idea.get_instance(idea_id)
        if(idea.discussion != extract.discussion):
            raise HTTPBadRequest(
                "Extract from discussion %s cannot be associated with an idea from a different discussion." % extract.get_discussion_id())
        extract.idea = idea
    else:
        extract.idea = None

    Extract.db.add(extract)
    #TODO: Merge ranges. Sigh.

    return {'ok': True}
Example #8
0
def home_view(request):
    user_id = authenticated_userid(request) or Everyone
    context = get_default_context(request)
    discussion = context["discussion"]
    canRead = user_has_permission(discussion.id, user_id, P_READ)
    if not canRead and user_id == Everyone:
        # User isn't logged-in and discussion isn't public:
        # redirect to login page
        login_url = request.route_url(
            'contextual_login', discussion_slug=discussion.slug)
        return HTTPSeeOther(login_url)
    elif not canRead:
        # User is logged-in but doesn't have access to the discussion
        return HTTPUnauthorized()

    canAddExtract = user_has_permission(context["discussion"].id, user_id, P_ADD_EXTRACT)
    context['canAddExtract'] = canAddExtract
    context['canDisplayTabs'] = True
    response = render_to_response('../../templates/index.jinja2', context, request=request)
    # Prevent caching the home, especially for proper login/logout
    response.cache_control.max_age = 0
    response.cache_control.prevent_auto = True
    return response
Example #9
0
def home_view(request):
    """The main view on a discussion"""
    user_id = authenticated_userid(request) or Everyone
    context = get_default_context(request)
    discussion = context["discussion"]
    canRead = user_has_permission(discussion.id, user_id, P_READ)
    if not canRead and user_id == Everyone:
        # User isn't logged-in and discussion isn't public:
        # redirect to login page
        # need to pass the route to go to *after* login as well

        # With regards to a next_view, if explicitly stated, then
        # that is the next view. If not stated, the referer takes
        # precedence. In case of failure, login redirects to the
        # discussion which is its context.
        next_view = request.params.get('next', None)
        if not next_view and discussion:
            # If referred here from a post url, want to be able to
            # send the user back. Usually, Assembl will send the user
            # here to login on private discussions.
            referrer = request.url
            next_view = path_qs(referrer)

        if discussion.preferences['authorization_server_backend']:
            login_url = request.route_url(
                "contextual_social_auth",
                discussion_slug=discussion.slug,
                backend=discussion.preferences['authorization_server_backend'],
                _query={"next": next_view})
        elif next_view:
            login_url = request.route_url("contextual_login",
                                          discussion_slug=discussion.slug,
                                          _query={"next": next_view})
        else:
            login_url = request.route_url('contextual_login',
                                          discussion_slug=discussion.slug)
        return HTTPTemporaryRedirect(login_url)
    elif not canRead:
        # User is logged-in but doesn't have access to the discussion
        # Would use render_to_response, except for the 401
        from pyramid_jinja2 import IJinja2Environment
        jinja_env = request.registry.queryUtility(IJinja2Environment,
                                                  name='.jinja2')
        template = jinja_env.get_template('cannot_read_discussion.jinja2')
        body = template.render(get_default_context(request))
        return Response(body, 401)

    # if the route asks for a post, get post content (because this is needed for meta tags)
    route_name = request.matched_route.name
    if route_name == "purl_posts":
        post_id = FrontendUrls.getRequestedPostId(request)
        if not post_id:
            return HTTPSeeOther(
                request.route_url('home', discussion_slug=discussion.slug))
        post = Post.get_instance(post_id)
        if not post or post.discussion_id != discussion.id:
            return HTTPSeeOther(
                request.route_url('home', discussion_slug=discussion.slug))
        context['post'] = post
    elif route_name == "purl_idea":
        idea_id = FrontendUrls.getRequestedIdeaId(request)
        if not idea_id:
            return HTTPSeeOther(
                request.route_url('home', discussion_slug=discussion.slug))
        idea = Idea.get_instance(idea_id)
        if not idea or idea.discussion_id != discussion.id:
            return HTTPSeeOther(
                request.route_url('home', discussion_slug=discussion.slug))
        context['idea'] = idea

    canAddExtract = user_has_permission(discussion.id, user_id, P_ADD_EXTRACT)
    context['canAddExtract'] = canAddExtract
    context['canDisplayTabs'] = True
    preferences = discussion.preferences
    if user_id != Everyone:
        from assembl.models import UserPreferenceCollection
        user = User.get(user_id)
        preferences = UserPreferenceCollection(user_id, discussion)
        # TODO: user may not exist. Case of session with BD change.
        user.is_visiting_discussion(discussion.id)
        session = Discussion.default_db

        if '_LOCALE_' in request.cookies:
            locale = request.cookies['_LOCALE_']
            process_locale(locale, user, session,
                           LanguagePreferenceOrder.Cookie)

        elif '_LOCALE_' in request.params:
            locale = request.params['_LOCALE_']
            process_locale(locale, user, session,
                           LanguagePreferenceOrder.Parameter)
        else:
            locale = locale_negotiator(request)
            process_locale(locale, user, session,
                           LanguagePreferenceOrder.OS_Default)
    else:
        locale = request.localizer.locale_name

    target_locale = Locale.get_or_create(strip_country(locale), discussion.db)

    translation_service_data = {}
    try:
        service = discussion.translation_service()
        if service:
            translation_service_data = service.serviceData()
    except:
        pass
    context['translation_service_data_json'] = json.dumps(
        translation_service_data)
    locale_labels = json.dumps(
        DummyGoogleTranslationService.target_locale_labels_cls(target_locale))
    context['translation_locale_names_json'] = locale_labels

    context['preferences_json'] = json.dumps(dict(preferences))

    response = render_to_response('../../templates/index.jinja2',
                                  context,
                                  request=request)
    # Prevent caching the home, especially for proper login/logout
    response.cache_control.max_age = 0
    response.cache_control.prevent_auto = True
    return response
Example #10
0
def discussion_permissions(request):
    user_id = authenticated_userid(request)
    assert user_id
    db = Discussion.default_db
    discussion_id = int(request.matchdict['discussion_id'])
    discussion = Discussion.get_instance(discussion_id)
    error = ''

    if not discussion:
        raise HTTPNotFound("Discussion with id '%d' not found." % (
            discussion_id,))

    roles = db.query(Role).all()
    roles_by_name = {r.name: r for r in roles}
    role_names = [r.name for r in roles]
    role_names.sort()
    permissions = db.query(Permission).all()
    perms_by_name = {p.name: p for p in permissions}
    permission_names = [p.name for p in permissions]
    permission_names.sort()

    disc_perms = db.query(DiscussionPermission).filter_by(
        discussion_id=discussion_id).join(Role, Permission).all()
    disc_perms_as_set = set((dp.role.name, dp.permission.name)
                            for dp in disc_perms)
    disc_perms_dict = {(dp.role.name, dp.permission.name): dp
                       for dp in disc_perms}
    local_roles = db.query(LocalUserRole).filter_by(
        discussion_id=discussion_id).join(Role, User).all()
    local_roles_as_set = set((lur.user.id, lur.role.name)
                             for lur in local_roles)
    local_roles_dict = {(lur.user.id, lur.role.name): lur
                        for lur in local_roles}
    users = set(lur.user for lur in local_roles)
    num_users = ''

    if request.POST:
        if 'submit_role_permissions' in request.POST:
            for role in role_names:
                if role == R_SYSADMIN:
                    continue
                for permission in permission_names:
                    allowed_text = 'allowed_%s_%s' % (role, permission)
                    if (role, permission) not in disc_perms_as_set and \
                            allowed_text in request.POST:
                        dp = DiscussionPermission(
                            role=roles_by_name[role],
                            permission=perms_by_name[permission],
                            discussion_id=discussion_id)
                        disc_perms_dict[(role, permission)] = dp
                        disc_perms_as_set.add((role, permission))
                        db.add(dp)
                    elif (role, permission) in disc_perms_as_set and \
                            allowed_text not in request.POST:
                        dp = disc_perms_dict[(role, permission)]
                        del disc_perms_dict[(role, permission)]
                        disc_perms_as_set.remove((role, permission))
                        db.delete(dp)
                if not role in SYSTEM_ROLES and\
                        'delete_'+role in request.POST:
                    db.delete(roles_by_name[role])
                    del roles_by_name[role]
                    role_names.remove(role)
        elif 'submit_add_role' in request.POST:
            #TODO: Sanitize role
            role = Role(name='r:'+request.POST['new_role'])
            roles_by_name[role.name] = role
            role_names.append(role.name)
            db.add(role)
        elif 'submit_user_roles' in request.POST:
            user_ids = {u.id for u in users}
            for role in role_names:
                if role == R_SYSADMIN:
                    continue
                prefix = 'has_'+role+'_'
                for name in request.POST:
                    if name.startswith(prefix):
                        a_user_id = int(name[len(prefix):])
                        if a_user_id not in user_ids:
                            users.add(User.get_instance(a_user_id))
                            user_ids.add(a_user_id)
                for user in users:
                    has_role_text = 'has_%s_%d' % (role, user.id)
                    if (user.id, role) not in local_roles_as_set and \
                            has_role_text in request.POST:
                        lur = LocalUserRole(
                            role=roles_by_name[role],
                            user=user,
                            discussion_id=discussion_id)
                        local_roles.append(lur)

                        # TODO revisit this if Roles and Subscription are
                        # de-coupled
                        if role == 'r:participant':
                            user.update_agent_status_subscribe(discussion)

                        local_roles_dict[(user.id, role)] = lur
                        local_roles_as_set.add((user.id, role))
                        db.add(lur)
                    elif (user.id, role) in local_roles_as_set and \
                            has_role_text not in request.POST:
                        lur = local_roles_dict[(user.id, role)]
                        del local_roles_dict[(user.id, role)]
                        local_roles_as_set.remove((user.id, role))
                        local_roles.remove(lur)

                        # TODO revisit this if Roles and Subscription are
                        # de-coupled
                        if role == 'r:participant':
                            user.update_agent_status_unsubscribe(discussion)

                        db.delete(lur)

        elif 'submit_look_for_user' in request.POST:
            search_string = '%' + request.POST['user_search'] + '%'
            other_users = db.query(User).outerjoin(Username).filter(
                AgentProfile.name.ilike(search_string)
                | Username.username.ilike(search_string)
                | User.preferred_email.ilike(search_string)).all()
            users.update(other_users)
        elif 'submit_user_file' in request.POST:
            role = request.POST['add_with_role'] or R_PARTICIPANT
            if role == R_SYSADMIN and not user_has_permission(
                    discussion_id, user_id, P_SYSADMIN):
                role = R_ADMINISTRATOR
            if 'user_csvfile' in request.POST:
                try:
                    num_users = add_multiple_users_csv(
                        request, request.POST['user_csvfile'].file,
                        discussion_id, role,
                        request.POST.get('send_invite', False),
                        request.POST['email_subject'],
                        request.POST['text_email_message'],
                        request.POST['html_email_message'],
                        request.POST['sender_name'],
                        request.POST.get('resend_notloggedin', False))
                except Exception as e:
                    error = repr(e)
                    transaction.doom()
            else:
                error = request.localizer.translate(_('No file given.'))

    def allowed(role, permission):
        if role == R_SYSADMIN:
            return True
        return (role, permission) in disc_perms_as_set

    def has_local_role(user_id, role):
        return (user_id, role) in local_roles_as_set

    users = list(users)
    users.sort(key=order_by_domain_and_name)

    context = dict(
        get_default_context(request),
        discussion=discussion,
        allowed=allowed,
        roles=role_names,
        permissions=permission_names,
        users=users,
        error=error,
        num_users=num_users,
        has_local_role=has_local_role,
        is_system_role=lambda r: r in SYSTEM_ROLES
    )

    return render_to_response(
        'admin/discussion_permissions.jinja2',
        context,
        request=request)
Example #11
0
def post_extract(request):
    """
    Create a new extract.
    """
    extract_data = json.loads(request.body)
    discussion_id = int(request.matchdict['discussion_id'])
    user_id = request.authenticated_userid
    if not user_id:
        # Straight from annotator
        token = request.headers.get('X-Annotator-Auth-Token')
        if token:
            token = decode_token(token,
                                 request.registry.settings['session.secret'])
            if token:
                user_id = token['userId']
    user_id = user_id or Everyone
    if not user_has_permission(discussion_id, user_id, P_ADD_EXTRACT):
        #TODO: maparent:  restore this code once it works:
        #return HTTPForbidden(result=ACLDenied(permission=P_ADD_EXTRACT))
        return HTTPForbidden()
    if not user_id or user_id == Everyone:
        # TODO: Create an anonymous user.
        raise HTTPServerError("Anonymous extracts are not implemeted yet.")
    content = None
    uri = extract_data.get('uri')
    important = extract_data.get('important', False)
    annotation_text = None
    if uri:
        # Straight from annotator
        annotation_text = extract_data.get('text')
    else:
        target = extract_data.get('target')
        if not (target or uri):
            raise HTTPBadRequest("No target")

        target_class = sqla.get_named_class(target.get('@type'))
        if issubclass(target_class, Post):
            post_id = target.get('@id')
            post = Post.get_instance(post_id)
            if not post:
                raise HTTPNotFound("Post with id '%s' not found." % post_id)
            content = post
        elif issubclass(target_class, Webpage):
            uri = target.get('url')
    if uri and not content:
        content = Webpage.get_instance(uri)
        if not content:
            # TODO: maparent:  This is actually a singleton pattern, should be
            # handled by the AnnotatorSource now that it exists...
            source = AnnotatorSource.default_db.query(
                AnnotatorSource).filter_by(discussion_id=discussion_id).filter(
                    cast(AnnotatorSource.name, Unicode) ==
                    'Annotator').first()
            if not source:
                source = AnnotatorSource(name='Annotator',
                                         discussion_id=discussion_id)
            content = Webpage(url=uri, discussion_id=discussion_id)
    extract_body = extract_data.get('quote', '')

    idea_id = extract_data.get('idIdea', None)
    if idea_id:
        idea = Idea.get_instance(idea_id)
        if (idea.discussion.id != discussion_id):
            raise HTTPBadRequest(
                "Extract from discussion %s cannot be associated with an idea from a different discussion."
                % extract.get_discussion_id())
    else:
        idea = None

    ranges = extract_data.get('ranges', [])
    extract_hash = Extract.get_extract_hash(
        None, u"".join([r['start'] for r in ranges]),
        u"".join([r['end'] for r in ranges]),
        u"".join([r['startOffset'] for r in ranges]),
        u"".join([r['endOffset'] for r in ranges]), content.id)
    new_extract = Extract(creator_id=user_id,
                          owner_id=user_id,
                          discussion_id=discussion_id,
                          body=extract_body,
                          idea=idea,
                          important=important,
                          annotation_text=annotation_text,
                          content=content,
                          extract_hash=extract_hash)
    Extract.default_db.add(new_extract)
    for range_data in ranges:
        range = TextFragmentIdentifier(extract=new_extract,
                                       xpath_start=range_data['start'],
                                       offset_start=range_data['startOffset'],
                                       xpath_end=range_data['end'],
                                       offset_end=range_data['endOffset'])
        TextFragmentIdentifier.default_db.add(range)

    Extract.default_db.flush()

    return {'ok': True, '@id': new_extract.uri()}
Example #12
0
def home_view(request):
    user_id = authenticated_userid(request) or Everyone
    context = get_default_context(request)
    discussion = context["discussion"]
    request.session["discussion"] = discussion.slug
    canRead = user_has_permission(discussion.id, user_id, P_READ)
    if not canRead and user_id == Everyone:
        # User isn't logged-in and discussion isn't public:
        # redirect to login page
        login_url = request.route_url('contextual_login',
                                      discussion_slug=discussion.slug)
        return HTTPSeeOther(login_url)
    elif not canRead:
        # User is logged-in but doesn't have access to the discussion
        return HTTPUnauthorized()

    # if the route asks for a post, get post content (because this is needed for meta tags)
    route_name = request.matched_route.name
    if route_name == "purl_posts":
        post_id = FrontendUrls.getRequestedPostId(request)
        if not post_id:
            return HTTPSeeOther(
                request.route_url('home', discussion_slug=discussion.slug))
        post = Post.get_instance(post_id)
        if not post or post.discussion_id != discussion.id:
            return HTTPSeeOther(
                request.route_url('home', discussion_slug=discussion.slug))
        context['post'] = post
    elif route_name == "purl_idea":
        idea_id = FrontendUrls.getRequestedIdeaId(request)
        if not idea_id:
            return HTTPSeeOther(
                request.route_url('home', discussion_slug=discussion.slug))
        idea = Idea.get_instance(idea_id)
        if not idea or idea.discussion_id != discussion.id:
            return HTTPSeeOther(
                request.route_url('home', discussion_slug=discussion.slug))
        context['idea'] = idea

    canAddExtract = user_has_permission(discussion.id, user_id, P_ADD_EXTRACT)
    context['canAddExtract'] = canAddExtract
    context['canDisplayTabs'] = True
    if user_id != Everyone:
        from assembl.models import AgentProfile
        user = AgentProfile.get(user_id)
        # TODO: user may not exist. Case of session with BD change.
        user.is_visiting_discussion(discussion.id)
        session = Discussion.default_db
        current_prefs = session.query(UserLanguagePreference).\
            filter_by(user_id = user_id).all()
        user = session.query(User).filter_by(id=user_id).first()

        def validate_locale(l):
            return ensure_locale_has_country(to_posix_format(locale))

        if '_LOCALE_' in request.cookies:
            locale = request.cookies['_LOCALE_']
            posix_locale = validate_locale(locale)
            process_locale(posix_locale, user_id, current_prefs, session,
                           LanguagePreferenceOrder.Cookie)

        elif '_LOCALE_' in request.params:
            locale = request.params['_LOCALE_']
            posix_locale = validate_locale(locale)
            process_locale(posix_locale, user_id, current_prefs, session,
                           LanguagePreferenceOrder.Parameter)
        else:
            locale = default_locale_negotiator(request)
            posix_locale = validate_locale(locale)
            process_locale(posix_locale, user_id, current_prefs, session,
                           LanguagePreferenceOrder.OS_Default)

    response = render_to_response('../../templates/index.jinja2',
                                  context,
                                  request=request)
    # Prevent caching the home, especially for proper login/logout
    response.cache_control.max_age = 0
    response.cache_control.prevent_auto = True
    return response
Example #13
0
def home_view(request):
    user_id = authenticated_userid(request) or Everyone
    context = get_default_context(request)
    discussion = context["discussion"]
    canRead = user_has_permission(discussion.id, user_id, P_READ)
    if not canRead and user_id == Everyone:
        # User isn't logged-in and discussion isn't public:
        # redirect to login page
        login_url = request.route_url(
            'contextual_login', discussion_slug=discussion.slug)
        return HTTPSeeOther(login_url)
    elif not canRead:
        # User is logged-in but doesn't have access to the discussion
        return HTTPUnauthorized()

    # if the route asks for a post, get post content (because this is needed for meta tags)
    route_name = request.matched_route.name
    if route_name == "purl_posts":
        post_id = FrontendUrls.getRequestedPostId(request)
        if post_id:
            post = Post.get_instance(post_id)
            if post and post.discussion_id == discussion.id:
                context['post'] = post
    elif route_name == "purl_idea":
        idea_id = FrontendUrls.getRequestedIdeaId(request)
        if idea_id:
            idea = Idea.get_instance(idea_id)
            if idea and idea.discussion_id == discussion.id:
                context['idea'] = idea

    canAddExtract = user_has_permission(discussion.id, user_id, P_ADD_EXTRACT)
    context['canAddExtract'] = canAddExtract
    context['canDisplayTabs'] = True
    if user_id != Everyone:
        from assembl.models import AgentProfile
        user = AgentProfile.get(user_id)
        # TODO: user may not exist. Case of session with BD change.
        user.is_visiting_discussion(discussion.id)
        session = Discussion.db()
        current_prefs = session.query(UserLanguagePreference).\
            filter_by(user_id = user_id).all()
        user = session.query(User).filter_by(id = user_id).first()

        if '_LOCALE_' in request.cookies:
            locale = request.cookies['_LOCALE_']
            posix_locale = to_posix_format(locale)
            process_locale(posix_locale,user_id,
                           current_prefs, session,
                           LanguagePreferenceOrder.Cookie)

        elif '_LOCALE_' in request.params:
            locale = request.params['_LOCALE_']
            posix_locale = to_posix_format(locale)
            process_locale(posix_locale, user_id,
                           current_prefs, session,
                           LanguagePreferenceOrder.Parameter)
        else:
            locale = default_locale_negotiator(request)
            posix_locale = to_posix_format(locale)
            process_locale(posix_locale, user_id,
                           current_prefs, session,
                           LanguagePreferenceOrder.OS_Default)


    response = render_to_response('../../templates/index.jinja2', context, request=request)
    # Prevent caching the home, especially for proper login/logout
    response.cache_control.max_age = 0
    response.cache_control.prevent_auto = True
    return response
Example #14
0
def discussion_permissions(request):
    user_id = authenticated_userid(request)
    assert user_id
    db = Discussion.default_db
    discussion_id = int(request.matchdict['discussion_id'])
    discussion = Discussion.get_instance(discussion_id)
    error = ''

    if not discussion:
        raise HTTPNotFound("Discussion with id '%d' not found." %
                           (discussion_id, ))

    roles = db.query(Role).all()
    roles_by_name = {r.name: r for r in roles}
    role_names = [r.name for r in roles]
    role_names.sort()
    permissions = db.query(Permission).all()
    perms_by_name = {p.name: p for p in permissions}
    permission_names = [p.name for p in permissions]
    permission_names.sort()

    disc_perms = db.query(DiscussionPermission).filter_by(
        discussion_id=discussion_id).join(Role, Permission).all()
    disc_perms_as_set = set(
        (dp.role.name, dp.permission.name) for dp in disc_perms)
    disc_perms_dict = {(dp.role.name, dp.permission.name): dp
                       for dp in disc_perms}
    local_roles = db.query(LocalUserRole).filter_by(
        discussion_id=discussion_id).join(Role, User).all()
    local_roles_as_set = set(
        (lur.user.id, lur.role.name) for lur in local_roles)
    local_roles_dict = {(lur.user.id, lur.role.name): lur
                        for lur in local_roles}
    users = set(lur.user for lur in local_roles)
    num_users = ''

    if request.POST:
        if 'submit_role_permissions' in request.POST:
            for role in role_names:
                if role == R_SYSADMIN:
                    continue
                for permission in permission_names:
                    allowed_text = 'allowed_%s_%s' % (role, permission)
                    if (role, permission) not in disc_perms_as_set and \
                            allowed_text in request.POST:
                        dp = DiscussionPermission(
                            role=roles_by_name[role],
                            permission=perms_by_name[permission],
                            discussion_id=discussion_id)
                        disc_perms_dict[(role, permission)] = dp
                        disc_perms_as_set.add((role, permission))
                        db.add(dp)
                    elif (role, permission) in disc_perms_as_set and \
                            allowed_text not in request.POST:
                        dp = disc_perms_dict[(role, permission)]
                        del disc_perms_dict[(role, permission)]
                        disc_perms_as_set.remove((role, permission))
                        db.delete(dp)
                if not role in SYSTEM_ROLES and\
                        'delete_'+role in request.POST:
                    db.delete(roles_by_name[role])
                    del roles_by_name[role]
                    role_names.remove(role)
        elif 'submit_add_role' in request.POST:
            #TODO: Sanitize role
            role = Role(name='r:' + request.POST['new_role'])
            roles_by_name[role.name] = role
            role_names.append(role.name)
            db.add(role)
        elif 'submit_user_roles' in request.POST:
            user_ids = {u.id for u in users}
            for role in role_names:
                if role == R_SYSADMIN:
                    continue
                prefix = 'has_' + role + '_'
                for name in request.POST:
                    if name.startswith(prefix):
                        a_user_id = int(name[len(prefix):])
                        if a_user_id not in user_ids:
                            users.add(User.get_instance(a_user_id))
                            user_ids.add(a_user_id)
                for user in users:
                    has_role_text = 'has_%s_%d' % (role, user.id)
                    if (user.id, role) not in local_roles_as_set and \
                            has_role_text in request.POST:
                        lur = LocalUserRole(role=roles_by_name[role],
                                            user=user,
                                            discussion_id=discussion_id)
                        local_roles.append(lur)

                        # TODO revisit this if Roles and Subscription are
                        # de-coupled
                        if role == 'r:participant':
                            user.update_agent_status_subscribe(discussion)

                        local_roles_dict[(user.id, role)] = lur
                        local_roles_as_set.add((user.id, role))
                        db.add(lur)
                    elif (user.id, role) in local_roles_as_set and \
                            has_role_text not in request.POST:
                        lur = local_roles_dict[(user.id, role)]
                        del local_roles_dict[(user.id, role)]
                        local_roles_as_set.remove((user.id, role))
                        local_roles.remove(lur)

                        # TODO revisit this if Roles and Subscription are
                        # de-coupled
                        if role == 'r:participant':
                            user.update_agent_status_unsubscribe(discussion)

                        db.delete(lur)

        elif 'submit_look_for_user' in request.POST:
            search_string = '%' + request.POST['user_search'] + '%'
            other_users = db.query(User).outerjoin(Username).filter(
                AgentProfile.name.ilike(search_string)
                | Username.username.ilike(search_string)
                | User.preferred_email.ilike(search_string)).all()
            users.update(other_users)
        elif 'submit_user_file' in request.POST:
            role = request.POST['add_with_role'] or R_PARTICIPANT
            if role == R_SYSADMIN and not user_has_permission(
                    discussion_id, user_id, P_SYSADMIN):
                role = R_ADMINISTRATOR
            if 'user_csvfile' in request.POST:
                try:
                    num_users = add_multiple_users_csv(
                        request, request.POST['user_csvfile'].file,
                        discussion_id, role,
                        request.POST.get('send_invite',
                                         False), request.POST['email_subject'],
                        request.POST['text_email_message'],
                        request.POST['html_email_message'],
                        request.POST['sender_name'],
                        request.POST.get('resend_notloggedin', False))
                except Exception as e:
                    error = repr(e)
                    transaction.doom()
            else:
                error = request.localizer.translate(_('No file given.'))

    def allowed(role, permission):
        if role == R_SYSADMIN:
            return True
        return (role, permission) in disc_perms_as_set

    def has_local_role(user_id, role):
        return (user_id, role) in local_roles_as_set

    users = list(users)
    users.sort(key=order_by_domain_and_name)

    context = dict(get_default_context(request),
                   discussion=discussion,
                   allowed=allowed,
                   roles=role_names,
                   permissions=permission_names,
                   users=users,
                   error=error,
                   num_users=num_users,
                   has_local_role=has_local_role,
                   is_system_role=lambda r: r in SYSTEM_ROLES)

    return render_to_response('admin/discussion_permissions.jinja2',
                              context,
                              request=request)
Example #15
0
def home_view(request):
    user_id = authenticated_userid(request) or Everyone
    context = get_default_context(request)
    discussion = context["discussion"]
    request.session["discussion"] = discussion.slug
    canRead = user_has_permission(discussion.id, user_id, P_READ)
    if not canRead and user_id == Everyone:
        # User isn't logged-in and discussion isn't public:
        # redirect to login page
        # need to pass the route to go to *after* login as well

        # With regards to a next_view, if explicitly stated, then
        # that is the next view. If not stated, the referer takes
        # precedence. In case of failure, login redirects to the
        # discussion which is its context.
        next_view = request.params.get('next_view', None)
        if not next_view and discussion:
            # If referred here from a post url, want to be able to
            # send the user back. Usually, Assembl will send the user
            # here to login on private discussions.
            referrer = request.url
            next_view = path_qs(referrer)

        if next_view:
            login_url = request.route_url("contextual_login",
                                          discussion_slug=discussion.slug,
                                          _query={"next_view": next_view})
        else:
            login_url = request.route_url(
                'contextual_login', discussion_slug=discussion.slug)
        return HTTPSeeOther(login_url)
    elif not canRead:
        # User is logged-in but doesn't have access to the discussion
        # Would use render_to_response, except for the 401
        from pyramid_jinja2 import IJinja2Environment
        jinja_env = request.registry.queryUtility(
            IJinja2Environment, name='.jinja2')
        template = jinja_env.get_template('cannot_read_discussion.jinja2')
        body = template.render(get_default_context(request))
        return Response(body, 401)

    # if the route asks for a post, get post content (because this is needed for meta tags)
    route_name = request.matched_route.name
    if route_name == "purl_posts":
        post_id = FrontendUrls.getRequestedPostId(request)
        if not post_id:
            return HTTPSeeOther(request.route_url(
                'home', discussion_slug=discussion.slug))
        post = Post.get_instance(post_id)
        if not post or post.discussion_id != discussion.id:
            return HTTPSeeOther(request.route_url(
                'home', discussion_slug=discussion.slug))
        context['post'] = post
    elif route_name == "purl_idea":
        idea_id = FrontendUrls.getRequestedIdeaId(request)
        if not idea_id:
            return HTTPSeeOther(request.route_url(
                'home', discussion_slug=discussion.slug))
        idea = Idea.get_instance(idea_id)
        if not idea or idea.discussion_id != discussion.id:
            return HTTPSeeOther(request.route_url(
                'home', discussion_slug=discussion.slug))
        context['idea'] = idea

    canAddExtract = user_has_permission(discussion.id, user_id, P_ADD_EXTRACT)
    context['canAddExtract'] = canAddExtract
    context['canDisplayTabs'] = True
    preferences = discussion.preferences
    if user_id != Everyone:
        from assembl.models import UserPreferenceCollection
        user = User.get(user_id)
        preferences = UserPreferenceCollection(user_id, discussion)
        # TODO: user may not exist. Case of session with BD change.
        user.is_visiting_discussion(discussion.id)
        session = Discussion.default_db

        if '_LOCALE_' in request.cookies:
            locale = request.cookies['_LOCALE_']
            process_locale(locale, user, session,
                           LanguagePreferenceOrder.Cookie)

        elif '_LOCALE_' in request.params:
            locale = request.params['_LOCALE_']
            process_locale(locale, user, session,
                           LanguagePreferenceOrder.Parameter)
        else:
            locale = locale_negotiator(request)
            process_locale(locale, user, session,
                           LanguagePreferenceOrder.OS_Default)
    else:
        locale = request.localizer.locale_name

    target_locale = Locale.get_or_create(
        strip_country(locale), discussion.db)

    translation_service_data = {}
    try:
        service = discussion.translation_service()
        if service:
            translation_service_data = service.serviceData()
    except:
        pass
    context['translation_service_data_json'] = json.dumps(
        translation_service_data)
    locale_labels = json.dumps(
        DummyGoogleTranslationService.target_locale_labels_cls(target_locale))
    context['translation_locale_names_json'] = locale_labels

    context['preferences_json'] = json.dumps(dict(preferences))

    response = render_to_response('../../templates/index.jinja2', context,
                                  request=request)
    # Prevent caching the home, especially for proper login/logout
    response.cache_control.max_age = 0
    response.cache_control.prevent_auto = True
    return response
Example #16
0
def post_extract(request):
    """
    Create a new extract.
    """
    extract_data = json.loads(request.body)
    discussion_id = int(request.matchdict['discussion_id'])
    user_id = authenticated_userid(request)
    if not user_id:
        # Straight from annotator
        token = request.headers.get('X-Annotator-Auth-Token')
        if token:
            token = decode_token(
                token, request.registry.settings['session.secret'])
            if token:
                user_id = token['userId']
    user_id = user_id or Everyone
    if not user_has_permission(discussion_id, user_id, P_ADD_EXTRACT):
        #TODO: maparent:  restore this code once it works:
        #return HTTPForbidden(result=ACLDenied(permission=P_ADD_EXTRACT))
        return HTTPForbidden()
    if not user_id or user_id == Everyone:
        # TODO: Create an anonymous user.
        raise HTTPServerError("Anonymous extracts are not implemeted yet.")
    content = None
    uri = extract_data.get('uri')
    important = extract_data.get('important', False)
    annotation_text = None
    if uri:
        # Straight from annotator
        annotation_text = extract_data.get('text')
    else:
        target = extract_data.get('target')
        if not (target or uri):
            raise HTTPBadRequest("No target")

        target_class = sqla.get_named_class(target.get('@type'))
        if issubclass(target_class, Post):
            post_id = target.get('@id')
            post = Post.get_instance(post_id)
            if not post:
                raise HTTPNotFound(
                    "Post with id '%s' not found." % post_id)
            content = post
        elif issubclass(target_class, Webpage):
            uri = target.get('url')
    if uri and not content:
        content = Webpage.get_instance(uri)
        if not content:
            # TODO: maparent:  This is actually a singleton pattern, should be
            # handled by the AnnotatorSource now that it exists...
            source = AnnotatorSource.default_db.query(AnnotatorSource).filter_by(
                discussion_id=discussion_id).filter(
                cast(AnnotatorSource.name, Unicode) == 'Annotator').first()
            if not source:
                source = AnnotatorSource(
                    name='Annotator', discussion_id=discussion_id,
                    type='source')
            content = Webpage(url=uri, discussion_id=discussion_id)
    extract_body = extract_data.get('quote', '')

    idea_id = extract_data.get('idIdea', None)
    if idea_id:
        idea = Idea.get_instance(idea_id)
        if(idea.discussion.id != discussion_id):
            raise HTTPBadRequest(
                "Extract from discussion %s cannot be associated with an idea from a different discussion." % extract.get_discussion_id())
    else:
        idea = None


    new_extract = Extract(
        creator_id=user_id,
        owner_id=user_id,
        discussion_id=discussion_id,
        body=extract_body,
        idea=idea,
        important=important,
        annotation_text=annotation_text,
        content=content
    )
    Extract.default_db.add(new_extract)

    for range_data in extract_data.get('ranges', []):
        range = TextFragmentIdentifier(
            extract=new_extract,
            xpath_start=range_data['start'],
            offset_start=range_data['startOffset'],
            xpath_end=range_data['end'],
            offset_end=range_data['endOffset'])
        TextFragmentIdentifier.default_db.add(range)
    Extract.default_db.flush()

    return {'ok': True, '@id': new_extract.uri()}
Example #17
0
def discussion_permissions(request):
    user_id = authenticated_userid(request)
    assert user_id
    db = Discussion.default_db
    discussion_id = int(request.matchdict["discussion_id"])
    discussion = Discussion.get_instance(discussion_id)
    error = ""

    if not discussion:
        raise HTTPNotFound("Discussion with id '%d' not found." % (discussion_id,))

    roles = db.query(Role).all()
    roles_by_name = {r.name: r for r in roles}
    role_names = [r.name for r in roles]
    permissions = db.query(Permission).all()
    perms_by_name = {p.name: p for p in permissions}
    permission_names = [p.name for p in permissions]

    disc_perms = db.query(DiscussionPermission).filter_by(discussion_id=discussion_id).join(Role, Permission).all()
    disc_perms_as_set = set((dp.role.name, dp.permission.name) for dp in disc_perms)
    disc_perms_dict = {(dp.role.name, dp.permission.name): dp for dp in disc_perms}
    local_roles = db.query(LocalUserRole).filter_by(discussion_id=discussion_id).join(Role, User).all()
    local_roles_as_set = set((lur.user.id, lur.role.name) for lur in local_roles)
    local_roles_dict = {(lur.user.id, lur.role.name): lur for lur in local_roles}
    users = set(lur.user for lur in local_roles)
    num_users = ""

    if request.POST:
        if "submit_role_permissions" in request.POST:
            for role in role_names:
                if role == R_SYSADMIN:
                    continue
                for permission in permission_names:
                    allowed_text = "allowed_%s_%s" % (role, permission)
                    if (role, permission) not in disc_perms_as_set and allowed_text in request.POST:
                        dp = DiscussionPermission(
                            role=roles_by_name[role], permission=perms_by_name[permission], discussion_id=discussion_id
                        )
                        disc_perms_dict[(role, permission)] = dp
                        disc_perms_as_set.add((role, permission))
                        db.add(dp)
                    elif (role, permission) in disc_perms_as_set and allowed_text not in request.POST:
                        dp = disc_perms_dict[(role, permission)]
                        del disc_perms_dict[(role, permission)]
                        disc_perms_as_set.remove((role, permission))
                        db.delete(dp)
                if not role in SYSTEM_ROLES and "delete_" + role in request.POST:
                    db.delete(roles_by_name[role])
                    del roles_by_name[role]
                    role_names.remove(role)
        elif "submit_add_role" in request.POST:
            # TODO: Sanitize role
            role = Role(name="r:" + request.POST["new_role"])
            roles_by_name[role.name] = role
            role_names.append(role.name)
            db.add(role)
        elif "submit_user_roles" in request.POST:
            user_ids = {u.id for u in users}
            for role in role_names:
                if role == R_SYSADMIN:
                    continue
                prefix = "has_" + role + "_"
                for name in request.POST:
                    if name.startswith(prefix):
                        a_user_id = int(name[len(prefix) :])
                        if a_user_id not in user_ids:
                            users.add(User.get_instance(a_user_id))
                            user_ids.add(a_user_id)
                for user in users:
                    has_role_text = "has_%s_%d" % (role, user.id)
                    if (user.id, role) not in local_roles_as_set and has_role_text in request.POST:
                        lur = LocalUserRole(role=roles_by_name[role], user=user, discussion_id=discussion_id)
                        local_roles.append(lur)
                        local_roles_dict[(user.id, role)] = lur
                        local_roles_as_set.add((user.id, role))
                        db.add(lur)
                    elif (user.id, role) in local_roles_as_set and has_role_text not in request.POST:
                        lur = local_roles_dict[(user.id, role)]
                        del local_roles_dict[(user.id, role)]
                        local_roles_as_set.remove((user.id, role))
                        local_roles.remove(lur)
                        db.delete(lur)

        elif "submit_look_for_user" in request.POST:
            search_string = "%" + request.POST["user_search"] + "%"
            other_users = (
                db.query(User)
                .outerjoin(Username)
                .filter(
                    AgentProfile.name.ilike(search_string)
                    | Username.username.ilike(search_string)
                    | User.preferred_email.ilike(search_string)
                )
                .all()
            )
            users.update(other_users)
        elif "submit_user_file" in request.POST:
            role = request.POST["add_with_role"] or R_PARTICIPANT
            if role == R_SYSADMIN and not user_has_permission(discussion_id, user_id, P_SYSADMIN):
                role = R_ADMINISTRATOR
            if "user_csvfile" in request.POST:
                try:
                    num_users = add_multiple_users_csv(request.POST["user_csvfile"].file, discussion_id, role)
                except Exception as e:
                    error = repr(e)
            else:
                error = request.localizer.translate(_("No file given."))

    def allowed(role, permission):
        if role == R_SYSADMIN:
            return True
        return (role, permission) in disc_perms_as_set

    def has_local_role(user_id, role):
        return (user_id, role) in local_roles_as_set

    context = dict(
        get_default_context(request),
        discussion=discussion,
        allowed=allowed,
        roles=role_names,
        permissions=permission_names,
        users=users,
        error=error,
        num_users=num_users,
        has_local_role=has_local_role,
        is_system_role=lambda r: r in SYSTEM_ROLES,
    )

    return render_to_response("admin/discussion_permissions.jinja2", context, request=request)