models.py 3.13 KB
Newer Older
1 2 3
from django.db import models
from django.contrib.auth.models import User
from django.core.urlresolvers import reverse
4
from django.core.exceptions import ValidationError
5
from django.utils.html import strip_tags
6 7
import json

8 9
from xmodule_django.models import CourseKeyField

Arthur Barrett committed
10

11 12
class Note(models.Model):
    user = models.ForeignKey(User, db_index=True)
13
    course_id = CourseKeyField(max_length=255, db_index=True)
14
    uri = models.CharField(max_length=255, db_index=True)
15 16
    text = models.TextField(default="")
    quote = models.TextField(default="")
17
    range_start = models.CharField(max_length=2048)  # xpath string
18
    range_start_offset = models.IntegerField()
19
    range_end = models.CharField(max_length=2048)  # xpath string
20
    range_end_offset = models.IntegerField()
Arthur Barrett committed
21
    tags = models.TextField(default="")  # comma-separated string
22 23
    created = models.DateTimeField(auto_now_add=True, null=True, db_index=True)
    updated = models.DateTimeField(auto_now=True, db_index=True)
24

25 26 27
    class Meta:
        app_label = 'notes'

28
    def clean(self, json_body):
e0d committed
29
        """
30
        Cleans the note object or raises a ValidationError.
e0d committed
31
        """
32 33 34 35
        if json_body is None:
            raise ValidationError('Note must have a body.')

        body = json.loads(json_body)
36
        if not isinstance(body, dict):
37 38
            raise ValidationError('Note body must be a dictionary.')

39 40 41 42 43 44
        # NOTE: all three of these fields should be considered user input
        # and may be output back to the user, so we need to sanitize them.
        # These fields should only contain _plain text_.
        self.uri = strip_tags(body.get('uri', ''))
        self.text = strip_tags(body.get('text', ''))
        self.quote = strip_tags(body.get('quote', ''))
45 46 47 48 49 50 51 52 53 54 55

        ranges = body.get('ranges')
        if ranges is None or len(ranges) != 1:
            raise ValidationError('Note must contain exactly one range.')

        self.range_start = ranges[0]['start']
        self.range_start_offset = ranges[0]['startOffset']
        self.range_end = ranges[0]['end']
        self.range_end_offset = ranges[0]['endOffset']

        self.tags = ""
56
        tags = [strip_tags(tag) for tag in body.get('tags', [])]
57 58
        if len(tags) > 0:
            self.tags = ",".join(tags)
59 60

    def get_absolute_url(self):
e0d committed
61
        """
e0d committed
62
        Returns the absolute url for the note object.
e0d committed
63
        """
64 65
        # pylint: disable=no-member
        kwargs = {'course_id': self.course_id.to_deprecated_string(), 'note_id': str(self.pk)}
66 67 68
        return reverse('notes_api_note', kwargs=kwargs)

    def as_dict(self):
e0d committed
69
        """
70
        Returns the note object as a dictionary.
e0d committed
71
        """
72
        return {
73 74
            'id': self.pk,
            'user_id': self.user.pk,
75 76 77 78 79 80 81 82 83
            'uri': self.uri,
            'text': self.text,
            'quote': self.quote,
            'ranges': [{
                'start': self.range_start,
                'startOffset': self.range_start_offset,
                'end': self.range_end,
                'endOffset': self.range_end_offset
            }],
84 85 86
            'tags': self.tags.split(","),
            'created': str(self.created),
            'updated': str(self.updated)
Arthur Barrett committed
87
        }