Blame progit/ui/app.py

Pierre-Yves Chibon 33b534
# -*- coding: utf-8 -*-
Pierre-Yves Chibon 2088eb
Pierre-Yves Chibon 2088eb
"""
Pierre-Yves Chibon 2088eb
 (c) 2014 - Copyright Red Hat Inc
Pierre-Yves Chibon 2088eb
Pierre-Yves Chibon 2088eb
 Authors:
Pierre-Yves Chibon 2088eb
   Pierre-Yves Chibon <pingou@pingoured.fr></pingou@pingoured.fr>
Pierre-Yves Chibon 2088eb
Pierre-Yves Chibon 2088eb
"""
Pierre-Yves Chibon 2088eb
Pierre-Yves Chibon 2088eb
import flask
Pierre-Yves Chibon 2088eb
import os
Pierre-Yves Chibon 2088eb
from math import ceil
Pierre-Yves Chibon 2088eb
Pierre-Yves Chibon 2088eb
import pygit2
Pierre-Yves Chibon 2088eb
from sqlalchemy.exc import SQLAlchemyError
Pierre-Yves Chibon 2088eb
from pygments import highlight
Pierre-Yves Chibon 2088eb
from pygments.lexers import guess_lexer
Pierre-Yves Chibon 2088eb
from pygments.lexers.text import DiffLexer
Pierre-Yves Chibon 2088eb
from pygments.formatters import HtmlFormatter
Pierre-Yves Chibon 2088eb
Pierre-Yves Chibon 8a621f
import progit.exceptions
Pierre-Yves Chibon 8a621f
import progit.lib
Pierre-Yves Chibon 8a621f
import progit.forms
Pierre-Yves Chibon c18283
from progit import (APP, SESSION, LOG, __get_file_in_tree, cla_required,
Pierre-Yves Chibon 4c609f
                    generate_gitolite_acls, generate_gitolite_key,
Pierre-Yves Chibon 06abd9
                    generate_authorized_key_file, markdown_filter)
Pierre-Yves Chibon 2088eb
Pierre-Yves Chibon 2088eb
Pierre-Yves Chibon 5f48ea
def chunks(item_list, chunks_size):
Pierre-Yves Chibon 5f48ea
    """ Yield successive n-sized chunks from item_list.
Pierre-Yves Chibon 5f48ea
    """
Pierre-Yves Chibon 5f48ea
    for i in xrange(0, len(item_list), chunks_size):
Pierre-Yves Chibon 5f48ea
        yield item_list[i: i + chunks_size]
Pierre-Yves Chibon 5f48ea
Pierre-Yves Chibon 5f48ea
Pierre-Yves Chibon 2088eb
### Application
Pierre-Yves Chibon 2088eb
Pierre-Yves Chibon 2088eb
Pierre-Yves Chibon 47950c
@APP.route('/')
Pierre-Yves Chibon 47950c
def index():
Pierre-Yves Chibon 47950c
    """ Front page of the application.
Pierre-Yves Chibon 2088eb
    """
Pierre-Yves Chibon 47950c
    page = flask.request.args.get('page', 1)
Pierre-Yves Chibon 2088eb
    try:
Pierre-Yves Chibon 47950c
        page = int(page)
Pierre-Yves Chibon 2088eb
    except ValueError:
Pierre-Yves Chibon 2088eb
        page = 1
Pierre-Yves Chibon 2088eb
Pierre-Yves Chibon 2088eb
    limit = APP.config['ITEM_PER_PAGE']
Pierre-Yves Chibon 2088eb
    start = limit * (page - 1)
Pierre-Yves Chibon 2088eb
Pierre-Yves Chibon 89ab20
    repos = progit.lib.search_projects(
Pierre-Yves Chibon 47950c
        SESSION,
Pierre-Yves Chibon 47950c
        fork=False,
Pierre-Yves Chibon 47950c
        start=start,
Pierre-Yves Chibon 47950c
        limit=limit)
Pierre-Yves Chibon 89ab20
    num_repos = progit.lib.search_projects(
Pierre-Yves Chibon 47950c
        SESSION,
Pierre-Yves Chibon 47950c
        fork=False,
Pierre-Yves Chibon 47950c
        count=True)
Pierre-Yves Chibon 47950c
Pierre-Yves Chibon 47950c
    total_page = int(ceil(num_repos / float(limit)))
Pierre-Yves Chibon bd2213
Johan Cwiklinski b95406
    repopage = None
Johan Cwiklinski b95406
    forkpage = None
