Esempio n. 1
0
class Role(DataObject, db.Model):
    name = db.Column(db.String(50), unique=True)
    description = db.Column(db.String(255))

    def __init__(self, name, description):
        self.name = name
        self.description = description
Esempio n. 2
0
class ErrorPage(SiteTree):
    id = alchemy.Column(alchemy.Integer,
                        alchemy.ForeignKey('sitetree.id'),
                        primary_key=True)

    content = alchemy.Column(alchemy.UnicodeText)
    content_json = alchemy.Column(alchemy.UnicodeText)
    error_code = alchemy.Column(alchemy.Integer, nullable=False)
Esempio n. 3
0
class UserRoles(db.Model):
    __tablename__ = "user_role"
    user_id = db.Column(db.Integer(),
                        db.ForeignKey('user.id', ondelete='CASCADE'),
                        primary_key=True)
    role_id = db.Column(db.Integer(),
                        db.ForeignKey('role.id', ondelete='CASCADE'),
                        primary_key=True)
Esempio n. 4
0
class SuperPage(SiteTree):

    db = {'second_content': 'UnicodeText'}

    __abstract_inherit__ = [Page]

    second_content = alchemy.Column(alchemy.UnicodeText)
    subtitle = alchemy.Column(alchemy.String)

    has_one = {'header_image': 'ImageObject'}

    has_many = {'images': 'GalleryImage'}

    many_many = {'sample_images': 'GalleryImage'}

    # template = "superpage.html"
    allowed_children = ["SuperPage"]
    icon = 'glyphicon glyphicon-flash'

    def get_cms_form(self):
        from wtforms import fields
        from silverflask.fields import AsyncFileUploadField
        # Setup Gridfield
        button_list = []
        button_list.append(GridField.AddButton())
        g = GridField(parent_record=self,
                      query=self.images,
                      controlled_class=GalleryImage,
                      buttons=button_list,
                      field_name="images",
                      display_cols=[{
                          "name": "id",
                          "hidden": True
                      }, "caption", {
                          "name": "sort_order",
                          "hidden": True
                      }],
                      name="images",
                      sortable=True)

        form = OrderedFormFactory()
        form.add_to_tab("Root.Main", fields.StringField(name="name",
                                                        default=1))
        form.add_to_tab("Root.Main",
                        fields.TextAreaField(name="content"),
                        before="asdasd")
        form.add_to_tab(
            "Root.Main",
            AsyncFileUploadField(ImageObject, name="header_image_id"))
        form.add_to_tab("Root.Gallery", g)
        form.add_to_tab("Root.Buttons", SubmitField("Save", name="Submit"))
        form.add_to_tab("Root.Buttons", SubmitField("Publish", name="Publish"))

        return form
Esempio n. 5
0
class FileObject(DataObject, db.Model):
    """
    Contains file information

    :ivar location: Location of the file
    :ivar name: Name of the file (usually filename without extension)
    :ivar type: Contains
    """

    location = db.Column(db.String(250))
    name = db.Column(db.String(250))
    type = db.Column(db.String(50))

    __mapper_args__ = {
        'polymorphic_identity': 'fileobject',
        'polymorphic_on': type
    }

    def __init__(self, file=None, location=None, folder=None):
        if file:
            location = current_app.storage_backend.store(file, location)
            print("Saving Location: ", location)
            self.location = location
            self.name = os.path.splitext(location)[0]

    def url(self):
        """
        Return the url for this file (handled by the :class:`FileStorageBackend`
        """
        return current_app.storage_backend.get_url(self.location)

    def delete_file(self):
        """
        Delete this file also from disk.
        """
        current_app.storage_backend.delete(self.location)
        self.location = ""
        self.name = "DELETED"

    @classmethod
    def get_cms_form(cls):
        form = super().get_cms_form()
        # form.location = fields.FileField("File")
        return form

    def as_dict(self):
        d = super().as_dict()
        d.update({"id": self.id, "name": self.name, "location": self.location})
        return d
