diff --git a/alembic/versions/c34f4b09ef18_star_a_project.py b/alembic/versions/c34f4b09ef18_star_a_project.py
new file mode 100644
index 0000000..dcaecd1
--- /dev/null
+++ b/alembic/versions/c34f4b09ef18_star_a_project.py
@@ -0,0 +1,51 @@
+"""star_a_project
+
+Revision ID: c34f4b09ef18
+Revises: 27a79ff0fb41
+Create Date: 2017-07-07 00:08:18.257075
+
+"""
+
+# revision identifiers, used by Alembic.
+revision = 'c34f4b09ef18'
+down_revision = '27a79ff0fb41'
+
+from alembic import op
+import sqlalchemy as sa
+
+
+def upgrade():
+ ''' Add a new table to store data about who starred which project '''
+ op.create_table(
+ 'stargazers',
+ sa.MetaData(),
+ sa.Column('id', sa.Integer, primary_key=True),
+ sa.Column(
+ 'project_id',
+ sa.Integer,
+ sa.ForeignKey(
+ 'projects.id', onupdate='CASCADE', ondelete='CASCADE'),
+ nullable=False
+ ),
+ sa.Column(
+ 'user_id',
+ sa.Integer,
+ sa.ForeignKey('users.id', onupdate='CASCADE', ondelete='CASCADE'),
+ nullable=False,
+ )
+ )
+
+ op.create_unique_constraint(
+ constraint_name='stargazers_project_id_user_id_key',
+ table_name='stargazers',
+ columns=['project_id', 'user_id']
+ )
+
+
+def downgrade():
+ ''' Remove the stargazers table from the database '''
+ op.drop_constraint(
+ constraint_name='stargazers_project_id_user_id_key',
+ table_name='stargazers'
+ )
+ op.drop_table('stargazers')
diff --git a/pagure/__init__.py b/pagure/__init__.py
index 0da5514..c58df7f 100644
--- a/pagure/__init__.py
+++ b/pagure/__init__.py
@@ -500,6 +500,9 @@ def set_variables():
flask.g.repo_forked = pagure.get_authorized_project(
SESSION, repo, user=flask.g.fas_user.username,
namespace=namespace)
+ flask.g.repo_starred = pagure.lib.has_starred(
+ SESSION, flask.g.repo, user=flask.g.fas_user.username,
+ )
if not flask.g.repo \
and APP.config.get('OLD_VIEW_COMMIT_ENABLED', False) \
diff --git a/pagure/lib/__init__.py b/pagure/lib/__init__.py
index db46b11..8e3d347 100644
--- a/pagure/lib/__init__.py
+++ b/pagure/lib/__init__.py
@@ -4359,3 +4359,101 @@ def get_pagination_metadata(flask_request, page, per_page, total):
'first': first_page,
'last': last_page
}
+
+
+def update_star_project(session, repo, star, user):
+ ''' Unset or set the star status depending on the star value.
+
+ :arg session: the session to use to connect to the database.
+ :arg repo: a model.Project object representing the project to star/unstar
+ :arg user: string representing the user
+ :return: string containg 'starred' or 'unstarred'
+ '''
+
+ if not all([repo, user, star]):
+ return
+ user_obj = get_user(session, user)
+ if star == '1':
+ msg = _star_project(
+ session,
+ repo=repo,
+ user=user_obj,
+ )
+ return msg
+ msg = _unstar_project(
+ session,
+ repo=repo,
+ user=user_obj,
+ )
+ return msg
+
+
+def _star_project(session, repo, user):
+ ''' Star a project
+
+ :arg session: Session object to connect to db with
+ :arg repo: model.Project object representing the repo to star
+ :arg user: model.User object who is starring this repo
+ '''
+
+ if not all([repo, user]):
+ return
+ stargazer_obj = model.Star(
+ project_id=repo.id,
+ user_id=user.id,
+ )
+ session.add(stargazer_obj)
+ return 'You liked this project!'
+
+
+def _unstar_project(session, repo, user):
+ ''' Unstar a project
+ :arg session: Session object to connect to db with
+ :arg repo: model.Project object representing the repo to star
+ :arg user: model.User object who is unstarring this repo
+ '''
+
+ if not all([repo, user]):
+ return
+ # First find the stargazer_obj object
+ stargazer_obj = _get_stargazer_obj(session, repo, user)
+ session.delete(stargazer_obj)
+ return 'You didn\'t like this project :('
+
+
+def _get_stargazer_obj(session, repo, user):
+ ''' Query the db to find stargazer object with given repo and user
+ :arg session: Session object to connect to db with
+ :arg repo: model.Project object representing the repo to star
+ :arg user: model.User object who is starring this repo
+ '''
+
+ if not all([repo, user]):
+ return
+ stargazer_obj = session.query(
+ model.Star,
+ ).filter(
+ model.Star.project_id == repo.id,
+ ).filter(
+ model.Star.user_id == user.id,
+ )
+
+ return stargazer_obj.first()
+
+
+def has_starred(session, repo, user):
+ ''' Check if a given user has starred a particular project
+
+ :arg session: The session object to query the db with
+ :arg repo: model.Project object for which the star is checked
+ :arg user: The username of the user in question
+ :type user: str
+ '''
+
+ if not all([repo, user]):
+ return
+ user_obj = get_user(session, user)
+ stargazer_obj = _get_stargazer_obj(session, repo, user_obj)
+ if isinstance(stargazer_obj, model.Star):
+ return True
+ return False
diff --git a/pagure/lib/model.py b/pagure/lib/model.py
index 15aacc0..15bfce2 100644
--- a/pagure/lib/model.py
+++ b/pagure/lib/model.py
@@ -2097,6 +2097,43 @@ class ProjectGroup(BASE):
__table_args__ = (sa.UniqueConstraint('project_id', 'group_id'),)
+class Star(BASE):
+ """ Stores users association with the all the projects which
+ they have starred
+
+ Table -- star
+ """
+
+ __tablename__ = 'stargazers'
+ __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
+ )
+ user = relation(
+ 'User', foreign_keys=[user_id], remote_side=[User.id],
+ backref=backref(
+ 'stars', cascade="delete, delete-orphan"
+ ),
+ )
+ project = relation(
+ 'Project', foreign_keys=[project_id], remote_side=[Project.id],
+ backref=backref(
+ 'stargazers', cascade="delete, delete-orphan",
+ ),
+ )
+
+
class Watcher(BASE):
""" Stores the user of a projects.
diff --git a/pagure/templates/master.html b/pagure/templates/master.html
index 24ea2cd..085c9a2 100644
--- a/pagure/templates/master.html
+++ b/pagure/templates/master.html
@@ -75,6 +75,11 @@
url_for('view_user_requests', username=g.fas_user.username)
}}">My Pull Requests
+ My Stars
+
+
Log Out
diff --git a/pagure/templates/repo_master.html b/pagure/templates/repo_master.html
index e109c7a..80c5e07 100644
--- a/pagure/templates/repo_master.html
+++ b/pagure/templates/repo_master.html
@@ -50,6 +50,46 @@
class="btn btn-success btn-sm">New Issue
{% endif %}
+{% endblock %}
diff --git a/pagure/templates/user_stars.html b/pagure/templates/user_stars.html
new file mode 100644
index 0000000..aec3a29
--- /dev/null
+++ b/pagure/templates/user_stars.html
@@ -0,0 +1,50 @@
+{% extends "master.html" %}
+
+{% block title %}My Starred Projects{% endblock %}
+
+{% block content %}
+
+
+
+ {{ repos | length }} Projects Starred by You
+
+
+
+
+
+ My Starred Projects |
+
+
+
+ {% for repo in repos %}
+
+
+ {% if repo.avatar_email %}
+
+ {{repo.fullname}}
+
+ {% else %}
+
+
+ {% endif %}
+ |
+
+ {% endfor %}
+
+
+
+
+
+{% endblock %}
diff --git a/pagure/ui/app.py b/pagure/ui/app.py
index 9e4f640..fae7d19 100644
--- a/pagure/ui/app.py
+++ b/pagure/ui/app.py
@@ -432,6 +432,26 @@ def view_user_issues(username):
)
+@APP.route('/user//stars/')
+@APP.route('/user//stars')
+def view_user_stars(username):
+ """
+ Shows the starred projects of the specified user.
+
+ :param username: The username whose stars we have to retrieve
+ :type username: str
+ """
+
+ user = _get_user(username=username)
+
+ return flask.render_template(
+ 'user_stars.html',
+ username=username,
+ user=user,
+ repos=[star.project for star in user.stars],
+ )
+
+
@APP.route('/new/', methods=('GET', 'POST'))
@APP.route('/new', methods=('GET', 'POST'))
@login_required
diff --git a/pagure/ui/repo.py b/pagure/ui/repo.py
index d09d3c3..e2d3acd 100644
--- a/pagure/ui/repo.py
+++ b/pagure/ui/repo.py
@@ -2263,6 +2263,53 @@ def view_project_activity(repo, namespace=None):
)
+@APP.route('//stargazers/')
+@APP.route('/fork///stargazers/')
+@APP.route('///stargazers/')
+@APP.route('/fork////stargazers/')
+def view_stargazers(repo, username=None, namespace=None):
+ ''' View all the users who have starred the project '''
+
+ stargazers = flask.g.repo.stargazers
+ users = [star.user for star in stargazers]
+ return flask.render_template(
+ 'repo_stargazers.html', repo=flask.g.repo, users=users)
+
+
+@APP.route('//star/', methods=["POST"])
+@APP.route('/fork///star/', methods=["POST"])
+@APP.route('///star/', methods=["POST"])
+@APP.route('/fork////star/', methods=["POST"])
+@login_required
+def star_project(repo, star, username=None, namespace=None):
+ ''' Star a project '''
+
+ return_point = flask.url_for('index')
+ if pagure.is_safe_url(flask.request.referrer):
+ return_point = flask.request.referrer
+
+ form = pagure.forms.ConfirmationForm()
+ if not form.validate_on_submit():
+ flask.abort(400)
+
+ if star not in ['0', '1']:
+ flask.abort(400)
+
+ try:
+ msg = pagure.lib.update_star_project(
+ SESSION,
+ user=flask.g.fas_user.username,
+ repo=flask.g.repo,
+ star=star,
+ )
+ SESSION.commit()
+ flask.flash(msg)
+ except SQLAlchemyError:
+ flask.flash('Could not star the project')
+
+ return flask.redirect(return_point)
+
+
@APP.route('//watch/settings/', methods=['POST'])
@APP.route('///watch/settings/', methods=['POST'])
@APP.route(