Esempio n. 1
0
class AgentProfileAttachment(Attachment):

    __tablename__ = "agent_profile_attachment"

    id = Column(Integer,
                ForeignKey('attachment.id',
                           ondelete='CASCADE',
                           onupdate='CASCADE'),
                primary_key=True)

    user_id = Column(Integer,
                     ForeignKey(
                         'agent_profile.id',
                         ondelete='CASCADE',
                         onupdate='CASCADE',
                     ),
                     nullable=False,
                     index=True)

    user = relationship(
        AgentProfile,
        backref=backref('profile_attachments', cascade="all, delete-orphan"),
    )
    __mapper_args__ = {
        'polymorphic_identity': 'agent_profile_attachment',
        'with_polymorphic': '*'
    }

    crud_permissions = CrudPermissions(P_READ, P_SYSADMIN, P_SYSADMIN,
                                       P_SYSADMIN, P_READ, P_READ, P_READ)

    def is_owner(self, user_id):
        return user_id == self.user_id
Esempio n. 2
0
class VoteSessionAttachment(Attachment):

    __tablename__ = "vote_session_attachment"

    id = Column(Integer,
                ForeignKey('attachment.id',
                           ondelete='CASCADE',
                           onupdate='CASCADE'),
                primary_key=True)

    vote_session_id = Column(Integer,
                             ForeignKey(
                                 'vote_session.id',
                                 ondelete='CASCADE',
                                 onupdate='CASCADE',
                             ),
                             nullable=False,
                             index=True)

    vote_session = relationship(
        VoteSession,
        backref=backref('attachments', cascade="all, delete-orphan"),
    )
    __mapper_args__ = {
        'polymorphic_identity': 'vote_session_attachment',
        'with_polymorphic': '*'
    }

    crud_permissions = CrudPermissions(create=P_ADMIN_DISC,
                                       read=P_READ,
                                       update=P_ADMIN_DISC,
                                       delete=P_ADMIN_DISC)
Esempio n. 3
0
class IdeaAttachment(Attachment):
    __tablename__ = "idea_attachment"
    id = Column(Integer,
                ForeignKey('attachment.id',
                           ondelete='CASCADE',
                           onupdate='CASCADE'),
                primary_key=True)

    idea_id = Column(Integer,
                     ForeignKey(
                         'idea.id',
                         ondelete='CASCADE',
                         onupdate='CASCADE',
                     ),
                     nullable=False,
                     index=True)

    idea = relationship(
        Idea,
        backref=backref('attachments', cascade="all, delete-orphan"),
    )
    __mapper_args__ = {
        'polymorphic_identity': 'idea_attachment',
        'with_polymorphic': '*'
    }

    # Same crud permissions as a idea
    crud_permissions = CrudPermissions(P_ADD_IDEA, P_READ, P_EDIT_IDEA,
                                       P_ADMIN_DISC, P_ADMIN_DISC,
                                       P_ADMIN_DISC)
Esempio n. 4
0
class ResourceAttachment(Attachment):

    __tablename__ = "resource_attachment"

    id = Column(Integer,
                ForeignKey('attachment.id',
                           ondelete='CASCADE',
                           onupdate='CASCADE'),
                primary_key=True)

    resource_id = Column(Integer,
                         ForeignKey(
                             'resource.id',
                             ondelete='CASCADE',
                             onupdate='CASCADE',
                         ),
                         nullable=False,
                         index=True)

    resource = relationship(
        Resource,
        backref=backref('attachments', cascade="all, delete-orphan"),
    )
    __mapper_args__ = {
        'polymorphic_identity': 'resource_attachment',
        'with_polymorphic': '*'
    }

    # Same crud permissions as a idea
    crud_permissions = CrudPermissions(P_MANAGE_RESOURCE, P_READ,
                                       P_MANAGE_RESOURCE, P_MANAGE_RESOURCE,
                                       P_MANAGE_RESOURCE, P_MANAGE_RESOURCE)
Esempio n. 5
0
class Resource(DiscussionBoundBase):

    """Resource for resources center."""

    __tablename__ = "resource"
    type = Column(String(60), nullable=False)

    id = Column(Integer, primary_key=True)

    discussion_id = Column(
        Integer,
        ForeignKey(
            'discussion.id',
            ondelete='CASCADE',
            onupdate='CASCADE',
        ),
        nullable=False, index=True)

    discussion = relationship(
        "Discussion",
        backref=backref(
            'resources',
            cascade="all, delete-orphan"),
    )

    title_id = Column(
        Integer(), ForeignKey(LangString.id))
    text_id = Column(
        Integer(), ForeignKey(LangString.id))
    title = relationship(
        LangString,
        lazy="joined", single_parent=True,
        primaryjoin=title_id == LangString.id,
        backref=backref("resource_from_title", lazy="dynamic"),
        cascade="all, delete-orphan")
    text = relationship(
        LangString,
        lazy="joined", single_parent=True,
        primaryjoin=text_id == LangString.id,
        backref=backref("resource_from_text", lazy="dynamic"),
        cascade="all, delete-orphan")

    embed_code = Column(UnicodeText)

    def get_discussion_id(self):
        return self.discussion_id or self.discussion.id

    @classmethod
    def get_discussion_conditions(cls, discussion_id, alias_maker=None):
        return (cls.discussion_id == discussion_id,)

    __mapper_args__ = {
        'polymorphic_identity': 'resource',
        'polymorphic_on': type,
        'with_polymorphic': '*'
    }

    crud_permissions = CrudPermissions(
        P_MANAGE_RESOURCE, P_READ, P_MANAGE_RESOURCE, P_MANAGE_RESOURCE,
        P_MANAGE_RESOURCE, P_MANAGE_RESOURCE)
Esempio n. 6
0
class LandingPageModuleType(Base):
    """Landing page module type."""

    __tablename__ = "landing_page_module_type"
    type = Column(String(60), nullable=False)

    id = Column(Integer, primary_key=True)

    identifier = Column(String(30), nullable=False)

    title_id = Column(Integer(), ForeignKey(LangString.id))
    title = relationship(LangString,
                         lazy="joined",
                         single_parent=True,
                         primaryjoin=title_id == LangString.id,
                         backref=backref("landing_page_module_type_from_title",
                                         lazy="dynamic"),
                         cascade="all, delete-orphan")

    default_order = Column(Float, nullable=False)

    editable_order = Column(Boolean, default=True)

    required = Column(Boolean, default=False)

    __mapper_args__ = {
        'polymorphic_identity': 'landing_page_module_type',
        'polymorphic_on': type,
        'with_polymorphic': '*'
    }

    crud_permissions = CrudPermissions(P_SYSADMIN, P_READ, P_SYSADMIN,
                                       P_SYSADMIN)

    @classmethod
    def populate_db(cls, db=None):
        engine = db.bind
        # table may not exist (alembic migration)
        if engine.dialect.has_table(engine.connect(),
                                    "landing_page_module_type"):
            db.execute("lock table %s in exclusive mode" % cls.__table__.name)
            current_module_types = [
                item[0] for item in db.query(cls.identifier).all()
            ]
            for info in module_types:
                if info['identifier'] not in current_module_types:
                    kw = info.copy()
                    title = None
                    for locale, value in info[u'title'].items():
                        if title:
                            title.add_value(value, locale)

                        title = LangString.create(value, locale)

                    kw['title'] = title
                    saobj = LandingPageModuleType(**kw)
                    db.add(saobj)