Esempio n. 6
0
class SiteConfigExtension(db.Model):
    __tablename__ = SiteConfig.__tablename__
    __table_args__ = {'extend_existing': True}

    background_color = db.Column(db.String(50))

    def __init__(self, baseclass):
        """overwrite methods of parent class"""
        pass
Esempio n. 7
0
class GalleryImage(OrderableMixin, DataObject, db.Model):
    caption = db.Column(db.String(250))
    image_id = db.Column(db.ForeignKey(FileObject.id))
    image = db.relationship("FileObject")

    page_id = db.Column(db.ForeignKey("superpage.id"))
    page = db.relationship("SuperPage")

    @classmethod
    def get_cms_form(cls):
        form = Form
        form.caption = fields.StringField("Caption")
        form.image_id = AsyncFileUploadField(relation=ImageObject)
        form.page_id = fields.IntegerField()
        form.submit = fields.SubmitField("Submit")
        return form

    def __init__(self):
        self.init_order()
Esempio n. 8
0
class SiteConfig(DataObject, db.Model):
    """
    class SiteConfig

    Holds global variables such as theme selection,
    site title or tagline.

    :ivar title: page title (shown in <title> tag)
    :ivar tagline: tagline of the website, can be used in the template
    :ivar theme: Not used now, could later hold the location of a template folder
    """
    __table_args__ = {'extend_existing': True}
    title = db.Column(db.String(250))
    tagline = db.Column(db.String(250))
    theme = db.Column(db.String(250))

    @staticmethod
    def get_available_themes():
        return [
            (theme.identifier, theme.name)
            for theme in current_app.silverflask_theme_manager.themes.values()
        ]

    def get_cms_form(cls):
        form = OrderedFormFactory()
        form.add_to_tab("Root.Main", fields.StringField(name='title'))
        form.add_to_tab("Root.Main", fields.StringField(name='tagline'))
        form.add_to_tab(
            "Root.Main",
            fields.SelectField(name='theme',
                               choices=cls.get_available_themes()))
        form.add_to_tab("Root.Buttons", fields.SubmitField("Save",
                                                           name='Save'))
        return form

    @classmethod
    def get_current(cls):
        return cls.query.one()
Esempio n. 9
0
class PolymorphicMixin(object):
    type = db.Column(db.String(50))

    @declared_attr
    def __mapper_args__(cls):
        if hasattr(cls, '__versioned_draft_class__'):
            # Use same identities as draft class
            ident = cls.__versioned_draft_class__.__mapper_args__[
                "polymorphic_identity"]
        else:
            ident = cls.__tablename__
        d = {
            'polymorphic_identity': ident,
        }
        # this is a base object, therefore we are not
        # redefining the column on which it is polymorphic

        if hasattr(cls.__table__.columns,
                   'id') and not cls.__table__.columns.id.foreign_keys:
            d['polymorphic_on'] = 'type'
        return d
