class LongSlugged(Slugged): long_slug = db.StringField(unique=True, required=True) mpath = db.StringField() def _create_mpath_long_slug(self): if hasattr(self, 'is_homepage'): # is channel if self.parent and self.parent != self: self.long_slug = "/".join([self.parent.long_slug, self.slug]) self.mpath = "".join([self.parent.mpath, self.slug, ',']) else: self.long_slug = self.slug self.mpath = ",%s," % self.slug else: # is Content self.long_slug = "/".join([self.channel.long_slug, self.slug]) self.mpath = "".join([self.channel.mpath, self.slug, ',']) def validate_long_slug(self): self._create_mpath_long_slug() filters = dict(long_slug=self.long_slug) if self.id: filters['id__ne'] = self.id exist = self.__class__.objects(**filters) if exist.count(): if current_app.config.get('SMART_SLUG_ENABLED', False): self.slug = "{0}-{1}".format(self.slug, random.getrandbits(32)) self._create_mpath_long_slug() else: raise db.ValidationError( _l("%(slug)s slug already exists", slug=self.long_slug))
class TemplateType(HasCustomValue): title = db.StringField(max_length=255, required=True) identifier = db.StringField(max_length=255, required=True, unique=True) template_suffix = db.StringField(max_length=255, required=True) theme_name = db.StringField(max_length=255, required=False) def __unicode__(self): return self.title
class SubContentPurpose(db.Document): title = db.StringField(max_length=255, required=True) identifier = db.StringField(max_length=255, required=True, unique=True) module = db.StringField() def save(self, *args, **kwargs): self.identifier = slugify(self.identifier or self.title) super(SubContentPurpose, self).save(*args, **kwargs) def __unicode__(self): return self.title
class CustomValue(db.EmbeddedDocument): FORMATS = ( ('json', "json"), ('text', "text"), ('int', "int"), ('float', "float"), ) DEFAULT_FORMATTER = default_formatter FORMATTERS = { 'json': json.loads, 'text': DEFAULT_FORMATTER, 'int': int, 'float': float } REVERSE_FORMATTERS = { 'json': lambda val: val if isinstance(val, str) else json.dumps(val), 'text': DEFAULT_FORMATTER, 'int': DEFAULT_FORMATTER, 'float': DEFAULT_FORMATTER } name = db.StringField(max_length=50, required=True) rawvalue = db.StringField(verbose_name=_l("Value"), required=True) formatter = db.StringField(choices=FORMATS, default="text", required=True) @property def value(self): return self.FORMATTERS.get(self.formatter, self.DEFAULT_FORMATTER)(self.rawvalue) @value.setter def value(self, value): self.rawvalue = self.REVERSE_FORMATTERS.get(self.formatter, self.STR_FORMATTER)(value) def clean(self): try: self.value except Exception as e: raise Exception(e.message) super(CustomValue, self).clean() def __unicode__(self): return u"{s.name} -> {s.value}".format(s=self)
class Slugged(object): slug = db.StringField(max_length=255, required=True) def validate_slug(self, title=None): if self.slug: self.slug = slugify(self.slug) else: self.slug = slugify(title or self.title)
class Link(Content): link = db.StringField(required=True) force_redirect = db.BooleanField(default=True) increment_visits = db.BooleanField(default=True) visits = db.IntField(default=0) show_on_channel = db.BooleanField(default=False) def pre_render(self, render_function, *args, **kwargs): if self.increment_visits: self.visits = self.visits + 1 self.save() if self.force_redirect: return redirect(self.link) return super(Link, self).pre_render(render_function, *args, **kwargs)
class SubContent(Publishable, Ordered, db.EmbeddedDocument): content = db.ReferenceField('Content', required=True) caption = db.StringField() purpose = db.ReferenceField(SubContentPurpose, required=True) identifier = db.StringField() @property def thumb(self): try: return url_for('flaskpress.core.media', filename=self.content.thumb) # return self.content.thumb except Exception as e: logger.warning(str(e)) return self.content.get_main_image_url(thumb=True) meta = {'ordering': ['order'], 'indexes': ['order']} def clean(self): self.identifier = self.purpose.identifier def __unicode__(self): return self.content and self.content.title or self.caption
class ContentFormat(object): content_format = db.StringField(choices=TEXT_FORMATS, default=get_setting_value( 'DEFAULT_TEXT_FORMAT', 'html'))
class Tagged(object): tags = db.ListField(db.StringField(max_length=50))
class Content(HasCustomValue, Publishable, LongSlugged, Channeling, Tagged, ContentFormat, db.DynamicDocument): title = db.StringField(max_length=255, required=True) summary = db.StringField(required=False) template_type = db.ReferenceField(ContentTemplateType, required=False, reverse_delete_rule=db.NULLIFY) contents = db.ListField(db.EmbeddedDocumentField(SubContent)) model = db.StringField() comments_enabled = db.BooleanField(default=True) license = db.EmbeddedDocumentField(License) shortened_url = db.EmbeddedDocumentField(ShortenedURL) meta = { 'allow_inheritance': True, 'indexes': ['-created_at', 'slug'], 'ordering': ['-created_at'], } @classmethod def available_objects(cls, **filters): now = datetime.datetime.now() default_filters = { "published": True, 'available_at__lte': now, } default_filters.update(filters) return cls.objects(**default_filters) def get_main_image_url(self, thumb=False, default=None, identifier='mainimage'): """method returns the path (url) of the main image """ if not isinstance(identifier, (list, tuple)): identifier = [identifier] for item in identifier: try: if not thumb: path = self.contents.get(identifier=item).content.path else: path = self.contents.get(identifier=item).content.thumb return url_for('flaskpress.core.media', filename=path) except Exception as e: logger.warning('get_main_image_url:' + str(e)) return default def get_main_image_http(self, thumb=False, default=None, identifier='mainimage'): """method returns the path of the main image with http """ site_url = get_site_url() image_url = self.get_main_image_url(thumb=thumb, default=default, identifier=identifier) return u"{}{}".format(site_url, image_url) def get_uid(self): return str(self.id) def get_themes(self): themes = self.channel.get_themes() theme = self.template_type and self.template_type.theme_name if theme: themes.insert(0, theme) return list(set(themes)) def get_http_url(self): site_url = get_site_url() absolute_url = self.get_absolute_url() absolute_url = absolute_url[1:] return u"{0}{1}".format(site_url, absolute_url) def get_absolute_url(self, endpoint='flaskpress.core.detail'): if self.channel.is_homepage: long_slug = self.slug else: long_slug = self.long_slug try: return url_for(self.URL_NAMESPACE, long_slug=long_slug) except: return url_for(endpoint, long_slug=long_slug) def get_canonical_url(self, *args, **kwargs): return self.get_absolute_url() def get_recommendations(self, limit=3, ordering='-created_at', *a, **k): now = datetime.datetime.now() filters = { 'published': True, 'available_at__lte': now, "id__ne": self.id } contents = Content.objects(**filters).filter(tags__in=self.tags or []) return contents.order_by(ordering)[:limit] def get_summary(self): if self.summary: return self.summary return self.get_text() def get_text(self): if hasattr(self, 'body'): text = self.body elif hasattr(self, 'description'): text = self.description else: text = self.summary or "" if self.content_format == "markdown": return markdown(text) else: return text def __unicode__(self): return self.title @property def short_url(self): return self.shortened_url.short if self.shortened_url else '' @property def model_name(self): return self.__class__.__name__.lower() @property def module_name(self): module = self.__module__ module_name = module.replace('flaskpress.modules.', '').split('.')[0] return module_name def heritage(self): self.model = "{0}.{1}".format(self.module_name, self.model_name) def save(self, *args, **kwargs): # all those functions should be in a dynamic pipeline self.validate_slug() self.validate_long_slug() self.heritage() self.populate_related_mpath() self.populate_channel_roles() self.populate_shorter_url() super(Content, self).save(*args, **kwargs) def pre_render(self, render_function, *args, **kwargs): return render_function(*args, **kwargs) def populate_shorter_url(self): if not self.published or not get_setting_value('SHORTENER_ENABLED'): return url = self.get_http_url() if not self.shortened_url or url != self.shortened_url.original: shortener = ShorterURL() self.shortened_url = ShortenedURL(original=url, short=shortener.short(url))
class ShortenedURL(db.EmbeddedDocument): original = db.StringField(max_length=255) short = db.StringField(max_length=255) def __str__(self): return self.short
class License(db.EmbeddedDocument): LICENSES = (('custom', 'custom'), ('creative_commons_by_nc_nd', 'creative_commons_by_nc_nd')) title = db.StringField(max_length=255) link = db.StringField(max_length=255) identifier = db.StringField(max_length=255, choices=LICENSES)
class Post(Content): # URL_NAMESPACE = 'flaskpress.modules.posts.detail' body = db.StringField(required=True)