Esempio n. 7
0
class DiscussionAttachment(Attachment):
    __mapper_args__ = {
        'polymorphic_identity': 'discussion_attachment',
        'with_polymorphic': '*'
    }

    # Same crud permissions as a post
    crud_permissions = CrudPermissions(P_ADMIN_DISC, P_READ)

    _discussion = relationship("Discussion", backref="discussion_attachments")
Esempio n. 8
0
class Keyword(DiscussionBoundBase):
    __tablename__ = "keyword"
    id = Column(Integer, primary_key=True)
    value = Column(UnicodeText)
    discussion_id = Column(Integer,
                           ForeignKey(
                               'discussion.id',
                               ondelete='CASCADE',
                               onupdate='CASCADE',
                           ),
                           nullable=False,
                           index=True)
    discussion = relationship("Discussion")
    __table_args__ = (UniqueConstraint('value',
                                       'discussion_id',
                                       name='unq_value_discussion'), )
    __mapper_args__ = {
        'polymorphic_identity': 'keyword',
        'with_polymorphic': '*'
    }
    crud_permissions = CrudPermissions(P_ADMIN_DISC, P_READ, P_ADMIN_DISC,
                                       P_ADMIN_DISC)

    def __repr__(self):
        return "<Tag %s (%d)>" % (self.value, self.id or -1)

    def get_discussion_id(self):
        return self.discussion_id or self.discussion.id

    @classmethod
    def get_discussion_conditions(cls, discussion_id, alias_maker=None):
        return (cls.discussion_id == discussion_id, )

    @classmethod
    def get_tag(cls, tag, discussion_id, db=None):
        db = db or cls.default_db
        return db.query(cls).filter(
            func.lower(cls.value) == func.lower(tag),
            cls.discussion_id == discussion_id).first()

    @classmethod
    def get_tags(cls, tags, discussion_id, db=None):
        old_tags = []
        new_tags = []
        db = db or cls.default_db
        for tag in tags:
            tag_obj = cls.get_tag(tag, discussion_id, db)
            if not tag_obj:
                new_tags.append(cls(value=tag, discussion_id=discussion_id))
            else:
                old_tags.append(tag_obj)

        return {'new_tags': new_tags, 'tags': old_tags}
Esempio n. 9
0
class LandingPageModule(DiscussionBoundBase):
    """Landing page module for a discussion."""

    __tablename__ = "landing_page_module"
    type = Column(String(60), nullable=False)

    id = Column(Integer, primary_key=True)

    discussion_id = Column(Integer,
                           ForeignKey(
                               'discussion.id',
                               ondelete='CASCADE',
                               onupdate='CASCADE',
                           ),
                           nullable=False,
                           index=True)

    discussion = relationship("Discussion",
                              backref=backref('landing_page_modules',
                                              cascade="all, delete-orphan"))

    module_type_id = Column(Integer,
                            ForeignKey('landing_page_module_type.id',
                                       ondelete='CASCADE',
                                       onupdate='CASCADE'),
                            nullable=False,
                            index=True)
    module_type = relationship("LandingPageModuleType",
                               backref=backref('landing_page_modules',
                                               cascade="all, delete-orphan"))

    configuration = Column(UnicodeText)

    order = Column(Float, nullable=False)

    enabled = Column(Boolean, default=False)

    def get_discussion_id(self):
        return self.discussion_id or self.discussion.id

    @classmethod
    def get_discussion_conditions(cls, discussion_id, alias_maker=None):
        return (cls.discussion_id == discussion_id, )

    __mapper_args__ = {
        'polymorphic_identity': 'landing_page_module',
        'polymorphic_on': type,
        'with_polymorphic': '*'
    }

    crud_permissions = CrudPermissions(P_ADMIN_DISC, P_READ, P_ADMIN_DISC,
                                       P_ADMIN_DISC)
Esempio n. 10
0
class IdeaAnnouncement(Announcement):
    """An announcement attached to an idea"""
    __tablename__ = "idea_announce"
    id = Column(Integer,
                ForeignKey('announce.id',
                           ondelete='CASCADE',
                           onupdate='CASCADE'),
                primary_key=True)

    idea_id = Column(Integer,
                     ForeignKey(
                         'idea.id',
                         ondelete='CASCADE',
                         onupdate='CASCADE',
                     ),
                     nullable=False,
                     index=True)

    idea = relationship(
        Idea,
        backref=backref(
            'announcement',
            # One-to-one relationship
            uselist=False,
            cascade="all, delete-orphan"),
    )

    # Should this announcement propagate down to it's descendant ideas in the messageList?
    should_propagate_down = Column(Boolean, nullable=False, server_default='0')

    __mapper_args__ = {
        'polymorphic_identity': 'idea_announce',
        'with_polymorphic': '*'
    }

    # Same crud permissions as a idea
    crud_permissions = CrudPermissions(P_ADD_IDEA, P_READ, P_EDIT_IDEA,
                                       P_ADMIN_DISC, P_ADMIN_DISC,
                                       P_ADMIN_DISC)
