code:
# -*- coding: utf-8 -*-

"""
 (c) 2014-2016 - Copyright Red Hat Inc

 Authors:
   Pierre-Yves Chibon <pingou@pingoured.fr>
   Farhaan Bukhsh <farhaan.bukhsh@gmail.com>

"""

import hashlib
import datetime
import urlparse
import bcrypt

import flask
from sqlalchemy.exc import SQLAlchemyError

import pagure.login_forms as forms
import pagure.lib
import pagure.lib.login
import pagure.lib.model as model
import pagure.lib.notify
from pagure import APP, SESSION, login_required
from pagure.lib.login import generate_hashed_value, check_password

# pylint: disable=E1101

@APP.route('/user/new/', methods=['GET', 'POST'])
@APP.route('/user/new', methods=['GET', 'POST'])
def new_user():
    """ Create a new user.
    """
    form = forms.NewUserForm()
    if form.validate_on_submit():

        username = form.user.data
        if pagure.lib.search_user(SESSION, username=username):
            flask.flash('Username already taken.', 'error')
            return flask.redirect(flask.request.url)

        email = form.email_address.data
        if pagure.lib.search_user(SESSION, email=email):
            flask.flash('Email address already taken.', 'error')
            return flask.redirect(flask.request.url)

        form.password.data = generate_hashed_value(form.password.data)

        token = pagure.lib.login.id_generator(40)

        user = model.User()
        user.token = token
        form.populate_obj(obj=user)
        user.default_email = form.email_address.data
        SESSION.add(user)
        SESSION.flush()

        emails = [email.email for email in user.emails]
        if form.email_address.data not in emails:
            useremail = model.UserEmail(
                user_id=user.id,
                email=form.email_address.data)
            SESSION.add(useremail)
            SESSION.flush()

        try:
            SESSION.commit()
            send_confirmation_email(user)
            flask.flash(
                'User created, please check your email to activate the '
                'account')
        except SQLAlchemyError as err:  # pragma: no cover
            SESSION.rollback()
            flask.flash('Could not create user.')
            APP.logger.debug('Could not create user.')
            APP.logger.exception(err)

        return flask.redirect(flask.url_for('auth_login'))

    return flask.render_template(
        'login/user_new.html',
        form=form,
    )

@APP.route('/dologin', methods=['POST'])
def do_login():
    """ Log the user in user.
    """
    form = forms.LoginForm()
    next_url = flask.request.args.get('next_url')
    if not next_url or next_url == 'None':
        next_url = flask.url_for('index')

    if form.validate_on_submit():
        username = form.username.data
        user_obj = pagure.lib.search_user(SESSION, username=username)
        if not user_obj:
            flask.flash('Username or password invalid.', 'error')
            return flask.redirect(flask.url_for('auth_login'))

        try:
            password_checks = check_password(
                form.password.data, user_obj.password,
                seed=APP.config.get('PASSWORD_SEED', None))
        except pagure.exceptions.PagureException as err:
            APP.logger.exception(err)
            flask.flash('Username or password of invalid format.', 'error')
            return flask.redirect(flask.url_for('auth_login'))

        if not password_checks:
            flask.flash('Username or password invalid.', 'error')
            return flask.redirect(flask.url_for('auth_login'))

        elif user_obj.token:
            flask.flash(
                'Invalid user, did you confirm the creation with the url '
                'provided by email?', 'error')
            return flask.redirect(flask.url_for('auth_login'))

        else:

            if not user_obj.password.startswith('$2$'):
                user_obj.password = generate_hashed_value(form.password.data)
                SESSION.add(user_obj)

            visit_key = pagure.lib.login.id_generator(40)
            now = datetime.datetime.utcnow()
            expiry = now + datetime.timedelta(days=30)
            session = model.PagureUserVisit(
                user_id=user_obj.id,
                user_ip=flask.request.remote_addr,
                visit_key=visit_key,
                expiry=expiry,
            )
            SESSION.add(session)
            try:
                SESSION.commit()
                flask.g.fas_user = user_obj
                flask.g.fas_session_id = visit_key
                flask.g.fas_user.login_time = now
                flask.flash('Welcome %s' % user_obj.username)
            except SQLAlchemyError as err:  # pragma: no cover
                flask.flash(
                    'Could not set the session in the db, '
                    'please report this error to an admin', 'error')
                APP.logger.exception(err)

        return flask.redirect(next_url)
    else:
        flask.flash('Insufficient information provided', 'error')
    return flask.redirect(flask.url_for('auth_login'))

@APP.route('/confirm/<token>/')
@APP.route('/confirm/<token>')
def confirm_user(token):
    """ Confirm a user account.
    """
    user_obj = pagure.lib.search_user(SESSION, token=token)
    if not user_obj:
        flask.flash('No user associated with this token.', 'error')
    else:
        user_obj.token = None
        SESSION.add(user_obj)

        try:
            SESSION.commit()
            flask.flash('Email confirmed, account activated')
            return flask.redirect(flask.url_for('auth_login'))
        except SQLAlchemyError as err:  # pragma: no cover
            flask.flash(
                'Could not set the account as active in the db, '
                'please report this error to an admin', 'error')
            APP.logger.exception(err)

    return flask.redirect(flask.url_for('index'))

@APP.route('/password/lost/', methods=['GET', 'POST'])
@APP.route('/password/lost', methods=['GET', 'POST'])
def lost_password():
    """ Method to allow a user to change his/her password assuming the email
    is not compromised.
    """
    form = forms.LostPasswordForm()
    if form.validate_on_submit():

        username = form.username.data
        user_obj = pagure.lib.search_user(SESSION, username=username)
        if not user_obj:
            flask.flash('Username invalid.', 'error')
            return flask.redirect(flask.url_for('auth_login'))
        elif user_obj.token:
            current_time = datetime.datetime.utcnow()
            invalid_period = user_obj.updated_on + \
                datetime.timedelta(minutes=3)
            if current_time < invalid_period:
                flask.flash('An email was sent to you less than 3 minutes ago, '
                            'did you check your spam folder? Otherwise, '
                            'try again after some time.', 'error')
                return flask.redirect(flask.url_for('auth_login'))

        token = pagure.lib.login.id_generator(40)
        user_obj.token = token
        SESSION.add(user_obj)

        try:
            SESSION.commit()
            send_lostpassword_email(user_obj)
            flask.flash(
                'Check your email to finish changing your password')
        except SQLAlchemyError as err:  # pragma: no cover
            SESSION.rollback()
            flask.flash(
                'Could not set the token allowing changing a password.',
                'error')
            APP.logger.debug('Password lost change - Error setting token.')
            APP.logger.exception(err)

        return flask.redirect(flask.url_for('auth_login'))

    return flask.render_template(
        'login/password_change.html',
        form=form,
    )

@APP.route('/password/reset/<token>/', methods=['GET', 'POST'])
@APP.route('/password/reset/<token>', methods=['GET', 'POST'])
def reset_password(token):
    """ Method to allow a user to reset his/her password.
    """
    form = forms.ResetPasswordForm()

    user_obj = pagure.lib.search_user(SESSION, token=token)
    if not user_obj:
        flask.flash('No user associated with this token.', 'error')
        return flask.redirect(flask.url_for('auth_login'))
    elif not user_obj.token:
        flask.flash(
            'Invalid user, this user never asked for a password change',
            'error')
        return flask.redirect(flask.url_for('auth_login'))

    if form.validate_on_submit():

        user_obj.password = generate_hashed_value(form.password.data)

        user_obj.token = None
        SESSION.add(user_obj)

        try:
            SESSION.commit()
            flask.flash(
                'Password changed')
        except SQLAlchemyError as err:  # pragma: no cover
            SESSION.rollback()
            flask.flash('Could not set the new password.', 'error')
            APP.logger.debug(
                'Password lost change - Error setting password.')
            APP.logger.exception(err)

        return flask.redirect(flask.url_for('auth_login'))

    return flask.render_template(
        'login/password_reset.html',
        form=form,
        token=token,
    )
#
# Methods specific to local login.
#

@APP.route('/password/change/', methods=['GET', 'POST'])
@APP.route('/password/change', methods=['GET', 'POST'])
@login_required
def change_password():
    """ Method to change the password for local auth users.
    """

    form = forms.ChangePasswordForm()
    user_obj = pagure.lib.search_user(
        SESSION, username=flask.g.fas_user.username)

    if not user_obj:
        flask.abort(404, 'User not found')

    if form.validate_on_submit():

        try:
            password_checks = check_password(
                form.old_password.data, user_obj.password,
                seed=APP.config.get('PASSWORD_SEED', None))
        except pagure.exceptions.PagureException as err:
            APP.logger.exception(err)
            flask.flash(
                'Could not update your password, either user or password '
                'could not be checked', 'error')
            return flask.redirect(flask.url_for('auth_login'))

        if password_checks:
            user_obj.password = generate_hashed_value(form.password.data)
            SESSION.add(user_obj)

        else:
            flask.flash(
                'Could not update your password, either user or password '
                'could not be checked', 'error')
            return flask.redirect(flask.url_for('auth_login'))

        try:
            SESSION.commit()
            flask.flash(
                'Password changed')
        except SQLAlchemyError as err:  # pragma: no cover
            SESSION.rollback()
            flask.flash('Could not set the new password.', 'error')
            APP.logger.debug(
                'Password change  - Error setting new password.')
            APP.logger.exception(err)

        return flask.redirect(flask.url_for('auth_login'))

    return flask.render_template(
        'login/password_recover.html',
        form=form,
    )

def send_confirmation_email(user):
    """ Sends the confirmation email asking the user to confirm its email
    address.
    """
    if not user.emails:
        return

    url = APP.config.get('APP_URL', flask.request.url_root)

    url = urlparse.urljoin(
        url or flask.request.url_root,
        flask.url_for('confirm_user', token=user.token),
    )

    message = """ Dear %(username)s,

Thank you for registering on pagure at %(url)s.

To finish your registration, please click on the following link or copy/paste
it in your browser:
  %(url)s

You account will not be activated until you finish this step.

Sincerely,
Your pagure admin.
""" % ({'username': user.username, 'url': url})

    pagure.lib.notify.send_email(
        text=message,
        subject='[Pagure] Confirm your user account',
        to_mail=user.emails[0].email,
    )

