Example #1
0
def add_topt(request, next_page=None):
    next_page, okay_auth, okay_2fa = _setup_view(request, next_page)

    if not okay_auth:
        return ForbiddenResponse(request)

    # This enforces that users have exactly one TOTP. That *seems* like the best practice.
    devices = TOTPDevice.objects.devices_for_user(request.maybe_stale_user, confirmed=True)
    if devices:
        return ForbiddenResponse(request, "You have already configured an authenticator with this account, and cannot add another. Contact %s if you are unable to authenticate with CourSys", (help_email(request),))

    device = TOTPDevice(user=request.maybe_stale_user, name='Authenticator, enabled %s' % (datetime.date.today()))
    device.save()

    l = LogEntry(userid=request.user.username,
                 description=("Added TOPT from %s") % (get_client_ip(request),),
                 related_object=device)
    l.save()

    # build QR code
    uri = totpauth_url(device)
    qr = qrcode.make(uri, image_factory=qrcode.image.svg.SvgPathImage)
    qrdata = BytesIO()
    qr.save(qrdata)
    # This is the OTP secret (bits) encoded as base32, wrapped in an otpauth URL, encoded as a QR code, encoded as an
    # SVG, encoded as base64, wrapped in a data URL. I'm strangely proud.
    dataurl = (b'data:image/svg+xml;base64,' + base64.b64encode(qrdata.getvalue())).decode("utf-8")

    context = {
        'device': device,
        'dataurl': dataurl,
        'next_page': next_page,
    }
    return render(request, 'otp/add_topt.html', context)
Example #2
0
def _delete_pagefile(request, course_slug, page_label, kind):
    """
    Delete page/file
    """
    with django.db.transaction.atomic():
        offering = get_object_or_404(CourseOffering, slug=course_slug)
        page = get_object_or_404(Page, offering=offering, label=page_label)
        version = page.current_version()
        member = _check_allowed(request, offering, page.can_write, page.editdate())
        if not member:
            return ForbiddenResponse(request, 'Not allowed to edit this '+kind+'.')
        can_create = member.role in MEMBER_ROLES[offering.page_creators()]
        if not can_create:
            return ForbiddenResponse(request, 'Not allowed to delete pages in for this offering (must have page-creator permission).')

        from django.core.validators import URLValidator
        from django.core.exceptions import ValidationError
        val = URLValidator()

        redirect = request.POST.get('redirect', 'Index')
        url = request.build_absolute_uri(urljoin(page.get_absolute_url(), redirect))

        try:
            val(url)
        except ValidationError:
            messages.error(request, "Bad redirect URL entered. Not deleted.")
            return HttpResponseRedirect(reverse('offering:pages:edit_page', kwargs={'course_slug': course_slug, 'page_label': page.label}))

        redir_version = PageVersion(page=page, title=version.title, redirect=redirect,
                                    editor=member, comment='automatically generated on deletion')
        redir_version.set_redirect_reason('delete')
        redir_version.save()

        messages.success(request, "Page deleted and will redirect to this location.")
        return HttpResponseRedirect(urljoin(page.get_absolute_url(), redirect))
Example #3
0
def _delete_pagefile(request, course_slug, page_label, kind):
    """
    Delete page/file
    """
    with django.db.transaction.atomic():
        offering = get_object_or_404(CourseOffering, slug=course_slug)
        page = get_object_or_404(Page, offering=offering, label=page_label)
        #version = page.current_version()
        member = _check_allowed(request, offering, page.can_write,
                                page.editdate())
        if not member:
            return ForbiddenResponse(request,
                                     'Not allowed to edit this ' + kind + '.')
        can_create = member.role in MEMBER_ROLES[offering.page_creators()]
        if not can_create:
            return ForbiddenResponse(
                request,
                'Not allowed to delete pages in for this offering (must have page-creator permission).'
            )

        page.safely_delete()

        messages.success(
            request,
            "Page deleted (but can be recovered by an administrator in an emergency)."
        )
        return HttpResponseRedirect(
            reverse(index_page, kwargs={'course_slug': course_slug}))
Example #4
0
def offering_by_id(request):
    if 'id' not in request.GET:
        return ForbiddenResponse(request, "Must provide 'id' query.")
    id_ = request.GET['id']
    try:
        int(id_)
    except ValueError:
        return ForbiddenResponse(request, "'id' must be an integer.")

    offering = get_object_or_404(CourseOffering, pk=id_)
    return HttpResponse(offering.search_label_value())