Esempio n. 11
0
class Preferences(MutableMapping, Base, NamedClassMixin):
    """
    Cascading preferences
    """
    __metaclass__ = DeclarativeAbstractMeta
    __tablename__ = "preferences"
    BASE_PREFS_NAME = "default"
    id = Column(Integer, primary_key=True)
    name = Column(CoerceUnicode, nullable=False, unique=True)
    cascade_id = Column(Integer, ForeignKey(id), nullable=True)
    pref_json = Column("values", Text())  # JSON blob

    cascade_preferences = relationship("Preferences", remote_side=[id])

    @classmethod
    def get_naming_column_name(self):
        return "name"

    @classmethod
    def get_by_name(cls, name=None, session=None):
        name = name or cls.BASE_PREFS_NAME
        session = session or cls.default_db
        return session.query(cls).filter_by(name=name).first()

    @classmethod
    def get_default_preferences(cls, session=None):
        return cls.get_by_name('default', session) or cls(name='default')

    @classmethod
    def get_discussion_conditions(cls, discussion_id):
        # This is not a DiscussionBoundBase, but protocol is otherwise useful
        from .discussion import Discussion
        return ((cls.id == Discussion.preferences_id),
                (Discussion.id == discussion_id))

    @classmethod
    def init_from_settings(cls, settings):
        """Initialize some preference values"""
        from ..auth.social_auth import get_active_auth_strategies
        # TODO: Give linguistic names to social auth providers.
        active_strategies = {
            k: k
            for k in get_active_auth_strategies(settings)
        }
        active_strategies[''] = _("No special authentication")
        cls.preference_data['authorization_server_backend'][
            'scalar_values'] = active_strategies

    @property
    def local_values_json(self):
        values = {}
        if self.pref_json:
            values = json.loads(self.pref_json)
            assert isinstance(values, dict)
        return values

    @local_values_json.setter
    def local_values_json(self, val):
        assert isinstance(val, dict)
        self.pref_json = json.dumps(val)

    @property
    def values_json(self):
        if not self.cascade_preferences:
            return self.local_values_json
        values = self.cascade_preferences.values_json
        values.update(self.local_values_json)
        return values

    def _get_local(self, key):
        if key not in self.preference_data:
            raise KeyError("Unknown preference: " + key)
        values = self.local_values_json
        if key in values:
            value = values[key]
            return True, value
        return False, None

    def get_local(self, key):
        exists, value = self._get_local(key)
        if exists:
            return value

    def __getitem__(self, key):
        if key == 'name':
            return self.name
        if key == '@extends':
            return (self.uri_generic(self.cascade_id)
                    if self.cascade_id else None)
        exists, value = self._get_local(key)
        if exists:
            return value
        elif self.cascade_id:
            return self.cascade_preferences[key]
        if key == "preference_data":
            return self.get_preference_data_list()
        return self.get_preference_data()[key].get("default", None)

    def __len__(self):
        return len(self.preference_data_list) + 2

    def __iter__(self):
        return chain(self.preference_data_key_list, ('name', '@extends'))

    def __contains__(self, key):
        return key in self.preference_data_key_set

    def __delitem__(self, key):
        values = self.local_values_json
        if key in values:
            oldval = values[key]
            del values[key]
            self.local_values_json = values
            return oldval

    def __setitem__(self, key, value):
        if key == 'name':
            old_value = self.name
            self.name = unicode(value)
            return old_value
        elif key == '@extends':
            old_value = self.get('@extends')
            new_pref = self.get_instance(value)
            if new_pref is None:
                raise KeyError("Does not exist:" + value)
            self.cascade_preferences = new_pref
            return old_value
        if key not in self.preference_data_key_set:
            raise KeyError("Unknown preference: " + key)
        values = self.local_values_json
        old_value = values.get(key, None)
        value = self.validate(key, value)
        values[key] = value
        self.local_values_json = values
        return old_value

    def can_edit(self, key, permissions=(P_READ, ), pref_data=None):
        if P_SYSADMIN in permissions:
            if key == 'name' and self.name == self.BASE_PREFS_NAME:
                # Protect the base name
                return False
            return True
        if key in ('name', '@extends', 'preference_data'):
            # TODO: Delegate permissions.
            return False
        if key not in self.preference_data_key_set:
            raise KeyError("Unknown preference: " + key)
        if pref_data is None:
            pref_data = self.get_preference_data()[key]
        req_permission = pref_data.get('modification_permission', P_ADMIN_DISC)
        if req_permission not in permissions:
            return False
        return True

    def safe_del(self, key, permissions=(P_READ, )):
        if not self.can_edit(key, permissions):
            raise HTTPUnauthorized("Cannot delete " + key)
        del self[key]

    def safe_set(self, key, value, permissions=(P_READ, )):
        if not self.can_edit(key, permissions):
            raise HTTPUnauthorized("Cannot edit " + key)
        self[key] = value

    def validate(self, key, value, pref_data=None):
        if pref_data is None:
            pref_data = self.get_preference_data()[key]
        validator = pref_data.get('backend_validator_function', None)
        if validator:
            # This has many points of failure, but all failures are meaningful.
            module, function = validator.rsplit(".", 1)
            from importlib import import_module
            mod = import_module(module)
            try:
                value = getattr(mod, function)(value)
                if value is None:
                    raise ValueError("Empty value after validation")
            except Exception as e:
                raise ValueError(e.message)
        data_type = pref_data.get("value_type", "json")
        return self.validate_single_value(key, value, pref_data, data_type)

    def validate_single_value(self, key, value, pref_data, data_type):
        # TODO: Validation for the datatypes.
        # base_type: (bool|json|int|string|text|scalar|url|email|domain|locale|langstr|permission|role)
        # type: base_type|list_of_(type)|dict_of_(base_type)_to_(type)
        if data_type.startswith("list_of_"):
            assert isinstance(value, (list, tuple)), "Not a list"
            return [
                self.validate_single_value(key, val, pref_data, data_type[8:])
                for val in value
            ]
        elif data_type.startswith("dict_of_"):
            assert isinstance(value, (dict)), "Not a dict"
            key_type, value_type = data_type[8:].split("_to_", 1)
            assert "_" not in key_type
            return {
                self.validate_single_value(key, k, pref_data, key_type):
                self.validate_single_value(key, v, pref_data, value_type)
                for (k, v) in value.iteritems()
            }
        elif data_type == "langstr":
            # Syntactic sugar for dict_of_locale_to_string
            assert isinstance(value, (dict)), "Not a dict"
            return {
                self.validate_single_value(key, k, pref_data, "locale"):
                self.validate_single_value(key, v, pref_data, "string")
                for (k, v) in value.iteritems()
            }
        elif data_type == "bool":
            assert isinstance(value, bool), "Not a boolean"
        elif data_type == "int":
            assert isinstance(value, int), "Not an integer"
        elif data_type == "json":
            # Will raise a JSONDecodeError if not a valid JSON
            json.loads(value)
        else:
            assert isinstance(value, (str, unicode)), "Not a string"
            if data_type in ("string", "text"):
                pass
            elif data_type == "scalar":
                assert value in pref_data.get(
                    "scalar_values", ()), ("value not allowed: " + value)
            elif data_type == "url":
                condition = False
                parsed_val = urlparse(value)
                val = parsed_val.netloc
                while not condition:
                    # Whilst not an address, requested feature
                    if value in ("*", ):
                        condition = True
                        break
                    elif not bool(parsed_val.scheme):
                        # Must have a scheme, as defined a definition of a URI
                        break
                    elif not val.strip():
                        # No empty strings allowed
                        break
                    elif is_valid_ipv4_address(val):
                        condition = True
                        break
                    elif is_valid_ipv6_address(val):
                        condition = True
                        break
                    else:
                        # Must be a regular URL then. TODO: Check that the location has a DNS record
                        condition = True
                        break
                assert condition, "Not a valid URL. Must follow the specification of a URI."
            elif data_type == "email":
                from pyisemail import is_email
                assert is_email(value), "Not an email"
            elif data_type == "locale":
                pass  # TODO
            elif data_type == "permission":
                assert value in ASSEMBL_PERMISSIONS
            elif data_type == "role":
                if value not in SYSTEM_ROLES:
                    from .auth import Role
                    assert self.db.query(Role).filter_by(
                        name=value).count() == 1, "Unknown role"
            elif data_type == "domain":
                from pyisemail.validators.dns_validator import DNSValidator
                v = DNSValidator()
                assert v.is_valid(value), "Not a valid domain"
                value = value.lower()
            else:
                raise RuntimeError("Invalid data_type: " + data_type)
        return value

    def generic_json(self,
                     view_def_name='default',
                     user_id=Everyone,
                     permissions=(P_READ, ),
                     base_uri='local:'):
        # TODO: permissions
        values = self.local_values_json
        values['name'] = self.name
        if self.cascade_preferences:
            values['@extends'] = self.cascade_preferences.name
        values['@id'] = self.uri()
        return values

    def _do_update_from_json(self,
                             json,
                             parse_def,
                             aliases,
                             context,
                             permissions,
                             user_id,
                             duplicate_handling=None,
                             jsonld=None):
        for key, value in json.iteritems():
            if key == '@id':
                if value != self.uri():
                    raise RuntimeError("Wrong id")
            else:
                self[key] = value
        return self

    def __hash__(self):
        return Base.__hash__(self)

    @classproperty
    def property_defaults(cls):
        return {
            p['id']: p.get("default", None)
            for p in cls.preference_data_list
        }

    def get_preference_data(self):
        if self.cascade_id:
            base = self.cascade_preferences.get_preference_data()
        else:
            base = self.preference_data
        exists, patch = self._get_local("preference_data")
        if exists:
            return merge_json(base, patch)
        else:
            return base

    def get_preference_data_list(self):
        data = self.get_preference_data()
        keys = self.preference_data_key_list
        return [data[key] for key in keys]

    crud_permissions = CrudPermissions(create=P_SYSADMIN, update=P_ADMIN_DISC)

    # This defines the allowed properties and their data format
    # Each preference metadata has the following format:
    # id (the key for the preference as a dictionary)
    # name (for interface)
    # description (for interface, hover help)
    # value_type: given by the following grammar:
    #       base_type = (bool|json|int|string|text|scalar|address|url|email|domain|locale|langstr|permission|role)
    #       type = base_type|list_of_(type)|dict_of_(base_type)_to_(type)
    #   more types may be added, but need to be added to both frontend and backend
    # show_in_preferences: Do we always hide this preference?
    # modification_permission: What permission do you need to change that preference?
    #   (default: P_DISCUSSION_ADMIN)
    # allow_user_override: Do we allow users to have their personal value for that permission?
    #   if so what permission is required? (default False)
    # scalar_values: "{value: "label"}" a dictionary of permitted options for a scalar value type
    # default: the default value
    # item_default: the default value for new items in a list_of_T... or dict_of_BT_to_T...

    preference_data_list = [
        # Languages used in the discussion.
        {
            "id":
            "preferred_locales",
            "value_type":
            "list_of_locale",
            "name":
            _("Languages used"),
            "description":
            _("All languages expected in the discussion"),
            "allow_user_override":
            None,
            "item_default":
            "en",
            "default": [
                strip_country(x) for x in config.get_config().get(
                    'available_languages', 'fr en').split()
            ]
        },
        # Whether the discussion uses the new React landing page
        {
            "id":
            "landing_page",
            "value_type":
            "bool",
            "name":
            _("Use landing page"),
            "description":
            _("Are users directed to the landing page and phases at login, or diretly to the debate"
              ),
            "allow_user_override":
            None,
            "default":
            False,
        },
        # full class name of translation service to use, if any
        # e.g. assembl.nlp.translate.GoogleTranslationService
        {
            "id": "translation_service",
            "name": _("Translation service"),
            "value_type": "scalar",
            "scalar_values": {
                "":
                _("No translation"),
                "assembl.nlp.translation_service.DummyTranslationServiceTwoSteps":
                _("Dummy translation service (two steps)"),
                "assembl.nlp.translation_service.DummyTranslationServiceOneStep":
                _("Dummy translation service (one step)"),
                "assembl.nlp.translation_service.DummyTranslationServiceTwoStepsWithErrors":
                _("Dummy translation service (two steps) with errors"),
                "assembl.nlp.translation_service.DummyTranslationServiceOneStepWithErrors":
                _("Dummy translation service (one step) with errors"),
                "assembl.nlp.translation_service.GoogleTranslationService":
                _("Google Translate")
            },
            "description": _("Translation service"),
            "allow_user_override": None,
            "modification_permission": P_SYSADMIN,
            # "frontend_validator_function": func_name...?,
            # "backend_validator_function": func_name...?,
            "default": ""
        },

        # Simple view panel order, eg NIM or NMI
        {
            "id": "simple_view_panel_order",
            "name": _("Panel order in simple view"),
            "value_type": "scalar",
            "scalar_values": {
                "NIM": _("Navigation, Idea, Messages"),
                "NMI": _("Navigation, Messages, Idea")
            },
            "description": _("Order of panels"),
            "allow_user_override": P_READ,
            "modification_permission": P_ADMIN_DISC,
            # "frontend_validator_function": func_name...?,
            # "backend_validator_function": func_name...?,
            "default": "NMI"
        },

        # Parameters for frontend settings, for experimentation purposes.
        # What is put there should become separate parameters for typechecking
        {
            "id": "extra_json",
            "name": _("Extra JSON parameters"),
            "value_type": "json",
            "description": _("Parameters for quick frontend settings"),
            "allow_user_override": None,
            "modification_permission": P_ADMIN_DISC,
            "default": {}
        },

        # Discussion terms of use
        {
            "id":
            "terms_of_use_url",
            "name":
            _("Terms of use URL"),
            "value_type":
            "url",
            "description":
            _("URL of a document presenting terms of use for the discussion"),
            "allow_user_override":
            None,
            "modification_permission":
            P_ADMIN_DISC,
            "default":
            None
        },

        # Discussion Video
        {
            "id": "video_url",
            "name": _("Video URL"),
            "value_type": "url",
            "description": _("URL of a video presenting the discussion"),
            "allow_user_override": None,
            "modification_permission": P_ADMIN_DISC,
            "default": None
        },

        # Title in the tab
        {
            "id": "tab_title",
            "name": _("Tab title"),
            "value_type": "string",
            "description":
            _("Title which appears on the tab, by default assembl"),
            "allow_user_override": None,
            "modification_permission": P_ADMIN_DISC,
            "default": ""
        },

        # Discussion Video description
        {
            "id": "video_description",
            "name": _("Video description"),
            "value_type": "dict_of_locale_to_text",
            "description":
            _("Description of the video presenting the discussion"),
            "allow_user_override": None,
            "modification_permission": P_ADMIN_DISC,
            "default": None
        },

        # Allow social sharing
        {
            "id": "social_sharing",
            "name": _("Social sharing"),
            "value_type": "bool",
            # "scalar_values": {value: "label"},
            "description": _("Show the share button on posts and ideas"),
            "allow_user_override": None,
            "modification_permission": P_ADMIN_DISC,
            # "frontend_validator_function": func_name...?,
            # "backend_validator_function": func_name...?,
            "default": True
        },

        # Public or private social Sharing
        {
            "id": "private_social_sharing",
            "name": _("Private social sharing"),
            "value_type": "bool",
            "description": _("Publicizing or privatizing social sharing"),
            "allow_user_override": None,
            "modification_permission": P_ADMIN_DISC,
            "default": True,
        },

        # Show generic error message
        {
            "id": "generic_errors",
            "name": _("Generic Errors"),
            "value_type": "bool",
            "description": _("Display a generic error message."),
            "allow_user_override": None,
            "modification_permission": P_ADMIN_DISC,
            "default": config.get_config().get('assembl.generic_errors'),
        },

        # Extra data from social fields to put in CSV reports
        {
            "id":
            "extra_csv_data",
            "name":
            _("Extra data for CSV reports"),
            "value_type":
            "dict_of_string_to_langstr",
            # "scalar_values": {value: "label"},
            "description":
            _("data taken from social_auth's extra_data to add to CSV reports"
              ),
            "allow_user_override":
            None,
            "modification_permission":
            P_ADMIN_DISC,
            # "frontend_validator_function": func_name...?,
            "backend_validator_function":
            "assembl.models.social_data_extraction.validate_json_paths",
            "item_default": {
                "": {
                    "en": ""
                }
            },
            "default": {}  # for development
        },

        # Require virus check
        {
            "id": "requires_virus_check",
            "name": _("Require anti-virus check"),
            "value_type": "bool",
            # "scalar_values": {value: "label"},
            "description": _("Run an anti-virus on file attachments"),
            "allow_user_override": None,
            "modification_permission": P_ADMIN_DISC,
            # "frontend_validator_function": func_name...?,
            # "backend_validator_function": func_name...?,
            "default": False  # for development
        },
        {
            "id":
            "authorization_server_backend",
            "value_type":
            "scalar",
            "scalar_values": {
                "": _("No special authentication"),
            },
            "name":
            _("Authentication service type"),
            "description":
            _("A primary authentication server for this discussion, defined "
              "as a python-social-auth backend. Participants will be "
              "auto-logged in to that server, and discussion auto-"
              "subscription will require an account from this backend."),
            "allow_user_override":
            None,
            "modification_permission":
            P_SYSADMIN,
            # "frontend_validator_function": func_name...?,
            # "backend_validator_function": func_name...?,
            "default":
            ""
        },

        # Are moderated posts simply hidden or made inaccessible by default?
        {
            "id":
            "default_allow_access_to_moderated_text",
            "name":
            _("Allow access to moderated text"),
            "value_type":
            "bool",
            # "scalar_values": {value: "label"},
            "description":
            _("Are moderated posts simply hidden or made inaccessible "
              "by default?"),
            "allow_user_override":
            None,
            "modification_permission":
            P_ADMIN_DISC,
            # "frontend_validator_function": func_name...?,
            # "backend_validator_function": func_name...?,
            "default":
            True
        },

        # Does the Idea panel automatically open when an idea is clicked? (and close when a special section is clicked)
        {
            "id":
            "idea_panel_opens_automatically",
            "name":
            _("Idea panel opens automatically"),
            "value_type":
            "bool",
            # "scalar_values": {value: "label"},
            "description":
            _("Does the Idea panel automatically open when an idea is clicked ? (and close when a special section is clicked)"
              ),
            "allow_user_override":
            P_READ,
            "modification_permission":
            P_ADMIN_DISC,
            # "frontend_validator_function": func_name...?,
            # "backend_validator_function": func_name...?,
            "default":
            True
        },

        # What are (ordered) identifiers of columns in multi-column views?
        {
            "id":
            "default_column_identifiers",
            "name":
            _("Ids of columns in column view"),
            "value_type":
            "list_of_string",
            "description":
            _("What are (ordered) identifiers of columns in multi-column views?"
              ),
            "allow_user_override":
            None,
            "modification_permission":
            P_ADMIN_DISC,
            # "frontend_validator_function": func_name...?,
            # "backend_validator_function": func_name...?,
            "item_default":
            "",
            "default": ["positive", "negative"],
        },

        # What are default theme colors of columns in multi-column view
        {
            "id":
            "default_column_colors",
            "name":
            _("Default colors of columns in column view"),
            "value_type":
            "dict_of_string_to_string",
            "description":
            _("What are (default) theme colors of columns in multi-column views?"
              ),
            "allow_user_override":
            None,
            "modification_permission":
            P_ADMIN_DISC,
            # "frontend_validator_function": func_name...?,
            # "backend_validator_function": func_name...?,
            "default": {
                "positive": "green",
                "negative": "red"
            },
        },

        # What are (default) names of columns in multi-column views, in each discussion language?
        {
            "id":
            "default_column_names",
            "name":
            _("Names of columns in column view"),
            "value_type":
            "dict_of_string_to_langstr",
            "description":
            _("What are (default) names of columns in multi-column views, in each discussion language?"
              ),
            "allow_user_override":
            None,
            "modification_permission":
            P_ADMIN_DISC,
            # "frontend_validator_function": func_name...?,
            # "backend_validator_function": func_name...?,
            "default": {
                "negative": {
                    "en": "Negative",
                    "fr": "Négatif"
                },
                "positive": {
                    "en": "Positive",
                    "fr": "Positif"
                }
            },
            "item_default": {
                "": {
                    "en": ""
                }
            },
        },

        # The specification of the default permissions for a discussion
        {
            "id": "default_permissions",
            "name": _("Default permissions"),
            "value_type": "dict_of_role_to_list_of_permission",
            "show_in_preferences": False,
            "description": _("The basic permissions for a new discussion"),
            "allow_user_override": None,
            "modification_permission": P_SYSADMIN,
            # "frontend_validator_function": func_name...?,
            # "backend_validator_function": func_name...?,
            "item_default": {
                R_PARTICIPANT: [P_READ],
            },
            "default": {
                R_ADMINISTRATOR: [
                    P_ADD_EXTRACT,
                    P_ADD_IDEA,
                    P_ADD_POST,
                    P_ADMIN_DISC,
                    P_DELETE_MY_POST,
                    P_DELETE_POST,
                    P_DISC_STATS,
                    P_EDIT_EXTRACT,
                    P_EDIT_IDEA,
                    P_EDIT_MY_EXTRACT,
                    P_EDIT_MY_POST,
                    P_EDIT_POST,
                    P_EDIT_SYNTHESIS,
                    P_EXPORT_EXTERNAL_SOURCE,
                    P_MANAGE_RESOURCE,
                    P_MODERATE,
                    P_OVERRIDE_SOCIAL_AUTOLOGIN,
                    P_SEND_SYNTHESIS,
                    P_VOTE,
                ],
                R_CATCHER: [
                    P_ADD_EXTRACT,
                    P_ADD_IDEA,
                    P_ADD_POST,
                    P_DELETE_MY_POST,
                    P_EDIT_EXTRACT,
                    P_EDIT_IDEA,
                    P_EDIT_MY_EXTRACT,
                    P_EDIT_MY_POST,
                    P_OVERRIDE_SOCIAL_AUTOLOGIN,
                    P_VOTE,
                ],
                R_MODERATOR: [
                    P_ADD_EXTRACT,
                    P_ADD_IDEA,
                    P_ADD_POST,
                    P_DELETE_MY_POST,
                    P_DELETE_POST,
                    P_DISC_STATS,
                    P_EDIT_EXTRACT,
                    P_EDIT_IDEA,
                    P_EDIT_MY_EXTRACT,
                    P_EDIT_MY_POST,
                    P_EDIT_POST,
                    P_EDIT_SYNTHESIS,
                    P_EXPORT_EXTERNAL_SOURCE,
                    P_MANAGE_RESOURCE,
                    P_MODERATE,
                    P_OVERRIDE_SOCIAL_AUTOLOGIN,
                    P_SEND_SYNTHESIS,
                    P_VOTE,
                ],
                R_PARTICIPANT: [
                    P_ADD_POST,
                    P_DELETE_MY_POST,
                    P_EDIT_MY_POST,
                    P_VOTE,
                ],
                Authenticated: [
                    P_SELF_REGISTER,
                ],
                Everyone: [
                    P_READ,
                    P_READ_PUBLIC_CIF,
                ],
            },
        },

        # Registration requires being a member of this email domain.
        {
            "id":
            "require_email_domain",
            "name":
            _("Require Email Domain"),
            "value_type":
            "list_of_domain",
            # "scalar_values": {value: "label"},
            "description":
            _("List of domain names of user email address required for "
              "self-registration. Only accounts with at least an email from those "
              "domains can self-register to this discussion. Anyone can "
              "self-register if this is empty."),
            "allow_user_override":
            None,
            "modification_permission":
            P_ADMIN_DISC,
            # "frontend_validator_function": func_name...?,
            # "backend_validator_function": func_name...?,
            "default": [],
            "item_default":
            ""
        },

        # Allow whitelist to be applied to SSO login process
        {
            "id":
            "whitelist_on_authentication_backend",
            "name":
            _("Whitelist on authentication service"),
            "value_type":
            "bool",
            # "scalar_values": {value: "label"},
            "description":
            _("Allow white list to be applied to the authentication service chosen"
              ),
            "allow_user_override":
            None,
            "modification_permission":
            P_ADMIN_DISC,
            # "frontend_validator_function": func_name...?,
            # "backend_validator_function": func_name...?,
            "default":
            False
        },

        # Allow whitelist to be applied to the regular login process
        {
            "id":
            "whitelist_on_register",
            "name":
            _("Whitelist on standard registration"),
            "value_type":
            "bool",
            # "scalar_values": {value: "label"},
            "description":
            _("Allow white list to be applied to the default login process"),
            "allow_user_override":
            None,
            "modification_permission":
            P_ADMIN_DISC,
            # "frontend_validator_function": func_name...?,
            # "backend_validator_function": func_name...?,
            "default":
            True
        },

        # A discussion administrator, if different from the server administrator
        {
            "id":
            "discussion_administrators",
            "name":
            _("Discussion administrators"),
            "value_type":
            "list_of_email",
            # "scalar_values": {value: "label"},
            "description":
            _("A list of discussion administrators, if different from the server administrator"
              ),
            "allow_user_override":
            None,
            "modification_permission":
            P_ADMIN_DISC,
            # "frontend_validator_function": func_name...?,
            # "backend_validator_function": func_name...?,
            "default":
            False
        },

        # Show the CI Dashboard in the panel group window
        {
            "id": "show_ci_dashboard",
            "name": _("Show CI Dashboard"),
            "value_type": "bool",
            # "scalar_values": {value: "label"},
            "description":
            _("Show the CI Dashboard in the panel group window"),
            "allow_user_override": None,
            "modification_permission": P_ADMIN_DISC,
            # "frontend_validator_function": func_name...?,
            # "backend_validator_function": func_name...?,
            "default": False
        },

        # Configuration of the visualizations shown in the CI Dashboard
        {
            "id":
            "ci_dashboard_url",
            "name":
            _("URL of CI Dashboard"),
            "value_type":
            "url",
            "description":
            _("Configuration of the visualizations shown in the "
              "CI Dashboard"),
            "allow_user_override":
            None,
            "modification_permission":
            P_ADMIN_DISC,
            # "frontend_validator_function": func_name...?,
            # "backend_validator_function": func_name...?,
            "default":
            "//cidashboard.net/ui/visualisations/index.php?"
            "width=1000&height=1000&vis=11,23,p22,13,p7,7,12,p2,p15,p9,"
            "p8,p1,p10,p14,5,6,16,p16,p17,18,p20,p4&lang=<%= lang %>"
            "&title=&url=<%= url %>&userurl=<%= user_url %>"
            "&langurl=&timeout=60"
        },

        # List of visualizations
        {
            "id":
            "visualizations",
            "name":
            _("Catalyst Visualizations"),
            "value_type":
            "json",
            # "scalar_values": {value: "label"},
            "description":
            _("A JSON description of available Catalyst visualizations"),
            "allow_user_override":
            None,
            "modification_permission":
            P_ADMIN_DISC,
            # "frontend_validator_function": func_name...?,
            # "backend_validator_function": func_name...?,
            "default": {}
        },

        # Extra navigation sections (refers to visualizations)
        {
            "id":
            "navigation_sections",
            "name":
            _("Navigation sections"),
            "value_type":
            "json",
            # "scalar_values": {value: "label"},
            "description":
            _("A JSON specification of Catalyst visualizations to show "
              "in the navigation section"),
            "allow_user_override":
            None,
            "modification_permission":
            P_ADMIN_DISC,
            # "frontend_validator_function": func_name...?,
            # "backend_validator_function": func_name...?,
            "default": {}
        },

        # Translations for the navigation sections
        {
            "id":
            "translations",
            "name":
            _("Catalyst translations"),
            "value_type":
            "json",
            # "scalar_values": {value: "label"},
            "description":
            _("Translations applicable to Catalyst visualizations, "
              "in Jed (JSON) format"),
            "allow_user_override":
            None,
            # "view_permission": P_READ,  # by default
            "modification_permission":
            P_ADMIN_DISC,
            # "frontend_validator_function": func_name...?,
            # "backend_validator_function": func_name...?,
            "default": {}
        },

        # Default expanded/collapsed state of each idea in the table of ideas.
        # A user can override it by opening/closing an idea.
        # This is a hash where keys are ideas ids, and values are booleans.
        # We could use dict_of_string_to_bool, but that would clutter the interface.
        {
            "id":
            "default_table_of_ideas_collapsed_state",
            "name":
            _("Default Table of Ideas Collapsed state"),
            "value_type":
            "json",
            # "scalar_values": {value: "label"},
            "description":
            _("Default expanded/collapsed state of each idea in the table "
              "of ideas. A user can override it by opening/closing an idea"),
            "allow_user_override":
            None,
            # "view_permission": P_READ,  # by default
            "modification_permission":
            P_ADD_IDEA,
            # "frontend_validator_function": func_name...?,
            # "backend_validator_function": func_name...?,
            "default": {},
            "show_in_preferences":
            False
        },

        # The specification of the preference data
        {
            "id":
            "preference_data",
            "name":
            _("Preference data"),
            "value_type":
            "json",
            "show_in_preferences":
            False,
            "description":
            _("The preference configuration; override only with care"),
            "allow_user_override":
            None,
            "modification_permission":
            P_SYSADMIN,
            # "frontend_validator_function": func_name...?,
            # "backend_validator_function": func_name...?,
            "default":
            None  # this should be recursive...
        },

        # The specification of the cookies banner
        {
            "id": "cookies_banner",
            "name": _("Cookies banner"),
            "value_type": "bool",
            "show_in_preferences": True,
            "description":
            _("Show the banner offering to disable Piwik cookies"),
            "allow_user_override": None,
            "modification_permission": P_ADMIN_DISC,
            # "frontend_validator_function": func_name...?,
            # "backend_validator_function": func_name...?,
            "default": True  # this should be recursive...
        },

        # Custom HTML code that will be integrated on the landing page of the debate, right after the <body> tag
        {
            "id":
            "custom_html_code_landing_page",
            "name":
            _("Custom HTML code on the landing page"),
            "value_type":
            "text",
            "show_in_preferences":
            True,
            "description":
            _("Custom HTML code that will be integrated on the landing page of the debate, right after the <body> tag"
              ),
            "allow_user_override":
            None,
            "modification_permission":
            P_ADMIN_DISC,
            "default":
            None
        },

        # Custom HTML code that will be integrated on the user registration page of the debate, right after the <body> tag
        {
            "id":
            "custom_html_code_user_registration_page",
            "name":
            _("Custom HTML code on the user registration page"),
            "value_type":
            "text",
            "show_in_preferences":
            True,
            "description":
            _("Custom HTML code that will be integrated on the user registration page of the debate, right after the <body> tag"
              ),
            "allow_user_override":
            None,
            "modification_permission":
            P_ADMIN_DISC,
            "default":
            None
        },

        # Harvesting translation
        {
            "id": "harvesting_translation",
            "name": _("Harvesting translation"),
            "value_type": "dict_of_string_to_string",
            "show_in_preferences": True,
            "description": _("Harvesting translation"),
            "allow_user_override": P_READ,
            "default": None
        },

        # Valid CORS paths
        {
            "id":
            "graphql_valid_cors",
            "name":
            _("Valid CORS paths for GraphQL API calls"),
            "value_type":
            "list_of_url",
            "show_in_preferences":
            True,
            "description":
            _("A list of valid domain names or IP addresses that are allowed to make CORS api calls to the GraphQL API"
              ),
            "allow_user_override":
            False,
            "default": [],
            "item_default":
            ""
        },
    ]

    # Precompute, this is not mutable.
    preference_data_key_list = [p["id"] for p in preference_data_list]
    preference_data_key_set = set(preference_data_key_list)
    preference_data = {p["id"]: p for p in preference_data_list}