Esempio n. 10
0
class SiteTree(VersionedMixin, PolymorphicMixin, OrderableMixin, DataObject,
               db.Model):
    """
    The SiteTree is the database model from which all pages have to inherit.
    It defines the parent/children relationships of the page tree.
    It also defines everything that's needed to get nice URL slugs working.
    """

    parent_id = db.Column(db.Integer, db.ForeignKey('sitetree.id'))
    name = db.Column(db.String)
    urlsegment = db.Column(db.String(250), nullable=False)

    template = "page.html"

    children = db.relationship(
        'SiteTree',
        cascade="all",
        # many to one + adjacency list - remote_side
        # is required to reference the 'remote'
        # column in the join condition.
        backref=db.backref("parent", remote_side='SiteTree.id'),
        order_by='SiteTree.sort_order')

    allowed_children = []
    can_be_root = True
    icon = 'glyphicon glyphicon-file'

    def get_siblings(self):
        return SiteTree.query.filter(SiteTree.parent_id == self.parent_id)

    @classmethod
    def default_template(cls):
        return cls.__name__.lower() + ".html"

    def parents(self):
        tree_el = self.parent
        parents = []
        while tree_el:
            parents.append(tree_el)
            tree_el = tree_el.parent
        return parents

    @staticmethod
    def get_sitetree(parent_id=None):
        base_page = SiteTree.query.filter(SiteTree.parent_id == parent_id)\
                                  .order_by(SiteTree.sort_order.asc())
        dest_list = []
        for p in base_page:
            dest_dict = {}
            SiteTree.recursive_build_tree(p, dest_dict)
            dest_list.append(dest_dict)
        return dest_list

    @staticmethod
    def recursive_build_tree(root_node, dest_dict):
        dest_dict.update(root_node.jqtree_dict())
        children = root_node.children
        if children:
            dest_dict['children'] = []
            for child in children:
                temp_dict = {}
                dest_dict['children'].append(temp_dict)
                SiteTree.recursive_build_tree(child, temp_dict)
        else:
            return

    @classmethod
    def get_cms_form(cls):
        form = super().get_cms_form()
        return form

    def append_child(self, child):
        self.children.append(child)

    def parent_at_level(self, level=1):
        level = level - 1
        parents = self.parents()
        n_parents = len(parents)
        if n_parents > level:
            return parents[level]
        if n_parents == level:
            return self
        else:
            return False

    def menu(self, level=1):
        # This returns the menu with
        # self as reference
        level = level - 1
        parents = self.parents()
        n_parents = len(parents)
        if n_parents > level:
            cls = self.__class__
            level_parent = parents[level]
            result = cls.query.filter(cls.parent_id == level_parent.id).all()
            return result
        if n_parents == level:
            return self.children
        else:
            return False

    def as_dict(self):
        d = dict()
        try:
            d = super().as_dict()
        except Exception as e:
            """
            This is due to problems with wsgi and reloading:
            http://stackoverflow.com/questions/9722343/python-super-behavior-not-dependable
            """
            for c in self.__class__.mro():
                if c != self.__class__:
                    super_object = super(c, self)
                    if hasattr(super_object, 'as_dict'):
                        d.update(super_object.as_dict())
        d.update({
            "parent_id": self.parent_id,
            "name": self.name,
            "type": self.type
        })
        return d

    def jqtree_dict(self):
        return {
            "text": self.name,
            "parent_id": self.parent_id,
            "created_on": self.created_on,
            "type": self.__class__.__name__,
            "li_attr": {
                "data-pageid": str(self.id)
            },
            "a_attr": {
                "href": url_for("PagesCMSController.edit_page",
                                page_id=self.id)
            }
        }

    @staticmethod
    def get_by_url(url, cls=None):
        if not cls:
            cls = SiteTree
        vars = url.split('/')
        node = cls.query.filter(cls.urlsegment == vars[0]).first()
        if not node:
            abort(404)

        for var in vars[1:]:
            node = cls.query.filter(cls.urlsegment == var,
                                    cls.parent_id == node.id).first()
            if not node:
                abort(404)
        return node

    def get_url(self):
        if self.urlsegment == current_app.config[
                "SILVERFLASK_HOME_URLSEGMENT"]:
            return "/"
        self_url = "/" + self.urlsegment
        url = ""
        el = self

        while el.parent:
            url = "/" + el.parent.urlsegment
            el = el.parent
        url += self_url
        return url

    def set_parent(self, parent_id):
        if not parent_id:
            if self.can_be_root:
                self.parent_id = None
                return
            else:
                raise Exception("This page type can not be a root node!")
        else:
            parent = SiteTree.query.get(int(parent_id))
            if parent:
                if hasattr(
                        parent, 'allowed_children'
                ) and self.__class__.__name__ in parent.allowed_children:
                    self.parent_id = parent_id
                else:
                    raise Exception("Parent not allowed!")
            else:
                raise Exception("Parent not existing!")
        return

    def __init__(self):
        pass
        # self.database.extend(super(SiteTree, self).database)

    @classmethod
    def create_slug(cls, target, id=None):
        possible_slug = slugify(target.name, to_lower=True)
        slug = possible_slug
        count = 0

        def get_query(target, slug, id=None):
            query = cls.query.filter(cls.parent_id == target.parent_id,
                                     cls.urlsegment == slug)
            if id:
                query = query.filter(cls.id != id)
            return query

        while get_query(target, slug, id).count() > 0:
            slug = "{0}-{1}".format(possible_slug, count)
        target.urlsegment = slug

    @classmethod
    def pagetypes(cls):
        polymorphic_map = cls.__mapper__.polymorphic_map
        sitetree_props = {}
        for mapper in polymorphic_map.values():
            if mapper.class_ != cls:
                mapped_class = mapper.class_
                sitetree_props[mapped_class.__name__] = {
                    'allowed_children': mapped_class.allowed_children,
                    'icon':
                    mapped_class.icon if mapped_class.icon else 'default'
                }
        return sitetree_props

    @classmethod
    def before_insert(cls, mapper, context, target):
        target.create_slug(target)

    @classmethod
    def before_update(cls, mapper, context, target):
        target.create_slug(target, target.id)