def send_lostpassword_email(user):
    """ Sends the email with the information on how to reset his/her password
    to the user.
    """
    if not user.emails:
        return

    url = APP.config.get('APP_URL', flask.request.url_root)

    url = urlparse.urljoin(
        url or flask.request.url_root,
        flask.url_for('reset_password', token=user.token),
    )

    message = """ Dear %(username)s,

The IP address %(ip)s has requested a password change for this account.

If you wish to change your password, please click on the following link or
copy/paste it in your browser:
  %(url)s

If you did not request this change, please inform an admin immediately!

Sincerely,
Your pagure admin.
""" % ({'username': user.username, 'url': url,
        'ip': flask.request.remote_addr})

    pagure.lib.notify.send_email(
        text=message,
        subject='[Pagure] Confirm your password change',
        to_mail=user.emails[0].email,
    )

def logout():
    """ Log the user out by expiring the user's session.
    """
    flask.g.fas_session_id = None
    flask.g.fas_user = None

def _check_session_cookie():
    """ Set the user into flask.g if the user is logged in.
    """
    cookie_name = APP.config.get('SESSION_COOKIE_NAME', 'pagure')
    cookie_name = '%s_local_cookie' % cookie_name
    session_id = None
    user = None
    login_time = None

    if cookie_name and cookie_name in flask.request.cookies:
        sessionid = flask.request.cookies.get(cookie_name)
        session = pagure.lib.login.get_session_by_visitkey(
            SESSION, sessionid)
        if session and session.user:
            now = datetime.datetime.now()
            if now > session.expiry:
                flask.flash('Session timed-out', 'error')
            elif APP.config.get('CHECK_SESSION_IP', True) \
                    and session.user_ip != flask.request.remote_addr:
                flask.flash('Session expired', 'error')
            else:
                new_expiry = now + datetime.timedelta(days=30)
                session_id = session.visit_key
                user = session.user
                login_time = session.created

                session.expiry = new_expiry
                SESSION.add(session)
                try:
                    SESSION.commit()
                except SQLAlchemyError as err:  # pragma: no cover
                    flask.flash(
                        'Could not prolong the session in the db, '
                        'please report this error to an admin', 'error')
                    APP.logger.exception(err)

    flask.g.fas_session_id = session_id
    flask.g.fas_user = user
    if user:
        flask.g.fas_user.login_time = login_time

def _send_session_cookie(response):
    """ Set the session cookie if the user is authenticated. """
    cookie_name = APP.config.get('SESSION_COOKIE_NAME', 'pagure')
    secure = APP.config.get('SESSION_COOKIE_SECURE', True)

    response.set_cookie(
        key='%s_local_cookie' % cookie_name,
        value=flask.g.get('fas_session_id') or '',
        secure=secure,
        httponly=True,
    )
    return response


code:
# -*- coding: utf-8 -*-

"""
 (c) 2014 - Copyright Red Hat Inc

 Authors:
   Pierre-Yves Chibon <pingou@pingoured.fr>

"""

import flask

from sqlalchemy.exc import SQLAlchemyError
from straight.plugin import load
from pagure.hooks import BaseHook

import pagure.exceptions
import pagure.lib
import pagure.forms
from pagure import APP, SESSION, login_required, is_repo_admin
from pagure.lib.model import BASE
from pagure.exceptions import FileNotFoundException
from pagure.hooks import jenkins_hook
from pagure.lib import model

try:
    from pagure.lib import pagure_ci
except ImportError:
    pagure_ci = None

import json
from kitchen.text.converters import to_bytes
from cryptography.hazmat.primitives import constant_time

# pylint: disable=E1101

def get_plugin_names(blacklist=None):
    ''' Return the list of plugins names. '''
    plugins = load('pagure.hooks', subclasses=BaseHook)
    if not blacklist:
        blacklist = []
    elif not isinstance(blacklist, list):
        blacklist = [blacklist]

    if pagure_ci is None and 'Pagure CI' not in blacklist:
        blacklist.append('Pagure CI')

    output = [
        plugin.name
        for plugin in plugins
        if plugin.name not in blacklist
    ]
    return sorted(output)

def get_plugin_tables():
    ''' Return the list of all plugins. '''
    plugins = load('pagure.hooks', subclasses=BASE)
    return plugins

def get_plugin(plugin_name):
    ''' Return the list of plugins names. '''
    plugins = load('pagure.hooks', subclasses=BaseHook)
    for plugin in plugins:
        if plugin.name == plugin_name:
            return plugin

@APP.route('/<repo:repo>/settings/<plugin>/', methods=('GET', 'POST'))
@APP.route('/<repo:repo>/settings/<plugin>', methods=('GET', 'POST'))
@APP.route('/<repo:repo>/settings/<plugin>/<int:full>/', methods=('GET', 'POST'))
@APP.route('/<repo:repo>/settings/<plugin>/<int:full>', methods=('GET', 'POST'))
@APP.route('/fork/<username>/<repo:repo>/settings/<plugin>/',
           methods=('GET', 'POST'))
@APP.route('/fork/<username>/<repo:repo>/settings/<plugin>',
           methods=('GET', 'POST'))
@APP.route('/fork/<username>/<repo:repo>/settings/<plugin>/<int:full>/',
           methods=('GET', 'POST'))
@APP.route('/fork/<username>/<repo:repo>/settings/<plugin>/<int:full>',
           methods=('GET', 'POST'))
@login_required
def view_plugin(repo, plugin, username=None, full=True):
    """ Presents the settings of the project.
    """
    repo = pagure.lib.get_project(SESSION, repo, user=username)

    if not repo:
        flask.abort(404, 'Project not found')

    if not is_repo_admin(repo):
        flask.abort(
            403,
            'You are not allowed to change the settings for this project')

    if plugin in APP.config.get('DISABLED_PLUGINS', []):
        flask.abort(404, 'Plugin disabled')

    plugin = get_plugin(plugin)
    fields = []
    new = True
    dbobj = plugin.db_object()
    pagure_ci_token = None

    if hasattr(repo, plugin.backref):
        dbobj = getattr(repo, plugin.backref)

        # There should always be only one, but let's double check
        if dbobj and len(dbobj) > 0:
            dbobj = dbobj[0]
            new = False
            # To populate the pagure CI token if generated
            if hasattr(dbobj, "pagure_ci_token") and plugin.backref == "hook_pagure_ci":
                pagure_ci_token = dbobj.pagure_ci_token
        else:
            dbobj = plugin.db_object()

    form = plugin.form(obj=dbobj)
    for field in plugin.form_fields:
        fields.append(getattr(form, field))

    if form.validate_on_submit():
        form.populate_obj(obj=dbobj)

        if new:
            dbobj.project_id = repo.id
            SESSION.add(dbobj)
        try:
            SESSION.flush()
        except SQLAlchemyError as err:  # pragma: no cover
            SESSION.rollback()
            APP.logger.debug('Could not add plugin %s', plugin.name)
            APP.logger.exception(err)
            flask.flash(
                'Could not add plugin %s, please contact an admin'
                % plugin.name)

            return flask.render_template(
                'plugin.html',
                select='settings',
                full=full,
                repo=repo,
                username=username,
                plugin=plugin,
                form=form,
                pagure_ci_token=pagure_ci_token,
                fields=fields)

        if form.active.data:
            # Set up the main script if necessary
            plugin.set_up(repo)
            # Install the plugin itself
            try:
                plugin.install(repo, dbobj)
                flask.flash('Hook %s activated' % plugin.name)
            except FileNotFoundException as err:
                pagure.APP.logger.exception(err)
                flask.abort(404, 'No git repo found')
        else:
            try:
                plugin.remove(repo)
                flask.flash('Hook %s inactived' % plugin.name)
            except FileNotFoundException as err:
                pagure.APP.logger.exception(err)
                flask.abort(404, 'No git repo found')

        SESSION.commit()

        return flask.redirect(flask.url_for(
            'view_settings', repo=repo.name, username=username))

    return flask.render_template(
        'plugin.html',
        select='settings',
        full=full,
        repo=repo,
        username=username,
        plugin=plugin,
        form=form,
        pagure_ci_token=pagure_ci_token,
        fields=fields)

if pagure_ci is not None:
    @APP.route('/hooks/<pagure_ci_token>/build-finished', methods=['POST'])
    def hook_finished(pagure_ci_token):
        """ Flags the Pull-request after getting notification from Jenkins
        """

        try:
            data = json.loads(flask.request.get_data())
            cfg = jenkins_hook.get_configs(
                data['name'], jenkins_hook.Service.JENKINS)[0]
            build_id = data['build']['number']

            if not constant_time.bytes_eq(
                      to_bytes(pagure_ci_token), to_bytes(cfg.pagure_ci_token)):
                return ('Token mismatch', 401)

        except (TypeError, ValueError, KeyError, jenkins_hook.ConfigNotFound) as exc:
            APP.logger.error('Error processing jenkins notification', exc_info=exc)
            flask.abort(400, "Bad Request")

        APP.logger.info('Received jenkins notification')
        pagure_ci.process_build(APP.logger, cfg, build_id)
        return ('', 204)

repo_name: bill/auger

license: mit


code:
# -*- coding: utf-8 -*-

"""
 (c) 2014-2016 - Copyright Red Hat Inc

 Authors:
   Pierre-Yves Chibon <pingou@pingoured.fr>

"""

import datetime
import shutil
import os
from math import ceil

import flask
import pygit2
import kitchen.text.converters as ktc
import werkzeug

from cStringIO import StringIO
from PIL import Image
from pygments import highlight
from pygments.formatters import HtmlFormatter
from pygments.lexers import guess_lexer_for_filename
from pygments.lexers.special import TextLexer
from pygments.util import ClassNotFound
from sqlalchemy.exc import SQLAlchemyError

import mimetypes
import chardet

from binaryornot.helpers import is_binary_string

import pagure.exceptions
import pagure.lib
import pagure.lib.git
import pagure.forms
import pagure
import pagure.ui.plugins
from pagure import (APP, SESSION, LOG, __get_file_in_tree, login_required,
                    is_repo_admin, admin_session_timedout, authenticated)

# pylint: disable=E1101

@APP.route('/<repo:repo>.git')
@APP.route('/fork/<username>/<repo:repo>.git')
def view_repo_git(repo, username=None):
    ''' Redirect to the project index page when user wants to view
    the git repo of the project
    '''
    return flask.redirect(flask.url_for('view_repo',repo=repo,username=username))