Esempio n. 12
0
class VoteSession(langstrings_base(langstrings_names, "VoteSession"),
                  VotingWidget):
    """ A vote session bound to a discussion phase.
        Uses TimelineEvent.description as frontend subtitle. """

    __tablename__ = "vote_session"
    __mapper_args__ = {
        'polymorphic_identity': 'vote_session',
    }

    id = Column(Integer,
                ForeignKey(VotingWidget.id,
                           ondelete='CASCADE',
                           onupdate='CASCADE'),
                primary_key=True)

    idea_id = Column(Integer,
                     ForeignKey(Idea.id,
                                onupdate="CASCADE",
                                ondelete='CASCADE'),
                     nullable=False,
                     unique=True)

    idea = relationship(
        Idea,
        backref=backref("vote_session",
                        single_parent=True,
                        uselist=False,
                        cascade="all, delete-orphan"),
    )

    see_current_votes = Column(Boolean,
                               nullable=False,
                               server_default='0',
                               default=False)

    @classmethod
    def filter_started(cls, query):
        return query
# cls.discussion_phase doesn't exist anymore
#        return query.join(cls.discussion_phase).filter(
#            (DiscussionPhase.start == None) | (DiscussionPhase.start <= datetime.utcnow()))  # noqa: E711

    @classmethod
    def test_active(cls):
        return ()