Esempio n. 11
0
class OrderableMixin(object):
    """
    A mixin that makes a DataObject sortable by adding a sort_order database field.
    Classes with this mixin automatically keep the sort_order in sync.
    """
    sort_order = db.Column(db.Integer, nullable=False, default=1)

    default_order = "sort_order ASC"

    def insert_after(self,
                     index,
                     orderable_base_class=None,
                     index_absolute=True,
                     query=None):
        """
        Inser after index variable

        :param index: index (this is the sort_order variable of the element that you want to insert after!)
        :param orderable_base_class: baseclass (useful in certain circumstances e.g. gridfields)
        :return: nothing
        """
        if orderable_base_class:
            cls = orderable_base_class
        else:
            cls = self.__class__
        cls.check_duplicates()
        query = query if query else db.session.query(cls)
        if not index_absolute:
            index_el = query.order_by(
                cls.sort_order.asc()).limit(1).offset(index).scalar()
            sort_order_index = index_el.sort_order

        db.session.query(cls)\
            .filter(cls.sort_order > sort_order_index)\
            .update({cls.sort_order: cls.sort_order + 1})

        self.sort_order = index + 1

        if hasattr(cls, 'LiveType'):
            # Repeat the same for the live version of the page
            cls = cls.LiveType
            cls.check_duplicates()
            live_self = cls.query.get(self.id)
            live_self.check_duplicates()
            db.session.commit()

            query = db.session.query(cls)
            if not index_absolute:
                print(query)
                print(index)
                print(
                    str(
                        query.order_by(
                            cls.sort_order.asc()).limit(1).offset(index)))
                print(query.order_by(cls.sort_order.asc()).all())
                index_el = query.order_by(
                    cls.sort_order.asc()).limit(1).offset(index).scalar()
                index = index_el.sort_order

            db.session.query(cls) \
                .filter(cls.sort_order > index) \
                .update({cls.sort_order: cls.sort_order + 1})
            live_self.sort_order = index + 1

    @classmethod
    def reindex(cls):
        """
        Reindexes the table.

        The sort order field can have "jumps" in it (e.g. 1, 4, 5, 8, 9) and reindex brings that back
        to a linearly ascending order: (1,2,3,4...)
        """
        for index, el in enumerate(
                db.session.query(cls).order_by(cls.sort_order.asc())):
            el.sort_order = index
        db.session.commit()

    @classmethod
    def check_duplicates(cls):
        """
        Check the table for duplicates and if there are duplicates reindex
        :return: nothing
        """
        duplicates = db.session.query(cls).group_by(cls.sort_order)\
                               .having(func.count(cls.id) > 1).count()
        if duplicates > 0:
            cls.reindex()

    def move_after(self, obj):
        """
        Move current DataObject after index (= sort_order) of another element

        :param index: obj element where to move after or sort order of other elements
        :return: nothing
        """
        if hasattr(obj, 'sort_order'):
            self.insert_after(obj.sort_order)
        else:
            _id = int(obj)
            if _id <= 0:
                sort_order = 0
            else:
                sort_order = db.session.query(
                    self.__class__).get(_id).sort_order
            self.insert_after(sort_order)

    @classmethod
    def before_insert(cls, mapper, connection, target):
        target.init_order()

    def init_order(self):
        """
        Sort element to the end
        """
        cls = self.__class__
        if not self.sort_order:
            query = db.session.query(func.max(cls.sort_order))
            v = query[0]
            if v[0]:
                self.sort_order = v[0] + 1
            else:
                self.sort_order = 1