def delete_savedsearch(request):
    current_user = Person.objects.get(userid=request.user.username)
    if request.method != 'POST':
        return ForbiddenResponse(request)
    savedsearches = SavedSearch.objects.filter(
                person=request.POST['person'], 
                query=request.POST['query'])
    if not savedsearches:
        return NotFoundResponse(request, "This Saved Search doesn't exist.")
    savedsearch = savedsearches[0]
    if current_user != savedsearch.person:
        return ForbiddenResponse(request, "You cannot delete this Saved Search.")
    savedsearch.delete()
    messages.add_message(request, messages.SUCCESS, "Saved Search '%s' was successfully deleted." % savedsearch.name())
    return HttpResponseRedirect(reverse('grad:index'))
Example #6
0
def download_file(request, course_slug, activity_slug, component_slug=None, submission_id=None, userid=None):
    course = get_object_or_404(CourseOffering, slug=course_slug)
    activity = get_object_or_404(course.activity_set, slug = activity_slug, deleted=False)
    staff = False
    if is_course_staff_by_slug(request, course_slug):
        staff = True
    
    # find the appropriate submission object
    if submission_id:
        # explicit request: get that one.
        submission = get_submission(submission_id)
        if not submission or submission.activity!=activity:
            return NotFoundResponse(request)
        submitted_components = get_submission_components(submission, activity, include_deleted=staff)

    elif userid:
        # userid specified: get their most recent submission
        student = get_object_or_404(Person, find_userid_or_emplid(userid))
        submission, submitted_components = get_current_submission(student, activity, include_deleted=staff)
        if not submission:
            return NotFoundResponse(request)
    
    else:
        return NotFoundResponse(request)

    
    # make sure this user is allowed to see the file
    if staff:
        pass
    elif isinstance(submission, GroupSubmission):
        membership = submission.group.groupmember_set.filter(student__person__userid=request.user.username, activity=activity, confirmed=True)
        if not membership:
            return ForbiddenResponse(request)
    elif isinstance(submission, StudentSubmission):
        if submission.member.person.userid != request.user.username:
            return ForbiddenResponse(request)

    # create the result
    if component_slug:
        # download single component if specified
        # get the actual component: already did the searching above, so just look in that list
        components = [sub for comp,sub in submitted_components if sub and sub.component.slug==component_slug]
        if not components:
            return NotFoundResponse(request)
        return components[0].download_response()
    else:
        # no component specified: give back the full ZIP file.
        return generate_zip_file(submission, submitted_components)
Example #7
0
def edit_contract(request, contract_slug):
    contract = get_object_or_404(SessionalContract, slug=contract_slug)
    if not _has_unit_role(request.user, contract):
        return ForbiddenResponse(request)
    if request.method == 'POST':
        form = SessionalContractForm(request, request.POST, instance=contract)
        if form.is_valid():
            contract = form.save(commit=False)
            # Let's convert the person in there to an AnyPerson
            person = form.cleaned_data['person']
            contract.sessional = AnyPerson.get_or_create_for(person=person)
            contract.save()
            messages.add_message(request,
                                 messages.SUCCESS,
                                 u'Contract was edited.'
                                 )
            l = LogEntry(userid=request.user.username,
                         description="edited sessional contract for: %s" % contract.sessional,
                         related_object=contract
                         )
            l.save()

            return HttpResponseRedirect(reverse('sessionals:sessionals_index'))
    else:
        form = SessionalContractForm(request, instance=contract)
        form.fields['person'].initial = contract.sessional.get_person().emplid
    return render(request, 'sessionals/edit_contract.html', {'form': form, 'contract': contract})