#        now = datetime.utcnow()
#        return ((DiscussionPhase.end == None) | (DiscussionPhase.end > now) & (DiscussionPhase.start == None) | (DiscussionPhase.start <= now))  # noqa: E711

    @classmethod
    def filter_active(cls, query):
        return query


#        return query.join(cls.discussion_phase).filter(cls.test_active())

    def vote_session_discussion_phase(self):
        return self.idea.get_associated_phase()

    def is_started(self):
        phase = self.vote_session_discussion_phase()
        if phase:
            return phase.start == None or phase.start <= datetime.utcnow(
            )  # noqa: E711
        return False

    def is_ended(self):
        phase = self.vote_session_discussion_phase()
        if phase:
            return phase.end != None and phase.end < datetime.utcnow(
            )  # noqa: E711
        return False

    def get_voter_ids_query(self, start=None, end=None):
        vote_specifications = []
        for proposal in self.idea.get_vote_proposals():
            vote_specifications.extend(proposal.criterion_for)
        from .votes import AbstractIdeaVote
        vote_class = with_polymorphic(AbstractIdeaVote, AbstractIdeaVote)
        query = self.db.query(
            vote_class.voter_id).filter_by(tombstone_date=None).filter(
                vote_class.vote_spec_id.in_([
                    vote_spec.id for vote_spec in vote_specifications
                ])).distinct()
        if start is not None:
            query = query.filter(vote_class.vote_date >= start)
        if end is not None:
            query = query.filter(vote_class.vote_date <= end)
        return query

    def get_num_votes(self, start=None, end=None):
        vote_specifications = []
        for proposal in self.idea.get_vote_proposals():
            vote_specifications.extend(proposal.criterion_for)
        from .votes import AbstractIdeaVote
        vote_class = with_polymorphic(AbstractIdeaVote, AbstractIdeaVote)
        query = self.db.query(
            vote_class.voter_id).filter_by(tombstone_date=None).filter(
                vote_class.vote_spec_id.in_(
                    [vote_spec.id for vote_spec in vote_specifications]))
        if start is not None:
            query = query.filter(vote_class.vote_date >= start)
        if end is not None:
            query = query.filter(vote_class.vote_date <= end)
        # There is no distinct on purpose here.
        # For a token vote spec, voting on two categories is counted as 2 votes.
        return query.count()

    crud_permissions = CrudPermissions(create=P_ADMIN_DISC,
                                       read=P_READ,
                                       update=P_ADMIN_DISC,
                                       delete=P_ADMIN_DISC)