Esempio n. 12
0
class DataObject(object):
    """
    The DataObject class is the basic building block of any CMS
    Element. It is a mixin that provides three basic database columns:

    Attributes:

    :ivar id: Primary key, integer id (use for joins and relationships)
    :ivar created_on: the datetime when the DataObject was created
    :ivar last_modified: the datetime when the DataObject was last modified
    """
    @declared_attr
    def __tablename__(cls):
        """tablename, defaults to the classname in lowercase"""
        return cls.__name__.lower()

    id = db.Column(db.Integer(), primary_key=True)
    created_on = db.Column(db.DateTime, default=db.func.now())
    last_modified = db.Column(db.DateTime,
                              default=db.func.now(),
                              onupdate=db.func.now())

    singular_name = None
    plural_name = None

    # has_one = {}
    # has_many = {}
    # many_many = {}
    # belongs_many_many = {}

    default_order = None

    auto_form_exclude = ['id', 'created_on', 'last_modified']

    # summary_fields = []
    # searchable_fields = []
    # allowed_actions = []

    # class CMSForm(Form):
    # name = fields.StringField("asdsadsa")
    #     submit = fields.SubmitField("Submit")

    def __new__(cls, *args, **kwargs):
        def before_insert_listener(mapper, connection, target):
            for c in target.__class__.mro():
                if hasattr(c, "before_insert"):
                    c.before_insert(mapper, connection, target)

        def before_update_listener(mapper, connection, target):
            for c in target.__class__.mro():
                if hasattr(c, "before_update"):
                    c.before_update(mapper, connection, target)

        event.listen(cls, 'before_insert', before_insert_listener)
        event.listen(cls, 'before_update', before_update_listener)

        event.listen(cls, 'before_insert', lambda m, c, t: t.can_create(m, c))
        event.listen(cls, 'before_update', lambda m, c, t: t.can_edit(m, c))
        event.listen(cls, 'before_delete', lambda m, c, t: t.can_delete(m, c))

        if not cls.singular_name:
            cls.singular_name = uncamel(cls.__name__)
            cls.plural_name = uncamel(cls.__name__ + "s")

        return super().__new__(cls)

    @classmethod
    def query_factory(cls):
        return db.session.query(cls).order_by(cls.last_modified.desc())

    @classmethod
    def get_cms_form(cls):
        """
        Build and return Form class.

        If you want to define your custom CMS Object, you likely want to override the default CMS Form. E.g.::

            from wtforms import fields
            def get_cms_form(cls):
                form = super().get_cms_form()
                form.textfield = fields.StringField("Textfield")
                return form

        :returns:  Form Class (has to be instantiated!).
        """
        if hasattr(cls, "CMSForm"):
            return cls.CMSForm
        form_factory = OrderedFormFactory()

        form_fields = model_fields(cls,
                                   db_session=db.session,
                                   exclude=cls.auto_form_exclude)

        for key in sorted(form_fields.keys()):
            form_fields[key].kwargs['name'] = key
            form_factory.add_to_tab("Root.Main", form_fields[key])
        form_factory.add_to_tab("Root.Buttons",
                                fields.SubmitField("Save", name="Save"))
        return form_factory

    def as_dict(self):
        """
        Get object as dict. Very useful for json responses.
        :return: dict with all database columns
        """
        return {c.name: getattr(self, c.name) for c in self.__table__.columns}

    def can_create(self, mapper, connection):
        if request and not current_user:
            raise CannotCreateError
        return True

    def can_edit(self, mapper, connection):
        if request and not current_user:
            raise CannotUpdateError
        return True

    def can_delete(self, mapper, connection):
        if request and not current_user:
            raise CannotDeleteError
        return True