Example #8
0
def get_letter(request, grad_slug, letter_slug):
    grad, authtype, units = _can_view_student(request, grad_slug)
    if grad is None or authtype == 'student':
        return ForbiddenResponse(request)
    letter = get_object_or_404(Letter,
                               slug=letter_slug,
                               student=grad,
                               student__program__unit__in=units)
    response = HttpResponse(content_type="application/pdf")
    response['Content-Disposition'] = 'inline; filename="%s.pdf"' % (
        letter_slug)

    doc = OfficialLetter(response, unit=letter.student.program.unit)
    l = LetterContents(to_addr_lines=letter.to_lines.split("\n"),
                       from_name_lines=letter.from_lines.split("\n"),
                       date=letter.date,
                       closing=letter.closing,
                       signer=letter.from_person,
                       use_sig=letter.use_sig(),
                       body_font_size=letter.template.body_font_size())
    content_lines = letter.content.split("\n\n")
    l.add_paragraphs(content_lines)
    doc.add_letter(l)
    doc.write()
    return response
Example #9
0
def artifact_search(request):
    if 'text-search' not in request.GET:
        return ForbiddenResponse(request, "must send search query")
    search = request.GET['text-search']
    query = get_query(search, ('text', ))
    artifact_notes = ArtifactNote.objects.filter(
        query, unit__in=request.units,
        hidden=False).order_by("-created_at")[:100]
    for a in artifact_notes:
        if a.course:
            a.url = reverse('advising:view_course_notes',
                            kwargs={'unit_course_slug': a.course.slug})
            a.description = a.course
        elif a.course_offering:
            a.url = reverse('advising:view_offering_notes',
                            kwargs={'course_slug': a.course_offering.slug})
            a.description = a.course_offering
        else:
            a.url = reverse('advising:view_artifact_notes',
                            kwargs={'artifact_slug': a.artifact.slug})
            a.description = a.artifact
    artifact_form = ArtifactSearchForm(prefix="text",
                                       initial={'search': search})
    context = {
        'artifact_notes': artifact_notes,
        'artifact_form': artifact_form
    }
    return render(request, 'advisornotes/artifact_search.html', context)
Example #10
0
def import_page(request, course_slug, page_label):
    with django.db.transaction.atomic():
        offering = get_object_or_404(CourseOffering, slug=course_slug)
        page = get_object_or_404(Page, offering=offering, label=page_label)
        version = page.current_version()
        member = _check_allowed(request, offering, page.can_write)
        if not member:
            return ForbiddenResponse(request, 'Not allowed to edit/create this page.')
        
        if request.method == 'POST':
            form = PageImportForm(data=request.POST, files=request.FILES)
            if form.is_valid():
                wiki = form.cleaned_data['file'] or form.cleaned_data['url']

                # create Page editing form for preview-before-save
                pageform = EditPageForm(instance=page, offering=offering)
                pageform.initial['wikitext'] = wiki
                
                # URL for submitting that form
                url = reverse(edit_page, kwargs={'course_slug': offering.slug, 'page_label': page.label})
                messages.warning(request, "Page has not yet been saved, but your HTML has been imported below.")
                context = {'offering': offering, 'page': page, 'form': pageform, 'kind': 'Page', 'import': True, 'url': url}
                return render(request, 'pages/edit_page.html', context)
        else:
            form = PageImportForm()
        
        context = {'offering': offering, 'page': page, 'version': version, 'form': form}
        return render(request, 'pages/import_page.html', context)
Example #11
0
def import_site(request, course_slug):
    with django.db.transaction.atomic():
        offering = get_object_or_404(CourseOffering, slug=course_slug)
        member = _check_allowed(request, offering,
                                'STAF')  # only staff can import
        if not member:
            return ForbiddenResponse(request,
                                     'Not allowed to edit/create pages.')

        if request.method == 'POST':
            form = SiteImportForm(offering=offering,
                                  editor=member,
                                  data=request.POST,
                                  files=request.FILES)
            if form.is_valid():
                pages, errors = form.cleaned_data['url']
                for label in pages:
                    page, pv = pages[label]
                    page.save()
                    pv.page_id = page.id
                    pv.save()
        else:
            form = SiteImportForm(offering=offering, editor=member)

        context = {'offering': offering, 'form': form}
        return render(request, 'pages/import_site.html', context)