Esempio n. 13
0
class Document(DiscussionBoundBase):
    """
    Represents a Document or file, local to the database or (more typically)
    a remote document
    """
    __tablename__ = "document"
    id = Column(Integer, primary_key=True)

    type = Column(String(60), nullable=False)

    __table_args__ = (UniqueConstraint('discussion_id', 'uri_id'), )
    """
    The cannonical identifier of this document.  If a URL, it's to be
    interpreted as a purl
    """
    uri_id = Column(URLString)
    creation_date = Column(DateTime, nullable=False, default=datetime.utcnow)
    discussion_id = Column(Integer,
                           ForeignKey(
                               'discussion.id',
                               ondelete='CASCADE',
                               onupdate='CASCADE',
                           ),
                           nullable=False,
                           index=True)

    discussion = relationship(
        "Discussion",
        backref=backref('documents', cascade="all, delete-orphan"),
    )

    oembed_type = Column(String(1024), server_default="")
    mime_type = Column(String(1024),
                       server_default="",
                       doc=docs.Document.mime_type)
    # From metadata, not the user
    title = Column(CoerceUnicode(1024), server_default="")

    # From metadata, not the user
    description = Column(UnicodeText)

    # From metadata, not the user
    author_name = Column(CoerceUnicode())

    # From metadata, not the user
    author_url = Column(URLString)

    # From metadata, not the user
    thumbnail_url = Column(URLString)

    # From metadata, not the user
    site_name = Column(CoerceUnicode())

    __mapper_args__ = {
        'polymorphic_identity': 'document',
        'polymorphic_on': 'type',
        'with_polymorphic': '*'
    }

    @property
    def external_url(self):
        return self.uri_id

    def generate_unique_id(self):
        """Method to override in order to create a unique URI of the entity"""
        import uuid
        u = uuid.uuid1()
        return u.urn

    def get_discussion_id(self):
        return self.discussion_id or self.discussion.id

    @classmethod
    def get_discussion_conditions(cls, discussion_id, alias_maker=None):
        return (cls.discussion_id == discussion_id, )

    default_duplicate_handling = DuplicateHandling.USE_ORIGINAL

    def unique_query(self):
        query, _ = super(Document, self).unique_query()
        return query.filter_by(uri_id=self.uri_id), True

    def update_fields(self, new_doc):
        """
        :param dict new_doc: dict object of all of the document types
            with keys:
            set(['url', 'title', 'description', 'oembed', 'mime_type',
                'author_name', 'author_url', 'thumbnail', 'site_name'])
        """
        self.uri_id = new_doc.get('url')
        self.title = new_doc.get('title')
        self.description = new_doc.get('description')
        self.oembed_type = new_doc.get('oembed')
        self.mime_type = new_doc.get('mime_type')
        self.author_name = new_doc.get('author_name')
        self.author_url = new_doc.get('author_url')
        self.thumbnail_url = new_doc.get('thumbnail')
        self.site_name = new_doc.get('site_name')

    # Same crud permissions as a post. Issue with idea edition,
    # but that is usually more restricted than post permission.
    crud_permissions = CrudPermissions(P_ADD_POST, P_READ, P_EDIT_POST,
                                       P_ADMIN_DISC, P_EDIT_POST, P_ADMIN_DISC)