@APP.route('/<repo:repo>/')
@APP.route('/<repo:repo>')
@APP.route('/fork/<username>/<repo:repo>/')
@APP.route('/fork/<username>/<repo:repo>')
def view_repo(repo, username=None):
    """ Front page of a specific repo.
    """
    repo = pagure.lib.get_project(SESSION, repo, user=username)

    if repo is None:
        flask.abort(404, 'Project not found')

    reponame = pagure.get_repo_path(repo)

    repo_obj = pygit2.Repository(reponame)

    if not repo_obj.is_empty and not repo_obj.head_is_unborn:
        head = repo_obj.head.shorthand
    else:
        head = None

    cnt = 0
    last_commits = []
    tree = []
    if not repo_obj.is_empty:
        try:
            for commit in repo_obj.walk(
                    repo_obj.head.target, pygit2.GIT_SORT_TIME):
                last_commits.append(commit)
                cnt += 1
                if cnt == 3:
                    break
            tree = sorted(last_commits[0].tree, key=lambda x: x.filemode)
        except pygit2.GitError:
            pass

    readme = None
    safe = False

    if not repo_obj.is_empty and not repo_obj.head_is_unborn:
        branchname = repo_obj.head.shorthand
    else:
        branchname = None
    for i in tree:
        name, ext = os.path.splitext(i.name)
        if name == 'README':
            content = __get_file_in_tree(
                repo_obj, last_commits[0].tree, [i.name]).data

            readme, safe = pagure.doc_utils.convert_readme(
                content, ext,
                view_file_url=flask.url_for(
                    'view_raw_file', username=username,
                    repo=repo.name, identifier=branchname, filename=''))

    diff_commits = []
    if repo.is_fork:
        parentname = os.path.join(
            APP.config['GIT_FOLDER'], repo.parent.path)
    else:
        parentname = os.path.join(APP.config['GIT_FOLDER'], repo.path)

    orig_repo = pygit2.Repository(parentname)

    if not repo_obj.is_empty and not orig_repo.is_empty:

        orig_branch = orig_repo.lookup_branch('master')
        branch = repo_obj.lookup_branch('master')
        if orig_branch and branch:

            master_commits = [
                commit.oid.hex
                for commit in orig_repo.walk(
                    orig_branch.get_object().hex,
                    pygit2.GIT_SORT_TIME)
            ]

            repo_commit = repo_obj[branch.get_object().hex]

            for commit in repo_obj.walk(
                    repo_commit.oid.hex, pygit2.GIT_SORT_TIME):
                if commit.oid.hex in master_commits:
                    break
                diff_commits.append(commit.oid.hex)

    return flask.render_template(
        'repo_info.html',
        select='overview',
        repo=repo,
        repo_obj=repo_obj,
        username=username,
        head=head,
        readme=readme,
        safe=safe,
        origin='view_repo',
        branches=sorted(repo_obj.listall_branches()),
        branchname=branchname,
        last_commits=last_commits,
        tree=tree,
        diff_commits=diff_commits,
        repo_admin=is_repo_admin(repo),
        form=pagure.forms.ConfirmationForm(),
    )

@APP.route('/<repo:repo>/branch/<path:branchname>')
@APP.route('/fork/<username>/<repo:repo>/branch/<path:branchname>')
def view_repo_branch(repo, branchname, username=None):
    ''' Returns the list of branches in the repo. '''

    repo = pagure.lib.get_project(SESSION, repo, user=username)

    if not repo:
        flask.abort(404, 'Project not found')

    reponame = pagure.get_repo_path(repo)

    repo_obj = pygit2.Repository(reponame)

    if branchname not in repo_obj.listall_branches():
        flask.abort(404, 'Branch no found')

    branch = repo_obj.lookup_branch(branchname)
    if not repo_obj.is_empty and not repo_obj.head_is_unborn:
        head = repo_obj.head.shorthand
    else:
        head = None
    cnt = 0
    last_commits = []
    for commit in repo_obj.walk(branch.get_object().hex, pygit2.GIT_SORT_TIME):
        last_commits.append(commit)
        cnt += 1
        if cnt == 3:
            break

    diff_commits = []

    if repo.is_fork:
        parentname = os.path.join(
            APP.config['GIT_FOLDER'], repo.parent.path)
    else:
        parentname = os.path.join(APP.config['GIT_FOLDER'], repo.path)

    orig_repo = pygit2.Repository(parentname)

    tree = None
    safe=False
    readme = None
    if not repo_obj.is_empty and not orig_repo.is_empty:

        if not orig_repo.head_is_unborn:
            compare_branch = orig_repo.lookup_branch(orig_repo.head.shorthand)
        else:
            compare_branch = None

        compare_commits = []

        if compare_branch:
            compare_commits = [
                commit.oid.hex
                for commit in orig_repo.walk(
                    compare_branch.get_object().hex,
                    pygit2.GIT_SORT_TIME)
            ]

        repo_commit = repo_obj[branch.get_object().hex]

        for commit in repo_obj.walk(
                repo_commit.oid.hex, pygit2.GIT_SORT_TIME):
            if commit.oid.hex in compare_commits:
                break
            diff_commits.append(commit.oid.hex)

        tree=sorted(last_commits[0].tree, key=lambda x: x.filemode)
        for i in tree:
            name, ext = os.path.splitext(i.name)
            if name == 'README':
                content = __get_file_in_tree(
                    repo_obj, last_commits[0].tree, [i.name]).data

                readme, safe = pagure.doc_utils.convert_readme(
                    content, ext,
                    view_file_url=flask.url_for(
                        'view_raw_file', username=username,
                        repo=repo.name, identifier=branchname, filename=''))

    return flask.render_template(
        'repo_info.html',
        select='overview',
        repo=repo,
        head=head,
        username=username,
        branches=sorted(repo_obj.listall_branches()),
        branchname=branchname,
        origin='view_repo_branch',
        last_commits=last_commits,
        tree=tree,
        safe=safe,
        readme=readme,
        diff_commits=diff_commits,
        repo_admin=is_repo_admin(repo),
        form=pagure.forms.ConfirmationForm(),
    )

@APP.route('/<repo:repo>/commits/')
@APP.route('/<repo:repo>/commits')
@APP.route('/<repo:repo>/commits/<path:branchname>')
@APP.route('/fork/<username>/<repo:repo>/commits/')
@APP.route('/fork/<username>/<repo:repo>/commits')
@APP.route('/fork/<username>/<repo:repo>/commits/<path:branchname>')
def view_commits(repo, branchname=None, username=None):
    """ Displays the commits of the specified repo.
    """
    repo = pagure.lib.get_project(SESSION, repo, user=username)

    if not repo:
        flask.abort(404, 'Project not found')

    reponame = pagure.get_repo_path(repo)

    repo_obj = pygit2.Repository(reponame)

    if branchname and branchname not in repo_obj.listall_branches():
        flask.abort(404, 'Branch no found')

    if branchname:
        branch = repo_obj.lookup_branch(branchname)
    elif not repo_obj.is_empty and not repo_obj.head_is_unborn:
        branch = repo_obj.lookup_branch(repo_obj.head.shorthand)
        branchname = branch.branch_name
    else:
        branch = None

    if not repo_obj.is_empty and not repo_obj.head_is_unborn:
        head = repo_obj.head.shorthand
    else:
        head = None

    try:
        page = int(flask.request.args.get('page', 1))
    except ValueError:
        page = 1

    limit = APP.config['ITEM_PER_PAGE']
    start = limit * (page - 1)
    end = limit * page

    n_commits = 0
    last_commits = []
    if branch:
        for commit in repo_obj.walk(
                branch.get_object().hex, pygit2.GIT_SORT_TIME):
            if n_commits >= start and n_commits <= end:
                last_commits.append(commit)
            n_commits += 1

    total_page = int(ceil(n_commits / float(limit)))

    diff_commits = []
    diff_commits_full = []
    if repo.is_fork:
        parentname = os.path.join(
            APP.config['GIT_FOLDER'], repo.parent.path)
    else:
        parentname = os.path.join(APP.config['GIT_FOLDER'], repo.path)

    orig_repo = pygit2.Repository(parentname)

    if not repo_obj.is_empty and not orig_repo.is_empty \
            and repo_obj.listall_branches() > 1:

        if not orig_repo.head_is_unborn:
            compare_branch = orig_repo.lookup_branch(
                orig_repo.head.shorthand)
        else:
            compare_branch = None

        compare_commits = []

        if compare_branch:
            compare_commits = [
                commit.oid.hex
                for commit in orig_repo.walk(
                    compare_branch.get_object().hex,
                    pygit2.GIT_SORT_TIME)
            ]

        if branch:
            repo_commit = repo_obj[branch.get_object().hex]

            for commit in repo_obj.walk(
                    repo_commit.oid.hex, pygit2.GIT_SORT_TIME):
                if commit.oid.hex in compare_commits:
                    break
                diff_commits.append(commit.oid.hex)
                diff_commits_full.append(commit)

    return flask.render_template(
        'commits.html',
        select='commits',
        origin='view_commits',
        repo_obj=repo_obj,
        repo=repo,
        username=username,
        head=head,
        branches=sorted(repo_obj.listall_branches()),
        branchname=branchname,
        last_commits=last_commits,
        diff_commits=diff_commits,
        diff_commits_full=diff_commits_full,
        number_of_commits=n_commits,
        page=page,
        total_page=total_page,
        repo_admin=is_repo_admin(repo),
        form=pagure.forms.ConfirmationForm(),
    )

@APP.route('/<repo>/c/<commit1>..<commit2>/')
@APP.route('/<repo>/c/<commit1>..<commit2>')
@APP.route('/fork/<username>/<repo>/c/<commit1>..<commit2>/')
@APP.route('/fork/<username>/<repo>/c/<commit1>..<commit2>')
def compare_commits(repo, commit1, commit2, username=None):
    """ Compares two commits for specified repo
    """
    repo = pagure.lib.get_project(SESSION, repo, user=username)

    if not repo:
        flask.abort(404, 'Project not found')

    reponame = pagure.get_repo_path(repo)

    repo_obj = pygit2.Repository(reponame)

    if not repo_obj.is_empty and not repo_obj.head_is_unborn:
        head = repo_obj.head.shorthand
    else:
        head = None

    # Check commit1 and commit2 existence
    commit1_obj = repo_obj.get(commit1)
    commit2_obj = repo_obj.get(commit2)
    if commit1_obj is None:
        flask.abort(404, 'First commit does not exist')
    if commit2_obj is None:
        flask.abort(404, 'Last commit does not exist')

    # Get commits diff data
    diff = repo_obj.diff(commit1, commit2)

    # Get commits list
    diff_commits = []
    order = pygit2.GIT_SORT_TIME
    first_commit = commit1
    last_commit = commit2

    commits = map(lambda x: x.oid.hex[:len(first_commit)],
                  repo_obj.walk(last_commit, pygit2.GIT_SORT_TIME))

    if first_commit not in commits:
        first_commit = commit2
        last_commit = commit1

    for commit in repo_obj.walk(last_commit, order):
        diff_commits.append(commit)

        if commit.oid.hex == first_commit \
                or commit.oid.hex.startswith(first_commit):
            break

    if first_commit == commit2:
        diff_commits.reverse()

    return flask.render_template(
        'pull_request.html',
        select='logs',
        origin='compare_commits',
        repo=repo,
        username=username,
        head=head,
        commit1=commit1,
        commit2=commit2,
        diff=diff,
        diff_commits=diff_commits,
        branches=sorted(repo_obj.listall_branches()),
        repo_admin=is_repo_admin(repo),
    )

