From 00f2c8d0e06401f435b0d0038fd02da71ae216f4 Mon Sep 17 00:00:00 2001 From: Gaurav Kumar Date: Jun 14 2016 13:11:20 +0000 Subject: Added watch feature. --- diff --git a/pagure/forms.py b/pagure/forms.py index a9d1e0a..3072838 100644 --- a/pagure/forms.py +++ b/pagure/forms.py @@ -20,6 +20,21 @@ PROJECT_NAME_REGEX = \ '^[a-zA-z0-9_][a-zA-Z0-9-_]*(/?[a-zA-z0-9_][a-zA-Z0-9-_]+)?$' +class WatchForm(wtf.Form): + ''' Form to update the watch status of a project for the requested user. ''' + repo_name = wtforms.TextField( + 'Repo name', + [wtforms.validators.Required()] + ) + repo_user = wtforms.TextField( + 'Repo User', + [wtforms.validators.optional()] + ) + watch = wtforms.TextField( + 'Watch', + [wtforms.validators.Required()] + ) + class ProjectFormSimplified(wtf.Form): ''' Form to edit the description of a project. ''' description = wtforms.TextField( diff --git a/pagure/lib/__init__.py b/pagure/lib/__init__.py index 8f36cd7..c00b940 100644 --- a/pagure/lib/__init__.py +++ b/pagure/lib/__init__.py @@ -2748,3 +2748,38 @@ def get_pull_request_of_user(session, username): ) return query.all() + +def update_watch_status(session, project, user, watch): + ''' Update the user status for watching a project. + ''' + user_obj = __get_user(session, user) + + if not user_obj: + raise pagure.exceptions.PagureException( + 'No user with username: %s' % user) + + watcher = session.query( + model.Watcher + ).filter( + sqlalchemy.and_( + model.Watcher.project_id == project.id, + model.Watcher.user_id == user_obj.id, + ) + ).first() + + if not watcher: + watcher = model.Watcher( + project_id=project.id, + user_id=user_obj.id, + watch=watch + ) + else: + watcher.watch = watch + + session.add(watcher) + session.flush() + + msg_success = 'From now you are watching this repo.' + if not int(watch): + msg_success = 'You are no longer watching this repo.' + return msg_success diff --git a/pagure/lib/model.py b/pagure/lib/model.py index 4ba212f..e4ec0a2 100644 --- a/pagure/lib/model.py +++ b/pagure/lib/model.py @@ -332,6 +332,10 @@ class Project(BASE): backref="projects", ) + unwatchers = relation("Watcher", + primaryjoin="and_(Project.id==Watcher.project_id, " + "Watcher.watch=='1')") + @property def path(self): ''' Return the name of the git repo on the filesystem. ''' @@ -482,6 +486,31 @@ class Project(BASE): return output + @property + def watchers_list(self): + ''' Return the list of username of users watching this project. + ''' + + watchers = set([self.user.user]) + for user in self.users: + if user.default_email: + watchers.add(user.user) + + for group in self.groups: + if group.creator.default_email: + watchers.add(group.creator.user) + for user in group.users: + if user.default_email: + watchers.add(user.default_email) + + for watcher in self.watchers: + if watcher.user.default_email and not watcher.watch: + watchers.delete(watcher.user.user) + if watcher.user.user not in watchers: + watchers.add(watcher.user.user) + + return watchers + class ProjectUser(BASE): """ Stores the user of a projects. @@ -1296,6 +1325,45 @@ class ProjectGroup(BASE): 'project_id', 'group_id'), ) +class Watcher(BASE): + """ Stores the user of a projects. + + Table -- watchers + """ + + __tablename__ = 'watchers' + __table_args__ = ( + sa.UniqueConstraint('project_id', 'user_id'), + ) + + id = sa.Column(sa.Integer, primary_key=True) + project_id = sa.Column( + sa.Integer, + sa.ForeignKey('projects.id', onupdate='CASCADE'), + nullable=False) + user_id = sa.Column( + sa.Integer, + sa.ForeignKey('users.id', onupdate='CASCADE'), + nullable=False, + index=True) + watch = sa.Column( + sa.Boolean, + nullable=False) + + user = relation( + 'User', foreign_keys=[user_id], remote_side=[User.id], + backref=backref( + 'watchers', cascade="delete, delete-orphan" + ), + ) + + project = relation( + 'Project', foreign_keys=[project_id], remote_side=[Project.id], + backref=backref( + 'watchers', cascade="delete, delete-orphan", + ), + ) + # # Class and tables specific for the API/token access # diff --git a/pagure/lib/notify.py b/pagure/lib/notify.py index 9e501d8..af58dc2 100644 --- a/pagure/lib/notify.py +++ b/pagure/lib/notify.py @@ -122,6 +122,11 @@ def _get_emails_for_issue(issue): if issue.assignee and issue.assignee.default_email: emails.add(issue.assignee.default_email) + # Remove the person list in unwatch + for unwatcher in issue.project.unwatchers: + if unwatcher.user.default_email in emails: + emails.delete(unwatcher.user.default_email) + # Drop the email used by pagure when sending emails = _clean_emails( emails, pagure.APP.config.get(pagure.APP.config.get( diff --git a/pagure/static/pagure.css b/pagure/static/pagure.css index ba55e94..7af4cd3 100644 --- a/pagure/static/pagure.css +++ b/pagure/static/pagure.css @@ -538,6 +538,7 @@ th[data-sort] { cursor: pointer; } +<<<<<<< 8e8c5a2fffb08f092e660188ae889367a5523ddd .pr-changes-description { font-size: 0.8em; @@ -560,4 +561,8 @@ th[data-sort] { } @media (min-width:1200px) { .pr_comment_form .card, .inline-pr-comment .card{max-width:680px} +======= +#watch_project a { + cursor: pointer; +>>>>>>> Added watch feature. } diff --git a/pagure/templates/repo_master.html b/pagure/templates/repo_master.html index b33fa8f..f319938 100644 --- a/pagure/templates/repo_master.html +++ b/pagure/templates/repo_master.html @@ -27,6 +27,61 @@ id="fork_project"> Fork {{ forkbuttonform.csrf_token }} +<<<<<<< 8e8c5a2fffb08f092e660188ae889367a5523ddd +======= + {% endif %} + + {% if authenticated %} +
+ + + +
+ {% endif %} + {% if branchname %} +
+ + + +>>>>>>> Added watch feature.
{% endif %} @@ -185,3 +240,25 @@ {% endblock %} {% endblock %} + +{% block jscripts %} +{{ super() }} + +{% endblock %} \ No newline at end of file diff --git a/pagure/ui/repo.py b/pagure/ui/repo.py index 79aa0d4..5f28240 100644 --- a/pagure/ui/repo.py +++ b/pagure/ui/repo.py @@ -1831,3 +1831,36 @@ def view_project_activity(repo): 'activity.html', repo=repo_obj, ) + +@APP.route('/watch', methods=['POST']) +@APP.route('/watch/', methods=['POST']) +def watch_repo(): + """ Marked for watching or Unwatching + """ + previous_url = flask.request.referrer + form = pagure.forms.WatchForm() + if form.validate_on_submit(): + watch = form.watch.data + repo_name = form.repo_name.data + repo_user = form.repo_user.data + username = flask.g.fas_user.username + if repo_user: + repo_obj = pagure.lib.get_project(SESSION, repo_name, repo_user) + else: + repo_obj = pagure.lib.get_project(SESSION, repo_name) + + if not repo_obj: + flask.abort(404, 'Project not found') + + try: + msg = pagure.lib.update_watch_status( + SESSION, repo_obj, + username, watch + ) + SESSION.commit() + flask.flash(msg) + except pagure.exceptions.PagureException as msg: + SESSION.rollback() + flask.flash(msg, 'error') + + return flask.redirect(previous_url) diff --git a/tests/test_pagure_flask_ui_repo.py b/tests/test_pagure_flask_ui_repo.py index 62f0462..9c2fcbd 100644 --- a/tests/test_pagure_flask_ui_repo.py +++ b/tests/test_pagure_flask_ui_repo.py @@ -2922,6 +2922,57 @@ index 0000000..fb7093d output = self.app.get('/foo/activity/') self.assertEqual(output.status_code, 404) + def test_watch_repo(self): + """ Test the watch_repo endpoint. """ + + output = self.app.get('/foo/watch') + self.assertEqual(output.status_code, 404) + tests.create_projects(self.session) + + user = tests.FakeUser() + user.username = 'pingou' + with tests.user_set(pagure.APP, user): + + output = self.app.get('/new/') + self.assertEqual(output.status_code, 200) + self.assertIn('Create new Project', output.data) + + csrf_token = output.data.split( + 'name="csrf_token" type="hidden" value="')[1].split('">')[0] + + data = { + 'csrf_token': csrf_token + } + data['repo_name'] = 'test' + data['watch'] = 1 + + output = self.app.post( + '/watch/', data=data) + self.assertEqual(output.status_code, 302) + repo = pagure.lib.get_project(self.session, 'test') + msg = pagure.lib.update_watch_status( + session=self.session, + project=repo, + user=user.username, + watch=data['watch'], + ) + self.session.commit() + self.assertEqual(msg, 'From now you are watching this repo.') + + data['watch'] = 0 + + output = self.app.post( + '/watch/', data=data) + self.assertEqual(output.status_code, 302) + repo = pagure.lib.get_project(self.session, 'test') + msg = pagure.lib.update_watch_status( + session=self.session, + project=repo, + user=user.username, + watch=data['watch'], + ) + self.session.commit() + self.assertEqual(msg, 'You are no longer watching this repo.') if __name__ == '__main__': SUITE = unittest.TestLoader().loadTestsFromTestCase(PagureFlaskRepotests)