Exemple #1
0
def edit(request, page_name):
    params = request.POST
    view_url = reverse('notepad:view', args=(page_name, ))
    redirect_url = view_url + '#bottom'
    if 'content' not in params:
        return HttpResponseRedirect(redirect_url)
    if is_bot_request(request):
        notes = [params.get('note')]
        return activity_notify(request, page_name, 'editing note', notes,
                               redirect_url)
    # Get the Note requested.
    try:
        note_id = int(params.get('note'))
    except (TypeError, ValueError):
        log.warning('Invalid note id for editing: {!r}'.format(
            params.get('note')))
        return HttpResponseRedirect(redirect_url)
    try:
        note = Note.objects.get(pk=note_id, page__name=page_name)
    except Note.DoesNotExist:
        log.warning(
            f'Visitor "{request.visit.visitor}" tried to submit an edit to non-existent note {note_id} '
            f'or gave the wrong page {page_name!r}.')
        return HttpResponseRedirect(redirect_url)
    redirect_url = f'{view_url}#note_{note_id}'
    # Verify that everything looks valid.
    if note.protected and not is_admin_and_secure(request):
        # Prevent editing protected notes.
        log.warning(
            f'User tried to edit protected note {note.id} from page {note.page.name!r}.'
        )
        return HttpResponseRedirect(redirect_url)
    if note.deleted:
        log.warning(
            f'User tried to edit deleted note {note.id} from page {note.page.name!r}.'
        )
        return HttpResponseRedirect(redirect_url)
    if note.archived:
        log.warning(
            f'User tried to edit archived note {note.id} from page {note.page.name!r}.'
        )
        return HttpResponseRedirect(redirect_url)
    # Double-check this note hasn't already been edited.
    if note.edited:
        log.warning(
            f'User tried to edit already-edited note {note.id} from page {note.page.name!r}.'
        )
        return HttpResponseRedirect(redirect_url)
    if 'content' not in params:
        log.warning('No "content" key in query parameters.')
        return HttpResponseRedirect(redirect_url)
    new_note = lib.edit_note(note, params['content'], request.visit)
    if page_name in PROTECTED_PAGES:
        # Notify that someone edited a note from a protected page.
        activity_notify(request,
                        page_name,
                        f'editing a note', [note],
                        blocked=False)
    redirect_url = f'{view_url}#note_{new_note.id}'
    return HttpResponseRedirect(redirect_url)
Exemple #2
0
def moveform(request, page_name):
    view_url = reverse('notepad:view', args=(page_name, ))
    params = request.POST
    notes = get_notes_from_params(params, archived=False, deleted=False)
    if not is_admin_and_secure(request):
        # Prevent appearance of being able to move protected notes.
        notes = [note for note in notes if not note.protected]
    context = {'page': page_name, 'notes': notes}
    return render(request, 'notepad/moveform.tmpl', context)
Exemple #3
0
def editform(request, page_name):
    view_url = reverse('notepad:view', args=(page_name, ))
    params = request.POST
    notes = get_notes_from_params(params)
    error = None
    warning = None
    if len(notes) == 0:
        note_ids = get_note_ids_from_params(params)
        notes_str = ', '.join([str(note_id) for note_id in note_ids])
        log.warning(
            f'No valid note selected for editing. User selected {notes_str}')
        error = 'No valid note selected.'
        note = None
    elif len(notes) > 1:
        log.info('Multiple notes selected.')
        warning = 'Multiple notes selected. Editing only the first one.'
        note = notes[0]
    else:
        note = notes[0]
    # Prevent appearance of being able to edit protected, archived, or deleted notes.
    if not note:
        pass
    elif note.protected and not is_admin_and_secure(request):
        log.warning('Non-admin attempted to edit protected note {}.'.format(
            note.id))
        error = 'This note is protected.'
    elif note.edited:
        log.warning(f'User attempted to edit already edited note {note.id}.')
        error = 'This note has already been edited.'
    elif note.archived:
        log.warning(f'User attempted to edit archived note {note.id}.')
        error = 'This note has been archived.'
    elif note.deleted:
        log.warning(f'User attempted to edit deleted note {note.id}.')
        error = 'This note has been deleted.'
    if error:
        context = {'page': page_name, 'error': error}
        #TODO: Return a non-200 HTTP status.
        return render(request, 'notepad/error.tmpl', context)
    elif note:
        notes = lib.get_notes(page_name, archived=False, deleted=False)
        lines = len(note.content.splitlines())
        context = {
            'page': page_name,
            'note': note,
            'notes': notes,
            'rows': round(lines * 1.1) + 2,
            'warning': warning,
        }
        return render(request, 'notepad/editform.tmpl', context)
    else:
        log.error(
            f'Ended up with neither a note ({note!r}) nor an error ({error!r}).'
        )
        return HttpResponseRedirect(view_url)
