From 2c78b7e0850daa92795a8604acdf896891890414 Mon Sep 17 00:00:00 2001 From: gfawcett Date: Tue, 10 Mar 2009 02:44:47 +0000 Subject: [PATCH] Added access-controls to admin-option editing; general cleanup of views.py. Admin-option editing was public; that's changed, you must now be an 'admin' which for now means you are Django's 'user.is_staff'. I reorganized views.py into more-logical sections, and think we are going to split it up into multiple views/* files. git-svn-id: svn://svn.open-ils.org/ILS-Contrib/servres/trunk@164 6d9bc8c9-1ec2-4278-b937-99fde70a366f --- .gitignore | 2 +- conifer/syrup/generics.py | 22 +++---- conifer/syrup/views.py | 149 ++++++++++++++++++++++++++++------------------ 3 files changed, 104 insertions(+), 69 deletions(-) diff --git a/.gitignore b/.gitignore index e7d734c..1805cf4 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,4 @@ sip/openncip/* sip/doc/sip2_developers_guide.pdf sip/java/* *.pyc - +PyZ3950_parsetab.py \ No newline at end of file diff --git a/conifer/syrup/generics.py b/conifer/syrup/generics.py index 2b2e3e0..fac85ef 100644 --- a/conifer/syrup/generics.py +++ b/conifer/syrup/generics.py @@ -4,6 +4,18 @@ from django.http import HttpResponseForbidden from django.shortcuts import get_object_or_404 from django.forms import ModelForm, ValidationError + +def generic_handler(form, decorator=lambda x: x): + def handler(request, obj_id=None, action=None): + if obj_id is None and action is None: + return generic_index(form) + elif action is None: + return generic_edit(form, request, obj_id) + elif action == 'delete': + return generic_delete(form, request, obj_id) + return decorator(handler) + + def generic_index(form): assert hasattr(form, 'Index') return g.render('generic/index.xhtml', form=form) @@ -33,16 +45,6 @@ def generic_delete(form, request, obj_id): instance.delete() return HttpResponseRedirect('../') -def generic_handler(form): - def handler(request, obj_id=None, action=None): - if obj_id is None and action is None: - return generic_index(form) - elif action is None: - return generic_edit(form, request, obj_id) - elif action == 'delete': - return generic_delete(form, request, obj_id) - return handler - def strip_and_nonblank(fieldname): def clean(self): diff --git a/conifer/syrup/views.py b/conifer/syrup/views.py index fe09314..e3593fe 100644 --- a/conifer/syrup/views.py +++ b/conifer/syrup/views.py @@ -1,33 +1,50 @@ -from django.http import HttpResponse, HttpResponseRedirect -from django.http import HttpResponseForbidden -from django.core.paginator import Paginator -from django.shortcuts import get_object_or_404 -from django.contrib.auth.decorators import login_required -from django.contrib.auth import authenticate, login, logout -import conifer.genshi_support as g -import re +#----------------------------------------------------------------------------- +# todo: break this up. It's getting long. I think we should have +# something like: +# +# views/__init__.py # which imports: +# views/course_site_handlers.py +# views/search_stuff.py +# views/add_edit_course.py +# ... +# views/common_imports.py # imported by all. +# +# though these are just examples. Everything in views/* would include +# 'from common_imports import *' just to keep the imports +# tidy. Views/__init__ would import all the other bits: that ought to +# satisfy Django. + from conifer.syrup import models +from datetime import datetime +from django.contrib.auth import authenticate, login, logout +from django.contrib.auth.decorators import login_required from django.contrib.auth.models import User +from django.core.paginator import Paginator from django.db.models import Q -import django.forms -from datetime import datetime +from django.http import HttpResponse, HttpResponseRedirect +from django.http import HttpResponseForbidden +from django.shortcuts import get_object_or_404 +from django.utils import simplejson from generics import * from gettext import gettext as _ # fixme, is this the right function to import? -from django.utils import simplejson +import conifer.genshi_support as g +import django.forms +import re import sys # Graham needs this import hackery to get PyZ3950 working. Presumably -# Art can 'import profile', so this code won't run for him. +# Art can 'import profile; import lex', so this hack won't run for +# him. try: import profile import lex import yacc except ImportError: - sys.modules['profile'] = sys # just get something called profile; + sys.modules['profile'] = sys # just get something called 'profile'; # it's not actually used. import ply.lex - import ply.yacc # pyz3950 thinks these are toplevel modules + import ply.yacc # pyz3950 thinks these are toplevel modules. sys.modules['lex'] = ply.lex sys.modules['yacc'] = ply.yacc @@ -36,6 +53,8 @@ except ImportError: from PyZ3950 import zoom #----------------------------------------------------------------------------- +# poor-man's logging. Not sure we need more yet. + def log(level, msg): print >> sys.stderr, '[%s] %s: %s' % (datetime.now(), level.upper(), msg) @@ -96,7 +115,7 @@ def _access_denied(message): content=message, _django_type=HttpResponseForbidden) - +# decorator def instructors_only(handler): def hdlr(request, course_id, *args, **kwargs): allowed = request.user.is_superuser @@ -110,6 +129,7 @@ def instructors_only(handler): return hdlr +# decorator def members_only(handler): def hdlr(request, course_id, *args, **kwargs): allowed = request.user.is_superuser @@ -121,6 +141,18 @@ def members_only(handler): return _access_denied(_('Only course members are allowed here.')) return hdlr +# decorator +def admin_only(handler): + # fixme, 'admin' is vaguely defined for now as anyone who is + # 'staff', i.e. who has access to the Django admin interface. + def hdlr(request, *args, **kwargs): + allowed = request.user.is_staff + if allowed: + return handler(request, *args, **kwargs) + else: + return _access_denied(_('Only administrators are allowed here.')) + return hdlr + #----------------------------------------------------------------------------- def welcome(request): @@ -134,8 +166,6 @@ def open_courses(request): page_num=page_num, count=count) -# -# def instructors(request): page_num = int(request.GET.get('page', 1)) count = int(request.GET.get('count', 5)) @@ -206,20 +236,27 @@ def browse_courses(request, browse_option=''): def my_courses(request): return g.render('my_courses.xhtml') -@members_only -def course_detail(request, course_id): - course = get_object_or_404(models.Course, pk=course_id) - if course.access != 'ANON' and request.user.is_anonymous(): - #fixme, don't stop access just if anonymous, but rather if not - #allowed to access. We need to set up a permissions model. - return login_required(lambda *args: None)(request) - return g.render('course_detail.xhtml', course=course) +def instructor_detail(request, instructor_id): + page_num = int(request.GET.get('page', 1)) + count = int(request.GET.get('count', 5)) + paginator = Paginator(models.Course.objects. + filter(member__id=instructor_id). + filter(active=True).order_by('title'), count) -@members_only -def course_search(request, course_id): - course = get_object_or_404(models.Course, pk=course_id) - return search(request, in_course=course) + return g.render('courses.xhtml', paginator=paginator, + page_num=page_num, + count=count) + +def department_detail(request, department_id): + page_num = int(request.GET.get('page', 1)) + count = int(request.GET.get('count', 5)) + paginator = Paginator(models.Course.objects. + filter(department__id=department_id). + filter(active=True).order_by('title'), count) + return g.render('courses.xhtml', paginator=paginator, + page_num=page_num, + count=count) #----------------------------------------------------------------------------- # Creating a new course @@ -237,8 +274,8 @@ class NewCourseForm(ModelForm): else: raise ValidationError, _('invalid course code') +# if we have course-code lookup, hack lookup support into the new-course form. -# hack the new-course form if we have course-code lookup COURSE_CODE_LIST = bool(models.course_codes.course_code_list) COURSE_CODE_LOOKUP_TITLE = bool(models.course_codes.course_code_lookup_title) @@ -252,6 +289,8 @@ if COURSE_CODE_LIST: NewCourseForm.base_fields['code'].widget = Select( choices = choices) NewCourseForm.base_fields['code'].empty_label = empty_label + +#-------------------- @login_required def add_new_course(request): @@ -295,6 +334,7 @@ def _add_or_edit_course(request, instance=None): else: return HttpResponseRedirect('../') # back to main view. +# no access-control needed to protect title lookup. def add_new_course_ajax_title(request): course_code = request.GET['course_code'] title = models.course_codes.course_code_lookup_title(course_code) @@ -407,6 +447,7 @@ def delete_course(request, course_id): return HttpResponseRedirect('../') #----------------------------------------------------------------------------- +# Course Invitation Code handler @login_required # must be, to avoid/audit brute force attacks. def course_invitation(request): @@ -437,30 +478,19 @@ def course_invitation(request): role='STUDT') mbr.save() return HttpResponseRedirect(crs.course_url()) - -#----------------------------------------------------------------------------- - -def instructor_detail(request, instructor_id): - page_num = int(request.GET.get('page', 1)) - count = int(request.GET.get('count', 5)) - paginator = Paginator(models.Course.objects. - filter(member__id=instructor_id). - filter(active=True).order_by('title'), count) - return g.render('courses.xhtml', paginator=paginator, - page_num=page_num, - count=count) +#----------------------------------------------------------------------------- +# Course-instance handlers -def department_detail(request, department_id): - page_num = int(request.GET.get('page', 1)) - count = int(request.GET.get('count', 5)) - paginator = Paginator(models.Course.objects. - filter(department__id=department_id). - filter(active=True).order_by('title'), count) +@members_only +def course_detail(request, course_id): + course = get_object_or_404(models.Course, pk=course_id) + return g.render('course_detail.xhtml', course=course) - return g.render('courses.xhtml', paginator=paginator, - page_num=page_num, - count=count) +@members_only +def course_search(request, course_id): + course = get_object_or_404(models.Course, pk=course_id) + return search(request, in_course=course) @members_only def item_detail(request, course_id, item_id): @@ -506,7 +536,7 @@ def item_add(request, course_id, item_id): course = get_object_or_404(models.Course, pk=course_id) else: parent_item = get_object_or_404(models.Item, pk=parent_item_id, course__id=course_id) - assert parent_item.item_type == 'HEADING', 'Can only add items to headings!' + assert parent_item.item_type == 'HEADING', _('You can only add items to headings!') course = parent_item.course if not course.can_edit(request.user): @@ -515,7 +545,7 @@ def item_add(request, course_id, item_id): item_type = request.GET.get('item_type') assert item_type, _('No item_type parameter was provided.') - # for the moment, only HEADINGs, URLs and ELECs can be added. + # for the moment, only HEADINGs, URLs and ELECs can be added. fixme. assert item_type in ('HEADING', 'URL', 'ELEC'), \ _('Sorry, only HEADINGs, URLs and ELECs can be added right now.') @@ -651,7 +681,9 @@ def get_query(query_string, search_fields): query = query & or_query return query + #----------------------------------------------------------------------------- +# Search and search support def search(request, in_course=None): ''' Need to work on this, the basic idea is @@ -736,12 +768,12 @@ def search(request, in_course=None): #----------------------------------------------------------------------------- -# administrative options +# Administrative options +@admin_only def admin_index(request): return g.render('admin/index.xhtml') -# fixme, no auth or permissions stuff yet. class TermForm(ModelForm): class Meta: @@ -763,7 +795,8 @@ class TermForm(ModelForm): raise ValidationError, _('start must precede finish') return cd -admin_terms = generic_handler(TermForm) +admin_terms = generic_handler(TermForm, decorator=admin_only) + class DeptForm(ModelForm): class Meta: @@ -778,7 +811,7 @@ class DeptForm(ModelForm): clean_abbreviation = strip_and_nonblank('abbreviation') clean_name = strip_and_nonblank('name') -admin_depts = generic_handler(DeptForm) +admin_depts = generic_handler(DeptForm, decorator=admin_only) class NewsForm(ModelForm): @@ -794,4 +827,4 @@ class NewsForm(ModelForm): clean_subject = strip_and_nonblank('subject') clean_body = strip_and_nonblank('body') -admin_news = generic_handler(NewsForm) +admin_news = generic_handler(NewsForm, decorator=admin_only) -- 2.11.0