@APP.route('/<repo:repo>/blob/<path:identifier>/f/<path:filename>')
@APP.route('/fork/<username>/<repo:repo>/blob/<path:identifier>/f/<path:filename>')
def view_file(repo, identifier, filename, username=None):
    """ Displays the content of a file or a tree for the specified repo.
    """
    repo = pagure.lib.get_project(SESSION, repo, user=username)

    if not repo:
        flask.abort(404, 'Project not found')

    reponame = pagure.get_repo_path(repo)

    repo_obj = pygit2.Repository(reponame)

    if repo_obj.is_empty:
        flask.abort(404, 'Empty repo cannot have a file')

    if identifier in repo_obj.listall_branches():
        branchname = identifier
        branch = repo_obj.lookup_branch(identifier)
        commit = branch.get_object()
    else:
        try:
            commit = repo_obj.get(identifier)
            branchname = identifier
        except ValueError:
            if 'master' not in repo_obj.listall_branches():
                flask.abort(404, 'Branch no found')
            # If it's not a commit id then it's part of the filename
            commit = repo_obj[repo_obj.head.target]
            branchname = 'master'

    if isinstance(commit, pygit2.Tag):
        commit = commit.get_object()

    if commit and not isinstance(commit, pygit2.Blob):
        content = __get_file_in_tree(
            repo_obj, commit.tree, filename.split('/'), bail_on_tree=True)
        if not content:
            flask.abort(404, 'File not found')
        content = repo_obj[content.oid]
    else:
        content = commit

    if not content:
        flask.abort(404, 'File not found')

    encoding = None
    readme = None
    safe = False
    readme_ext = None

    if isinstance(content, pygit2.Blob):
        rawtext = str(flask.request.args.get('text')).lower() in ['1', 'true']
        ext = filename[filename.rfind('.'):]
        if ext in (
                '.gif', '.png', '.bmp', '.tif', '.tiff', '.jpg',
                '.jpeg', '.ppm', '.pnm', '.pbm', '.pgm', '.webp', '.ico'):
            try:
                Image.open(StringIO(content.data))
                output_type = 'image'
            except IOError as err:
                LOG.debug(
                    'Failed to load image %s, error: %s', filename, err
                )
                output_type = 'binary'
        elif ext in ('.rst', '.mk', '.md', '.markdown') and not rawtext:
            content, safe = pagure.doc_utils.convert_readme(content.data, ext)
            output_type = 'markup'
        elif not is_binary_string(content.data):
            encoding = chardet.detect(ktc.to_bytes(content.data))['encoding']
            file_content = content.data
            try:
                lexer = guess_lexer_for_filename(
                    filename,
                    file_content
                )
            except (ClassNotFound, TypeError):
                lexer = TextLexer()

            content = highlight(
                file_content,
                lexer,
                HtmlFormatter(
                    noclasses=True,
                    style="tango",)
            )
            output_type = 'file'
        else:
            output_type = 'binary'
    else:
        content = sorted(content, key=lambda x: x.filemode)
        for i in content:
            name, ext = os.path.splitext(i.name)
            if name == 'README':
                readme_file = __get_file_in_tree(
                    repo_obj, content, [i.name]).data

                readme, safe = pagure.doc_utils.convert_readme(
                    readme_file, ext)

                readme_ext = ext
        output_type = 'tree'

    headers = {}
    if encoding:
        headers['Content-Encoding'] = encoding

    return (
            flask.render_template(
            'file.html',
            select='tree',
            repo=repo,
            origin='view_file',
            username=username,
            branches=sorted(repo_obj.listall_branches()),
            branchname=branchname,
            filename=filename,
            content=content,
            output_type=output_type,
            repo_admin=is_repo_admin(repo),
            readme=readme,
            readme_ext=readme_ext,
            safe=safe,
        ),
        200,
        headers
    )

@APP.route('/<repo:repo>/raw/<path:identifier>', defaults={'filename': None})
@APP.route('/<repo:repo>/raw/<path:identifier>/f/<path:filename>')
@APP.route('/fork/<username>/<repo:repo>/raw/<path:identifier>',
           defaults={'filename': None})
@APP.route('/fork/<username>/<repo:repo>/raw/<path:identifier>/f/<path:filename>')
def view_raw_file(repo, identifier, filename=None, username=None):
    """ Displays the raw content of a file of a commit for the specified repo.
    """
    repo = pagure.lib.get_project(SESSION, repo, user=username)

    if not repo:
        flask.abort(404, 'Project not found')

    reponame = pagure.get_repo_path(repo)

    repo_obj = pygit2.Repository(reponame)

    if repo_obj.is_empty:
        flask.abort(404, 'Empty repo cannot have a file')

    if identifier in repo_obj.listall_branches():
        branch = repo_obj.lookup_branch(identifier)
        commit = branch.get_object()
    else:
        try:
            commit = repo_obj.get(identifier)
        except ValueError:
            if 'master' not in repo_obj.listall_branches():
                flask.abort(404, 'Branch no found')
            # If it's not a commit id then it's part of the filename
            commit = repo_obj[repo_obj.head.target]

    if not commit:
        flask.abort(400, 'Commit %s not found' % (identifier))

    if isinstance(commit, pygit2.Tag):
        commit = commit.get_object()

    mimetype = None
    encoding = None
    if filename:
        content = __get_file_in_tree(
            repo_obj, commit.tree, filename.split('/'), bail_on_tree=True)
        if not content or isinstance(content, pygit2.Tree):
            flask.abort(404, 'File not found')

        mimetype, encoding = mimetypes.guess_type(filename)
        data = repo_obj[content.oid].data
    else:
        if commit.parents:
            diff = commit.tree.diff_to_tree()

            try:
                parent = repo_obj.revparse_single('%s^' % identifier)
                diff = repo_obj.diff(parent, commit)
            except (KeyError, ValueError):
                flask.abort(404, 'Identifier not found')
        else:
            # First commit in the repo
            diff = commit.tree.diff_to_tree(swap=True)
        data = diff.patch

    if not data:
        flask.abort(404, 'No content found')

    if not mimetype and data[:2] == '#!':
        mimetype = 'text/plain'

    headers = {}
    if not mimetype:
        if '\0' in data:
            mimetype = 'application/octet-stream'
        else:
            mimetype = 'text/plain'
    elif 'html' in mimetype:
        mimetype = 'application/octet-stream'
        headers['Content-Disposition'] = 'attachment'

    if mimetype.startswith('text/') and not encoding:
        encoding = chardet.detect(ktc.to_bytes(data))['encoding']

    headers['Content-Type'] = mimetype
    if encoding:
        headers['Content-Encoding'] = encoding

    return (data, 200, headers)

if APP.config.get('OLD_VIEW_COMMIT_ENABLED', False):
    @APP.route('/<repo>/<string(length=40):commitid>/')
    @APP.route('/<repo>/<string(length=40):commitid>')
    @APP.route('/fork/<username>/<repo>/<string(length=40):commitid>/')
    @APP.route('/fork/<username>/<repo>/<string(length=40):commitid>')
    def view_commit_old(repo, commitid, username=None):
        """ Redirect from the old view_commit to the new one.

        This method aims to provide backward compatibility with the old URL
        scheme where the commit id was right after the repo name.
        This is problematic since we now allow up to one slash ('/') in the
        project name, making the url parsing a little trickier.
        So if the element after the '/' is exactly 40 characters long, then we
        consider it's a commit hash and redirect to the new URL scheme for
        viewing commits.
        If the element is more (not possible) or less than 40 characters, then
        the other URLs should catch it.

        """
        return flask.redirect(flask.url_for(
            'view_commit', repo=repo, commitid=commitid, username=username))

@APP.route('/<repo:repo>/c/<commitid>/')
@APP.route('/<repo:repo>/c/<commitid>')
@APP.route('/fork/<username>/<repo:repo>/c/<commitid>/')
@APP.route('/fork/<username>/<repo:repo>/c/<commitid>')
def view_commit(repo, commitid, username=None):
    """ Render a commit in a repo
    """
    repo = pagure.lib.get_project(SESSION, repo, user=username)

    if not repo:
        flask.abort(404, 'Project not found')

    reponame = pagure.get_repo_path(repo)

    repo_obj = pygit2.Repository(reponame)

    branchname = flask.request.args.get('branch', None)

    if branchname and branchname not in repo_obj.listall_branches():
        branchname = None

    try:
        commit = repo_obj.get(commitid)
    except ValueError:
        flask.abort(404, 'Commit not found')

    if commit is None:
        flask.abort(404, 'Commit not found')

    if commit.parents:
        diff = commit.tree.diff_to_tree()

        parent = repo_obj.revparse_single('%s^' % commitid)
        diff = repo_obj.diff(parent, commit)
    else:
        # First commit in the repo
        diff = commit.tree.diff_to_tree(swap=True)

    return flask.render_template(
        'commit.html',
        select='commits',
        repo=repo,
        branchname=branchname,
        username=username,
        repo_admin=is_repo_admin(repo),
        commitid=commitid,
        commit=commit,
        diff=diff,
        form=pagure.forms.ConfirmationForm(),
    )

@APP.route('/<repo:repo>/c/<commitid>.patch')
@APP.route('/fork/<username>/<repo:repo>/c/<commitid>.patch')
def view_commit_patch(repo, commitid, username=None):
    """ Render a commit in a repo as patch
    """
    repo = pagure.lib.get_project(SESSION, repo, user=username)

    if not repo:
        flask.abort(404, 'Project not found')

    reponame = pagure.get_repo_path(repo)

    repo_obj = pygit2.Repository(reponame)

    try:
        commit = repo_obj.get(commitid)
    except ValueError:
        flask.abort(404, 'Commit not found')

    if commit is None:
        flask.abort(404, 'Commit not found')

    patch = pagure.lib.git.commit_to_patch(repo_obj, commit)

    return flask.Response(patch, content_type="text/plain;charset=UTF-8")

