예제 #1
0
class User(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(256))
    admin = db.Column(db.Boolean)
    friend = db.Column(db.Boolean)
    posse_targets = db.relationship('PosseTarget',
                                    backref='user',
                                    order_by='PosseTarget.id')
    credentials = db.relationship('Credential', backref='user')

    # Flask-Login integration

    def is_authenticated(self):
        return self.admin

    def is_active(self):
        return True

    def is_anonymous(self):
        return False

    def get_id(self):
        return self.id

    def __eq__(self, other):
        return self.id == other.id

    def __repr__(self):
        return '<User id={}, name={}>'.format(self.id, self.name)
예제 #2
0
class Tag(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(256))
    posts = db.relationship('Post', secondary=posts_to_tags)

    def __init__(self, name):
        self.name = name

    def __repr__(self):
        return self.name
예제 #3
0
class Contact(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(256))
    nicks = db.relationship('Nick',
                            backref='contact',
                            cascade='all,delete-orphan')
    image = db.Column(db.String(512))
    url = db.Column(db.String(512))
    social = db.Column(JsonType)

    def __init__(self, **kwargs):
        self.name = kwargs.get('name')
        self.url = kwargs.get('url')
        self.image = kwargs.get('image')
예제 #4
0
class Mention(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    url = db.Column(db.String(512))
    permalink = db.Column(db.String(512))
    author_name = db.Column(db.String(128))
    author_url = db.Column(db.String(512))
    author_image = db.Column(db.String(512))
    content = db.Column(db.Text)
    content_plain = db.Column(db.Text)
    published = db.Column(db.DateTime)
    title = db.Column(db.String(512))
    syndication = db.Column(JsonType)
    reftype = db.Column(db.String(32))
    rsvp = db.Column(db.String(32))
    person_mention = db.Column(db.Boolean)
    posts = db.relationship('Post', secondary=posts_to_mentions)

    def __init__(self):
        self.index = None
        self.url = None
        self.permalink = None
        self.author_name = None
        self.author_url = None
        self.author_image = None
        self.content = None
        self.content_plain = None
        self.published = None
        self.title = None
        self.reftype = None
        self.rsvp = None
        self.person_mention = False
        self.syndication = []
        self._children = []

    @property
    def fragment_id(self):
        return '{}-{}'.format(
            self.author_name.lower().replace(' ', '_')
            if self.author_name else 'unnamed', self.id)

    @property
    def title_or_url(self):
        return self.title or util.prettify_url(self.permalink)
예제 #5
0
class Post(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    path = db.Column(db.String(256))
    historic_path = db.Column(db.String(256))
    short_path = db.Column(db.String(16))
    post_type = db.Column(db.String(64))
    draft = db.Column(db.Boolean)
    deleted = db.Column(db.Boolean)
    hidden = db.Column(db.Boolean)
    redirect = db.Column(db.String(256))
    tags = db.relationship('Tag', secondary=posts_to_tags)
    people = db.relationship('Contact', secondary=posts_to_people)
    friends_only = db.Column(db.Boolean)

    in_reply_to = db.Column(JsonType)
    repost_of = db.Column(JsonType)
    like_of = db.Column(JsonType)
    bookmark_of = db.Column(JsonType)

    reply_contexts = db.relationship('Context',
                                     secondary=posts_to_reply_contexts)
    like_contexts = db.relationship('Context',
                                    secondary=posts_to_like_contexts)
    repost_contexts = db.relationship('Context',
                                      secondary=posts_to_repost_contexts)
    bookmark_contexts = db.relationship('Context',
                                        secondary=posts_to_bookmark_contexts)

    title = db.Column(db.String(256))
    published = db.Column(db.DateTime, index=True)
    updated = db.Column(db.DateTime)
    start_utc = db.Column(db.DateTime)
    end_utc = db.Column(db.DateTime)
    start_utcoffset = db.Column(db.Interval)
    end_utcoffset = db.Column(db.Interval)
    slug = db.Column(db.String(256))

    syndication = db.Column(JsonType)
    sent_webmentions = db.Column(JsonType)

    location = db.Column(JsonType)
    venue_id = db.Column(db.Integer, db.ForeignKey('venue.id'))
    venue = db.relationship('Venue', uselist=False)

    mentions = db.relationship('Mention',
                               secondary=posts_to_mentions,
                               order_by='Mention.published')

    content = db.Column(db.Text)
    content_html = db.Column(db.Text)
    attachments = db.relationship('Attachment', backref='post')

    # reviews
    item = db.Column(JsonType)
    rating = db.Column(db.Integer)

    @classmethod
    def load_by_id(cls, dbid):
        return cls.query.get(dbid)

    @classmethod
    def load_by_path(cls, path):
        return cls.query.filter_by(path=path).first()

    @classmethod
    def load_by_short_path(cls, path):
        return cls.query.filter_by(short_path=path).first()

    @classmethod
    def load_by_historic_path(cls, path):
        return cls.query.filter_by(historic_path=path).first()

    def __init__(self, post_type):
        self.post_type = post_type
        self.draft = False
        self.deleted = False
        self.hidden = False
        self.redirect = None
        self.previous_permalinks = []
        self.in_reply_to = []
        self.repost_of = []
        self.like_of = []
        self.bookmark_of = []
        self.title = None
        self.published = None
        self.start_time = None
        self.end_time = None
        self.slug = None
        self.location = None
        self.syndication = []
        self.sent_webmentions = []
        self.audience = []  # public
        self.mention_urls = []
        self.content = None
        self.content_html = None
        self.rating = None
        self.item = None

    def get_image_path(self):
        site_url = get_settings().site_url or 'http://localhost'
        return '/'.join((site_url, self.path, 'files'))

    def map_image(self, width, height):
        location = self.location or (self.venue and self.venue.location)
        if location:
            lat = location.get('latitude')
            lng = location.get('longitude')
            return maps.get_map_image(
                width, height, 13, [maps.Marker(lat, lng, 'dot-small-blue')])

    def get_location_as_geo_uri(self):
        location = self.location or (self.venue and self.venue.location)
        if location:
            lat = location.get('latitude')
            lng = location.get('longitude')
            return 'geo:%f,%f' % (lat, lng)

    @property
    def start(self):
        if self.start_utc and self.start_utcoffset:
            tz = datetime.timezone(self.start_utcoffset)
            return self.start_utc\
                       .replace(tzinfo=datetime.timezone.utc)\
                       .astimezone(tz)
        return self.start_utc

    @start.setter
    def start(self, value):
        self.start_utc = value and value\
            .astimezone(datetime.timezone.utc)\
            .replace(tzinfo=None)
        self.start_utcoffset = value and value.utcoffset()

    @property
    def end(self):
        if self.end_utc and self.end_utcoffset:
            tz = datetime.timezone(self.end_utcoffset)
            return self.end_utc\
                       .replace(tzinfo=datetime.timezone.utc)\
                       .astimezone(tz)
        return self.end_utc

    @end.setter
    def end(self, value):
        self.end_utc = value and value\
            .astimezone(datetime.timezone.utc)\
            .replace(tzinfo=None)
        self.end_utcoffset = value and value.utcoffset()

    @property
    def permalink(self):
        site_url = get_settings().site_url or 'http://localhost'
        return '/'.join((site_url, self.path))

    @property
    def shortlink(self):
        if self.short_path:
            site_url = get_settings().site_url or 'http://localhost'
            return '/'.join((site_url, self.short_path))

    def _dedupe(self, mentions):
        all_children = set()
        mentions = list(mentions)
        for m in mentions:
            if m.syndication:
                m._children = [
                    n for n in mentions if n.permalink in m.syndication
                ]
                all_children.update(m._children)
        return [m for m in mentions if m not in all_children]

    @property
    def likes(self):
        return self._dedupe(m for m in self.mentions if m.reftype == 'like')

    @property
    def reposts(self):
        return self._dedupe(m for m in self.mentions if m.reftype == 'repost')

    @property
    def replies(self):
        return self._dedupe(m for m in self.mentions if m.reftype == 'reply')

    @property
    def rsvps(self):
        return self._dedupe(m for m in self.mentions if m.reftype == 'rsvp')

    @property
    def rsvps_yes(self):
        return [r for r in self.rsvps if r.rsvp == 'yes']

    @property
    def rsvps_maybe(self):
        return [r for r in self.rsvps if r.rsvp == 'maybe']

    @property
    def rsvps_no(self):
        return [r for r in self.rsvps if r.rsvp == 'no']

    @property
    def references(self):
        return self._dedupe(m for m in self.mentions
                            if m.reftype == 'reference')

    @property
    def tweet_id(self):
        for url in self.syndication:
            match = util.TWITTER_RE.match(url)
            if match:
                return match.group(2)

    def _fill_in_action_handler(self, url):
        return url.replace('{url}', urllib.parse.quote_plus(self.permalink))

    @property
    def reply_url(self):
        handlers = session.get('action-handlers', {})
        handler = handlers.get('reply')
        if handler:
            return self._fill_in_action_handler(handler)

        tweet_id = self.tweet_id
        if tweet_id:
            return TWEET_INTENT_URL.format(tweet_id)
        return '#'

    @property
    def retweet_url(self):
        handlers = session.get('action-handlers', {})
        handler = handlers.get('repost')
        if handler:
            return self._fill_in_action_handler(handler)

        tweet_id = self.tweet_id
        if tweet_id:
            return RETWEET_INTENT_URL.format(tweet_id)
        return '#'

    @property
    def favorite_url(self):
        handlers = session.get('action-handlers', {})
        handler = handlers.get('favorite') or handlers.get('like')
        if handler:
            return self._fill_in_action_handler(handler)

        tweet_id = self.tweet_id
        if tweet_id:
            return FAVORITE_INTENT_URL.format(tweet_id)
        return '#'

    @property
    def location_url(self):
        if (self.location and 'latitude' in self.location
                and 'longitude' in self.location):
            return OPEN_STREET_MAP_URL.format(self.location['latitude'],
                                              self.location['longitude'])

    @property
    def mf2_type(self):
        if self.post_type == 'review':
            return 'h-review'
        if self.post_type == 'event':
            return 'h-event'
        return 'h-entry'

    @property
    def title_or_fallback(self):
        """Feeds and <title> attributes require a human-readable
        title, even for posts that do not have an explicit title. Try
        here to create a reasonable one.
        """
        def format_context(ctx):
            if ctx.title and ctx.author_name:
                return '“{}” by {}'.format(ctx.title, ctx.author_name)
            if ctx.title:
                return ctx.title
            if ctx.author_name:
                return 'a post by {}'.format(ctx.author_name)
            return util.prettify_url(ctx.permalink)

        if self.title:
            return self.title
        if self.post_type == 'checkin' and self.venue:
            return 'Checked in to {}'.format(self.venue.name)
        if self.repost_contexts:
            return 'Shared {}'.format(', '.join(
                map(format_context, self.repost_contexts)))
        if self.like_contexts:
            return 'Liked {}'.format(', '.join(
                map(format_context, self.like_contexts)))
        if self.bookmark_contexts:
            return 'Bookmarked {}'.format(', '.join(
                map(format_context, self.bookmark_contexts)))
        if self.content:
            return util.format_as_text(self.content)
        return 'A {} from {}'.format(self.post_type, self.published)

    def generate_slug(self):
        if self.title:
            title = self.title
            if self.post_type == 'event' and self.start:
                title = self.start.strftime('%b %d') + ' ' + title
            return util.slugify(title)

        if self.item:
            item_name = self.item.get('name')
            if item_name:
                if 'author' in self.item:
                    item_name += ' by ' + self.item.get('author')
                return util.slugify('review of ' + item_name)

        content_plain = util.format_as_text(self.content or '', None)
        if content_plain:
            return util.slugify(content_plain, 48) or 'untitled'

        if self.post_type == 'checkin' and self.venue:
            return util.slugify(
                'checked into ' + self.venue.name + ' ' + content_plain, 48)

        for ctxs, prefix in ((self.bookmark_contexts, 'bookmark-of-'),
                             (self.like_contexts, 'like-of-'),
                             (self.repost_contexts, 'repost-of-'),
                             (self.reply_contexts, 'reply-to-')):
            if ctxs:
                return util.slugify(prefix + ctxs[0].get_slugify_target(), 48)

        return 'untitled'

    def add_syndication_url(self, url):
        """Add a new URL to the list of syndicated posts. We cannot do this
        directly because syndication is a JsonType that cannot detect
        modifications to itself.
        """
        # JsonType cannot detect changes to itself. Have to create a new list
        # to modify it
        new_synd = list(self.syndication)
        new_synd.append(url)
        self.syndication = new_synd

    def __repr__(self):
        if self.title:
            return 'post:{}'.format(self.path)
        else:
            return 'post:{}'.format(self.path)