Open ndarville opened 11 years ago
I probably need to only allow a user to be in one group.
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.
Using the admin is going to be a mess, because it still won’t scale with a large number of objects.
Linked issue trackers:
A default search system needs to be written, I wager.
Reproducing the look and behaviour:
For all:
For co-editors:
views.py
and manage-users-js.js
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
inanother_object
Question is whether to use—or extend—simple_js()
and nonjs()
or to go a new route.
Resource: http://effectivedjango.com/forms.html
The documentation makes it sound as if the queries are sanitized.
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.
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 “{{ query }}”</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 %}
elif (not request.user == thread.author and
not request.user.has_perm('forum.appoint_coeditor')):
Co-editor changes got merged: e8bed6ee23193e4b2a03556a0d27f1579b1a98d5. Punted on other management for now.
Use the admin back-end for this for the time being? Problems:
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.
admins.py
Consider per-object permissions later (thread, category).
Default Permissions