Esempio n. 1
0
def publish(request):
    """
    Publish page: Simply render the publish form if the request method is GET, or actually publishes (create or modify)
    a blog if the request method if POST.
    Note that creating and modifying requires different permissions.
    """
    # The permission authenticating part is the same, no matter which method the request is.
    if (('id' in request.GET or 'id' in request.POST) and not request.user.has_perm('blog.add_blog')) \
            or not request.user.has_perm('blog.change_blog'):
        raise PermissionDenied

    # Check the request method to distinguish between page requests and actual publishes.
    if request.method == 'GET':
        context = get_universal_context('', False)
        # Add field 'stit' so that the edit option will not appear in the top-right menu again.
        context['stit'] = ''
        # Get an existing blog from database and fill out the default values, if an `id` is given. Otherwise, this is
        # creation, nothing to be done.
        if 'id' in request.GET:
            try:
                context.update(
                    model_to_dict(
                        Blog.objects.get(id=int(request.GET.get('id')))))
            except Blog.DoesNotExist or ValueError:
                raise Http404()
        return render(request, 'blog-publish.html', context)

    if request.method == 'POST':
        if 'id' in request.POST:
            try:
                blog = Blog.objects.get(id=int(request.POST.get('id')))
            except Blog.DoesNotExist or ValueError:
                raise Http404()
        else:
            # This is required because django refuses to create a new blog object with empty publish date.
            blog = Blog.objects.create(
                publish_date=request.POST.get('publish_date'))
        # Handle static resources first.
        # Validate the uploaded files first, if any of them is invalid, the whole publish request will not be handled.
        for image in request.FILES.getlist('static_files'):
            if image.size > 10485760:  # 10M
                return JsonResponse({'error': image.name + '体积过大'})
        for image in request.FILES.getlist('static_files'):
            # Although we do not restrict the type of uploaded files, we still store all those files under `image`
            # directory.
            # TODO this may be improvable.
            with open(os.path.join(settings.STATIC_ROOT, 'images', image.name),
                      'wb') as f:
                for chunk in image.chunks():
                    f.write(chunk)
        # Iterate through all fields and update them. It is guaranteed that the POST parameters' name is the same as
        # database columns.
        for field in Blog._meta.fields:
            if field.name != 'id':
                blog.__setattr__(field.name, request.POST.get(field.name, ''))
        blog.save()
        # Since publishing blogs require certain privileges, we only log if a publish succeeded.
        Log.new_log(request, 'blog', 'publish', str(blog.id))
        return redirect('blog-content', path=blog.publish_path +
                        '/')  # the trailing slash is vital
Esempio n. 2
0
def content(request, path):
    """
    Blog content page: accept an access path, and return the corresponding page.
    If the path has a matching blog, render that blog.
    If the path has not matching blog, but it is the prefix of one or more blogs (i.e. there exists blogs under this
    directory), render an index page.
    Otherwise, trigger a 404 error.
    """
    path = unquote('/'.join(path[:-1].split('/')))  # remove the trailing slash
    # Add log even if the request failed.
    Log.new_log(request, 'blog', 'access', path)
    context = get_universal_context(path, True)
    # Assume that there is a matching blog.
    try:
        context.update(blog_to_dict(Blog.objects.get(publish_path=path)))
        return render(request, 'blog-content.html', context)
    except Blog.DoesNotExist:
        # If the blog specified by this path does not exists, then check if there are any blogs under this directory. If
        # yes, display an index page. Otherwise, return 404.
        query_set = Blog.objects.filter(
            publish_path__startswith=path).order_by('-publish_date')
        # Note that empty path (i.e. root) will never raise 404, otherwise there will be no entrance if there are no
        # blogs online.
        if len(query_set) == 0 and path != '':
            raise Http404()
        context['page'], context['plim'], context['pcnt'], context[
            'blog'] = utils.paginate(request, 10, query_set)
        context['blog'] = [
            blog_to_dict(blog, False) for blog in context['blog']
        ]
        return render(request, 'blog-indices.html', context)