Exemple #4
0
def view_ip(request, ip=None):
  if ip is None:
    ip = request.visit.visitor.ip
  # Only allow regular users to view their own IP address.
  if is_admin_and_secure(request):
    admin = True
  else:
    admin = False
    ip = request.visit.visitor.ip
  # Get query parameters.
  params = request.GET
  format = params.get('format', 'html')
  # Get the IpInfos for the IP address.
  query = IpInfo.objects.filter(ip=ip).order_by('-timestamp')
  fields = ('timestamp', 'version', 'asn', 'isp', 'hostname', 'timezone', 'latitude', 'longitude',
            'country', 'region', 'town', 'zip', 'label')
  if format == 'plain':
    if not query:
      return HttpResponse('No data on ip '+ip, content_type=settings.PLAINTEXT)
    response = HttpResponse(content_type=settings.PLAINTEXT)
    response.write(ip+':\n')
    response.write('\t'.join(fields)+'\n')
    for ipinfo in query:
      values = [str(getattr(ipinfo, field, None)) for field in fields]
      response.write('\t'.join(values)+'\n')
    return response
  else:
    ipinfos = []
    try:
      last_ipinfo = None
      for ipinfo in query:
        if last_ipinfo is None:
          ipinfos.append(ipinfo)
        else:
          changed = False
          for field in fields:
            new_value = getattr(ipinfo, field, None)
            old_value = getattr(last_ipinfo, field, None)
            if field != 'timestamp' and new_value != old_value:
              changed = True
          if changed:
            ipinfos.append(ipinfo)
        last_ipinfo = ipinfo
    except DataError:
      # DataError is thrown on executing the query on an invalid ip address.
      log.warning('DataError on IP address'+ip)
      ipinfos = []
    context = {
      'ip':ip,
      'admin':admin,
      'timezone': set_timezone(request),  # Set and retrieve timezone.
      'ipinfos': ipinfos
    }
    return render(request, 'traffic/ips.tmpl', context)
Exemple #5
0
def view(request, page):
    params = request.GET
    is_admin = is_admin_and_secure(request)
    editing = False
    if params.get('editing'):
        if is_admin:
            editing = True
        else:
            return HttpResponseRedirect(get_view_url(page))
    context = {'editing': editing, 'editing_text': False, 'admin': is_admin}
    return show_page(request, page, context)
Exemple #6
0
def _move_page(request, old_page_name, notes):
    view_url = reverse('notepad:view', args=(old_page_name, ))
    params = request.POST
    new_page_name = params.get('new_page')
    if new_page_name:
        view_url = reverse('notepad:view', args=(new_page_name, ))
    else:
        log.warning('No new_page received for move.')
        return HttpResponseRedirect(view_url)
    try:
        old_page = Page.objects.get(name=old_page_name)
    except Page.DoesNotExist:
        log.warning('Page {!r} does not exist.'.format(old_page_name))
        return HttpResponseRedirect(view_url)
    try:
        new_page = Page.objects.get(name=new_page_name)
    except Page.DoesNotExist:
        new_page = Page(name=new_page_name)
        new_page.save()
    for note in notes:
        if note.page.name != old_page_name:
            log.warning(
                'User tried to move note {!r} from page {!r}, but gave page name {!r}.'
                .format(note.id, note.page.name, old_page_name))
            continue
        if note.protected and not is_admin_and_secure(request):
            # Prevent moving protected notes.
            continue
        move = Move(
            type='page',
            note=note,
            old_page=old_page,
            new_page=new_page,
            visit=request.visit,
        )
        note.page = new_page
        # Save both or, if something goes wrong, neither.
        try:
            with transaction.atomic():
                move.save()
                note.save()
        except django.db.Error as dbe:
            log.error('Error on saving Move or Note: {}'.format(dbe))
    return HttpResponseRedirect(view_url)
