Enabled ANON courses. Search and browse results are now limited by user-permissions...
authorgfawcett <gfawcett@6d9bc8c9-1ec2-4278-b937-99fde70a366f>
Mon, 27 Apr 2009 00:07:11 +0000 (00:07 +0000)
committergfawcett <gfawcett@6d9bc8c9-1ec2-4278-b937-99fde70a366f>
Mon, 27 Apr 2009 00:07:11 +0000 (00:07 +0000)
ANON courses are courses which are open to non-authenticated
users. I'm not sure that this is a good idea yet, but it is useful for
testing.

Browsing will no longer show, for example, courses which the current
user cannot enter. Search will not return items that the current user
cannot view. This is extended to browse-instructors: if the instructor
has no accessible courses, don't bother showing the course.

git-svn-id: svn://svn.open-ils.org/ILS-Contrib/servres/trunk@396 6d9bc8c9-1ec2-4278-b937-99fde70a366f

conifer/TODO
conifer/syrup/models.py
conifer/syrup/views/_common.py
conifer/syrup/views/courses.py
conifer/syrup/views/general.py
conifer/syrup/views/search.py

index 6faf533..3bd75dd 100644 (file)
@@ -2,14 +2,16 @@ CRITICAL:
 
 IMPORTANT:
 
-* set up a proper issue-tracker?
+* "create course site" (not "add course")
 
-* CSS fixes for Internet Explorer. It looks crappy in IE.
+* save-sequence fails on IE6.
 
-* if someone has item checked out, show due date/time on item-about page.
+* set up a proper issue-tracker?
 
 * need more than 10 results on physical-item search results.
 
+* if someone has item checked out, show due date/time on item-about page.
+
 * People should be able to register themselves into open courses.
   That is, actually become a member of them.
 
@@ -21,8 +23,6 @@ IMPORTANT:
 
 * Send me email when my sites change?
 
-* "create course site" (not "add course")
-
 MAYBE:
 
 * Generating barcodes in emails, printable screens? (3 of 9 enough?)
index c038980..df55421 100644 (file)
@@ -56,7 +56,7 @@ class UserExtensionHack(object):
         """Return a queryset of all active instructors."""
         # We are using the Django is_active flag to model activeness.
         return cls.objects.filter(member__role='INSTR', is_active=True) \
-            .order_by('-last_name','-first_name')
+            .order_by('-last_name','-first_name').distinct()
     
 for k,v in [(k,v) for k,v in UserExtensionHack.__dict__.items() \
                 if not k.startswith('_')]:
index 070ff05..60587a0 100644 (file)
@@ -164,8 +164,8 @@ def members_only(handler):
         allowed = user.is_superuser
         if not allowed:
             course = models.Course.objects.get(pk=course_id)
-            allowed = ((user.is_anonymous() and course.access=='ANON') or \
-                       (user.is_authenticated() and course.access=='LOGIN'))
+            allowed = course.access=='ANON' or \
+                (user.is_authenticated() and course.access=='LOGIN')
         if not allowed:
             allowed = _fast_user_membership_query(user.id, course_id)
         if allowed:
@@ -216,3 +216,26 @@ def custom_400_handler(request):
     msg = simple_message(_('Not found'), 
                           _('The page you requested could not be found'))
     return HttpResponse(msg._container, status=404)