@APP.route('/<repo:repo>/tree/')
@APP.route('/<repo:repo>/tree')
@APP.route('/<repo:repo>/tree/<path:identifier>')
@APP.route('/fork/<username>/<repo:repo>/tree/')
@APP.route('/fork/<username>/<repo:repo>/tree')
@APP.route('/fork/<username>/<repo:repo>/tree/<path:identifier>')
def view_tree(repo, identifier=None, username=None):
    """ Render the tree of the repo
    """
    repo = pagure.lib.get_project(SESSION, repo, user=username)

    if repo is None:
        flask.abort(404, 'Project not found')

    reponame = pagure.get_repo_path(repo)

    repo_obj = pygit2.Repository(reponame)

    branchname = None
    content = None
    output_type = None
    commit = None
    readme = None
    safe = False
    readme_ext = None
    if not repo_obj.is_empty:
        if identifier in repo_obj.listall_branches():
            branchname = identifier
            branch = repo_obj.lookup_branch(identifier)
            commit = branch.get_object()
        else:
            try:
                commit = repo_obj.get(identifier)
                branchname = identifier
            except (ValueError, TypeError):
                # If it's not a commit id then it's part of the filename
                if 'master' in repo_obj.listall_branches():
                    commit = repo_obj[repo_obj.head.target]
                    branchname = 'master'
        # If we're arriving here from the release page, we may have a Tag
        # where we expected a commit, in this case, get the actual commit
        if isinstance(commit, pygit2.Tag):
            commit = commit.get_object()

        if commit:
            content = sorted(commit.tree, key=lambda x: x.filemode)
            for i in commit.tree:
                name, ext = os.path.splitext(i.name)
                if name == 'README':
                    readme_file = __get_file_in_tree(
                        repo_obj, commit.tree, [i.name]).data

                    readme, safe = pagure.doc_utils.convert_readme(
                        readme_file, ext)

                    readme_ext = ext
        output_type = 'tree'

    return flask.render_template(
        'file.html',
        select='tree',
        repo_obj=repo_obj,
        origin='view_tree',
        repo=repo,
        username=username,
        branchname=branchname,
        branches=sorted(repo_obj.listall_branches()),
        filename='',
        content=content,
        output_type=output_type,
        repo_admin=is_repo_admin(repo),
        readme=readme,
        readme_ext=readme_ext,
        safe=safe,
    )

@APP.route('/<repo:repo>/forks/')
@APP.route('/<repo:repo>/forks')
@APP.route('/fork/<username>/<repo:repo>/forks/')
@APP.route('/fork/<username>/<repo:repo>/forks')
def view_forks(repo, username=None):
    """ Presents all the forks of the project.
    """
    repo = pagure.lib.get_project(SESSION, repo, user=username)

    if not repo:
        flask.abort(404, 'Project not found')

    return flask.render_template(
        'forks.html',
        select='forks',
        username=username,
        repo=repo,
        repo_admin=is_repo_admin(repo),
    )

@APP.route('/<repo:repo>/releases/')
@APP.route('/<repo:repo>/releases')
@APP.route('/fork/<username>/<repo:repo>/releases/')
@APP.route('/fork/<username>/<repo:repo>/releases')
def view_tags(repo, username=None):
    """ Presents all the tags of the project.
    """
    repo = pagure.lib.get_project(SESSION, repo, user=username)

    if not repo:
        flask.abort(404, 'Project not found')

    reponame = pagure.get_repo_path(repo)
    repo_obj = pygit2.Repository(reponame)

    tags = pagure.lib.git.get_git_tags_objects(repo)

    return flask.render_template(
        'releases.html',
        select='tags',
        username=username,
        repo=repo,
        tags=tags,
        repo_admin=is_repo_admin(repo),
        repo_obj=repo_obj,
    )

@APP.route('/<repo:repo>/upload/', methods=('GET', 'POST'))
@APP.route('/<repo:repo>/upload', methods=('GET', 'POST'))
@APP.route('/fork/<username>/<repo:repo>/upload/', methods=('GET', 'POST'))
@APP.route('/fork/<username>/<repo:repo>/upload', methods=('GET', 'POST'))
@login_required
def new_release(repo, username=None):
    """ Upload a new release.
    """
    if not APP.config.get('UPLOAD_FOLDER_PATH') \
            and not APP.config.get('UPLOAD_FOLDER'):
        flask.abort(404)

    repo = pagure.lib.get_project(SESSION, repo, user=username)

    if not repo:
        flask.abort(404, 'Project not found')

    if not is_repo_admin(repo):
        flask.abort(
            403,
            'You are not allowed to change the settings for this project')

    form = pagure.forms.UploadFileForm()

    if form.validate_on_submit():
        for filestream in flask.request.files.getlist('filestream'):
            filename = werkzeug.secure_filename(filestream.filename)
            try:
                folder = os.path.join(
                    APP.config['UPLOAD_FOLDER_PATH'],
                    werkzeug.secure_filename(repo.fullname))
                if not os.path.exists(folder):
                    os.mkdir(folder)
                filestream.save(os.path.join(folder, filename))
                flask.flash('File "%s" uploaded' % filename)
            except Exception as err:  # pragma: no cover
                APP.logger.exception(err)
                flask.flash('Upload failed', 'error')
        return flask.redirect(
            flask.url_for('view_tags', repo=repo.name, username=username))

    return flask.render_template(
        'new_release.html',
        select='tags',
        username=username,
        repo=repo,
        form=form,
    )

@APP.route('/<repo:repo>/settings/', methods=('GET', 'POST'))
@APP.route('/<repo:repo>/settings', methods=('GET', 'POST'))
@APP.route('/fork/<username>/<repo:repo>/settings/', methods=('GET', 'POST'))
@APP.route('/fork/<username>/<repo:repo>/settings', methods=('GET', 'POST'))
@login_required
def view_settings(repo, username=None):
    """ Presents the settings of the project.
    """
    if admin_session_timedout():
        if flask.request.method == 'POST':
            flask.flash('Action canceled, try it again', 'error')
        return flask.redirect(
            flask.url_for('auth_login', next=flask.request.url))

    repo = pagure.lib.get_project(SESSION, repo, user=username)

    if not repo:
        flask.abort(404, 'Project not found')

    repo_admin = is_repo_admin(repo)
    if not repo_admin:
        flask.abort(
            403,
            'You are not allowed to change the settings for this project')

    reponame = pagure.get_repo_path(repo)
    repo_obj = pygit2.Repository(reponame)

    plugins = pagure.ui.plugins.get_plugin_names(
        APP.config.get('DISABLED_PLUGINS'))
    tags = pagure.lib.get_tags_of_project(SESSION, repo)

    form = pagure.forms.ConfirmationForm()
    tag_form = pagure.forms.AddIssueTagForm()

    branches = repo_obj.listall_branches()
    branches_form = pagure.forms.DefaultBranchForm(branches=branches)
    if form.validate_on_submit():
        settings = {}
        for key in flask.request.form:
            if key == 'csrf_token':
                continue
            settings[key] = flask.request.form[key]

        try:
            message = pagure.lib.update_project_settings(
                SESSION,
                repo=repo,
                settings=settings,
                user=flask.g.fas_user.username,
            )
            SESSION.commit()
            flask.flash(message)
            return flask.redirect(flask.url_for(
                'view_repo', username=username, repo=repo.name))
        except pagure.exceptions.PagureException as msg:
            SESSION.rollback()
            flask.flash(msg, 'error')
        except SQLAlchemyError as err:  # pragma: no cover
            SESSION.rollback()
            flask.flash(str(err), 'error')

    if not repo_obj.is_empty and not repo_obj.head_is_unborn:
        branchname = repo_obj.head.shorthand
    else:
        branchname = None

    if flask.request.method == 'GET' and branchname:
        branches_form.branches.data = branchname

    return flask.render_template(
        'settings.html',
        select='settings',
        username=username,
        repo=repo,
        repo_obj=repo_obj,
        form=form,
        tag_form=tag_form,
        branches_form=branches_form,
        tags=tags,
        plugins=plugins,
        repo_admin=repo_admin,
        branchname = branchname,
    )

@APP.route('/<repo:repo>/update', methods=['POST'])
@APP.route('/fork/<username>/<repo:repo>/update', methods=['POST'])
@login_required
def update_project(repo, username=None):
    """ Update the description of a project.
    """
    if admin_session_timedout():
        flask.flash('Action canceled, try it again', 'error')
        url = flask.url_for(
            'view_settings', username=username, repo=repo)
        return flask.redirect(
            flask.url_for('auth_login', next=url))

    repo = pagure.lib.get_project(SESSION, repo, user=username)

    if not repo:
        flask.abort(404, 'Project not found')

    if not is_repo_admin(repo):
        flask.abort(
            403,
            'You are not allowed to change the settings for this project')

    form = pagure.forms.ProjectFormSimplified()

    if form.validate_on_submit():
        try:
            repo.description = form.description.data
            repo.avatar_email = form.avatar_email.data.strip()
            repo.url = form.url.data.strip()
            pagure.lib.update_tags(
                SESSION, repo,
                tags=[t.strip() for t in form.tags.data.split(',')],
                username=flask.g.fas_user.username,
                ticketfolder=None,
            )
            SESSION.add(repo)
            SESSION.commit()
            flask.flash('Project updated')
        except SQLAlchemyError as err:  # pragma: no cover
            SESSION.rollback()
            flask.flash(str(err), 'error')

    return flask.redirect(flask.url_for(
        'view_settings', username=username, repo=repo.name))

@APP.route('/<repo:repo>/update/priorities', methods=['POST'])
@APP.route('/fork/<username>/<repo:repo>/update/priorities', methods=['POST'])
@login_required
def update_priorities(repo, username=None):
    """ Update the priorities of a project.
    """
    if admin_session_timedout():
        flask.flash('Action canceled, try it again', 'error')
        url = flask.url_for(
            'view_settings', username=username, repo=repo)
        return flask.redirect(
            flask.url_for('auth_login', next=url))

    repo = pagure.lib.get_project(SESSION, repo, user=username)

    if not repo:
        flask.abort(404, 'Project not found')

    if not repo.settings.get('issue_tracker', True):
        flask.abort(404, 'No issue tracker found for this project')

    if not is_repo_admin(repo):
        flask.abort(
            403,
            'You are not allowed to change the settings for this project')

    form = pagure.forms.ConfirmationForm()

    error = False
    if form.validate_on_submit():
        weights = [
            w.strip() for w in flask.request.form.getlist('priority_weigth')
            if w.strip()
        ]
        try:
            weights = [int(w) for w in weights]
        except:
            flask.flash(
                'Priorities weights must be numbers',
                'error')
            error = True

        titles = [
            p.strip() for p in flask.request.form.getlist('priority_title')
            if p.strip()
        ]

        if len(weights) != len(titles):
            flask.flash(
                'Priorities weights and titles are not of the same length',
                'error')
            error = True

        for weight in weights:
            if weights.count(weight) != 1:
                flask.flash(
                    'Priority weight %s is present %s times' % (
                        weight, weights.count(weight)
                    ),
                    'error')
                error = True
                break

        for title in titles:
            if titles.count(title) != 1:
                flask.flash(
                    'Priority %s is present %s times' % (
                        title, titles.count(title)
                    ),
                    'error')
                error = True
                break

        if not error:
            priorities = {}
            for cnt in range(len(weights)):
                priorities[weights[cnt]] = titles[cnt]
            priorities[''] = ''
            try:
                repo.priorities = priorities
                SESSION.add(repo)
                SESSION.commit()
                flask.flash('Priorities updated')
            except SQLAlchemyError as err:  # pragma: no cover
                SESSION.rollback()
                flask.flash(str(err), 'error')

    return flask.redirect(flask.url_for(
        'view_settings', username=username, repo=repo.name))