Johan Cwiklinski b95406
    user_repos = None
Johan Cwiklinski b95406
    user_forks = None
Johan Cwiklinski b95406
    total_page_repos = None
Johan Cwiklinski b95406
    total_page_forks = None
Johan Cwiklinski b95406
    repos_obj = None
Johan Cwiklinski b95406
    forks_obj = None
Johan Cwiklinski b95406
    username = None
Johan Cwiklinski 4fbfc0
    user_repos_length = None
Johan Cwiklinski 4fbfc0
    user_forks_length = None
Johan Cwiklinski b95406
Johan Cwiklinski b95406
    if flask.g.fas_user:
Johan Cwiklinski b95406
        username = flask.g.fas_user.username
Johan Cwiklinski b95406
Johan Cwiklinski b95406
        repopage = flask.request.args.get('repopage', 1)
Johan Cwiklinski b95406
        try:
Johan Cwiklinski b95406
            repopage = int(repopage)
Johan Cwiklinski b95406
        except ValueError:
Johan Cwiklinski b95406
            repopage = 1
Johan Cwiklinski b95406
Johan Cwiklinski b95406
        forkpage = flask.request.args.get('forkpage', 1)
Johan Cwiklinski b95406
        try:
Johan Cwiklinski b95406
            forkpage = int(forkpage)
Johan Cwiklinski b95406
        except ValueError:
Johan Cwiklinski b95406
            forkpage = 1
Johan Cwiklinski b95406
Johan Cwiklinski b95406
        repo_start = limit * (repopage - 1)
Johan Cwiklinski b95406
        fork_start = limit * (forkpage - 1)
Johan Cwiklinski b95406
Pierre-Yves Chibon 89ab20
        user_repos = progit.lib.search_projects(
Johan Cwiklinski b95406
            SESSION,
Johan Cwiklinski b95406
            username=username,
Johan Cwiklinski b95406
            fork=False,
Johan Cwiklinski b95406
            start=repo_start,
Johan Cwiklinski b95406
            limit=limit)
Pierre-Yves Chibon 89ab20
        user_repos_length = progit.lib.search_projects(
Johan Cwiklinski b95406
            SESSION,
Johan Cwiklinski b95406
            username=username,
Johan Cwiklinski b95406
            fork=False,
Johan Cwiklinski b95406
            count=True)
Johan Cwiklinski b95406
Pierre-Yves Chibon 89ab20
        user_forks = progit.lib.search_projects(
Johan Cwiklinski b95406
            SESSION,
Johan Cwiklinski b95406
            username=username,
Johan Cwiklinski b95406
            fork=True,
Johan Cwiklinski b95406
            start=fork_start,
Johan Cwiklinski b95406
            limit=limit)
Pierre-Yves Chibon 89ab20
        user_forks_length = progit.lib.search_projects(
Johan Cwiklinski b95406
            SESSION,
Johan Cwiklinski b95406
            username=username,
Johan Cwiklinski b95406
            fork=True,
Johan Cwiklinski b95406
            count=True)
Johan Cwiklinski b95406
Johan Cwiklinski b95406
        total_page_repos = int(ceil(user_repos_length / float(limit)))
Johan Cwiklinski b95406
        total_page_forks = int(ceil(user_forks_length / float(limit)))
Johan Cwiklinski b95406
Johan Cwiklinski 63918a
        repos_obj = [
Johan Cwiklinski 63918a
            pygit2.Repository(
Johan Cwiklinski 63918a
                os.path.join(APP.config['GIT_FOLDER'], repo.path))
Johan Cwiklinski 63918a
            for repo in user_repos]
Johan Cwiklinski b95406
Johan Cwiklinski 63918a
        forks_obj = [
Johan Cwiklinski 63918a
            pygit2.Repository(
Johan Cwiklinski 63918a
                os.path.join(APP.config['FORK_FOLDER'], repo.path))
Johan Cwiklinski 63918a
            for repo in user_forks]
