Skip to content

includes/manage-users.html #107

Open
ndarville opened this Issue Feb 23, 2013 · 12 comments

1 participant

@ndarville
Owner

Use the admin back-end for this for the time being? Problems:

  • Not visible to users
  • Not visible to !staff

  1. Manage co-editors
  2. Manage moderators
    • Permissions
    • Category-specific privileges
  3. Manage user ban/activation status

Probably better to focus on the moderation/administration-specific views for the time being. Then the co-editor thingy can be done last.

Afterwards, create a basic moderator group and check the documentation.


  1. Sketch
  2. Use x as template, as it has the most widgets
  3. Disabled sections that won’t be used
  4. Rewrite admins.py
  5. Write default group postinstall and read
  6. Permissions
  7. Template

Consider per-object permissions later (thread, category).

Default Permissions

  • Thread
    1. Remove
    2. Merge
    3. Sticky
    4. Move
    5. Lock
  • Post
    1. Delete
    2. Moderate
  • User
    1. Tempban
    2. Permaban

Manage Co-Editor
================

Template
--------
1. entries.html
    * edit post
    * manage co-editors
2. manage co-editor

Views/Permissions
-----------------
1. edit()
2. co_editors()

Problems
--------
1. No system for picking users from a list
2. Preferable to detect whether a post is OP for DB optimization
@ndarville
Owner

IMG_0836
IMG_0838

@ndarville
Owner

I probably need to only allow a user to be in one group.

@ndarville
Owner

Can’t figure out what the field name for the User.groups.through objects is for defining its presentation with filter_horizontal.

It is possible that it won’t even work, but that seems unlikely.

@ndarville
Owner

For all:

  • Basic search
  • Access, links, and buttons through the general interface
  • Working JS-based POST submission
  • Working HTML-based POST submission
  • Tests
  • Documentation
  • Polish

For co-editors:

  • Basic search
  • Access, links, and buttons through the general interface
  • Working JS-based POST submission
  • Working HTML-based POST submission
  • Docstring/documentation, in views.py and manage-users-js.js
  • Tests
  • Polish
@ndarville
Owner

simple_js() and nonjs() rely on an action and an object_id:

verb object

manage_users() would rely on an action, an object_id, and another_object_id:

verb object in another_object

Question is whether to use—or extend—simple_js() and nonjs() or to go a new route.

@ndarville
Owner
  • Clean/sanitize form POST requests

Resource: http://effectivedjango.com/forms.html

The documentation makes it sound as if the queries are sanitized.

@ndarville
Owner

A URL like /manage/co-editors/5 or /manage/co-editors/5/3/ doesn’t even make a lot of sense, since the digit represents the thread and not the object ID of one or several co-editors.

Maybe something like /manage/thread/5/co-editors/3/? It doesn’t look very standardized, though.

I’ll try to find some examples.

@ndarville
Owner

Excised for now:

urls.py

  (r'^manage/(?P<user_type>\w+(?:-\w+)?)/(?P<object_id>\d+)/$',
                                               'manage_users'),
  (r'^manage/js/$',                            'manage_users_js'),

views.py

@login_required()
def manage_users(request, user_type, object_id):
    """Inspect and edit permissions and groups for

    1. Post co-editors
    2. Moderators
    3. Groups

    The POST submissions to promote and demote co-editors are handled by views
    simple_js() and nonjs().
    """
    people, thread, query = None, None, None

  # if not user_type in ['co-editors', 'moderators', 'groups']:
    if not user_type == 'co-editors':
        return "404"

    if user_type == 'co-editors':
        thread = get_object_or_404(Thread, pk=object_id)

        if thread.is_removed:
            messages.info(request, "This thread no longer exists.")
            return HttpResponseRedirect(reverse('forum.views.thread',
                args=(thread.id,)))
        elif (not request.user == thread.author and
              not request.user.has_perm('forum.appoint_coeditor')):
            messages.info(request,
                "You are not authorized to assign co-editors to this thread.")
            return HttpResponseRedirect(reverse('forum.views.thread',
                args=(thread.id,)))

    if request.method == "POST":
        if request.POST['user-id-search'] != "":  # Search by user ID
            try:
                people = [User.objects.get(pk=request.POST['user-id-search'])]
            except ObjectDoesNotExist:
                messages.info(request, "No user matching query exists.")
        elif 'username-search' in request.POST:  # Search by username
            #TODO Hide users already in other list
            query = request.POST['username-search']
            people = search(request, query)
    # elif user_type in ['moderators', 'groups'] and not request.user.is_superusers:
    #     messages.info("You do not have permission to access this page.")
    #     return HttpResponseRedirect(reverse('forum.views.home', args=()))
        elif 'mote' in request.POST:  # (Pro/De)mote
            coeditor = get_object_or_404(User, pk=request.POST[''])
            if 'promote' in request.POST:
                thread.coeditors.add(coeditor)
            else:  # Demote
                thread.coeditors.remove(coeditor)

    return render(request, 'manage_users.html', {
        'user_type': user_type,
        'object_id': object_id,
        'people':    people,
        'thread':    thread,
        'query':     query})