@APP.route('/<repo:repo>/update/milestones', methods=['POST'])
@APP.route('/fork/<username>/<repo:repo>/update/milestones', methods=['POST'])
@login_required
def update_milestones(repo, username=None):
    """ Update the milestones of a project.
    """
    if admin_session_timedout():
        flask.flash('Action canceled, try it again', 'error')
        url = flask.url_for(
            'view_settings', username=username, repo=repo)
        return flask.redirect(
            flask.url_for('auth_login', next=url))

    repo = pagure.lib.get_project(SESSION, repo, user=username)

    if not repo:
        flask.abort(404, 'Project not found')

    if not repo.settings.get('issue_tracker', True):
        flask.abort(404, 'No issue tracker found for this project')

    if not is_repo_admin(repo):
        flask.abort(
            403,
            'You are not allowed to change the settings for this project')

    form = pagure.forms.ConfirmationForm()

    error = False
    if form.validate_on_submit():
        milestones = [
            w.strip() for w in flask.request.form.getlist('milestones')
            if w.strip()
        ]

        milestone_dates = [
            p.strip() for p in flask.request.form.getlist('milestone_dates')
        ]

        if len(milestones) != len(milestone_dates):
            flask.flash(
                'Milestones and dates are not of the same length',
                'error')
            error = True

        for milestone in milestones:
            if milestones.count(milestone) != 1:
                flask.flash(
                    'Milestone %s is present %s times' % (
                        milestone, milestones.count(milestone)
                    ),
                    'error')
                error = True
                break

        for milestone_date in milestone_dates:
            if milestone_date and milestone_dates.count(milestone_date) != 1:
                flask.flash(
                    'Date %s is present %s times' % (
                        milestone_date, milestone_dates.count(milestone_date)
                    ),
                    'error')
                error = True
                break

        if not error:
            miles = {}
            for cnt in range(len(milestones)):
                miles[milestones[cnt]] = milestone_dates[cnt]
            try:
                repo.milestones = miles
                SESSION.add(repo)
                SESSION.commit()
                flask.flash('Milestones updated')
            except SQLAlchemyError as err:  # pragma: no cover
                SESSION.rollback()
                flask.flash(str(err), 'error')

    return flask.redirect(flask.url_for(
        'view_settings', username=username, repo=repo.name))

@APP.route('/<repo:repo>/default/branch/', methods=['POST'])
@APP.route('/fork/<username>/<repo:repo>/default/branch/', methods=['POST'])
@login_required
def change_ref_head(repo, username=None):
    """ Change HEAD reference
    """

    if admin_session_timedout():
        flask.flash('Action canceled, try it again', 'error')
        url = flask.url_for(
            'view_settings', username=username, repo=repo)
        return flask.redirect(
            flask.url_for('auth_login', next=url))

    repo = pagure.lib.get_project(SESSION, repo, user=username)
    if not repo:
        flask.abort(404, 'Project not found')

    if not is_repo_admin(repo):
        flask.abort(
            403,
            'You are not allowed to change the settings for this project')
    repopath = pagure.get_repo_path(repo)
    repo_obj = pygit2.Repository(repopath)
    branches = repo_obj.listall_branches()
    form = pagure.forms.DefaultBranchForm(branches=branches)

    if form.validate_on_submit():
        branchname = form.branches.data
        try:
            reference = repo_obj.lookup_reference('refs/heads/%s'%branchname).resolve()
            repo_obj.set_head(reference.name)
            flask.flash('Default branch updated to %s'%branchname)
        except Exception as err:  # pragma: no cover
            APP.logger.exception(err)

    return flask.redirect(flask.url_for(
                'view_settings', username=username, repo=repo.name))

@APP.route('/<repo:repo>/delete', methods=['POST'])
@APP.route('/fork/<username>/<repo:repo>/delete', methods=['POST'])
@login_required
def delete_repo(repo, username=None):
    """ Delete the present project.
    """
    if not pagure.APP.config.get('ENABLE_DEL_PROJECTS', True):
        flask.abort(404)

    if admin_session_timedout():
        flask.flash('Action canceled, try it again', 'error')
        url = flask.url_for(
            'view_settings', username=username, repo=repo)
        return flask.redirect(
            flask.url_for('auth_login', next=url))

    repo = pagure.lib.get_project(SESSION, repo, user=username)

    if not repo:
        flask.abort(404, 'Project not found')

    if not is_repo_admin(repo):
        flask.abort(
            403,
            'You are not allowed to change the settings for this project')

    try:
        for issue in repo.issues:
            for comment in issue.comments:
                SESSION.delete(comment)
            SESSION.commit()
            SESSION.delete(issue)
        SESSION.delete(repo)
        SESSION.commit()
    except SQLAlchemyError as err:  # pragma: no cover
        SESSION.rollback()
        APP.logger.exception(err)
        flask.flash('Could not delete the project', 'error')

    repopath = os.path.join(APP.config['GIT_FOLDER'], repo.path)
    docpath = os.path.join(APP.config['DOCS_FOLDER'], repo.path)
    ticketpath = os.path.join(APP.config['TICKETS_FOLDER'], repo.path)
    requestpath = os.path.join(APP.config['REQUESTS_FOLDER'], repo.path)

    try:
        shutil.rmtree(repopath)
        shutil.rmtree(docpath)
        shutil.rmtree(ticketpath)
        shutil.rmtree(requestpath)
    except (OSError, IOError) as err:
        APP.logger.exception(err)
        flask.flash(
            'Could not delete all the repos from the system', 'error')

    return flask.redirect(
        flask.url_for('view_user', username=flask.g.fas_user.username))

@APP.route('/<repo:repo>/hook_token', methods=['POST'])
@APP.route('/fork/<username>/<repo:repo>/hook_token', methods=['POST'])
@login_required
def new_repo_hook_token(repo, username=None):
    """ Re-generate a hook token for the present project.
    """
    if not pagure.APP.config.get('WEBHOOK', False):
        flask.abort(404)

    if admin_session_timedout():
        flask.flash('Action canceled, try it again', 'error')
        url = flask.url_for(
            'view_settings', username=username, repo=repo)
        return flask.redirect(
            flask.url_for('auth_login', next=url))

    repo = pagure.lib.get_project(SESSION, repo, user=username)

    if not repo:
        flask.abort(404, 'Project not found')

    if not is_repo_admin(repo):
        flask.abort(
            403,
            'You are not allowed to change the settings for this project')

    form = pagure.forms.ConfirmationForm()
    if not form.validate_on_submit():
        flask.abort(400, 'Invalid request')

    try:
        repo.hook_token = pagure.lib.login.id_generator(40)
        SESSION.commit()
        flask.flash('New hook token generated')
    except SQLAlchemyError as err:  # pragma: no cover
        SESSION.rollback()
        APP.logger.exception(err)
        flask.flash('Could not generate a new token for this project', 'error')

    return flask.redirect(
        flask.url_for('view_settings', repo=repo.name, username=username))

@APP.route('/<repo:repo>/dropuser/<int:userid>', methods=['POST'])
@APP.route('/fork/<username>/<repo:repo>/dropuser/<int:userid>',
           methods=['POST'])
@login_required
def remove_user(repo, userid, username=None):
    """ Remove the specified user from the project.
    """

    if not pagure.APP.config.get('ENABLE_USER_MNGT', True):
        flask.abort(404, 'User management not allowed in the pagure instance')

    if admin_session_timedout():
        flask.flash('Action canceled, try it again', 'error')
        url = flask.url_for(
            'view_settings', username=username, repo=repo)
        return flask.redirect(
            flask.url_for('auth_login', next=url))

    repo = pagure.lib.get_project(SESSION, repo, user=username)

    if not repo:
        flask.abort(404, 'Project not found')

    if not is_repo_admin(repo):
        flask.abort(
            403,
            'You are not allowed to change the users for this project')

    form = pagure.forms.ConfirmationForm()
    if form.validate_on_submit():
        userids = [str(user.id) for user in repo.users]

        if str(userid) not in userids:
            flask.flash(
                'User does not have commit or cannot loose it right', 'error')
            return flask.redirect(
                flask.url_for(
                    '.view_settings', repo=repo.name, username=username)
            )

        for user in repo.users:
            if str(user.id) == str(userid):
                repo.users.remove(user)
                break
        try:
            SESSION.commit()
            pagure.lib.git.generate_gitolite_acls()
            flask.flash('User removed')
        except SQLAlchemyError as err:  # pragma: no cover
            SESSION.rollback()
            APP.logger.exception(err)
            flask.flash('User could not be removed', 'error')

    return flask.redirect(
        flask.url_for('.view_settings', repo=repo.name, username=username)
    )