Exemple #7
0
def hide(request, page_name):
    params = request.POST
    action = params.get('action')
    notes = get_notes_from_params(params)
    admin = None
    if is_bot_request(request):
        activity_notify(request, page_name, f'{action}-ing', notes)
    elif action in ('archive', 'delete'):
        for note in notes:
            if note.page.name != page_name:
                log.warning(
                    'User attempted to {} note {} from page {!r}, but gave page name {!r}'
                    .format(action, note.id, note.page.name, page_name))
                continue
            if note.protected:
                if admin is None:
                    admin = is_admin_and_secure(request)
                if not admin:
                    log.warning(
                        'Non-admin attempted to delete protected note {!r}.'.
                        format(note.id))
                    continue
            if action == 'archive':
                note.archived = True
                note.archiving_visit = request.visit
            elif action == 'delete':
                note.deleted = True
                note.deleting_visit = request.visit
            note.save()
        if page_name in PROTECTED_PAGES:
            # Notify that someone archived/deleted notes from a protected page.
            activity_notify(request,
                            page_name,
                            f'{action}-ing note(s)',
                            notes,
                            blocked=False)
    view_url = reverse('notepad:view', args=(page_name, ))
    return HttpResponseRedirect(view_url + '#bottom')
Exemple #8
0
def monitor(request):
  # The query string parameters and their defaults.
  params = QueryParams()
  params.add('p', type=int, default=1, min=1)
  params.add('perpage', default=PER_PAGE_DEFAULT, type=int, min=1)
  params.add('include_me', type=boolish, default=False, choices=(True, False))
  params.add('bot_thres', default=None, type=int)
  params.add('user', default=None, type=int)
  params.parse(request.GET)
  # If one of the parameters was invalid, redirect to a fixed url.
  # - QueryParams object will automatically set the parameter to a valid value.
  if params.invalid_value:
    return HttpResponseRedirect(reverse('traffic_monitor')+str(params))
  admin = is_admin_and_secure(request)
  # Non-admins can only view their own traffic.
  if not admin:
    this_user = request.visit.visitor.user.id
    if params['user']:
      # If they gave a user, but it's not themselves, redirect back to themself.
      if params['user'] != this_user:
        return HttpResponseRedirect(reverse('traffic_monitor')+str(params.but_with(user=None)))
    else:
      # If they gave no user, that's fine. Silently show only themself.
      params['user'] = this_user
      params.params['user'].default = this_user
  # Obtain visits list from database.
  if params['user'] is not None:
    visits = Visit.objects.filter(visitor__user__id=params['user'])
  elif params['include_me']:
    visits = Visit.objects.all()
  else:
    visits = Visit.objects.exclude(visitor__user__id=1)
  # Exclude robots, if requested.
  if params['bot_thres'] is not None and params['user'] is None:
    visits = visits.filter(visitor__bot_score__lt=params['bot_thres'])
  # Order by id.
  visits = visits.order_by('-id')
  # Create a Paginator.
  pages = django.core.paginator.Paginator(visits, params['perpage'])
  try:
    page = pages.page(params['p'])
  except django.core.paginator.EmptyPage:
    return HttpResponseRedirect(reverse('traffic_monitor')+str(params.but_with(p=pages.num_pages)))
  # Construct the navigation links.
  links = collections.OrderedDict()
  if page.has_previous():
    links['Later'] = str(params.but_with(p=page.previous_page_number()))
  if admin:
    if params['include_me']:
      links['Hide me'] = str(params.but_with(include_me=None))
    else:
      links['Include me'] = str(params.but_with(include_me=True))
    if params['bot_thres'] is not None and params['bot_thres'] != 0:
      links['Show all'] = str(params.but_with(bot_thres=None))
    if params['bot_thres'] is None or params['bot_thres'] > categorize.SCORES['bot_in_ua']:
      links['Hide robots'] = str(params.but_with(bot_thres=categorize.SCORES['bot_in_ua']))
    if params['bot_thres'] is None or params['bot_thres'] > categorize.SCORES['sent_cookies']+1:
      links['Show humans'] = str(params.but_with(bot_thres=categorize.SCORES['sent_cookies']+1))
  if page.has_next():
    links['Earlier'] = str(params.but_with(p=page.next_page_number()))
  context = {
    'visits': page,
    'admin':admin,
    'links': links,
    'this_visit': request.visit,
    'timezone': set_timezone(request),  # Set and retrieve timezone.
    'ua_exact_thres': categorize.SCORES['ua_exact'],
    'referrer_exact_thres': categorize.SCORES['referrer_exact'],
  }
  return render(request, 'traffic/monitor.tmpl', context)