Esempio n. 3
0
def indices(request):
    """
    Search page: accept a keyword and search for it in titles and tags. Render the search result as an index page.
    """
    keyword = unquote(request.GET.get('keyword', ''))
    # Add log in all cases.
    Log.new_log(request, 'blog', 'search', keyword)
    # We should disable subdirectories since this is not a real access path.
    context = get_universal_context('', False)
    query_set = Blog.objects \
        .filter(Q(content_name__icontains=keyword) | Q(content_tags__icontains=keyword)) \
        .order_by('-publish_date')
    context['page'], context['plim'], context['pcnt'], context[
        'blog'] = utils.paginate(request, 10, query_set)
    context['blog'] = [blog_to_dict(blog, False) for blog in context['blog']]
    # This parameter is used to fill out the default value of the search bar.
    context['skey'] = keyword
    return render(request, 'blog-indices.html', context)
Esempio n. 4
0
def wcmd_exec(request):
    # The command may have consecutive whitespaces, so we cannot simply `.split(' ')`.
    text = re.split(r'\s+', request.POST.get('_', '').strip())
    # Some commands require special privileges, but technically everyone can access the web commandline page, so we log
    # everything.
    Log.new_log(request, 'wcmd', 'execute', ' '.join(text))
    # All possible failures are raised as WebCommand.Failed, so other exceptions will trigger 502 normally.
    try:
        if len(text) == 0 or text[0] not in WebCommand.commands:
            raise WebCommand.Failed('No such command.')
        command, args, kwargs = WebCommand.commands[text[0]], [], {}
        # Check whether the command is available for the current request.
        if not command.available(request):
            return HttpResponse(status=403, content='Command unavailable')
        # Parse arguments.
        for i in range(1, len(text)):
            # Ignore tokens starting with '--'.
            # Instead of detecting the keyword of a parameter (i.e. --something), we detect the parameter value itself
            # and determine whether it is a positional parameter or a keyword parameter.
            if not text[i].startswith('--'):
                if text[i - 1].startswith('--'):
                    # This is a keyword parameter.
                    name = text[i - 1][2:]
                    if name not in command.key_params:
                        raise WebCommand.Failed(
                            'Unknown keyword parameter %s of value "%s". ' %
                            (name, text[i]))
                    try:
                        kwargs[name] = command.key_params[name].type(text[i])
                    except ValueError:
                        raise WebCommand.Failed(
                            'Preprocessor of keyword parameter %s rejected value "%s".'
                            % (name, text[i]))
                else:
                    # This is a positional parameter. The total count must not exceed.
                    if len(args) >= len(command.pos_params):
                        raise WebCommand.Failed('Too much arguments.')
                    param = command.pos_params[len(args)]
                    try:
                        args.append(param.type(text[i]))
                    except ValueError:
                        raise WebCommand.Failed(
                            'Preprocessor of positional parameter %s rejected value "%s".'
                            % (param.name, text[i]))
        # If there are any positional parameters missing, use the default ones. If some parameter has no default value
        # (i.e. it is required), raise an error.
        for i in range(len(args), len(command.pos_params)):
            param = command.pos_params[i]
            if param.default is None:
                raise WebCommand.Failed(
                    'Positional parameter %s is required but not given.' %
                    param.name)
            args.append(param.default)
        for name, param in command.key_params.items():
            if name not in kwargs:
                if param.default is None:
                    raise WebCommand.Failed(
                        'Keyword parameter %s is required but not given.' %
                        name)
                kwargs[name] = param.default
        # Execute the command.
        resp = command(request, *args, **kwargs)
        # Use a single space as default if the command returns nothing. The space is needed because the front-end can
        # not correctly render empty strings.
        return HttpResponse(status=200, content=escape(resp or ' '))
    except WebCommand.Failed as e:
        return HttpResponse(status=400, content=escape(e.message))