Johan Cwiklinski b95406
Pierre-Yves Chibon 2088eb
    return flask.render_template(
Pierre-Yves Chibon 47950c
        'index.html',
Johan Cwiklinski b95406
        repos=repos,
Pierre-Yves Chibon 2088eb
        total_page=total_page,
Pierre-Yves Chibon 47950c
        page=page,
Johan Cwiklinski b95406
        username=username,
Johan Cwiklinski b95406
        repopage=repopage,
Johan Cwiklinski b95406
        forkpage=forkpage,
Johan Cwiklinski b95406
        user_repos=user_repos,
Johan Cwiklinski b95406
        user_forks=user_forks,
Johan Cwiklinski b95406
        total_page_repos=total_page_repos,
Johan Cwiklinski b95406
        total_page_forks=total_page_forks,
Johan Cwiklinski b95406
        repos_obj=repos_obj,
Johan Cwiklinski b95406
        forks_obj=forks_obj,
Johan Cwiklinski 5e96ef
        user_repos_length=user_repos_length,
Johan Cwiklinski 5e96ef
        user_forks_length=user_forks_length,
Johan Cwiklinski 5e96ef
        repos_length=num_repos,
Pierre-Yves Chibon 2088eb
    )
Pierre-Yves Chibon 2088eb
Pierre-Yves Chibon 2088eb
Pierre-Yves Chibon 47950c
@APP.route('/users/')
Pierre-Yves Chibon 47950c
def view_users():
Pierre-Yves Chibon 47950c
    """ Present the list of users.
Pierre-Yves Chibon 2088eb
    """
Pierre-Yves Chibon 47950c
    page = flask.request.args.get('page', 1)
Pierre-Yves Chibon 47950c
    try:
Pierre-Yves Chibon 47950c
        page = int(page)
Pierre-Yves Chibon 47950c
    except ValueError:
Pierre-Yves Chibon 47950c
        page = 1
Mathieu Bridon 4c73b0
Pierre-Yves Chibon a1a039
    users = progit.lib.search_user(SESSION)
Mathieu Bridon 4c73b0
Pierre-Yves Chibon 47950c
    limit = APP.config['ITEM_PER_PAGE']
Pierre-Yves Chibon 47950c
    start = limit * (page - 1)
Pierre-Yves Chibon 47950c
    end = limit * page
Pierre-Yves Chibon 47950c
    users_length = len(users)
Pierre-Yves Chibon 47950c
    users = users[start:end]
Pierre-Yves Chibon 2088eb
Pierre-Yves Chibon 47950c
    total_page = int(ceil(users_length / float(limit)))
Pierre-Yves Chibon 2088eb
Pierre-Yves Chibon 2088eb
    return flask.render_template(
Pierre-Yves Chibon 47950c
        'user_list.html',
Pierre-Yves Chibon 47950c
        users=users,
Pierre-Yves Chibon 4cdfa6
        users_length=users_length,
Pierre-Yves Chibon 47950c
        total_page=total_page,
Pierre-Yves Chibon 47950c
        page=page,
Pierre-Yves Chibon 2088eb
    )
Pierre-Yves Chibon 2088eb
Pierre-Yves Chibon 84f83a
Pierre-Yves Chibon 47950c
@APP.route('/user/<username>')</username>
Pierre-Yves Chibon 47950c
def view_user(username):
Pierre-Yves Chibon 47950c
    """ Front page of a specific user.
Pierre-Yves Chibon 2088eb
    """
Pierre-Yves Chibon 2088eb
Pierre-Yves Chibon 47950c
    repopage = flask.request.args.get('repopage', 1)
Pierre-Yves Chibon 2088eb
    try:
Pierre-Yves Chibon 47950c
        repopage = int(repopage)
Pierre-Yves Chibon 2088eb
    except ValueError:
Pierre-Yves Chibon 47950c
        repopage = 1
Mathieu Bridon 6dd1b4
Pierre-Yves Chibon 47950c
    forkpage = flask.request.args.get('forkpage', 1)
Pierre-Yves Chibon 47950c
    try:
Pierre-Yves Chibon 47950c
        forkpage = int(forkpage)
Pierre-Yves Chibon 47950c
    except ValueError:
Pierre-Yves Chibon 47950c
        forkpage = 1
Pierre-Yves Chibon 12e862
Pierre-Yves Chibon 47950c
    limit = APP.config['ITEM_PER_PAGE']
Pierre-Yves Chibon 47950c
    repo_start = limit * (repopage - 1)
Pierre-Yves Chibon 47950c
    fork_start = limit * (forkpage - 1)
Pierre-Yves Chibon 47950c
Pierre-Yves Chibon 89ab20
    repos = progit.lib.search_projects(
Pierre-Yves Chibon 47950c
        SESSION,
Pierre-Yves Chibon e480a3
        username=username,
Pierre-Yves Chibon 47950c
        fork=False,
Pierre-Yves Chibon 47950c
        start=repo_start,
Pierre-Yves Chibon 47950c
        limit=limit)
