def on_auth_sign_up(user: auth.AbstractUser): # Set session notification router.session().add_success_message( lang.t('auth_ui@registration_form_success')) # Send a confirmation email to the user if auth.is_sign_up_confirmation_required(): msg = tpl.render( 'auth_ui@mail/{}/sign-up'.format(lang.get_current()), { 'user': user, 'confirm_url': router.rule_url('auth_ui@sign_up_confirm', {'code': user.confirmation_hash}) if not user.is_confirmed else None }) mail.Message(user.login, lang.t('auth_ui@confirm_registration'), msg).send() # Send a notification emails to admins if auth.is_sign_up_admins_notification_enabled(): for admin in auth.get_admin_users(): msg = tpl.render( 'auth_ui@mail/{}/sign-up-admin-notify'.format( lang.get_current()), { 'admin': admin, 'user': user, }) mail.Message(admin.login, lang.t('auth_ui@registration_admin_notify'), msg).send()
def odm_ui_widget_select_search_entities(self, f: odm.MultiModelFinder, args: dict): f.eq('language', args.get('language', lang.get_current())) query = args.get('q') if query: f.regex('title', '{}'.format(query), True)
def exec(self): reporter = auth.get_current_user() if reporter.is_anonymous: raise self.forbidden() model = self.arg('model') try: entity = _api.dispense(model, self.arg('uid')) except odm.error.EntityNotFound: raise self.not_found() tpl_name = 'content@mail/{}/abuse'.format(lang.get_current()) subject = lang.t('content@mail_subject_abuse') for recipient in auth.find_users( query.Query(query.Eq('status', 'active'))): if not entity.odm_auth_check_entity_permissions( [PERM_MODIFY, PERM_DELETE], recipient): continue body = tpl.render(tpl_name, { 'reporter': reporter, 'recipient': recipient, 'entity': entity }) mail.Message(entity.author.login, subject, body).send() return {'message': lang.t('content@abuse_receipt_confirm')}
def _on_submit(self): try: user = auth.get_user(self.val('login')) if user.status != auth.USER_STATUS_ACTIVE: return token = util.random_password(64, True) _RESET_TOKENS_POOL.put(token, user.login, _RESET_TOKEN_TTL) reset_url = router.rule_url('auth_ui_password@reset', {'token': token}) msg_body = tpl.render( 'auth_ui_password@mail/{}/reset-password'.format( lang.get_current()), { 'user': user, 'reset_url': reset_url }) mail.Message( user.login, lang.t('auth_ui_password@reset_password_mail_subject'), msg_body).send() router.session().add_info_message( lang.t('auth_ui_password@check_email_for_instructions')) except auth.error.UserNotFound: pass
def odm_ui_m_form_setup(self, frm: form.Form): """Hook """ if not self.is_new and self.has_field('language') and self.language != lang.get_current(): raise errors.NotFound('Entity for this language does not exist') frm.css += ' content-m-form'
def find(model: str, **kwargs) -> odm.SingleModelFinder: """Instantiate content entities finder """ check_publish_time = kwargs.get('check_publish_time', True) language = kwargs.get('language', lang.get_current()) status = kwargs.get('status', [CONTENT_STATUS_PUBLISHED]) if not is_model_registered(model): raise KeyError(f"Model '{model}' is not registered as content model") f = odm.find(model) mock = f.mock # type: Content # Publish time if mock.has_field('publish_time'): f.sort([('publish_time', odm.I_DESC)]) if check_publish_time: f.lte('publish_time', datetime.now()).no_cache('publish_time') else: f.sort([('_modified', odm.I_DESC)]) # Language if language != '*' and mock.has_field('language'): f.eq('language', language) # Status if status != '*' and mock.has_field('status'): if isinstance(status, str): status = [status] elif not isinstance(status, (list, tuple)): raise TypeError(f"'status' must be a string, list or tuple, not {type(status)}") f.inc('status', status) return f
def on_auth_user_status_change(user: auth.AbstractUser, status: str): if auth.is_user_status_change_notification_enabled(): msg = tpl.render( 'auth_ui@mail/{}/user-status-change'.format(lang.get_current()), { 'user': user, 'status': status, }) mail.Message(user.login, lang.t('auth_ui@user_status_change_notify'), msg).send()
def odm_http_api_get_entities(cls, finder: odm.SingleModelFinder, args: routing.ControllerArgs): """Called by 'odm_http_api@get_entities' route """ if 'search' in args: query = args['search'] if args.get('search_by') == 'title' and finder.mock.has_field('title'): finder.regex('title', query) else: finder.text(query, lang.get_current())
def dispense(model: str, title: str, alias: str = None, language: str = None, parent: Term = None) -> Term: """Dispense a new term or raise exception if term with specified alias already exists """ if alias and get(model, alias=alias, language=language): raise _error.TermExists(model, alias, language or lang.get_current()) term = odm.dispense(model) # type: Term term.title = title.strip() term.parent = parent if term.has_field('language'): term.language = language or lang.get_current() if term.has_field('alias') and alias: term.alias = alias or util.transform_str_2(title, language) return term
def find(content_language: str = None) -> _odm.Finder: """Get ODM finder for 'content_import' model. """ f = _odm.find('content_import') if content_language is None: f.eq('content_language', _lang.get_current()) elif content_language != '*': f.eq('content_language', content_language) return f
def _setup_fields(self): """Hook """ self.define_field(odm.field.String('title', is_required=True)) self.define_field(odm.field.String('alias', is_required=True)) self.define_field( odm.field.String('language', is_required=True, default=lang.get_current())) self.define_field(odm.field.Integer('weight')) self.define_field(odm.field.Integer('order')) self.define_field(file_storage_odm.field.Image('image'))
def _content_notify_author_status_change(self): """Notify content author about status change by another user """ if auth.get_current_user() == self.author: return m_subject = lang.t('content@content_status_change_mail_subject') m_body = tpl.render('content@mail/{}/content-status-change'.format(lang.get_current()), { 'entity': self, 'status': self.t('content_status_{}_{}'.format(self.model, self.status)), }) mail.Message(self.author.login, m_subject, m_body).send()
def __init__(self): super().__init__() self.define_option(console.option.Bool('short')) self.define_option(console.option.Bool('no-html')) self.define_option(console.option.Bool('no-tags')) self.define_option(console.option.Bool('no-sections')) self.define_option(console.option.Str('author')) self.define_option(console.option.Str('lang', default=lang.get_current())) self.define_option(console.option.PositiveInt('num', default=10)) self.define_option(console.option.PositiveInt('title-len', default=7)) self.define_option(console.option.PositiveInt('description-len', default=28)) self.define_option(console.option.PositiveInt('images', default=1))
def _content_notify_admins_waiting_status(self): """Notify administrators about waiting content """ if auth.get_current_user().is_admin or self.status != CONTENT_STATUS_WAITING: return for u in auth.get_admin_users(): m_subject = lang.t('content@content_waiting_mail_subject') m_body = tpl.render('content@mail/{}/waiting-content'.format(lang.get_current()), { 'user': u, 'entity': self, }) mail.Message(u.login, m_subject, m_body).send()
def on_comments_create_comment(comment: comments.model.AbstractComment): """comments.create_comment """ entity = _api.find_by_url(comment.thread_uid) if comment.is_reply or not entity or comment.author == entity.author: return tpl_name = 'content@mail/{}/comment'.format(lang.get_current()) subject = lang.t('content@mail_subject_new_comment') body = tpl.render(tpl_name, {'comment': comment, 'entity': entity}) m_from = '{} <{}>'.format(comment.author.first_last_name, mail.mail_from()[1]) mail.Message(entity.author.login, subject, body, m_from).send()
def _render_sidebar(sidebar: admin.SideBar) -> htmler.Aside: """Render admin's sidebar """ aside = htmler.Aside(css='main-sidebar') sidebar_section_em = htmler.Section(css='sidebar') aside.append_child(sidebar_section_em) root_menu_ul = htmler.Ul(css='sidebar-menu') sidebar_section_em.append_child(root_menu_ul) sections, menus = sidebar.items # Do actual rendering for section in sections: li = htmler.Li(lang.t(section['title']), css='header', data_section_weight=section['weight']) root_menu_ul.append_child(li) # Building top level menu item for menu in menus[section['sid']]: # Link a = htmler.A( href=router.url(menu['path'], lang=lang.get_current())) # Icon if menu['icon']: a.append_child(htmler.I(css=menu['icon'])) # Title a.append_child(htmler.Span(lang.t(menu['title']))) # Label if menu['label']: label_class = 'label pull-right label-' + menu[ 'label_class'] a.append_child( htmler.Span(lang.t(menu['label']), css=label_class)) # List element li = htmler.Li(data_menu_weight=menu['weight']) # Active state if menu['active']: li.set_attr('css', 'active') li.append_child(a) root_menu_ul.append_child(li) return aside
def comments_report_comment(uid: str): try: comment = comments.get_comment(uid, 'pytsite') except comments.error.CommentNotExist: return tpl_name = 'comments_odm@mail/{}/report'.format(lang.get_current()) m_subject = lang.t('comments_odm@mail_subject_report_comment') for user in auth.find_users(query.Query(query.Eq('status', 'active'))): if not user.has_permission('*****@*****.**'): continue m_body = tpl.render(tpl_name, {'comment': comment, 'recipient': user}) mail.Message(user.login, m_subject, m_body).send()
def odm_auth_permissions(self) -> List[str]: """Hook """ r = [PERM_CREATE, CONTENT_PERM_VIEW, PERM_MODIFY, PERM_DELETE, CONTENT_PERM_VIEW_OWN, PERM_MODIFY_OWN, PERM_DELETE_OWN] if self.has_field('status') and CONTENT_STATUS_WAITING in self.content_statuses(): r.append(CONTENT_PERM_BYPASS_MODERATION) if self.has_field('localization_' + lang.get_current()): r.append(CONTENT_PERM_SET_LOCALIZATION) if self.has_field('publish_time'): r.append(CONTENT_PERM_SET_PUBLISH_TIME) return r
def odm_ui_browser_setup(cls, browser): """Hook. :type browser: odm_ui._browser.Browser """ browser.default_sort_field = 'driver' browser.finder_adjust = lambda f: f.eq('content_language', _lang.get_current()) browser.data_fields = [ ('content_model', 'content_import@content_model'), ('driver', 'content_import@driver'), ('driver_opts', 'content_import@driver_opts'), ('content_section', 'content_import@content_section'), ('content_author', 'content_import@content_author'), ('enabled', 'content_import@enabled'), ('errors', 'content_import@errors'), ('paused_till', 'content_import@paused_till'), ]
def create_comment(thread_id: str, body: str, author: auth.model.AbstractUser, status: str = 'published', parent_uid: str = None, driver_name: str = None) -> _model.AbstractComment: """Create a new comment """ # Check min length if len(body) < get_comment_min_body_length(): raise _error.CommentTooShort(lang.t('comments@error_body_too_short')) # Check max length if len(body) > get_comment_max_body_length(): raise _error.CommentTooLong(lang.t('comments@error_body_too_long')) # Check status if status not in get_comment_statuses(): raise _error.InvalidCommentStatus( "'{}' is not a valid comment's status.".format(status)) # Load driver driver = get_driver(driver_name) # Create comment comment = driver.create_comment(thread_id, body, author, status, parent_uid) events.fire('comments@create_comment', comment=comment) # Send email notification about reply if reg.get('comments.email_notify', True) and comment.is_reply: parent_comment = get_comment(comment.parent_uid) if comment.author != parent_comment.author: tpl_name = 'comments@mail/{}/reply'.format(lang.get_current()) m_subject = lang.t('comments@mail_subject_new_reply') m_body = tpl.render( tpl_name, { 'reply': comment, 'comment': get_comment(comment.parent_uid, driver_name) }) m_from = '{} <{}>'.format(author.first_last_name, mail.mail_from()[1]) mail.Message(parent_comment.author.login, m_subject, m_body, m_from).send() return comment
def on_dispatch(): settings = reg.get('app') # Add meta tags for home page if settings and router.is_base_url(): lng = lang.get_current() for s_key in ['title', 'description', 'keywords']: s_full_key = 'home_{}_{}'.format(s_key, lng) if s_full_key in settings: s_val = settings[s_full_key] if isinstance(s_val, list): s_val = ','.join(s_val) metatag.t_set(s_key, s_val) if s_key in ['title', 'description']: metatag.t_set('og:' + s_key, s_val) metatag.t_set('twitter:' + s_key, s_val)
def _on_pre_save(self, **kwargs): """Hook """ super()._on_pre_save(**kwargs) c_user = auth.get_current_user() # Content must be reviewed by moderator if self.has_field('status'): sts = self.content_statuses() if CONTENT_STATUS_WAITING in sts and CONTENT_STATUS_PUBLISHED in sts \ and self.status == CONTENT_STATUS_PUBLISHED \ and not self.odm_auth_check_entity_permissions(CONTENT_PERM_BYPASS_MODERATION): self.f_set('status', CONTENT_STATUS_WAITING) # Language is required if not self.language or not self.f_get('language_db'): self.f_set('language', lang.get_current()) # Author is required if self.has_field('author') and self.get_field('author').is_required and not self.author: if not c_user.is_anonymous: self.f_set('author', c_user) else: raise RuntimeError('Cannot assign author, because current user is anonymous') # Extract inline images from the body if self.has_field('body') and self.has_field('images'): body, images = _extract_images(self) # If new images has been extracted if images: self.f_set('body', body) self.f_set('images', list(self.images) + images) # Extract inline videos from the body if self.has_field('body') and self.has_field('video_links'): body, video_links = _extract_video_links(self) # If new video links has been extracted if video_links: self.f_set('body', body) self.f_set('video_links', list(self.video_links) + video_links) events.fire('[email protected]_save', entity=self) events.fire('content@entity.{}.pre_save.'.format(self.model), entity=self)
def _sanitize_nickname(self, s: str) -> str: """Generate unique nickname. """ cnt = 0 s = util.transform_str_2(s[:32], lang.get_current()) nickname = s while True: try: user = auth.get_user(nickname=nickname) # If nickname of THIS user was not changed if user.nickname == self.f_get('nickname'): return s except auth.error.UserNotFound: return nickname cnt += 1 nickname = s + '-' + str(cnt)
def exec(self) -> dict: if not auth.get_current_user().is_admin: raise self.forbidden() e_type = self.arg('e_type') sort_order = -1 if self.arg('order') == 'desc' else 1 q = None total = 0 limit = self.arg('limit', 10) skip = self.arg('offset', 0) rows = [] if self.arg('search'): q = query.Query(query.Text(self.arg('search'), lang.get_current())) if e_type == 'role': total = auth.count_roles() - 2 # Minus admin and dev f = auth.find_roles(q, sort=[(self.arg('sort', 'name'), sort_order)], limit=limit, skip=skip) for role in f: if role.name in ('admin', 'dev'): continue rows.append(self._get_role_row(role)) elif e_type == 'user': total = auth.count_users() for user in auth.find_users(q, sort=[(self.arg('sort', 'login'), sort_order)], limit=limit, skip=skip): rows.append(self._get_user_row(user)) return { 'rows': rows, 'total': total, }
def find(model: str, language: str = None): """Get finder for the taxonomy model """ if not is_model_registered(model): raise RuntimeError( "Model '{}' is not registered as taxonomy model.".format(model)) f = odm.find(model) if f.mock.has_field('weight'): f.sort([('weight', odm.I_DESC)]) elif f.mock.has_field('order'): f.sort([('order', odm.I_ASC)]) if not language: f.eq('language', lang.get_current()) elif language != '*': f.eq('language', language) return f
def exec(self) -> dict: lng = _lang.get_current() email = _validation.rule.Email(value=self.arg('email')).validate() # Search for subscriber s = _odm.find('content_digest_subscriber').eq('email', email).eq( 'language', lng).first() # Create new subscriber if not s: s = _odm.dispense('content_digest_subscriber').f_set( 'email', email).f_set('language', lng).save() # Enable subscriber s.f_set('enabled', True).save() return { '__alert': _lang.t('content_digest@digest_subscription_success'), '__reset': True, }
def get(model: str, title: str = None, alias: str = None, language: str = None, exceptions: bool = False) -> Optional[Term]: """Find a term by title """ if not (title or alias): raise ValueError('At least title or alias argument must be specified') f = find(model, language) if title: f.regex('title', '^{}$'.format(title), True) if alias: f.eq('alias', alias) term = f.first() if not term and exceptions: raise _error.TermNotExist(model, alias, language or lang.get_current()) return term
def __init__(self, uid: str, **kwargs): """Init """ self._uid = uid self._inherit_cid = kwargs.get('inherit_cid', True) self._wrap_em = kwargs.get('wrap_em', htmler.Div()) # type: htmler.Element self._name = kwargs.get('name', uid) self._language = kwargs.get('language', lang.get_current()) self._weight = kwargs.get('weight', 0) self._default = kwargs.get('default') self._value = None # Wil be set later self._label = kwargs.get('label') self._title = kwargs.get('title') self._label_hidden = kwargs.get('label_hidden', False) self._label_disabled = kwargs.get('label_disabled', False) self._placeholder = kwargs.get('placeholder') self._css = kwargs.get('css', '') self._data = kwargs.get('data', {}) self._has_messages = kwargs.get('has_messages', True) self._has_success = kwargs.get('has_success', False) self._has_warning = kwargs.get('has_warning', False) self._has_error = kwargs.get('has_error', False) self._help = kwargs.get('help') self._h_size = kwargs.get('h_size') self._h_size_label = kwargs.get('h_size_label', False) self._h_size_row_css = kwargs.get('h_size_row_css', '') self._hidden = kwargs.get('hidden', False) self._rules = kwargs.get('rules', []) # type: List[validation.rule.Rule] self._form_area = kwargs.get('form_area', 'body') self._replaces = kwargs.get('replaces') self._required = kwargs.get('required', False) self._enabled = kwargs.get('enabled', True) self._parent = kwargs.get('parent') self._children = [] # type: List[Abstract] self._children_uids = [] # type: List[str] self._children_sep = kwargs.get('children_sep', '') self._last_children_weight = 0 self._form_group = kwargs.get('form_group', True) # Check validation rules if not isinstance(self._rules, (list, tuple)): self._rules = [self._rules] if isinstance(self._rules, tuple): self._rules = list(self._rules) for rule in self._rules: if not isinstance(rule, validation.rule.Rule): raise TypeError( 'Instance of pytsite.validation.rule.Base expected.') # Required if self.required: self.add_rule(validation.rule.NonEmpty()) # It is important to filter value through the setter-method if 'value' in kwargs: self.set_val(kwargs.get('value')) else: self.set_val(deepcopy(self._default)) # Process data-attributes for k, v in kwargs.items(): if k.startswith('data_'): self._data[k.replace('data_', '')] = v
def odm_ui_m_form_setup_widgets(self, frm: form.Form): """Hook """ # Parent from ._widget import TermSelect frm.add_widget( TermSelect( uid='_parent', model=self.model, exclude=self if not self.is_new else None, exclude_descendants=True, label=self.t('parent'), append_none_item=not self.get_field('_parent').is_required, value=self.parent, )) # Title if self.has_field('title'): frm.add_widget( widget.input.Text( uid='title', label=lang.t('taxonomy@title'), value=self.title, required=self.get_field('title').is_required, )) # Alias if self.has_field('alias'): frm.add_widget( widget.input.Text( uid='alias', label=lang.t('taxonomy@alias'), value=self.f_get('alias'), )) # Weight if self.has_field('weight'): frm.add_widget( widget.input.Integer(uid='weight', label=lang.t('taxonomy@weight'), value=self.weight, h_size='col-sm-3 col-md-2 col-lg-1')) # Image if self.has_field('image'): frm.add_widget( file_ui.widget.ImagesUpload( uid='image', label=lang.t('taxonomy@image'), required=self.get_field('image').is_required, value=self.image, )) # Language if self.has_field('language'): lng = lang.get_current() if self.is_new else self.language frm.add_widget( widget.static.Text( uid='language', label=lang.t('taxonomy@language'), text=lang.lang_title(lng), value=lng, hidden=len(lang.langs()) == 1, ))
def odm_ui_browser_setup_finder(self, finder: odm.SingleModelFinder, args: routing.ControllerArgs): super().odm_ui_browser_setup_finder(finder, args) finder.eq('language', lang.get_current())