def manage_users_js(request):
    """Lets users do one of the following:

    * add co-editors to a thread
    * add users to a group of moderators
    * create new groups, and modify group permissions
    """
    if request.is_ajax() and request.method == "POST":
        action = request.POST['action']
        thread = Thread.objects.get(pk=request.POST['object_id'])
        person = User.objects.get(pk=request.POST['user_id'])

        if action == "Promote" or action == "Demoted":
            thread.coeditors.add(person)
            new_action = "Promoted"
        else:
            thread.coeditors.remove(person)
            new_action = "Demoted"

        thread.save()

    return HttpResponse(new_action)

manage-users-js.js

$(document).ready(function() {
// This CSRF token allows us to make POST requests
    $(document).ajaxSend(function(event, xhr, settings) {
        function getCookie(name) {
            var cookieValue = null;
            if (document.cookie && document.cookie != '') {
                var cookies = document.cookie.split(';');
                for (var i = 0; i < cookies.length; i++) {
                    var cookie = jQuery.trim(cookies[i]);
                    // Does this cookie string begin with the name we want?
                    if (cookie.substring(0, name.length + 1) == (name + '=')) {
                        cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
                        break;
                    }
                }
            }
            return cookieValue;
        }
        function sameOrigin(url) {
            // url could be relative or scheme relative or absolute
            var host = document.location.host, // host + port
                protocol = document.location.protocol,
                sr_origin = '//' + host,
                origin = protocol + sr_origin;
            // Allow absolute or scheme relative URLs to same origin
            return (url == origin || url.slice(0, origin.length + 1) == origin + '/') ||
                (url == sr_origin || url.slice(0, sr_origin.length + 1) == sr_origin + '/') ||
                // or any other URL that isn't scheme relative or absolute i.e relative.
                !(/^(\/\/|http:|https:).*/.test(url));
        }
        function safeMethod(method) {
            return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
        }

        if (!safeMethod(settings.type) && sameOrigin(settings.url)) {
            xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken'));
        }
    });

    $('.js').on('click', function(e) {
        // Only perform the following if user is logged in,
        // detected by checking for a "Log out" in navigation.
        if ($('.last:contains("Log Out")').length) {
            // Overrule default nonjs action when submit button is clicked
            // to allow handling the the logic with our JavaScript instead.
            e.preventDefault();

            var $this     = $(this),
                object_id = $("#thread-id").val(),
                user_id   = this.id,
                action    = $this.text()
                // object_id   = this.id,      // id="{{ person.id }}"
                // object_type = "thread",
                // user_type   = "co-editor",
                // href        = this.href;    // Will that suffice here?

            $.post("/manage/js/", {
                object_id: object_id,
                user_id:   user_id,
                action:    action
                // href:      href
                },
                function(data) {
                    $this.text(data);
            });
        }
    });
});

manage_users.html

{% extends "page.html" %}

{% block title %}Manage {{ user_type|capfirst }}{% endblock %}
{% block canonical_url %}{% url forum.views.manage_users user_type object_id %}{% endblock %}
{% block content_body %}
    <form action="{% url forum.views.manage_users user_type object_id %}" method="post" id="search-form">
        {% csrf_token %}
        <label for="search" role="search">Search</label>
        <input
            type="search"
            x-webkit-speech
            name="username-search"
            value=""
            placeholder="Search for user by name"
        />
        <input
            type="number"
            x-webkit-speech
            name="user-id-search"
            value=""
            min="1"
            step="1"
            placeholder="Search by ID"
        />

        {% comment %}
            {% if people %}<p>Matches for usernames with &ldquo;{{ query }}&rdquo;</p>{% endif %}
        {% endcomment %}

        <div id="left-aligned-button-group">
        {% if user_type == "co-editors" %}
        {% with editors=thread.coeditors.all %}
            {% if people %}
                <ul class="user-list">
                    {% for person in people %}
                        <li><a class="button js" role="button" href="" id="{{ person.id }}">Promote</a> {{ person.username }}</li>
                    {% endfor %}
                </ul>
                <hr />
            {% endif %}
        <!-- Current co-editors -->
            {% if editors %}
                <ul class="user-list">
                    {% for person in editors %}
                        <li><a class="button js" role="button" href="" id="{{ person.id }}">Demote</a> {{ person.username }}</li>
                    {% endfor %}
                </ul>
            {% else %}
                <p>This thread currently has no co-editors.</p>
            {% endif %}
        {% endwith %}
        {% endif %}
        </div>

        <div id="button-group">
            <input type="submit" value="Search" />
            <input type="hidden" id="thread-id" value="{{ object_id }}" />
        </div>
    </form>
{% endblock %}
{% block js %}
        {% include "includes/jquery.html" %}
            <script type="text/javascript" src="{{ STATIC_URL }}js/manage-users-js.js"></script>
{% endblock %}
@ndarville
Owner
  • Implement measures whereby people cannot demote highjack a thread as co-editor.
elif (not request.user == thread.author and
          not request.user.has_perm('forum.appoint_coeditor')):
@ndarville
Owner

Check CSS diff and implement in /sass branch.

@ndarville
Owner

Co-editor changes got merged: e8bed6e. Punted on other management for now.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.