@APP.route('/<repo:repo>/adduser/', methods=('GET', 'POST'))
@APP.route('/<repo:repo>/adduser', methods=('GET', 'POST'))
@APP.route('/fork/<username>/<repo:repo>/adduser/', methods=('GET', 'POST'))
@APP.route('/fork/<username>/<repo:repo>/adduser', methods=('GET', 'POST'))
@login_required
def add_user(repo, username=None):
    """ Add the specified user from the project.
    """

    if not pagure.APP.config.get('ENABLE_USER_MNGT', True):
        flask.abort(404, 'User management is not allowed in this pagure instance')

    if admin_session_timedout():
        if flask.request.method == 'POST':
            flask.flash('Action canceled, try it again', 'error')
        return flask.redirect(
            flask.url_for('auth_login', next=flask.request.url))

    repo = pagure.lib.get_project(SESSION, repo, user=username)

    if not repo:
        flask.abort(404, 'Project not found')

    if not is_repo_admin(repo):
        flask.abort(
            403,
            'You are not allowed to add users to this project')

    form = pagure.forms.AddUserForm()

    if form.validate_on_submit():
        try:
            msg = pagure.lib.add_user_to_project(
                SESSION, repo,
                new_user=form.user.data,
                user=flask.g.fas_user.username,
            )
            SESSION.commit()
            pagure.lib.git.generate_gitolite_acls()
            flask.flash(msg)
            return flask.redirect(
                flask.url_for(
                    '.view_settings', repo=repo.name, username=username)
            )
        except pagure.exceptions.PagureException as msg:
            SESSION.rollback()
            flask.flash(msg, 'error')
        except SQLAlchemyError as err:  # pragma: no cover
            SESSION.rollback()
            APP.logger.exception(err)
            flask.flash('User could not be added', 'error')

    return flask.render_template(
        'add_user.html',
        form=form,
        username=username,
        repo=repo,
    )

@APP.route('/<repo:repo>/dropgroup/<int:groupid>', methods=['POST'])
@APP.route('/fork/<username>/<repo:repo>/dropgroup/<int:groupid>', methods=['POST'])
@login_required
def remove_group_project(repo, groupid, username=None):
    """ Remove the specified group from the project.
    """

    if not pagure.APP.config.get('ENABLE_USER_MNGT', True):
        flask.abort(404, 'User management is not allowed in this pagure instance')

    if admin_session_timedout():
        flask.flash('Action canceled, try it again', 'error')
        url = flask.url_for(
            'view_settings', username=username, repo=repo)
        return flask.redirect(
            flask.url_for('auth_login', next=url))

    repo = pagure.lib.get_project(SESSION, repo, user=username)

    if not repo:
        flask.abort(404, 'Project not found')

    if not is_repo_admin(repo):
        flask.abort(
            403,
            'You are not allowed to change the users for this project')

    form = pagure.forms.ConfirmationForm()
    if form.validate_on_submit():
        grpids = [grp.id for grp in repo.groups]

        if groupid not in grpids:
            flask.flash(
                'Group does not seem to be part of this project', 'error')
            return flask.redirect(
                flask.url_for(
                    '.view_settings', repo=repo.name, username=username)
            )

        for grp in repo.groups:
            if grp.id == groupid:
                repo.groups.remove(grp)
                break
        try:
            SESSION.commit()
            pagure.lib.git.generate_gitolite_acls()
            flask.flash('Group removed')
        except SQLAlchemyError as err:  # pragma: no cover
            SESSION.rollback()
            APP.logger.exception(err)
            flask.flash('Group could not be removed', 'error')

    return flask.redirect(
        flask.url_for('.view_settings', repo=repo.name, username=username)
    )

@APP.route('/<repo:repo>/addgroup/', methods=('GET', 'POST'))
@APP.route('/<repo:repo>/addgroup', methods=('GET', 'POST'))
@APP.route('/fork/<username>/<repo:repo>/addgroup/', methods=('GET', 'POST'))
@APP.route('/fork/<username>/<repo:repo>/addgroup', methods=('GET', 'POST'))
@login_required
def add_group_project(repo, username=None):
    """ Add the specified group from the project.
    """

    if not pagure.APP.config.get('ENABLE_USER_MNGT', True):
        flask.abort(404, 'User management is not allowed in this pagure instance')

    if admin_session_timedout():
        if flask.request.method == 'POST':
            flask.flash('Action canceled, try it again', 'error')
        return flask.redirect(
            flask.url_for('auth_login', next=flask.request.url))

    repo = pagure.lib.get_project(SESSION, repo, user=username)

    if not repo:
        flask.abort(404, 'Project not found')

    if not is_repo_admin(repo):
        flask.abort(
            403,
            'You are not allowed to add groups to this project')

    form = pagure.forms.AddGroupForm()

    if form.validate_on_submit():
        try:
            msg = pagure.lib.add_group_to_project(
                SESSION, repo,
                new_group=form.group.data,
                user=flask.g.fas_user.username,
                create=not pagure.APP.config.get('ENABLE_GROUP_MNGT', False),
                is_admin=pagure.is_admin(),
            )
            SESSION.commit()
            pagure.lib.git.generate_gitolite_acls()
            flask.flash(msg)
            return flask.redirect(
                flask.url_for(
                    '.view_settings', repo=repo.name, username=username)
            )
        except pagure.exceptions.PagureException as msg:
            SESSION.rollback()
            flask.flash(msg, 'error')
        except SQLAlchemyError as err:  # pragma: no cover
            SESSION.rollback()
            APP.logger.exception(err)
            flask.flash('Group could not be added', 'error')

    return flask.render_template(
        'add_group_project.html',
        form=form,
        username=username,
        repo=repo,
    )

@APP.route('/<repo:repo>/regenerate', methods=['POST'])
@APP.route('/fork/<username>/<repo:repo>/regenerate', methods=['POST'])
@login_required
def regenerate_git(repo, username=None):
    """ Regenerate the specified git repo with the content in the project.
    """
    if admin_session_timedout():
        flask.flash('Action canceled, try it again', 'error')
        url = flask.url_for(
            'view_settings', username=username, repo=repo)
        return flask.redirect(
            flask.url_for('auth_login', next=url))

    repo = pagure.lib.get_project(SESSION, repo, user=username)

    if not repo:
        flask.abort(404, 'Project not found')

    if not is_repo_admin(repo):
        flask.abort(403, 'You are not allowed to regenerate the git repos')

    regenerate = flask.request.form.get('regenerate')
    if not regenerate or regenerate.lower() not in ['tickets', 'requests']:
        flask.abort(400, 'You can only regenerate tickest or requests repos')

    form = pagure.forms.ConfirmationForm()
    if form.validate_on_submit():
        if regenerate.lower() == 'requests':
            for request in repo.requests:
                pagure.lib.git.update_git(
                    request, repo=repo,
                    repofolder=APP.config['REQUESTS_FOLDER'])
            flask.flash('Requests git repo updated')
        elif regenerate.lower() == 'tickets':
            for ticket in repo.issues:
                # Do not store private issues in the git
                if ticket.private:
                    continue
                pagure.lib.git.update_git(
                    ticket, repo=repo,
                    repofolder=APP.config['TICKETS_FOLDER'])
            flask.flash('Tickets git repo updated')

    return flask.redirect(
        flask.url_for('.view_settings', repo=repo.name, username=username)
    )

@APP.route('/<repo:repo>/token/new/', methods=('GET', 'POST'))
@APP.route('/<repo:repo>/token/new', methods=('GET', 'POST'))
@APP.route('/fork/<username>/<repo:repo>/token/new/', methods=('GET', 'POST'))
@APP.route('/fork/<username>/<repo:repo>/token/new', methods=('GET', 'POST'))
@login_required
def add_token(repo, username=None):
    """ Add a token to a specified project.
    """
    if admin_session_timedout():
        if flask.request.method == 'POST':
            flask.flash('Action canceled, try it again', 'error')
        return flask.redirect(
            flask.url_for('auth_login', next=flask.request.url))

    repo = pagure.lib.get_project(SESSION, repo, user=username)

    if not repo:
        flask.abort(404, 'Project not found')

    repo_admin = is_repo_admin(repo)
    if not repo_admin:
        flask.abort(
            403,
            'You are not allowed to change the settings for this project')

    acls = pagure.lib.get_acls(SESSION)
    form = pagure.forms.NewTokenForm(acls=acls)

    if form.validate_on_submit():
        try:
            msg = pagure.lib.add_token_to_user(
                SESSION,
                repo,
                acls=form.acls.data,
                username=flask.g.fas_user.username,
            )
            SESSION.commit()
            flask.flash(msg)
            return flask.redirect(
                flask.url_for(
                    '.view_settings', repo=repo.name, username=username)
            )
        except SQLAlchemyError as err:  # pragma: no cover
            SESSION.rollback()
            APP.logger.exception(err)
            flask.flash('User could not be added', 'error')

    return flask.render_template(
        'add_token.html',
        select='settings',
        form=form,
        acls=acls,
        repo_admin=repo_admin,
        username=username,
        repo=repo,
    )

@APP.route('/<repo:repo>/token/revoke/<token_id>', methods=['POST'])
@APP.route('/fork/<username>/<repo:repo>/token/revoke/<token_id>',
           methods=['POST'])
@login_required
def revoke_api_token(repo, token_id, username=None):
    """ Revokie a token to a specified project.
    """
    if admin_session_timedout():
        flask.flash('Action canceled, try it again', 'error')
        url = flask.url_for(
            'view_settings', username=username, repo=repo)
        return flask.redirect(
            flask.url_for('auth_login', next=url))

    repo = pagure.lib.get_project(SESSION, repo, user=username)

    if not repo:
        flask.abort(404, 'Project not found')

    if not is_repo_admin(repo):
        flask.abort(
            403,
            'You are not allowed to change the settings for this project')

    token = pagure.lib.get_api_token(SESSION, token_id)

    if not token \
            or token.project.fullname != repo.fullname \
            or token.user.username != flask.g.fas_user.username:
        flask.abort(404, 'Token not found')

    form = pagure.forms.ConfirmationForm()

    if form.validate_on_submit():
        try:
            token.expiration = datetime.datetime.utcnow()
            SESSION.commit()
            flask.flash('Token revoked')
        except SQLAlchemyError as err:  # pragma: no cover
            SESSION.rollback()
            APP.logger.exception(err)
            flask.flash(
                'Token could not be revoked, please contact an admin',
                'error')

    return flask.redirect(
        flask.url_for(
            '.view_settings', repo=repo.name, username=username)
    )

@APP.route(
    '/<repo:repo>/edit/<path:branchname>/f/<path:filename>',
    methods=('GET', 'POST'))
@APP.route(
    '/fork/<username>/<repo:repo>/edit/<path:branchname>/f/<path:filename>',
    methods=('GET', 'POST'))
