def get_roles_for_select_field(self): """ :return: list of DictLikeClass instances representing available Roles (to be used in select fields """ result = list() for role_id in UserRoleInWorkspace.get_all_role_values(): role = RoleType(role_id) result.append(role) return result
def serialize_role_in_workspace(role: UserRoleInWorkspace, context: Context): """ Actually, roles are serialized as users (with minimal information) :param role: :param context: :return: """ result = DictLikeClass() result['id'] = role.user_id result['icon'] = role.icon result['name'] = role.user.display_name result['role'] = role.role result['style'] = role.style result['role_description'] = role.role_as_label() result['email'] = role.user.email result['user'] = context.toDict(role.user) result['notifications_subscribed'] = role.do_notify return result
def serialize_role_in_list_for_user(role: UserRoleInWorkspace, context: Context): """ Actually, roles are serialized as users (with minimal information) :param role: :param context: :return: """ result = DictLikeClass() result['id'] = role.role result['icon'] = role.icon result['label'] = role.role_as_label() result['style'] = RoleType(role.role).css_style result['workspace'] = context.toDict(role.workspace) result['user'] = Context(CTX.DEFAULT).toDict(role.user) result['notifications_subscribed'] = role.do_notify # result['workspace_name'] = role.workspace.label return result
def _build_email_body(self, mako_template_filepath: str, role: UserRoleInWorkspace, content: Content, actor: User) -> str: """ Build an email body and return it as a string :param mako_template_filepath: the absolute path to the mako template to be used for email body building :param role: the role related to user to whom the email must be sent. The role is required (and not the user only) in order to show in the mail why the user receive the notification :param content: the content item related to the notification :param actor: the user at the origin of the action / notification (for example the one who wrote a comment :param config: the global configuration :return: the built email body as string. In case of multipart email, this method must be called one time for text and one time for html """ logger.debug(self, 'Building email content from MAKO template {}'.format(mako_template_filepath)) template = Template(filename=mako_template_filepath) # TODO - D.A. - 2014-11-06 - move this # Import is here for circular import problem import tracim.lib.helpers as helpers dictified_item = Context(CTX.EMAIL_NOTIFICATION, self._global_config.WEBSITE_BASE_URL).toDict(content) dictified_actor = Context(CTX.DEFAULT).toDict(actor) main_title = dictified_item.label content_intro = '' content_text = '' call_to_action_text = '' action = content.get_last_action().id if ActionDescription.COMMENT == action: content_intro = _('<span id="content-intro-username">{}</span> added a comment:').format(actor.display_name) content_text = content.description call_to_action_text = _('Answer') elif ActionDescription.CREATION == action: # Default values (if not overriden) content_text = content.description call_to_action_text = _('View online') if ContentType.Thread == content.type: call_to_action_text = _('Answer') content_intro = _('<span id="content-intro-username">{}</span> started a thread entitled:').format(actor.display_name) content_text = '<p id="content-body-intro">{}</p>'.format(content.label) + \ content.get_last_comment_from(actor).description elif ContentType.File == content.type: content_intro = _('<span id="content-intro-username">{}</span> added a file entitled:').format(actor.display_name) if content.description: content_text = content.description else: content_text = '<span id="content-body-only-title">{}</span>'.format(content.label) elif ContentType.Page == content.type: content_intro = _('<span id="content-intro-username">{}</span> added a page entitled:').format(actor.display_name) content_text = '<span id="content-body-only-title">{}</span>'.format(content.label) elif ActionDescription.REVISION == action: content_text = content.description call_to_action_text = _('View online') if ContentType.File == content.type: content_intro = _('<span id="content-intro-username">{}</span> uploaded a new revision.').format(actor.display_name) content_text = '' elif ContentType.Page == content.type: content_intro = _('<span id="content-intro-username">{}</span> updated this page.').format(actor.display_name) previous_revision = content.get_previous_revision() title_diff = '' if previous_revision.label != content.label: title_diff = htmldiff(previous_revision.label, content.label) content_text = _('<p id="content-body-intro">Here is an overview of the changes:</p>')+ \ title_diff + \ htmldiff(previous_revision.description, content.description) elif ContentType.Thread == content.type: content_intro = _('<span id="content-intro-username">{}</span> updated the thread description.').format(actor.display_name) previous_revision = content.get_previous_revision() title_diff = '' if previous_revision.label != content.label: title_diff = htmldiff(previous_revision.label, content.label) content_text = _('<p id="content-body-intro">Here is an overview of the changes:</p>')+ \ title_diff + \ htmldiff(previous_revision.description, content.description) # elif ContentType.Thread == content.type: # content_intro = _('<span id="content-intro-username">{}</span> updated this page.').format(actor.display_name) # previous_revision = content.get_previous_revision() # content_text = _('<p id="content-body-intro">Here is an overview of the changes:</p>')+ \ # htmldiff(previous_revision.description, content.description) elif ActionDescription.EDITION == action: call_to_action_text = _('View online') if ContentType.File == content.type: content_intro = _('<span id="content-intro-username">{}</span> updated the file description.').format(actor.display_name) content_text = '<p id="content-body-intro">{}</p>'.format(content.get_label()) + \ content.description if '' == content_intro and content_text == '': # Skip notification, but it's not normal logger.error( self, 'A notification is being sent but no content. ' 'Here are some debug informations: [content_id: {cid}]' '[action: {act}][author: {actor}]'.format( cid=content.content_id, act=action, actor=actor ) ) raise ValueError('Unexpected empty notification') # Import done here because cyclic import from tracim.config.app_cfg import CFG body_content = template.render( base_url=self._global_config.WEBSITE_BASE_URL, _=_, h=helpers, user_display_name=role.user.display_name, user_role_label=role.role_as_label(), workspace_label=role.workspace.label, content_intro=content_intro, content_text=content_text, main_title=main_title, call_to_action_text=call_to_action_text, result = DictLikeClass(item=dictified_item, actor=dictified_actor), CFG=CFG.get_instance(), ) return body_content
def _build_email_body(self, mako_template_filepath: str, role: UserRoleInWorkspace, content: Content, actor: User) -> str: """ Build an email body and return it as a string :param mako_template_filepath: the absolute path to the mako template to be used for email body building :param role: the role related to user to whom the email must be sent. The role is required (and not the user only) in order to show in the mail why the user receive the notification :param content: the content item related to the notification :param actor: the user at the origin of the action / notification (for example the one who wrote a comment :param config: the global configuration :return: the built email body as string. In case of multipart email, this method must be called one time for text and one time for html """ logger.debug( self, 'Building email content from MAKO template {}'.format( mako_template_filepath)) template = Template(filename=mako_template_filepath) # TODO - D.A. - 2014-11-06 - move this # Import is here for circular import problem import tracim.lib.helpers as helpers dictified_item = Context( CTX.EMAIL_NOTIFICATION, self._global_config.WEBSITE_BASE_URL).toDict(content) dictified_actor = Context(CTX.DEFAULT).toDict(actor) main_title = dictified_item.label content_intro = '' content_text = '' call_to_action_text = '' action = content.get_last_action().id if ActionDescription.COMMENT == action: content_intro = l_( '<span id="content-intro-username">{}</span> added a comment:' ).format(actor.display_name) content_text = content.description call_to_action_text = l_('Answer') elif ActionDescription.CREATION == action: # Default values (if not overriden) content_text = content.description call_to_action_text = l_('View online') if ContentType.Thread == content.type: call_to_action_text = l_('Answer') content_intro = l_( '<span id="content-intro-username">{}</span> started a thread entitled:' ).format(actor.display_name) content_text = '<p id="content-body-intro">{}</p>'.format(content.label) + \ content.get_last_comment_from(actor).description elif ContentType.File == content.type: content_intro = l_( '<span id="content-intro-username">{}</span> added a file entitled:' ).format(actor.display_name) if content.description: content_text = content.description else: content_text = '<span id="content-body-only-title">{}</span>'.format( content.label) elif ContentType.Page == content.type: content_intro = l_( '<span id="content-intro-username">{}</span> added a page entitled:' ).format(actor.display_name) content_text = '<span id="content-body-only-title">{}</span>'.format( content.label) elif ActionDescription.REVISION == action: content_text = content.description call_to_action_text = l_('View online') if ContentType.File == content.type: content_intro = l_( '<span id="content-intro-username">{}</span> uploaded a new revision.' ).format(actor.display_name) content_text = '' elif ContentType.Page == content.type: content_intro = l_( '<span id="content-intro-username">{}</span> updated this page.' ).format(actor.display_name) previous_revision = content.get_previous_revision() title_diff = '' if previous_revision.label != content.label: title_diff = htmldiff(previous_revision.label, content.label) content_text = str(l_('<p id="content-body-intro">Here is an overview of the changes:</p>'))+ \ title_diff + \ htmldiff(previous_revision.description, content.description) elif ContentType.Thread == content.type: content_intro = l_( '<span id="content-intro-username">{}</span> updated the thread description.' ).format(actor.display_name) previous_revision = content.get_previous_revision() title_diff = '' if previous_revision.label != content.label: title_diff = htmldiff(previous_revision.label, content.label) content_text = str(l_('<p id="content-body-intro">Here is an overview of the changes:</p>'))+ \ title_diff + \ htmldiff(previous_revision.description, content.description) # elif ContentType.Thread == content.type: # content_intro = l_('<span id="content-intro-username">{}</span> updated this page.').format(actor.display_name) # previous_revision = content.get_previous_revision() # content_text = l_('<p id="content-body-intro">Here is an overview of the changes:</p>')+ \ # htmldiff(previous_revision.description, content.description) elif ActionDescription.EDITION == action: call_to_action_text = l_('View online') if ContentType.File == content.type: content_intro = l_( '<span id="content-intro-username">{}</span> updated the file description.' ).format(actor.display_name) content_text = '<p id="content-body-intro">{}</p>'.format(content.get_label()) + \ content.description elif ActionDescription.STATUS_UPDATE == action: call_to_action_text = l_('View online') intro_user_msg = l_('<span id="content-intro-username">{}</span> ' 'updated the following status:') content_intro = intro_user_msg.format(actor.display_name) intro_body_msg = '<p id="content-body-intro">{}: {}</p>' content_text = intro_body_msg.format( content.get_label(), content.get_status().label, ) if '' == content_intro and content_text == '': # Skip notification, but it's not normal logger.error( self, 'A notification is being sent but no content. ' 'Here are some debug informations: [content_id: {cid}]' '[action: {act}][author: {actor}]'.format( cid=content.content_id, act=action, actor=actor)) raise ValueError('Unexpected empty notification') # Import done here because cyclic import from tracim.config.app_cfg import CFG body_content = template.render( base_url=self._global_config.WEBSITE_BASE_URL, _=l_, h=helpers, user_display_name=role.user.display_name, user_role_label=role.role_as_label(), workspace_label=role.workspace.label, content_intro=content_intro, content_text=content_text, main_title=main_title, call_to_action_text=call_to_action_text, result=DictLikeClass(item=dictified_item, actor=dictified_actor), CFG=CFG.get_instance(), ) return body_content
def create_role(self) -> UserRoleInWorkspace: role = UserRoleInWorkspace() return role
class RoleApi(object): ALL_ROLE_VALUES = UserRoleInWorkspace.get_all_role_values() # Dict containing readable members roles for given role members_read_rights = { UserRoleInWorkspace.NOT_APPLICABLE: [], UserRoleInWorkspace.READER: [ UserRoleInWorkspace.WORKSPACE_MANAGER, ], UserRoleInWorkspace.CONTRIBUTOR: [ UserRoleInWorkspace.WORKSPACE_MANAGER, UserRoleInWorkspace.CONTENT_MANAGER, UserRoleInWorkspace.CONTRIBUTOR, ], UserRoleInWorkspace.CONTENT_MANAGER: [ UserRoleInWorkspace.WORKSPACE_MANAGER, UserRoleInWorkspace.CONTENT_MANAGER, UserRoleInWorkspace.CONTRIBUTOR, UserRoleInWorkspace.READER, ], UserRoleInWorkspace.WORKSPACE_MANAGER: [ UserRoleInWorkspace.WORKSPACE_MANAGER, UserRoleInWorkspace.CONTENT_MANAGER, UserRoleInWorkspace.CONTRIBUTOR, UserRoleInWorkspace.READER, ], } @classmethod def role_can_read_member_role(cls, reader_role: int, tested_role: int) \ -> bool: """ :param reader_role: role as viewer :param tested_role: role as viwed :return: True if given role can view member role in workspace. """ if reader_role in cls.members_read_rights: return tested_role in cls.members_read_rights[reader_role] return False def __init__(self, current_user: User): self._user = current_user def create_role(self) -> UserRoleInWorkspace: role = UserRoleInWorkspace() return role def _get_one_rsc(self, user_id, workspace_id): """ :param user_id: :param workspace_id: :return: a Query object, filtered query but without fetching the object. """ return DBSession.query(UserRoleInWorkspace).\ filter(UserRoleInWorkspace.workspace_id==workspace_id).\ filter(UserRoleInWorkspace.user_id==user_id) def get_one(self, user_id, workspace_id): return self._get_one_rsc(user_id, workspace_id).one() def create_one(self, user: User, workspace: Workspace, role_level: int, with_notif: bool, flush: bool = True) -> UserRoleInWorkspace: role = self.create_role() role.user_id = user.user_id role.workspace = workspace role.role = role_level if with_notif is not None: from tracim.lib.helpers import on_off_to_boolean role.do_notify = on_off_to_boolean(with_notif) if flush: DBSession.flush() return role def delete_one(self, user_id, workspace_id, flush=True): self._get_one_rsc(user_id, workspace_id).delete() if flush: DBSession.flush() def _get_all_for_user(self, user_id): return DBSession.query(UserRoleInWorkspace).filter( UserRoleInWorkspace.user_id == user_id) def get_all_for_user(self, user_id): return self._get_all_for_user(user_id).all() def get_all_for_user_order_by_workspace( self, user_id: int) -> UserRoleInWorkspace: return self._get_all_for_user(user_id).join( UserRoleInWorkspace.workspace).order_by(Workspace.label).all() def get_all_for_workspace(self, workspace_id): return DBSession.query(UserRoleInWorkspace).filter( UserRoleInWorkspace.workspace_id == workspace_id).all() def save(self, role: UserRoleInWorkspace): DBSession.flush() def get_roles_for_select_field(self): """ :return: list of DictLikeClass instances representing available Roles (to be used in select fields """ result = list() for role_id in UserRoleInWorkspace.get_all_role_values(): role = RoleType(role_id) result.append(role) return result