Example #12
0
def show_components_submission_history(request, course_slug, activity_slug, userid=None):
    if userid is None:
        userid = request.GET.get('userid')
    course = get_object_or_404(CourseOffering, slug=course_slug)
    activity = get_object_or_404(course.activity_set,slug = activity_slug, deleted=False)
    staff = False

    if userid is None:
        # can always see your own submissions
        userid = request.user.username
    else:
        # specifying a userid: must be course staff
        if is_course_staff_by_slug(request, course.slug):
            staff = True
        else:
            return ForbiddenResponse(request)

    member = get_object_or_404(Member, find_member(userid), offering=course)
    if activity.group:
        messages.add_message(request, messages.INFO, "This is a group submission. This history is based on submissions from all your group members.")
        gms = GroupMember.objects.filter(student=member, confirmed=True, activity=activity)
        submissions = GroupSubmission.objects.filter(activity=activity, group__groupmember__in=gms)
    else:
        submissions = StudentSubmission.objects.filter(activity=activity, member=member)

    # get all submission components
    component_list = select_all_components(activity, include_deleted=staff)
    all_submitted_components = []
    for submission in submissions:
        c = get_submission_components(submission, activity, component_list)
        all_submitted_components.append({'sub':submission, 'comp':c})
    
    return render(request, "submission/submission_history_view.html",
        {"course":course, "activity":activity,'userid':userid,'submitted_components': all_submitted_components})
Example #13
0
def groupmanage(request, course_slug, activity_slug=None):
    if is_course_staff_by_slug(request, course_slug):
        return _groupmanage_staff(request, course_slug, activity_slug)
    elif is_course_student_by_slug(request, course_slug):
        return _groupmanage_student(request, course_slug)
    else:
        return ForbiddenResponse(request)
Example #14
0
def view_registration(request, registration_id):
    registration = get_object_or_404(OutreachEventRegistration,
                                     pk=registration_id)
    if not _has_unit_role(request.user, registration.event):
        return ForbiddenResponse(request)
    print registration.extra_questions
    return render(request, 'outreach/view_registration.html',
                  {'registration': registration})
Example #15
0
def new_file(request, course_slug, case_slug):
    course = get_object_or_404(CourseOffering, slug=course_slug)
    case = get_object_or_404(DisciplineCaseBase, slug=case_slug, offering__slug=course_slug)
    case = case.subclass()

    if not case.can_edit('attach'):
        # once case is closed, don't allow editing
        return ForbiddenResponse(request)
Example #16
0
def student_search(request):
    # check permissions
    roles = Role.all_roles(request.user.username)
    allowed = set(['ADVS', 'ADMN', 'GRAD', 'FUND', 'SYSA'])
    if not (roles & allowed) and not has_formgroup(request):
        # doesn't have any allowed roles
        return ForbiddenResponse(request,
                                 "Not permitted to do student search.")

    if 'term' not in request.GET:
        return ForbiddenResponse(request, "Must provide 'term' query.")
    term = request.GET['term']
    response = HttpResponse(content_type='application/json')

    # do the query with Haystack
    # experimentally, score >= 1 seems to correspond to useful things
    student_qs = SearchQuerySet().models(Person).filter(text=term)[:20]
    data = [{
        'value': r.emplid,
        'label': r.search_display
    } for r in student_qs
            if r and r.score >= 1 and unicode(r.emplid) not in EXCLUDE_EMPLIDS]

    # non-haystack version of the above query
    if len(student_qs) == 0:
        studentQuery = get_query(
            term, ['userid', 'emplid', 'first_name', 'last_name'])
        students = Person.objects.filter(studentQuery)[:20]
        data = [{
            'value': s.emplid,
            'label': s.search_label_value()
        } for s in students if unicode(s.emplid) not in EXCLUDE_EMPLIDS]

    if 'nonstudent' in request.GET and 'ADVS' in roles:
        nonStudentQuery = get_query(
            term, ['first_name', 'last_name', 'pref_first_name'])
        nonStudents = NonStudent.objects.filter(nonStudentQuery)[:10]
        data.extend([{
            'value': n.slug,
            'label': n.search_label_value()
        } for n in nonStudents])

    #data.sort(key = lambda x: x['label'])

    json.dump(data, response, indent=1)
    return response
Example #17
0
def XXX_sims_person_search(request):
    # check permissions
    roles = Role.all_roles(request.user.username)
    allowed = set(['ADVS', 'ADMN', 'GRAD', 'FUND'])
    if not (roles & allowed):
        # doesn't have any allowed roles
        return ForbiddenResponse(request, "Not permitted to do person search.")

    if 'emplid' not in request.GET:
        return ForbiddenResponse(request, "Must provide 'emplid' query.")
    emplid = request.GET['emplid']
    response = HttpResponse(content_type='application/json')

    data = find_person(emplid)

    json.dump(data, response, indent=1)
    return response