Pierre-Yves Chibon 89ab20
    repos_length = progit.lib.search_projects(
Pierre-Yves Chibon 47950c
        SESSION,
Pierre-Yves Chibon e480a3
        username=username,
Pierre-Yves Chibon 47950c
        fork=False,
Pierre-Yves Chibon 47950c
        count=True)
Pierre-Yves Chibon 47950c
Pierre-Yves Chibon 89ab20
    forks = progit.lib.search_projects(
Pierre-Yves Chibon 47950c
        SESSION,
Pierre-Yves Chibon e480a3
        username=username,
Pierre-Yves Chibon 47950c
        fork=True,
Pierre-Yves Chibon 47950c
        start=fork_start,
Pierre-Yves Chibon 47950c
        limit=limit)
Pierre-Yves Chibon 89ab20
    forks_length = progit.lib.search_projects(
Pierre-Yves Chibon 47950c
        SESSION,
Pierre-Yves Chibon e480a3
        username=username,
Pierre-Yves Chibon 47950c
        fork=True,
Pierre-Yves Chibon 47950c
        count=True)
Pierre-Yves Chibon 47950c
Pierre-Yves Chibon 47950c
    total_page_repos = int(ceil(repos_length / float(limit)))
Pierre-Yves Chibon 47950c
    total_page_forks = int(ceil(forks_length / float(limit)))
Pierre-Yves Chibon 47950c
Pierre-Yves Chibon 47950c
    repos_obj = [
Pierre-Yves Chibon 47950c
        pygit2.Repository(
Pierre-Yves Chibon 47950c
            os.path.join(APP.config['GIT_FOLDER'], repo.path))
Pierre-Yves Chibon 47950c
        for repo in repos]
Pierre-Yves Chibon 47950c
Pierre-Yves Chibon 47950c
    forks_obj = [
Pierre-Yves Chibon 47950c
        pygit2.Repository(
Pierre-Yves Chibon 47950c
            os.path.join(APP.config['FORK_FOLDER'], repo.path))
Pierre-Yves Chibon 47950c
        for repo in forks]
Pierre-Yves Chibon 12e862
Pierre-Yves Chibon 12e862
    return flask.render_template(
Pierre-Yves Chibon 47950c
        'user_info.html',
Pierre-Yves Chibon ac8023
        username=username,
Pierre-Yves Chibon 47950c
        repos=repos,
Pierre-Yves Chibon 47950c
        repos_obj=repos_obj,
Pierre-Yves Chibon 47950c
        total_page_repos=total_page_repos,
Pierre-Yves Chibon 47950c
        forks=forks,
Pierre-Yves Chibon 47950c
        forks_obj=forks_obj,
Pierre-Yves Chibon 47950c
        total_page_forks=total_page_forks,
Pierre-Yves Chibon 47950c
        repopage=repopage,
Pierre-Yves Chibon 47950c
        forkpage=forkpage,
Johan Cwiklinski 63918a
        repos_length=repos_length,
Johan Cwiklinski 63918a
        forks_length=forks_length,
Pierre-Yves Chibon 12e862
    )
Pierre-Yves Chibon eb2817
Pierre-Yves Chibon eb2817
Pierre-Yves Chibon 47950c
@APP.route('/new/', methods=('GET', 'POST'))
Pierre-Yves Chibon 47950c
@cla_required
Pierre-Yves Chibon 47950c
def new_project():
Pierre-Yves Chibon 47950c
    """ Form to create a new project.
Pierre-Yves Chibon a204a8
    """
Pierre-Yves Chibon 47950c
    form = progit.forms.ProjectForm()
Pierre-Yves Chibon a204a8
    if form.validate_on_submit():
Pierre-Yves Chibon 47950c
        name = form.name.data
Pierre-Yves Chibon 47950c
        description = form.description.data
Pierre-Yves Chibon a204a8
Pierre-Yves Chibon a204a8
        try:
Pierre-Yves Chibon 47950c
            message = progit.lib.new_project(
Pierre-Yves Chibon a204a8
                SESSION,
Pierre-Yves Chibon 47950c
                name=name,
Pierre-Yves Chibon 47950c
                description=description,
Pierre-Yves Chibon a204a8
                user=flask.g.fas_user.username,
Pierre-Yves Chibon 6160b6
                gitfolder=APP.config['GIT_FOLDER'],
Pierre-Yves Chibon 4b7a7d
                docfolder=APP.config['DOCS_FOLDER'],
Pierre-Yves Chibon 9f005d
                ticketfolder=APP.config['TICKETS_FOLDER']
Pierre-Yves Chibon a204a8
            )