Esempio n. 13
0
def create_live_table(cls):
    tablename = cls.__tablename__ + "_live"
    if tablename in created_tables or cls.__tablename__ in created_tables:
        return
    created_tables.append(tablename)
    columns = []
    logger.debug("Creating Live table for: %s" % cls.__tablename__)

    ins = sainspect(cls)

    versioned_basetable = tablename
    baseclass = tuple()
    for c in inspect.getmro(cls):
        if c != cls and c.__name__ in versioned_classes:
            versioned_basetable = c.__table__.name + "_live"
            baseclass = (c.LiveType, ) + baseclass
        elif c != cls and c.__name__ != "VersionedMixin":
            baseclass = (c, ) + baseclass

    # Reverse baseclass mro
    baseclass = baseclass[::-1]

    for c in cls.__table__.columns:
        # What is happening
        # TODO: Check if this also works with different relationships...
        new_keys = []
        if c.foreign_keys:
            for k in c.foreign_keys:
                key_target = k.column.table.name
                if key_target in versioned_tables:
                    new_keys.append(
                        db.ForeignKey(key_target + "_live." + k.column.key))
                else:
                    new_keys.append(db.ForeignKey(k.target_fullname))

            columns.append(
                db.Column(c.key,
                          db.Integer(),
                          new_keys[0],
                          primary_key=c.primary_key,
                          default=c.default))
        else:
            columns.append(c.copy())

    cls.LiveTable = table = sa.schema.Table(tablename, cls.__table__.metadata,
                                            *columns)

    args = {}
    for key, value in cls.__dict__.items():
        if type(value) is types.FunctionType:
            args.update({key: value})

    args.update({"__table__": table})

    for column in columns:
        args[column.name] = column

    backrefs = []
    rs = [r for r in ins.relationships]
    for r in rs:
        if r.parent == cls.__mapper__:
            print("There is a relation defined %s with Key: %r" %
                  (cls.__name__, r))

            if hasattr(r.target,
                       'fullname') and r.target.fullname.endswith("version"):
                continue

            if r.key in backrefs:
                continue

            key = r.key
            target = ""
            if hasattr(r.target,
                       'fullname') and r.target.fullname in versioned_tables:
                target = r.mapper.entity.__name__ + "Live"
            else:
                if r.direction == MANYTOONE:
                    args[key] = db.relationship(r.mapper)
                elif r.direction == MANYTOMANY:
                    # if hasattr(r, 'secondary') and r.secondary:
                    #     print("SECONDARY for ... ", r)
                    #     kwargs['secondary'] = r.secondary
                    primaryjoin = copy.copy(r.primaryjoin)
                    primaryjoin.left = args[primaryjoin.left.key]
                    secondaryjoin = copy.copy(r.secondaryjoin)
                    args[key] = db.relationship(
                        r.mapper,
                        viewonly=True,
                        primaryjoin=primaryjoin,
                        foreign_keys=[primaryjoin.right, secondaryjoin.right],
                        secondary=r.secondary,
                        secondaryjoin=secondaryjoin)
                else:
                    primaryjoin = copy.copy(r.primaryjoin)
                    primaryjoin.left = args[primaryjoin.left.key]
                    args[key] = db.relationship(
                        r.mapper,
                        viewonly=True,
                        primaryjoin=primaryjoin,
                        foreign_keys=[primaryjoin.right])
                continue

            kwargs = {}
            if hasattr(r, "backref") and r.backref:
                backref_key = r.backref[0]
                backrefs.append(backref_key)
                backref = r.backref[1]
                remote_side = None
                if hasattr(backref["remote_side"].cls,
                           "LiveType") or backref["remote_side"].cls == cls:
                    orig_arg = backref["remote_side"].arg
                    arg_v = orig_arg.split(".")
                    arg_v[0] += "Live"
                    remote_side = ".".join(arg_v)
                    kwargs['backref'] = db.backref(backref_key,
                                                   remote_side=remote_side)

            args[key] = db.relationship(target, cascade="none, ", **kwargs)

    if args.get("before_insert"): del args["before_insert"]
    if args.get("before_update"): del args["before_update"]
    if args.get("__versioned__"): del args["__versioned__"]

    args["template"] = getattr(cls, "template") if hasattr(cls,
                                                           "template") else ""

    mapper_args = {}
    if hasattr(cls, "__mapper_args__"):
        mapper_args = cls.__mapper_args__

    args['__versioned_draft_class__'] = cls
    args['__create_live__'] = False

    cls.LiveType = type('%sLive' % cls.__name__, baseclass, args)

    cls.LiveType.__create_live__ = False

    for key, arg in mapper_args.items():
        if key == "polymorphic_on":
            mapper_args[key] = table.columns[arg]

    live_mapper = mapper(cls, cls.LiveTable, non_primary=True, **mapper_args)

    cls.live_mapper = live_mapper
    return cls.LiveTable