Example #18
0
        def can_access_report(request, report, **kwargs):
            rep = get_object_or_404(Report, slug=report)
            if not _has_access(request, rep):
                return ForbiddenResponse(request)
            if needs_privacy_signature(request):
                return privacy_redirect(request)

            request.report = rep # no need to re-fetch it later
            return view_func(request=request, report=report, **kwargs)
Example #19
0
def show_components(request, course_slug, activity_slug):
    #if course staff
    if is_course_staff_by_slug(request, course_slug):
        return _show_components_staff(request, course_slug, activity_slug)
    #else course member
    elif is_course_member_by_slug(request, course_slug):
        return _show_components_student(request, course_slug, activity_slug)
    #else not found, return 403
    else:
        return ForbiddenResponse(request);
Example #20
0
def view_event_registrations(request, event_slug):
    event = get_object_or_404(OutreachEvent, slug=event_slug)
    if not _has_unit_role(request.user, event):
        return ForbiddenResponse(request)
    registrations = OutreachEventRegistration.objects.filter(event=event,
                                                             hidden=False)
    return render(request, 'outreach/event_registrations.html', {
        'event': event,
        'registrations': registrations
    })
Example #21
0
def view_event(request, event_slug):
    event = get_object_or_404(OutreachEvent, slug=event_slug)
    if not _has_unit_role(request.user, event):
        return ForbiddenResponse(request)
    register_url = request.build_absolute_uri(
        reverse('outreach:register', kwargs={'event_slug': event.slug}))
    return render(request, 'outreach/view_event.html', {
        'event': event,
        'register_url': register_url
    })
Example #22
0
def note_search(request):
    if 'text-search' not in request.GET:
        return ForbiddenResponse(request, "must send search query")
    search = request.GET['text-search']
    query = get_query(search, ('text',))
    notes = AdvisorNote.objects.filter(query, unit__in=request.units) \
            .select_related('student', 'advisor').order_by("-created_at")[:100]
    note_form = NoteSearchForm(prefix="text", initial={'search': search})
    context = {'notes': notes, 'note_form': note_form}
    return render(request, 'advisornotes/note_search.html', context)
Example #23
0
def download_file(request,
                  course_slug,
                  activity_slug,
                  component_slug=None,
                  submission_id=None,
                  userid=None):
    course = get_object_or_404(CourseOffering, slug=course_slug)
    activity = get_object_or_404(course.activity_set,
                                 slug=activity_slug,
                                 deleted=False)
    staff = False
    if is_course_staff_by_slug(request, course_slug):
        staff = True

    # find the appropriate submission object
    if submission_id:
        # explicit request: get that one.
        try:
            submission_info = SubmissionInfo.from_submission_id(submission_id)
        except ValueError:
            return NotFoundResponse(request)
    elif userid:
        # userid specified: get their most recent submission
        student = get_object_or_404(Person, find_userid_or_emplid(userid))
        submission_info = SubmissionInfo(student=student,
                                         activity=activity,
                                         include_deleted=staff)
    else:
        return NotFoundResponse(request)

    if not submission_info.have_submitted(
    ) or submission_info.activity != activity:
        return NotFoundResponse(request)

    if not submission_info.accessible_by(request):
        return ForbiddenResponse(request)

    # create the result
    if component_slug:
        # download single component if specified
        submission_info.get_most_recent_components()
        submitted_components = [
            subcomp
            for comp, subcomp in submission_info.components_and_submitted()
            if subcomp and comp.slug == component_slug
        ]
        if not submitted_components:
            return NotFoundResponse(request)

        submitted_component = submitted_components[0]
        return submitted_component.download_response(
            slug=submission_info.submissions[0].file_slug())
    else:
        # no component specified: give back the full ZIP file.
        return submission_info.generate_student_zip()
Example #24
0
def search_scholarships_by_student(request, student_id):
    #check permissions
    roles = Role.all_roles(request.user.username)
    allowed = set(['FUND'])
    if not (roles & allowed):
        return ForbiddenResponse(request, "Not permitted to search scholarships by student.")
    scholarships = Scholarship.objects.filter(student__person__emplid=student_id)
    response = HttpResponse(content_type="application/json")
    data = [{'value': s.pk, 'display': s.scholarship_type.unit.label + ": " + s.scholarship_type.name + " (" + s.start_semester.name + " to " + s.end_semester.name + ")"}  for s in scholarships]
    json.dump(data, response, indent=1)
    return response