Pierre-Yves Chibon a204a8
            SESSION.commit()
Pierre-Yves Chibon c18283
            generate_gitolite_acls()
Pierre-Yves Chibon a204a8
            flask.flash(message)
Pierre-Yves Chibon 47950c
            return flask.redirect(flask.url_for('view_repo', repo=name))
Pierre-Yves Chibon 542ff6
        except progit.exceptions.ProgitException, err:
Pierre-Yves Chibon 542ff6
            flask.flash(str(err), 'error')
Pierre-Yves Chibon 542ff6
        except SQLAlchemyError, err:  # pragma: no cover
Pierre-Yves Chibon 542ff6
            SESSION.rollback()
Pierre-Yves Chibon 542ff6
            flask.flash(str(err), 'error')
Pierre-Yves Chibon 542ff6
Pierre-Yves Chibon 542ff6
    return flask.render_template(
Pierre-Yves Chibon 47950c
        'new_project.html',
Pierre-Yves Chibon 542ff6
        form=form,
Pierre-Yves Chibon c30d42
    )
Pierre-Yves Chibon 68537a
Pierre-Yves Chibon 68537a
Pierre-Yves Chibon 68537a
@APP.route('/settings/', methods=('GET', 'POST'))
Pierre-Yves Chibon 68537a
@cla_required
Pierre-Yves Chibon 68537a
def user_settings():
Pierre-Yves Chibon 68537a
    """ Update the user settings.
Pierre-Yves Chibon 68537a
    """
Pierre-Yves Chibon f05535
    user = progit.lib.search_user(
Pierre-Yves Chibon f05535
        SESSION, username=flask.g.fas_user.username)
Pierre-Yves Chibon 68537a
Pierre-Yves Chibon 68537a
    form = progit.forms.UserSettingsForm()
Pierre-Yves Chibon 68537a
    if form.validate_on_submit():
Pierre-Yves Chibon 68537a
        ssh_key = form.ssh_key.data
Pierre-Yves Chibon 68537a
Pierre-Yves Chibon 68537a
        try:
Pierre-Yves Chibon 68537a
            message = progit.lib.update_user_ssh(
Pierre-Yves Chibon 68537a
                SESSION,
Pierre-Yves Chibon 68537a
                user=user,
Pierre-Yves Chibon 68537a
                ssh_key=ssh_key,
Pierre-Yves Chibon 68537a
            )
Pierre-Yves Chibon 265c53
            if message != 'Nothing to update':
Pierre-Yves Chibon 265c53
                generate_gitolite_key(user.user, ssh_key)
Pierre-Yves Chibon 265c53
                generate_authorized_key_file()
Pierre-Yves Chibon 68537a
            SESSION.commit()
Pierre-Yves Chibon 68537a
            flask.flash(message)
Pierre-Yves Chibon 68537a
            return flask.redirect(
Pierre-Yves Chibon 68537a
                flask.url_for('view_user', username=user.user))
Pierre-Yves Chibon 68537a
        except SQLAlchemyError, err:  # pragma: no cover
Pierre-Yves Chibon 68537a
            SESSION.rollback()
Pierre-Yves Chibon 68537a
            flask.flash(str(err), 'error')
Pierre-Yves Chibon 68537a
    elif flask.request.method == 'GET':
Pierre-Yves Chibon 68537a
        form.ssh_key.data = user.public_ssh_key
Pierre-Yves Chibon 68537a
Pierre-Yves Chibon 68537a
    return flask.render_template(
Pierre-Yves Chibon 68537a
        'user_settings.html',
Pierre-Yves Chibon 68537a
        user=user,
Pierre-Yves Chibon 68537a
        form=form,
Pierre-Yves Chibon 68537a
    )
Pierre-Yves Chibon 06abd9
Pierre-Yves Chibon 06abd9
Pierre-Yves Chibon 06abd9
@APP.route('/markdown/', methods=['POST'])
Pierre-Yves Chibon 06abd9
@cla_required
Pierre-Yves Chibon 06abd9
def markdown_preview():
Pierre-Yves Chibon 06abd9
    """ Return the provided markdown text in html.
Pierre-Yves Chibon 06abd9
Pierre-Yves Chibon 06abd9
    The text has to be provided via the parameter 'content' of a POST query.
Pierre-Yves Chibon 06abd9
    """
Pierre-Yves Chibon 06abd9
    return progit.markdown_filter(flask.request.form['content'])