def set_status(self, repo, status, user, comment, revision=None, pull_request=None, dont_allow_on_closed_pull_request=False): """ Creates new status for changeset or updates the old ones bumping their version, leaving the current status at the value of 'status'. :param repo: :param status: :param user: :param comment: :param revision: :param pull_request: :param dont_allow_on_closed_pull_request: don't allow a status change if last status was for pull request and it's closed. We shouldn't mess around this manually """ repo = Repository.guess_instance(repo) q = ChangesetStatus.query() if revision is not None: assert pull_request is None q = q.filter(ChangesetStatus.repo == repo) q = q.filter(ChangesetStatus.revision == revision) revisions = [revision] else: assert pull_request is not None pull_request = PullRequest.guess_instance(pull_request) repo = pull_request.org_repo q = q.filter(ChangesetStatus.repo == repo) q = q.filter(ChangesetStatus.revision.in_(pull_request.revisions)) revisions = pull_request.revisions cur_statuses = q.all() #if statuses exists and last is associated with a closed pull request # we need to check if we can allow this status change if (dont_allow_on_closed_pull_request and cur_statuses and getattr(cur_statuses[0].pull_request, 'status', '') == PullRequest.STATUS_CLOSED): raise StatusChangeOnClosedPullRequestError( 'Changing status on closed pull request is not allowed' ) #update all current statuses with older version for st in cur_statuses: st.version += 1 new_statuses = [] for rev in revisions: new_status = ChangesetStatus() new_status.version = 0 # default new_status.author = User.guess_instance(user) new_status.repo = Repository.guess_instance(repo) new_status.status = status new_status.comment = comment new_status.revision = rev new_status.pull_request = pull_request new_statuses.append(new_status) Session().add(new_status) return new_statuses
def set_status(self, repo, status, user, comment, revision=None, pull_request=None): """ Creates new status for changeset or updates the old ones bumping their version, leaving the current status at the value of 'status'. :param repo: :param status: :param user: :param comment: :param revision: :param pull_request: """ repo = Repository.guess_instance(repo) q = ChangesetStatus.query() if revision is not None: assert pull_request is None q = q.filter(ChangesetStatus.repo == repo) q = q.filter(ChangesetStatus.revision == revision) revisions = [revision] else: assert pull_request is not None pull_request = PullRequest.guess_instance(pull_request) repo = pull_request.org_repo q = q.filter(ChangesetStatus.repo == repo) q = q.filter(ChangesetStatus.revision.in_(pull_request.revisions)) revisions = pull_request.revisions cur_statuses = q.all() # update all current statuses with older version for st in cur_statuses: st.version += 1 new_statuses = [] for rev in revisions: new_status = ChangesetStatus() new_status.version = 0 # default new_status.author = User.guess_instance(user) new_status.repo = Repository.guess_instance(repo) new_status.status = status new_status.comment = comment new_status.revision = rev new_status.pull_request = pull_request new_statuses.append(new_status) Session().add(new_status) return new_statuses
def grant_user_group_permission(self, repo, group_name, perm): """ Grant permission for user group on given repository, or update existing one if found :param repo: Instance of Repository, repository_id, or repository name :param group_name: Instance of UserGroup, users_group_id, or user group name :param perm: Instance of Permission, or permission_name """ repo = Repository.guess_instance(repo) group_name = UserGroup.guess_instance(group_name) permission = Permission.guess_instance(perm) # check if we have that permission already obj = UserGroupRepoToPerm.query() \ .filter(UserGroupRepoToPerm.users_group == group_name) \ .filter(UserGroupRepoToPerm.repository == repo) \ .scalar() if obj is None: # create new obj = UserGroupRepoToPerm() Session().add(obj) obj.repository = repo obj.users_group = group_name obj.permission = permission log.debug('Granted perm %s to %s on %s', perm, group_name, repo) return obj
def grant_user_permission(self, repo, user, perm): """ Grant permission for user on given repository, or update existing one if found :param repo: Instance of Repository, repository_id, or repository name :param user: Instance of User, user_id or username :param perm: Instance of Permission, or permission_name """ user = User.guess_instance(user) repo = Repository.guess_instance(repo) permission = Permission.guess_instance(perm) # check if we have that permission already obj = UserRepoToPerm.query() \ .filter(UserRepoToPerm.user == user) \ .filter(UserRepoToPerm.repository == repo) \ .scalar() if obj is None: # create new ! obj = UserRepoToPerm() Session().add(obj) obj.repository = repo obj.user = user obj.permission = permission log.debug('Granted perm %s to %s on %s', perm, user, repo) return obj
def delete(self, repo, forks=None, fs_remove=True, cur_user=None): """ Delete given repository, forks parameter defines what do do with attached forks. Throws AttachedForksError if deleted repo has attached forks :param repo: :param forks: str 'delete' or 'detach' :param fs_remove: remove(archive) repo from filesystem """ if not cur_user: cur_user = getattr(get_current_authuser(), 'username', None) repo = Repository.guess_instance(repo) if repo is not None: if forks == 'detach': for r in repo.forks: r.fork = None elif forks == 'delete': for r in repo.forks: self.delete(r, forks='delete') elif [f for f in repo.forks]: raise AttachedForksError() old_repo_dict = repo.get_dict() try: Session().delete(repo) if fs_remove: self._delete_filesystem_repo(repo) else: log.debug('skipping removal from filesystem') log_delete_repository(old_repo_dict, deleted_by=cur_user) except Exception: log.error(traceback.format_exc()) raise
def create(self, repo_name): repo = c.db_repo try: _form = PullRequestForm(repo.repo_id)().to_python(request.POST) except formencode.Invalid as errors: log.error(traceback.format_exc()) log.error(str(errors)) msg = _('Error creating pull request: %s') % errors.msg h.flash(msg, 'error') raise HTTPBadRequest # heads up: org and other might seem backward here ... org_ref = _form[ 'org_ref'] # will have merge_rev as rev but symbolic name org_repo = Repository.guess_instance(_form['org_repo']) other_ref = _form[ 'other_ref'] # will have symbolic name and head revision other_repo = Repository.guess_instance(_form['other_repo']) reviewers = [] title = _form['pullrequest_title'] description = _form['pullrequest_desc'].strip() owner = User.get(request.authuser.user_id) try: cmd = CreatePullRequestAction(org_repo, other_repo, org_ref, other_ref, title, description, owner, reviewers) except CreatePullRequestAction.ValidationError as e: h.flash(e, category='error', logf=log.error) raise HTTPNotFound try: pull_request = cmd.execute() Session().commit() except Exception: h.flash(_('Error occurred while creating pull request'), category='error') log.error(traceback.format_exc()) raise HTTPFound( location=url('pullrequest_home', repo_name=repo_name)) h.flash(_('Successfully opened new pull request'), category='success') raise HTTPFound(location=pull_request.url())
def get_user_permission(self, repository, user): repository = Repository.guess_instance(repository) user = User.guess_instance(user) return UserRepoToPerm.query() \ .filter(UserRepoToPerm.user == user) \ .filter(UserRepoToPerm.repository == repository) \ .scalar()
def create(self, repo_name): repo = c.db_repo try: _form = PullRequestForm(repo.repo_id)().to_python(request.POST) except formencode.Invalid as errors: log.error(traceback.format_exc()) log.error(str(errors)) msg = _('Error creating pull request: %s') % errors.msg h.flash(msg, 'error') raise HTTPBadRequest # heads up: org and other might seem backward here ... org_ref = _form['org_ref'] # will have merge_rev as rev but symbolic name org_repo = Repository.guess_instance(_form['org_repo']) other_ref = _form['other_ref'] # will have symbolic name and head revision other_repo = Repository.guess_instance(_form['other_repo']) reviewers = [] title = _form['pullrequest_title'] description = _form['pullrequest_desc'].strip() owner = User.get(request.authuser.user_id) try: cmd = CreatePullRequestAction(org_repo, other_repo, org_ref, other_ref, title, description, owner, reviewers) except CreatePullRequestAction.ValidationError as e: h.flash(str(e), category='error', logf=log.error) raise HTTPNotFound try: pull_request = cmd.execute() Session().commit() except Exception: h.flash(_('Error occurred while creating pull request'), category='error') log.error(traceback.format_exc()) raise HTTPFound(location=url('pullrequest_home', repo_name=repo_name)) h.flash(_('Successfully opened new pull request'), category='success') raise HTTPFound(location=pull_request.url())
def update(self, repo, **kwargs): try: cur_repo = Repository.guess_instance(repo) org_repo_name = cur_repo.repo_name if 'owner' in kwargs: cur_repo.owner = User.get_by_username(kwargs['owner']) if 'repo_group' in kwargs: assert kwargs[ 'repo_group'] != u'-1', kwargs # RepoForm should have converted to None cur_repo.group = RepoGroup.get(kwargs['repo_group']) cur_repo.repo_name = cur_repo.get_new_name(cur_repo.just_name) log.debug('Updating repo %s with params:%s', cur_repo, kwargs) for k in [ 'repo_enable_downloads', 'repo_description', 'repo_enable_locking', 'repo_landing_rev', 'repo_private', 'repo_enable_statistics', ]: if k in kwargs: setattr(cur_repo, remove_prefix(k, 'repo_'), kwargs[k]) clone_uri = kwargs.get('clone_uri') if clone_uri is not None and clone_uri != cur_repo.clone_uri_hidden: cur_repo.clone_uri = clone_uri if 'repo_name' in kwargs: cur_repo.repo_name = cur_repo.get_new_name(kwargs['repo_name']) #if private flag is set, reset default permission to NONE if kwargs.get('repo_private'): EMPTY_PERM = 'repository.none' RepoModel().grant_user_permission(repo=cur_repo, user='******', perm=EMPTY_PERM) #handle extra fields for field in filter(lambda k: k.startswith(RepositoryField.PREFIX), kwargs): k = RepositoryField.un_prefix_key(field) ex_field = RepositoryField.get_by_key_name(key=k, repo=cur_repo) if ex_field: ex_field.field_value = kwargs[field] if org_repo_name != cur_repo.repo_name: # rename repository self._rename_filesystem_repo(old=org_repo_name, new=cur_repo.repo_name) return cur_repo except Exception: log.error(traceback.format_exc()) raise
def update(self, repo, **kwargs): try: cur_repo = Repository.guess_instance(repo) org_repo_name = cur_repo.repo_name if 'owner' in kwargs: cur_repo.owner = User.get_by_username(kwargs['owner']) if 'repo_group' in kwargs: assert kwargs['repo_group'] != u'-1', kwargs # RepoForm should have converted to None cur_repo.group = RepoGroup.get(kwargs['repo_group']) cur_repo.repo_name = cur_repo.get_new_name(cur_repo.just_name) log.debug('Updating repo %s with params:%s', cur_repo, kwargs) for k in ['repo_enable_downloads', 'repo_description', 'repo_enable_locking', 'repo_landing_rev', 'repo_private', 'repo_enable_statistics', ]: if k in kwargs: setattr(cur_repo, remove_prefix(k, 'repo_'), kwargs[k]) clone_uri = kwargs.get('clone_uri') if clone_uri is not None and clone_uri != cur_repo.clone_uri_hidden: cur_repo.clone_uri = clone_uri if 'repo_name' in kwargs: cur_repo.repo_name = cur_repo.get_new_name(kwargs['repo_name']) #if private flag is set, reset default permission to NONE if kwargs.get('repo_private'): EMPTY_PERM = 'repository.none' RepoModel().grant_user_permission( repo=cur_repo, user='******', perm=EMPTY_PERM ) #handle extra fields for field in filter(lambda k: k.startswith(RepositoryField.PREFIX), kwargs): k = RepositoryField.un_prefix_key(field) ex_field = RepositoryField.get_by_key_name(key=k, repo=cur_repo) if ex_field: ex_field.field_value = kwargs[field] if org_repo_name != cur_repo.repo_name: # rename repository self._rename_filesystem_repo(old=org_repo_name, new=cur_repo.repo_name) return cur_repo except Exception: log.error(traceback.format_exc()) raise
def delete_stats(self, repo_name): """ removes stats for given repo :param repo_name: """ repo = Repository.guess_instance(repo_name) try: obj = Statistics.query() \ .filter(Statistics.repository == repo).scalar() if obj is not None: Session().delete(obj) except Exception: log.error(traceback.format_exc()) raise
def revoke_user_group_permission(self, repo, group_name): """ Revoke permission for user group on given repository :param repo: Instance of Repository, repository_id, or repository name :param group_name: Instance of UserGroup, users_group_id, or user group name """ repo = Repository.guess_instance(repo) group_name = UserGroup.guess_instance(group_name) obj = UserGroupRepoToPerm.query() \ .filter(UserGroupRepoToPerm.repository == repo) \ .filter(UserGroupRepoToPerm.users_group == group_name) \ .scalar() if obj is not None: Session().delete(obj) log.debug('Revoked perm to %s on %s', repo, group_name)
def revoke_user_permission(self, repo, user): """ Revoke permission for user on given repository :param repo: Instance of Repository, repository_id, or repository name :param user: Instance of User, user_id or username """ user = User.guess_instance(user) repo = Repository.guess_instance(repo) obj = UserRepoToPerm.query() \ .filter(UserRepoToPerm.repository == repo) \ .filter(UserRepoToPerm.user == user) \ .scalar() if obj is not None: Session().delete(obj) log.debug('Revoked perm on %s on %s', repo, user)
def _get_status_query(self, repo, revision, pull_request, with_revisions=False): repo = Repository.guess_instance(repo) q = ChangesetStatus.query() \ .filter(ChangesetStatus.repo == repo) if not with_revisions: # only report the latest vote across all users! TODO: be smarter! q = q.filter(ChangesetStatus.version == 0) if revision: q = q.filter(ChangesetStatus.revision == revision) elif pull_request: pull_request = PullRequest.guess_instance(pull_request) q = q.filter(ChangesetStatus.pull_request == pull_request) else: raise Exception('Please specify revision or pull_request') q = q.order_by(ChangesetStatus.version.asc()) return q
def _create_repo(self, repo_name, repo_type, description, owner, private=False, clone_uri=None, repo_group=None, landing_rev='rev:tip', fork_of=None, copy_fork_permissions=False, enable_statistics=False, enable_locking=False, enable_downloads=False, copy_group_permissions=False, state=Repository.STATE_PENDING): """ Create repository inside database with PENDING state. This should only be executed by create() repo, with exception of importing existing repos. """ from kallithea.model.scm import ScmModel owner = User.guess_instance(owner) fork_of = Repository.guess_instance(fork_of) repo_group = RepoGroup.guess_instance(repo_group) try: repo_name = safe_unicode(repo_name) description = safe_unicode(description) # repo name is just a name of repository # while repo_name_full is a full qualified name that is combined # with name and path of group repo_name_full = repo_name repo_name = repo_name.split(self.URL_SEPARATOR)[-1] new_repo = Repository() new_repo.repo_state = state new_repo.enable_statistics = False new_repo.repo_name = repo_name_full new_repo.repo_type = repo_type new_repo.owner = owner new_repo.group = repo_group new_repo.description = description or repo_name new_repo.private = private new_repo.clone_uri = clone_uri new_repo.landing_rev = landing_rev new_repo.enable_statistics = enable_statistics new_repo.enable_locking = enable_locking new_repo.enable_downloads = enable_downloads if repo_group: new_repo.enable_locking = repo_group.enable_locking if fork_of: parent_repo = fork_of new_repo.fork = parent_repo Session().add(new_repo) if fork_of and copy_fork_permissions: repo = fork_of user_perms = UserRepoToPerm.query() \ .filter(UserRepoToPerm.repository == repo).all() group_perms = UserGroupRepoToPerm.query() \ .filter(UserGroupRepoToPerm.repository == repo).all() for perm in user_perms: UserRepoToPerm.create(perm.user, new_repo, perm.permission) for perm in group_perms: UserGroupRepoToPerm.create(perm.users_group, new_repo, perm.permission) elif repo_group and copy_group_permissions: user_perms = UserRepoGroupToPerm.query() \ .filter(UserRepoGroupToPerm.group == repo_group).all() group_perms = UserGroupRepoGroupToPerm.query() \ .filter(UserGroupRepoGroupToPerm.group == repo_group).all() for perm in user_perms: perm_name = perm.permission.permission_name.replace('group.', 'repository.') perm_obj = Permission.get_by_key(perm_name) UserRepoToPerm.create(perm.user, new_repo, perm_obj) for perm in group_perms: perm_name = perm.permission.permission_name.replace('group.', 'repository.') perm_obj = Permission.get_by_key(perm_name) UserGroupRepoToPerm.create(perm.users_group, new_repo, perm_obj) else: self._create_default_perms(new_repo, private) # now automatically start following this repository as owner ScmModel().toggle_following_repo(new_repo.repo_id, owner.user_id) # we need to flush here, in order to check if database won't # throw any exceptions, create filesystem dirs at the very end Session().flush() return new_repo except Exception: log.error(traceback.format_exc()) raise
def get_repo(self, repository): return Repository.guess_instance(repository)
def _create_repo(self, repo_name, repo_type, description, owner, private=False, clone_uri=None, repo_group=None, landing_rev='rev:tip', fork_of=None, copy_fork_permissions=False, enable_statistics=False, enable_locking=False, enable_downloads=False, copy_group_permissions=False, state=Repository.STATE_PENDING): """ Create repository inside database with PENDING state. This should only be executed by create() repo, with exception of importing existing repos. """ from kallithea.model.scm import ScmModel owner = User.guess_instance(owner) fork_of = Repository.guess_instance(fork_of) repo_group = RepoGroup.guess_instance(repo_group) try: repo_name = safe_unicode(repo_name) description = safe_unicode(description) # repo name is just a name of repository # while repo_name_full is a full qualified name that is combined # with name and path of group repo_name_full = repo_name repo_name = repo_name.split(self.URL_SEPARATOR)[-1] new_repo = Repository() new_repo.repo_state = state new_repo.enable_statistics = False new_repo.repo_name = repo_name_full new_repo.repo_type = repo_type new_repo.owner = owner new_repo.group = repo_group new_repo.description = description or repo_name new_repo.private = private new_repo.clone_uri = clone_uri new_repo.landing_rev = landing_rev new_repo.enable_statistics = enable_statistics new_repo.enable_locking = enable_locking new_repo.enable_downloads = enable_downloads if repo_group: new_repo.enable_locking = repo_group.enable_locking if fork_of: parent_repo = fork_of new_repo.fork = parent_repo Session().add(new_repo) if fork_of and copy_fork_permissions: repo = fork_of user_perms = UserRepoToPerm.query() \ .filter(UserRepoToPerm.repository == repo).all() group_perms = UserGroupRepoToPerm.query() \ .filter(UserGroupRepoToPerm.repository == repo).all() for perm in user_perms: UserRepoToPerm.create(perm.user, new_repo, perm.permission) for perm in group_perms: UserGroupRepoToPerm.create(perm.users_group, new_repo, perm.permission) elif repo_group and copy_group_permissions: user_perms = UserRepoGroupToPerm.query() \ .filter(UserRepoGroupToPerm.group == repo_group).all() group_perms = UserGroupRepoGroupToPerm.query() \ .filter(UserGroupRepoGroupToPerm.group == repo_group).all() for perm in user_perms: perm_name = perm.permission.permission_name.replace( 'group.', 'repository.') perm_obj = Permission.get_by_key(perm_name) UserRepoToPerm.create(perm.user, new_repo, perm_obj) for perm in group_perms: perm_name = perm.permission.permission_name.replace( 'group.', 'repository.') perm_obj = Permission.get_by_key(perm_name) UserGroupRepoToPerm.create(perm.users_group, new_repo, perm_obj) else: self._create_default_perms(new_repo, private) # now automatically start following this repository as owner ScmModel().toggle_following_repo(new_repo.repo_id, owner.user_id) # we need to flush here, in order to check if database won't # throw any exceptions, create filesystem dirs at the very end Session().flush() return new_repo except Exception: log.error(traceback.format_exc()) raise
def create_repo_fork(form_data, cur_user): """ Creates a fork of repository using interval VCS methods :param form_data: :param cur_user: """ from kallithea.model.repo import RepoModel DBS = celerylib.get_session() base_path = kallithea.CONFIG['base_path'] cur_user = User.guess_instance(cur_user) repo_name = form_data['repo_name'] # fork in this case repo_name_full = form_data['repo_name_full'] repo_type = form_data['repo_type'] owner = cur_user private = form_data['private'] clone_uri = form_data.get('clone_uri') repo_group = form_data['repo_group'] landing_rev = form_data['landing_rev'] copy_fork_permissions = form_data.get('copy_permissions') try: fork_of = Repository.guess_instance(form_data.get('fork_parent_id')) RepoModel()._create_repo(repo_name=repo_name_full, repo_type=repo_type, description=form_data['description'], owner=owner, private=private, clone_uri=clone_uri, repo_group=repo_group, landing_rev=landing_rev, fork_of=fork_of, copy_fork_permissions=copy_fork_permissions) action_logger(cur_user, 'user_forked_repo:%s' % repo_name_full, fork_of.repo_name, '') DBS.commit() source_repo_path = os.path.join(base_path, fork_of.repo_name) # now create this repo on Filesystem RepoModel()._create_filesystem_repo( repo_name=repo_name, repo_type=repo_type, repo_group=RepoGroup.guess_instance(repo_group), clone_uri=source_repo_path, ) repo = Repository.get_by_repo_name(repo_name_full) log_create_repository(repo.get_dict(), created_by=owner.username) # update repo changeset caches initially repo.update_changeset_cache() # set new created state repo.set_state(Repository.STATE_CREATED) DBS.commit() except Exception as e: log.warning('Exception %s occurred when forking repository, ' 'doing cleanup...' % e) # rollback things manually ! repo = Repository.get_by_repo_name(repo_name_full) if repo: Repository.delete(repo.repo_id) DBS.commit() RepoModel()._delete_filesystem_repo(repo) raise return True
def get_followers(self, repo): repo = Repository.guess_instance(repo) return UserFollowing.query() \ .filter(UserFollowing.follows_repository == repo).count()
def create(self, text, repo, author, revision=None, pull_request=None, f_path=None, line_no=None, status_change=None, closing_pr=False, send_email=True): """ Creates a new comment for either a changeset or a pull request. status_change and closing_pr is only for the optional email. Returns the created comment. """ if not status_change and not text: log.warning('Missing text for comment, skipping...') return None repo = Repository.guess_instance(repo) author = User.guess_instance(author) comment = ChangesetComment() comment.repo = repo comment.author = author comment.text = text comment.f_path = f_path comment.line_no = line_no if revision is not None: comment.revision = revision elif pull_request is not None: pull_request = PullRequest.guess_instance(pull_request) comment.pull_request = pull_request else: raise Exception('Please specify revision or pull_request_id') Session().add(comment) Session().flush() if send_email: (subj, body, recipients, notification_type, email_kwargs) = self._get_notification_data( repo, comment, author, comment_text=text, line_no=line_no, revision=revision, pull_request=pull_request, status_change=status_change, closing_pr=closing_pr) email_kwargs['is_mention'] = False # create notification objects, and emails NotificationModel().create( created_by=author, subject=subj, body=body, recipients=recipients, type_=notification_type, email_kwargs=email_kwargs, ) mention_recipients = extract_mentioned_users(body).difference( recipients) if mention_recipients: email_kwargs['is_mention'] = True subj = _('[Mention]') + ' ' + subj # FIXME: this subject is wrong and unused! NotificationModel().create(created_by=author, subject=subj, body=body, recipients=mention_recipients, type_=notification_type, email_kwargs=email_kwargs) return comment
def get_pull_requests(self, repo): repo = Repository.guess_instance(repo) return PullRequest.query() \ .filter(PullRequest.other_repo == repo) \ .filter(PullRequest.status != PullRequest.STATUS_CLOSED).count()
def get_forks(self, repo): repo = Repository.guess_instance(repo) return Repository.query() \ .filter(Repository.fork == repo).count()
def create(self, text, repo, author, revision=None, pull_request=None, f_path=None, line_no=None, status_change=None, closing_pr=False, send_email=True): """ Creates a new comment for either a changeset or a pull request. status_change and closing_pr is only for the optional email. Returns the created comment. """ if not status_change and not text: log.warning('Missing text for comment, skipping...') return None repo = Repository.guess_instance(repo) author = User.guess_instance(author) comment = ChangesetComment() comment.repo = repo comment.author = author comment.text = text comment.f_path = f_path comment.line_no = line_no if revision is not None: comment.revision = revision elif pull_request is not None: pull_request = PullRequest.guess_instance(pull_request) comment.pull_request = pull_request else: raise Exception('Please specify revision or pull_request_id') Session().add(comment) Session().flush() if send_email: (subj, body, recipients, notification_type, email_kwargs) = self._get_notification_data( repo, comment, author, comment_text=text, line_no=line_no, revision=revision, pull_request=pull_request, status_change=status_change, closing_pr=closing_pr) email_kwargs['is_mention'] = False # create notification objects, and emails NotificationModel().create( created_by=author, subject=subj, body=body, recipients=recipients, type_=notification_type, email_kwargs=email_kwargs, ) mention_recipients = extract_mentioned_users(body).difference(recipients) if mention_recipients: email_kwargs['is_mention'] = True subj = _('[Mention]') + ' ' + subj # FIXME: this subject is wrong and unused! NotificationModel().create( created_by=author, subject=subj, body=body, recipients=mention_recipients, type_=notification_type, email_kwargs=email_kwargs ) return comment