Exemple #9
0
def view(request, page_name):
    params = QueryParams()
    params.add('note', type=int, default=None)
    params.add('version', type=int, default=None)
    params.add('format', default='html', choices=('html', 'plain'))
    params.add('admin', type=boolish, default=False, choices=(True, False))
    params.add('archived', type=boolish, default=False, choices=(True, False))
    params.add('deleted', type=boolish, default=False, choices=(True, False))
    params.add('select', default='none', choices=('all', 'none'))
    params.parse(request.GET)
    # If one of the parameters was invalid, redirect to a fixed url.
    # - QueryParams object will automatically set the parameter to a valid value.
    if params.invalid_value:
        return HttpResponseRedirect(
            reverse('notepad:view', args=(page_name, )) + str(params))
    # Only allow showing deleted notes to the admin over HTTPS.
    is_admin = is_admin_and_secure(request)
    if not is_admin and (params['admin'] or params['deleted']):
        query_str = str(params.but_with(admin=False, deleted=False))
        return HttpResponseRedirect(
            reverse('notepad:view', args=(page_name, )) + query_str)
    # Fetch the note(s).
    if params['version']:
        notes = lib.get_notes_from_ids([params['version']], page_name,
                                       params['archived'], params['deleted'])
    elif params['note']:
        note = lib.get_latest_version(params['note'], page_name,
                                      params['archived'], params['deleted'])
        notes = [note]
    else:
        notes = lib.get_notes(page_name, params['archived'], params['deleted'])
    # Bundle up the data and display the notes.
    if params['format'] == 'plain':
        contents = [note.content for note in notes]
        return HttpResponse('\n\n'.join(contents),
                            content_type=settings.PLAINTEXT)
    else:
        links = collections.OrderedDict()
        randomness = rand.randint(1, 1000000)
        links['☑ Select all'] = str(
            params.but_with(select='all', reload=randomness))
        links['□ Select none'] = str(
            params.but_with(select='none', reload=randomness))
        if is_admin:
            if params['admin']:
                links['Admin off'] = str(params.but_with(admin=False))
            else:
                links['Admin on'] = str(params.but_with(admin=True))
        if params['archived']:
            links['Hide archived'] = str(params.but_with(archived=False))
        else:
            links['Show archived'] = str(params.but_with(archived=True))
        if is_admin:
            if params['deleted']:
                links['Hide deleted'] = str(params.but_with(deleted=False))
            else:
                links['Show deleted'] = str(params.but_with(deleted=True))
        context = {
            'page': page_name,
            'notes': notes,
            'links': links,
            'admin_view': params['admin'],
            'select': params['select'],
        }
        return render(request, 'notepad/view.tmpl', context)
Exemple #10
0
def _move_order(request, page_name, notes, direction):
    page_notes = lib.get_notes(page_name, archived=False, deleted=False)
    # Save old display_orders.
    old_orders = {}
    for note in page_notes:
        old_orders[note.id] = note.display_order
    # Go through the notes in the opposite direction they're moving in.
    # E.g. if we're moving notes down, then start from the last note and move up.
    reverse_order = direction == 'down'
    sorted_notes = sorted(page_notes,
                          reverse=reverse_order,
                          key=lambda note: (note.display_order, note.id))
    # Set new display_orders.
    for i in range(len(sorted_notes)):
        note = sorted_notes[i]
        if i == 0:
            # We can't move this note earlier, since it's already at the end.
            continue
        last_note = sorted_notes[i - 1]
        if note not in notes:
            # This isn't one of the notes being moved.
            continue
        if last_note in notes:
            # Don't swap a note with another note being moved. They should all move in a block.
            # If a bunch of notes in a block are already at the end, the first rule (i == 0) prevents the
            # note at the end from moving, and this prevents the others in the block from moving past it.
            continue
        if note.protected and not is_admin_and_secure(request):
            # Prevent moving protected notes.
            continue
        # Swap display orders.
        note.display_order, last_note.display_order = last_note.display_order, note.display_order
        # Swap positions in the list.
        sorted_notes[i] = last_note
        sorted_notes[i - 1] = note
    # Create Moves and save changes to database.
    for note in page_notes:
        if note.page.name != page_name:
            log.warning(
                'User tried to move note {!r} order on page {!r}, but gave page name {!r}.'
                .format(note.id, note.page.name, page_name))
            continue
        if note.display_order == old_orders[note.id]:
            continue
        move = Move(
            type='order',
            note=note,
            old_display_order=old_orders[note.id],
            new_display_order=note.display_order,
            visit=request.visit,
        )
        log.info('Moving note {} display_order from {} to {}.'.format(
            note.id, move.old_display_order, move.new_display_order))
        # Save both or, if something goes wrong, neither.
        try:
            with transaction.atomic():
                move.save()
                note.save()
        except django.db.Error as dbe:
            log.error('Error on saving Move or Note: {}'.format(dbe))
    view_url = reverse('notepad:view', args=(page_name, ))
    return HttpResponseRedirect(view_url + '#bottom')