|
Pierre-Yves Chibon |
2088eb |
#-*- 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 |
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 |
4c609f |
generate_authorized_key_file)
|
|
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 |
47950c |
repos = progit.lib.list_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 |
47950c |
num_repos = progit.lib.list_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 |
|
|
Pierre-Yves Chibon |
2088eb |
return flask.render_template(
|
|
Pierre-Yves Chibon |
47950c |
'index.html',
|
|
Pierre-Yves Chibon |
5f48ea |
repos=chunks(repos, 3),
|
|
Pierre-Yves Chibon |
2088eb |
total_page=total_page,
|
|
Pierre-Yves Chibon |
47950c |
page=page,
|
|
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 |
f65b61 |
users = progit.lib.get_all_users(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 |
47950c |
repos = progit.lib.list_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 |
47950c |
repos_length = progit.lib.list_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 |
47950c |
forks = progit.lib.list_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 |
47950c |
forks_length = progit.lib.list_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,
|
|
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 |
68537a |
user = progit.lib.get_user(SESSION, 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 |
)
|