Example #25
0
def edit_related(request, course_slug, case_slug):
    """
    View function to edit related items: more difficult than the generic function above.
    """
    course = get_object_or_404(CourseOffering, slug=course_slug)
    case = get_object_or_404(DisciplineCaseBase, slug=case_slug, offering__slug=course_slug)
    case = case.subclass()

    if not case.can_edit('related'):
        # once case is closed, don't allow editing
        return ForbiddenResponse(request)
Example #26
0
def convert_content(request, course_slug, page_label=None):
    """
    Convert between wikicreole and HTML (AJAX called in editor when switching editing modes)
    """
    if request.method != 'POST':
        return ForbiddenResponse(request, 'POST only')
    if 'to' not in request.POST:
        return ForbiddenResponse(request, 'must send "to" language')
    if 'data' not in request.POST:
        return ForbiddenResponse(request, 'must sent source "data"')

    offering = get_object_or_404(CourseOffering, slug=course_slug)

    to = request.POST['to']
    data = request.POST['data']
    if to == 'html':
        # convert wikitext to HTML
        # temporarily change the current version to get the result (but don't save)
        if page_label:
            page = get_object_or_404(Page, offering=offering, label=page_label)
            pv = page.current_version()
        else:
            # create temporary Page for conversion during creation
            p = Page(offering=offering)
            pv = PageVersion(page=p)

        pv.wikitext = data
        pv.diff_from = None
        result = {'data': pv.html_contents()}
        return HttpResponse(json.dumps(result),
                            content_type="application/json")
    else:
        # convert HTML to wikitext
        converter = HTMLWiki([])
        try:
            wiki = converter.from_html(data)
        except converter.ParseError:
            wiki = ''
        result = {'data': wiki}
        return HttpResponse(json.dumps(result),
                            content_type="application/json")
Example #27
0
def delete_asset(request, asset_id):
    asset = get_object_or_404(Asset, pk=asset_id)
    if not _has_unit_role(request.user, asset):
        return ForbiddenResponse(request)
    if request.method == 'POST':
        asset.delete()
        messages.success(request, 'Hid asset %s' % asset)
        l = LogEntry(userid=request.user.username,
                     description="Deleted asset: %s" % asset,
                     related_object=asset)
        l.save()
    return HttpResponseRedirect(reverse('inventory:inventory_index'))
Example #28
0
def delete_event(request, event_id):
    event = get_object_or_404(OutreachEvent, pk=event_id)
    if not _has_unit_role(request.user, event):
        return ForbiddenResponse(request)
    if request.method == 'POST':
        event.delete()
        messages.success(request, 'Hid event %s' % event)
        l = LogEntry(userid=request.user.username,
                     description="Deleted event: %s" % event,
                     related_object=event)
        l.save()
    return HttpResponseRedirect(reverse('outreach:outreach_index'))
Example #29
0
def edit_case_info(request, course_slug, case_slug, field):
    """
    View function for all of the "edit this aspect of the case" steps.  Uses the STEP_* dictionaries to get relevant strings/classes.
    """
    course = get_object_or_404(CourseOffering, slug=course_slug)
    case = get_object_or_404(DisciplineCaseBase, slug=case_slug, offering__slug=course_slug)
    case = case.subclass()

    # permisson checks
    roles = request.session['discipline-'+course_slug]
    if not case.can_edit(field):
        # once instructor finished, don't allow editing those fields
        return ForbiddenResponse(request, "letter has been sent: cannot edit this field")
Example #30
0
def edit_attach(request, course_slug, case_slug):
    """
    Front page of the file attachments interface
    """
    course = get_object_or_404(CourseOffering, slug=course_slug)
    case = get_object_or_404(DisciplineCaseBase, slug=case_slug, offering__slug=course_slug)
    case = case.subclass()
    attach_pub = CaseAttachment.objects.filter(case=case, public=True)
    attach_pri = CaseAttachment.objects.filter(case=case, public=False)

    if not case.can_edit('attach'):
        # once case is closed, don't allow editing
        return ForbiddenResponse(request)