Esempio n. 14
0
class ImageObject(FileObject):
    """
    A basic ImageObject, that inherits all properties from the file object
    and adds some functionality related to images, such as cropping and resizing,
    as well as caching the cropped and/or resized images.
    """
    __tablename__ = "imageobject"
    __mapper_args__ = {'polymorphic_identity': __tablename__}

    # Specifies special operations on Image Files
    id = db.Column(db.Integer,
                   db.ForeignKey('fileobject.id'),
                   primary_key=True)
    __cache_dir__ = "_resized"

    def resize(self, width=None, height=None, mode='crop', background="white"):
        """
        Resize image

        :param width: define width in pixels
        :param height: height in pixels. If no height is set, it will be
                       set to width (i.e. the result will be a square)
        :param mode: string, one of 'crop' (default), 'pad', 'fit' or 'reshape'
        :param background: background color, as string (i.e. 'blue' or hex '#F0F000').
                           As supported by the ImageColor module of Pillow.
        """
        if not height:
            height = width

        def resized_location():
            orig_url = self.location
            (orig_folder, tail) = os.path.split(orig_url)
            (fn, ext) = os.path.splitext(tail)
            cache_path = os.path.join(orig_folder, self.__cache_dir__)
            new_fn = fn + mode + str(width) + str(height) + ext
            full_path = os.path.join(cache_path, new_fn)
            return cache_path, new_fn, full_path

        resized_path, resized_filename, resize_fullpath = resized_location()

        if current_app.storage_backend.exists(resize_fullpath):
            return current_app.storage_backend.get_url(resize_fullpath)
        else:
            fp = current_app.storage_backend.retrieve(self.location)
            unused_var, ext = os.path.splitext(self.location)
            resized_img = self._resize(fp, width, height, mode, background)
            if not resized_img:
                return ""
            tmp_file_path = "/tmp/" + str(uuid.uuid4()) + ext
            resized_img.save(tmp_file_path)
            tmp_file = open(tmp_file_path, "rb")
            current_app.storage_backend.store(tmp_file,
                                              resized_path,
                                              filename=resized_filename)
            return current_app.storage_backend.get_url(resize_fullpath)

    @staticmethod
    def _resize(filepointer,
                width=None,
                height=None,
                mode=None,
                background=None):
        print("resizing %r %r %r %r" % (filepointer, width, height, mode))
        try:
            img = Image.open(filepointer)
        except:
            return False
        orig_width, orig_height = img.size

        width = min(width, orig_width) if width else None
        height = min(height, orig_height) if height else None

        if not img.mode.lower().startswith('rgb'):
            img = img.convert('RGBA')

        if width and height:

            fit, crop = sorted([(width, orig_height * width // orig_width),
                                (orig_width * height // orig_height, height)])

            if mode == 'fit' or mode == 'pad':
                img = img.resize(fit, Image.ANTIALIAS)

                if mode == 'pad':
                    pad_color = str(background or 'black')
                    back = Image.new('RGBA', (width, height), pad_color)
                    back.paste(img,
                               ((width - fit[0]) // 2, (height - fit[1]) // 2))
                    img = back

            elif mode == 'crop':
                dx = (crop[0] - width) // 2
                dy = (crop[1] - height) // 2
                img = img.resize(crop, Image.ANTIALIAS).crop(
                    (dx, dy, dx + width, dy + height))

            elif mode == 'reshape' or mode is None:
                img = img.resize((width, height), Image.ANTIALIAS)

            else:
                raise ValueError('unsupported mode %r' % mode)

        elif width:
            height = orig_height * width // orig_width
            img = img.resize((width, height), Image.ANTIALIAS)

        elif height:
            width = orig_width * height // orig_height
            img = img.resize((width, height), Image.ANTIALIAS)

        return img
Esempio n. 15
0
class User(DataObject, UserMixin, db.Model):
    """
    The base User model. Defines the following fields:

    username = String, and unique,
    firstname, lastname = String

    email = String, unique

    The password is set with user.set_password("password"), and then stored with
    encryption.

    """
    username = db.Column(db.String, unique=True)

    firstname = db.Column(db.String)
    lastname = db.Column(db.String)

    email = db.Column(db.String, unique=True)

    password = db.Column(db.String)

    confirmed_at = db.Column(db.DateTime)
    is_enabled = db.Column(db.Boolean(), nullable=False, server_default='1')

    roles = db.relationship('Role',
                            secondary=UserRoles.__tablename__,
                            backref=db.backref('users', lazy='dynamic'))

    auto_form_exclude = DataObject.auto_form_exclude + [
        'confirmed_at', 'is_enabled'
    ]

    def __init__(self, username, password, email=None, is_enabled=True):
        self.username = username
        self.password = current_app.user_manager.hash_password(password)
        self.email = email
        self.is_enabled = is_enabled

    @property
    def name(self):
        return str(self.firstname) + " " + str(self.lastname)

    def is_authenticated(self):
        if isinstance(self, AnonymousUserMixin):
            return False
        else:
            return True

    def is_active(self):
        return True

    def is_anonymous(self):
        if isinstance(self, AnonymousUserMixin):
            return True
        else:
            return False

    def get_id(self):
        return self.id

    def __repr__(self):
        return '<User %r>' % self.username

    def set_password(self, new_password):
        self.password = current_app.user_manager.hash_password(new_password)

    def as_dict(self):
        d = super().as_dict()
        if d["password"]:
            del d["password"]
        d.update({"name": self.name})
        return d

    @classmethod
    def get_cms_form(cls):
        form = super().get_cms_form()
        roles = [(r.id, r.name) for r in db.session.query(Role).all()]
        form.add_to_tab(
            "Root.Main",
            fields.PasswordField("New Password", [
                validators.EqualTo('new_password_confirmation',
                                   message='Passwords must match')
            ],
                                 name='new_password'))
        form.add_to_tab(
            "Root.Main",
            fields.PasswordField("Repeat New Password",
                                 name='new_password_confirmation'))
        form.add_to_tab(
            "Root.Main",
            QuerySelectMultipleField('Role',
                                     query_factory=lambda: Role.query,
                                     get_label=lambda x: x.name,
                                     get_pk=lambda x: x.id,
                                     name='roles'))
        del form.fields["password"]
        return form