Esempio n. 14
0
class VoteSession(langstrings_base(langstrings_names, "VoteSession"),
                  VotingWidget):
    """ A vote session bound to a discussion phase.
        Uses TimelineEvent.description as frontend subtitle. """

    __tablename__ = "vote_session"
    __mapper_args__ = {
        'polymorphic_identity': 'vote_session',
    }

    id = Column(Integer,
                ForeignKey(VotingWidget.id,
                           ondelete='CASCADE',
                           onupdate='CASCADE'),
                primary_key=True)

    discussion_phase_id = Column(Integer,
                                 ForeignKey(DiscussionPhase.id),
                                 nullable=False)

    discussion_phase = relationship(
        DiscussionPhase,
        backref=backref("vote_session",
                        single_parent=True,
                        uselist=False,
                        cascade="all, delete-orphan"),
    )

    see_current_votes = Column(Boolean,
                               nullable=False,
                               server_default='0',
                               default=False)

    @classmethod
    def filter_started(cls, query):
        return query.join(cls.discussion_phase).filter(
            (DiscussionPhase.start == None) |
            (DiscussionPhase.start <= datetime.utcnow()))  # noqa: E711

    @classmethod
    def test_active(cls):
        now = datetime.utcnow()
        return (
            (DiscussionPhase.end == None) | (DiscussionPhase.end > now) &
            (DiscussionPhase.start == None) | (DiscussionPhase.start <= now)
        )  # noqa: E711

    @classmethod
    def filter_active(cls, query):
        return query.join(cls.discussion_phase).filter(cls.test_active())

    def is_started(self):
        return self.discussion_phase.start == None or self.discussion_phase.start <= datetime.utcnow(
        )  # noqa: E711

    def is_ended(self):
        return self.discussion_phase.end != None and self.discussion_phase.end < datetime.utcnow(
        )  # noqa: E711

    crud_permissions = CrudPermissions(create=P_ADMIN_DISC,
                                       read=P_READ,
                                       update=P_ADMIN_DISC,
                                       delete=P_ADMIN_DISC)