utils.py 3.54 KB
Newer Older
1 2 3 4 5
"""
Utility functions for course_wiki.
"""

from django.core.exceptions import ObjectDoesNotExist
6 7
from xmodule import modulestore
import courseware
8

9

10 11 12 13
def user_is_article_course_staff(user, article):
    """
    The root of a course wiki is /<course_number>. This means in case there
    are two courses which have the same course_number they will end up with
14
    the same course wiki root e.g. MITx/Phy101/Spring and HarvardX/Phy101/Fall
15 16 17 18 19
    will share /Phy101.

    This looks at the course wiki root of the article and returns True if
    the user belongs to a group whose name starts with 'instructor_' or
    'staff_' and contains '/<course_wiki_root_slug>/'. So if the user is
20 21
    staff on course MITx/Phy101/Spring they will be in
    'instructor_MITx/Phy101/Spring' or 'staff_MITx/Phy101/Spring' groups and
22 23 24
    so this will return True.
    """

25
    wiki_slug = article_course_wiki_root_slug(article)
26

27
    if wiki_slug is None:
28 29
        return False

30 31 32 33 34 35 36 37
    modstore = modulestore.django.modulestore()
    return _has_wiki_staff_access(user, wiki_slug, modstore)


def _has_wiki_staff_access(user, wiki_slug, modstore):
    """Returns whether the user has staff access to the wiki represented by wiki_slug"""
    course_keys = modstore.get_courses_for_wiki(wiki_slug)

38 39 40 41
    # The wiki expects article slugs to contain at least one non-digit so if
    # the course number is just a number the course wiki root slug is set to
    # be '<course_number>_'. This means slug '202_' can belong to either
    # course numbered '202_' or '202' and so we need to consider both.
42 43
    if wiki_slug.endswith('_') and slug_is_numerical(wiki_slug[:-1]):
        course_keys.extend(modstore.get_courses_for_wiki(wiki_slug[:-1]))
44

45 46 47
    for course_key in course_keys:
        course = modstore.get_course(course_key)
        if courseware.access.has_access(user, 'staff', course, course_key):
48
            return True
49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108
    return False


def slug_is_numerical(slug):
    """Returns whether the slug can be interpreted as a number."""
    try:
        float(slug)
    except ValueError:
        return False

    return True


def course_wiki_slug(course):
    """Returns the slug for the course wiki root."""
    slug = course.wiki_slug

    # Django-wiki expects article slug to be non-numerical. In case the
    # course number is numerical append an underscore.
    if slug_is_numerical(slug):
        slug = slug + "_"

    return slug


def article_course_wiki_root_slug(article):
    """
    We assume the second level ancestor is the course wiki root. Examples:
    / returns None
    /Phy101 returns 'Phy101'
    /Phy101/Mechanics returns 'Phy101'
    /Chem101/Metals/Iron returns 'Chem101'

    Note that someone can create an article /random-article/sub-article on the
    wiki. In this case this function will return 'some-random-article' even
    if no course with course number 'some-random-article' exists.
    """

    try:
        urlpath = article.urlpath_set.get()
    except ObjectDoesNotExist:
        return None

    # Ancestors of /Phy101/Mechanics/Acceleration/ is a list of URLPaths
    # ['Root', 'Phy101', 'Mechanics']
    ancestors = urlpath.cached_ancestors

    course_wiki_root_urlpath = None

    if len(ancestors) == 0:  # It is the wiki root article.
        course_wiki_root_urlpath = None
    elif len(ancestors) == 1:  # It is a course wiki root article.
        course_wiki_root_urlpath = urlpath
    else:  # It is an article inside a course wiki.
        course_wiki_root_urlpath = ancestors[1]

    if course_wiki_root_urlpath is not None:
        return course_wiki_root_urlpath.slug

    return None