+
+#-----------------------------------------------------------
+
+def user_filters(user):
+    """Returns a dict of filters for Item, Course, etc. querysets,
+    based on the given user's permissions."""
+    # TODO, figure out a way of EXPLAIN'ing these queries! I have no
+    # idea of their complexity.
+    if user.is_anonymous():
+        # then only anonymous-access courses are available.
+        filters = {'items': Q(course__access='ANON'),
+                   'courses': Q(access='ANON'),
+                   'instructors': Q(member__course__access='ANON'),
+                   }
+    else:
+        # logged-in users have access to courses which are of the
+        # LOGIN class ('all logged-in users') or in which they
+        # have explicit Member-ship.
+        filters = {'items': (Q(course__access__in=('LOGIN','ANON')) | Q(course__member__user=user)),
+                   'courses': (Q(access__in=('LOGIN','ANON')) | Q(member__user=user)),
+                   'instructors': (Q(member__course__access__in=('LOGIN','ANON')) | Q(member__course__member__user=user)),
+                   }
+    return filters
index 812dc46..062ca2a 100644 (file)
@@ -91,6 +91,7 @@ def edit_course_permissions(request, course_id):
     # the ones in 'models'.
     choices = [
         # note: I'm leaving ANON out for now, until we discuss it further.
+        (u'ANON', _(u'Anyone on the planet may access this site.')),
         (u'CLOSE', _(u'No students: this site is closed.')),
         (u'STUDT', _(u'Students in my course -- I will provide section numbers')),
         (u'INVIT', _(u'Students in my course -- I will share an Invitation Code with them')),
index edd17b9..0c3a0c4 100644 (file)
@@ -85,16 +85,19 @@ def browse(request, browse_option=''):
         template = 'browse_index.xhtml'
     elif browse_option == 'instructors':
         queryset = models.User.active_instructors()
+        queryset = queryset.filter(user_filters(request.user)['instructors'])
         template = 'instructors.xhtml'
     elif browse_option == 'departments':
         queryset = models.Department.objects.filter(active=True)
         template = 'departments.xhtml'
     elif browse_option == 'courses':
         # fixme, course filter should not be (active=True) but based on user identity.
-        queryset = models.Course.objects.all()
+        for_courses = user_filters(request.user)['courses']
+        queryset = models.Course.objects.filter(for_courses)
         template = 'courses.xhtml'
 
-    paginator = queryset and Paginator(queryset, count) or None # index has no queryset.
+    queryset = queryset and queryset.distinct()
+    paginator = Paginator(queryset, count)
     return g.render(template, paginator=paginator,
                     page_num=page_num,
                     count=count)
@@ -112,6 +115,8 @@ def instructor_detail(request, instructor_id):
     '''
     courses = models.Course.objects.filter(member__user=instructor_id,
                                            member__role='INSTR')
+    filters = user_filters(request.user)
+    courses = courses.filter(filters['courses'])
     paginator = Paginator(courses.order_by('title'), count)
 
     '''
index f83ad63..4525f4a 100644 (file)
@@ -63,11 +63,14 @@ def search(request, in_course=None):
         # we start with an empty results_list, as a default
         results_list = models.Item.objects.filter(pk=-1)
 
+        # Next, we filter based on user permissions.
+        flt = user_filters(request.user)
+        user_filter_for_items, user_filter_for_courses = flt['items'], flt['courses'] 
+        # Note, we haven't user-filtered anything yet; we've just set
+        # up the filters.
+
         # numeric search: If the query-string is a single number, then
-        # we do an item-ID search, or a barcode search.  fixme:
-        # item-ID is not a good short-id, since the physical item may
-        # be represented in multiple Item records. We need a
-        # short-number for barcodes.
+        # we do a short-number search, or a barcode search.
 
         if re.match(r'\d+', query_string):
             # Search by short ID.
@@ -85,7 +88,13 @@ def search(request, in_course=None):
             results_list = models.Item.objects.filter(item_query)
 
         if in_course:
+            # For an in-course search, we know the user has
+            # permissions to view the course; no need for
+            # user_filter_for_items.
             results_list = results_list.filter(course=in_course)
+        else:
+            results_list = results_list.filter(user_filter_for_items)
+
         results_list = results_list.order_by('title')
         results_len = len(results_list)
         paginator = Paginator(results_list, count)
@@ -96,22 +105,21 @@ def search(request, in_course=None):
             course_list = []; course_len = 0
         else:
             course_query = get_query(query_string, ['title', 'department__name'])
-            print 'course_query'
-            print course_query
-            course_results = models.Course.objects.filter(course_query).all()
-            # course_list = models.Course.objects.filter(course_query).filter(active=True).order_by('title')[0:5]
-            course_list = course_results.order_by('title')[0:5]
-            #there might be a better way of doing this, though instr and course tables should not be expensive to query
-            #len directly on course_list will reflect limit
+            # apply the search-filter and the user-filter
+            course_results = models.Course.objects.filter(course_query).filter(user_filter_for_courses)
+            course_list = course_results.order_by('title')
             course_len = len(course_results)
 
         #instructor search
-        instr_query = get_query(query_string, ['user__last_name'])
-        instructor_results = models.Member.objects.filter(instr_query).filter(role='INSTR')
         if in_course:
-            instructor_results = instructor_results.filter(course=in_course)
-        instructor_list = instructor_results.order_by('user__last_name')[0:5]
-        instr_len = len(instructor_results)
+            instructor_list = []; instr_len = 0
+        else:
+            instr_query = get_query(query_string, ['user__last_name'])
+            instructor_results = models.Member.objects.filter(instr_query).filter(role='INSTR')
+            if in_course:
+                instructor_results = instructor_results.filter(course=in_course)
+            instructor_list = instructor_results.order_by('user__last_name')[0:5]
+            instr_len = len(instructor_results)
     elif in_course:
         # we are in a course, but have no query? Return to the course-home page.
         return HttpResponseRedirect('../')