@login_required
def edit_file(repo, branchname, filename, username=None):
    """ Edit a file online.
    """
    repo = pagure.lib.get_project(SESSION, repo, user=username)

    if not repo:
        flask.abort(404, 'Project not found')

    if not is_repo_admin(repo):
        flask.abort(
            403,
            'You are not allowed to change the settings for this project')

    user = pagure.lib.search_user(
        SESSION, username=flask.g.fas_user.username)

    reponame = pagure.get_repo_path(repo)

    repo_obj = pygit2.Repository(reponame)

    if repo_obj.is_empty:
        flask.abort(404, 'Empty repo cannot have a file')

    form = pagure.forms.EditFileForm(emails=user.emails)

    branch = None
    if branchname in repo_obj.listall_branches():
        branch = repo_obj.lookup_branch(branchname)
        commit = branch.get_object()
    else:
        flask.abort(400, 'Invalid branch specified')

    if form.validate_on_submit():
        try:
            pagure.lib.git.update_file_in_git(
                repo,
                branch=branchname,
                branchto=form.branch.data,
                filename=filename,
                content=form.content.data,
                message='%s\n\n%s' % (
                    form.commit_title.data.strip(),
                    form.commit_message.data.strip()
                ),
                user=flask.g.fas_user,
                email=form.email.data,
            )
            flask.flash('Changes committed')
            return flask.redirect(
                flask.url_for(
                    '.view_commits', repo=repo.name, username=username,
                    branchname=form.branch.data)
            )
        except pagure.exceptions.PagureException as err:  # pragma: no cover
            APP.logger.exception(err)
            flask.flash('Commit could not be done', 'error')
            data = form.content.data
    elif flask.request.method == 'GET':
        content = __get_file_in_tree(
            repo_obj, commit.tree, filename.split('/'))
        if not content or isinstance(content, pygit2.Tree):
            flask.abort(404, 'File not found')

        if is_binary_string(content.data):
            flask.abort(400, 'Cannot edit binary files')

        data = repo_obj[content.oid].data.decode('utf-8')
    else:
        data = form.content.data.decode('utf-8')

    return flask.render_template(
        'edit_file.html',
        select='tree',
        repo=repo,
        username=username,
        branchname=branchname,
        data=data,
        filename=filename,
        form=form,
        user=user,
        branches=repo_obj.listall_branches(),
    )

@APP.route('/<repo:repo>/b/<path:branchname>/delete', methods=['POST'])
@APP.route('/fork/<username>/<repo:repo>/b/<path:branchname>/delete',
           methods=['POST'])
@login_required
def delete_branch(repo, branchname, username=None):
    """ Delete the branch of a project.
    """
    repo_obj = pagure.lib.get_project(SESSION, repo, user=username)

    if not repo_obj:
        flask.abort(404, 'Project not found')

    if not is_repo_admin(repo_obj):
        flask.abort(
            403,
            'You are not allowed to delete branch for this project')

    if branchname == 'master':
        flask.abort(403, 'You are not allowed to delete the master branch')

    reponame = pagure.get_repo_path(repo_obj)
    repo_git = pygit2.Repository(reponame)

    if branchname not in repo_git.listall_branches():
        flask.abort(404, 'Branch no found')

    try:
        branch = repo_git.lookup_branch(branchname)
        branch.delete()
        flask.flash('Branch `%s` deleted' % branchname)
    except pygit2.GitError as err:
        APP.logger.exception(err)
        flask.flash('Could not delete `%s`' % branchname, 'error')

    return flask.redirect(
        flask.url_for('view_repo', repo=repo, username=username))

@APP.route('/docs/<repo:repo>/')
@APP.route('/docs/<repo:repo>/<path:filename>')
@APP.route('/docs/fork/<username>/<repo:repo>/')
@APP.route('/docs/fork/<username>/<repo:repo>/<path:filename>')
def view_docs(repo, username=None, filename=None):
    """ Display the documentation
    """
    repo_obj = pagure.lib.get_project(SESSION, repo, user=username)

    if not repo_obj:
        flask.abort(404, 'Project not found')

    if not APP.config.get('DOC_APP_URL'):
        flask.abort(404, 'This pagure instance has no doc server')

    return flask.render_template(
        'docs.html',
        select='docs',
        repo=repo_obj,
        username=username,
        filename=filename,
        endpoint='view_docs',
    )

@APP.route('/<repo:repo>/activity/')
@APP.route('/<repo:repo>/activity')
def view_project_activity(repo):
    """ Display the activity feed
    """

    if not APP.config.get('DATAGREPPER_URL'):
        flask.abort(404)

    repo_obj = pagure.lib.get_project(SESSION, repo, user=None)

    if not repo_obj:
        flask.abort(404, 'Project not found')

    return flask.render_template(
        'activity.html',
        repo=repo_obj,
    )

@APP.route('/watch/<repo:repo>/settings/<watch>', methods=['POST'])
@APP.route('/watch/fork/<user>/<repo:repo>/settings/<watch>', methods=['POST'])
@login_required
def watch_repo(repo, watch, user=None):
    """ Marked for watching or Unwatching
    """
    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 str(watch) not in ['0', '1']:
        flask.abort(400)

    username = flask.g.fas_user.username
    repo_obj = pagure.lib.get_project(SESSION, repo)
    if user is not None:
        repo_obj = pagure.lib.get_project(SESSION, repo, user)

    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:
        flask.flash(msg, 'error')

    return flask.redirect(return_point)


code:
Pagure CI
=========

This is to setup Pagure CI for development. It is assumed that all the
dependencies are resolved. It is advised to use a virtual envivironment
for development.

 * Run::

        python setup.py develop

Now in pagureCI/consumer.py add the following elements in `topic` list

::

    'org.fedoraproject.dev.pagure.pull-request.new',
    'org.fedoraproject.dev.pagure.pull-request.comment.added',

Configuring Jenkins
===================

Jenkins configuration is the most important part of how the Pagure CI works,
after you login to your Jenkins Instance.

* Go to Manage Jenkins -> Configuire Global Security and under that select
  'Project-based Matrix Authorization Strategy'

* Add a user and give all the permission to that user.

* Download the following plugins:

::

      Build Authorization Root Plugin
      Git Plugins
      Notification Plugin

* Click on the New Item

* Select Freestyle Project

* Click OK and enter the name of the project, make sure the project name you
  filled in the Pagure CI form should match the name you entered here.

* Under 'Job Notification'  click 'Add Endpoint'

* Fields in Endpoint will be :

::

		FORMAT: JSON
		PROTOCOL: HTTP
		EVENT: Job Finalized
		URL: <The POST URL that Jenkins form returned>
		TIMEOUT: 3000
		LOG: 1

* Tick the build is parameterized

* From the Add Parameter drop down select String Parameter

* Two string parameters need to be created REPO and BRANCH

* Source Code Management select Git  and give the URL of the pagure project

* Under Build Trigger click on Trigger build remotely and give the same token
  that you gave in the Pagure CI form.

* Under Build -> Add build step -> Execute Shell

* In the box given  enter the shell steps you want for testing your project.

Example Script

::

        if [ -n "$REPO" -a -n "$BRANCH" ]; then
        git remote rm proposed || true
        git remote add proposed "$REPO"
        git fetch proposed
        git checkout origin/master
        git config --global user.email "you@example.com"
        git config --global user.name "Your Name"
        git merge --no-ff "proposed/$BRANCH" -m "Merge PR"
        fi

* After all the configuration done, go to the dev instance of pagure running
  and under project settings in `Plugin` select Pagure CI and fill the appropriate
  information. Which on submiting should give you a POST url.

* Copy and paste the URL in the Notification section under the Jenkins project
  you want the CI to work,

Get It Running:
===============

In one terminal window run:

::

    fedmsg-relay

Another window:

::

    fedmsg-hub

* Now clone the project locally and make a branch. make some changes and push it
  to the repo and try to make a PR. You will notice if everything works fine a lot
  of logs in the server console and `build fail` flag on the PR.
  Build fail because there is no git server running.


code:
# -*- coding: utf-8 -*-
import fedmsg.consumers
from pagure.hooks import jenkins_hook
import pagure.lib
from pagure.lib import pagure_ci
from pagure.lib.model import BASE, Project, User
from pagure import APP, SESSION
import pagure.exceptions
PAGURE_MAIN_REPO = '{base}{name}.git'
PAGURE_FORK_REPO = '{base}forks/{user}/{name}.git'

class Integrator(fedmsg.consumers.FedmsgConsumer):
    ''' Integrates Jenkins with Pagure. '''
    topic = [
        'io.pagure.prod.pagure.pull-request.comment.added',
        'io.pagure.prod.pagure.pull-request.new',
        'org.fedoraproject.prod.jenkins.build',
    ]

    config_key = 'integrator.enabled'

    def __init__(self, hub):
        super(Integrator, self).__init__(hub)

    def consume(self, msg):
        ''' Pagure CI consumer which consumes message from
        fedmsg and triggers Jenkins build.
        '''
        topic, msg = msg['topic'], msg['body']
        self.log.info("Received %r, %r", topic, msg.get('msg_id', None))
        msg = msg['msg']
        try:
            if topic.endswith('.pull-request.comment.added'):
                if is_rebase(msg):
                    self.trigger_build(msg)
            elif topic.endswith('.pull-request.new'):
                self.trigger_build(msg)
            else:
                self.process_build(msg)
        except jenkins_hook.ConfigNotFound as exc:
            self.log.info('Unconfigured project %r', str(exc))
        except pagure.exceptions.HookInactiveException as exc:
            self.log.info('Hook Inactive for project %r', str(exc))

    def trigger_build(self, msg):
        ''' Triggers or requests to start a build in Jenkins. '''
        pr_id = msg['pullrequest']['id']
        project = msg['pullrequest']['project']['name']
        branch = msg['pullrequest']['branch_from']

        for cfg in jenkins_hook.get_configs(project, jenkins_hook.Service.PAGURE):
            repo = msg['pullrequest'].get('remote_git') or get_repo(cfg, msg)
            self.log.info("Trigger on %s PR #%s from %s: %s",
                          project, pr_id, repo, branch)

            pagure_ci.process_pr(self.log, cfg, pr_id, repo, branch)

    def process_build(self, msg):
        ''' Extracts the information from the build and flag the pull-request. '''
        for cfg in jenkins_hook.get_configs(msg['project'], jenkins_hook.Service.JENKINS):
            pagure_ci.process_build(self.log, cfg, msg['build'])

def get_repo(cfg, msg):
    ''' Formats the URL for pagure repo. '''
    url = PAGURE_MAIN_REPO
    if msg['pullrequest']['repo_from']['parent']:
        url = PAGURE_FORK_REPO
    return url.format(
        base=APP.config['APP_URL'],
        user=msg['pullrequest']['repo_from']['user']['name'],
        name=msg['pullrequest']['repo_from']['name'])

def is_rebase(msg):
    ''' Returns Rebase if the Pull-request is rebased. '''
    if msg['pullrequest']['status'] != 'Open':
        return False
    try:
        return msg['pullrequest']['comments'][-1]['notification']
    except (IndexError, KeyError):
        return False