# -*- coding: utf-8 -*- """ (c) 2014-2016 - Copyright Red Hat Inc Authors: Pierre-Yves Chibon """ # pylint: disable=no-member # pylint: disable=too-many-lines # pylint: disable=too-many-branches # pylint: disable=too-many-locals # pylint: disable=too-many-statements # pylint: disable=bare-except # pylint: disable=broad-except 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, admin_session_timedout) @APP.route('/.git') @APP.route('//.git') @APP.route('/fork//.git') @APP.route('/fork///.git') def view_repo_git(repo, username=None, namespace=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, namespace=namespace)) @APP.route('//') @APP.route('/') @APP.route('///') @APP.route('//') @APP.route('/fork///') @APP.route('/fork//') @APP.route('/fork////') @APP.route('/fork///') def view_repo(repo, username=None, namespace=None): """ Front page of a specific repo. """ repo_db = flask.g.repo reponame = flask.g.reponame repo_obj = flask.g.repo_obj 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_db.name, identifier=branchname, filename='')) diff_commits = [] if repo_db.is_fork and repo_db.parent: parentname = os.path.join( APP.config['GIT_FOLDER'], repo_db.parent.path) else: parentname = os.path.join(APP.config['GIT_FOLDER'], repo_db.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_db, username=username, head=head, readme=readme, safe=safe, origin='view_repo', branchname=branchname, last_commits=last_commits, tree=tree, diff_commits=diff_commits, form=pagure.forms.ConfirmationForm(), ) @APP.route('//branch/') @APP.route('///branch/') @APP.route('/fork///branch/') @APP.route('/fork////branch/') def view_repo_branch(repo, branchname, username=None, namespace=None): ''' Returns the list of branches in the repo. ''' repo = flask.g.repo reponame = flask.g.reponame repo_obj = flask.g.repo_obj 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 and repo.parent: 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 commit_list = [] if compare_branch: commit_list = [ 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 commit_list: 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, namespace=repo.namespace, repo=repo.name, identifier=branchname, filename='')) return flask.render_template( 'repo_info.html', select='overview', repo=repo, head=head, username=username, branchname=branchname, origin='view_repo_branch', last_commits=last_commits, tree=tree, safe=safe, readme=readme, diff_commits=diff_commits, form=pagure.forms.ConfirmationForm(), ) @APP.route('//commits/') @APP.route('//commits') @APP.route('//commits/') @APP.route('///commits/') @APP.route('///commits') @APP.route('///commits/') @APP.route('/fork///commits/') @APP.route('/fork///commits') @APP.route('/fork///commits/') @APP.route('/fork////commits/') @APP.route('/fork////commits') @APP.route('/fork////commits/') def view_commits(repo, branchname=None, username=None, namespace=None): """ Displays the commits of the specified repo. """ repo = flask.g.repo reponame = flask.g.reponame repo_obj = flask.g.repo_obj 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 and repo.parent: 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 commit_list = [] if compare_branch: commit_list = [ 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 commit_list: break diff_commits.append(commit.oid.hex) diff_commits_full.append(commit) return flask.render_template( 'commits.html', select='commits', origin='view_commits', repo=repo, username=username, head=head, 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, form=pagure.forms.ConfirmationForm(), ) @APP.route('//c/../') @APP.route('//c/..') @APP.route('///c/../') @APP.route('///c/..') @APP.route('/fork///c/../') @APP.route('/fork///c/..') @APP.route('/fork////c/../') @APP.route('/fork////c/..') def compare_commits(repo, commit1, commit2, username=None, namespace=None): """ Compares two commits for specified repo """ repo = flask.g.repo reponame = flask.g.reponame repo_obj = flask.g.repo_obj 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 = [ commit.oid.hex[:len(first_commit)] for commit in 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, ) @APP.route('//blob//f/') @APP.route('///blob//f/') @APP.route( '/fork///blob//f/') @APP.route( '/fork////blob//f/') def view_file(repo, identifier, filename, username=None, namespace=None): """ Displays the content of a file or a tree for the specified repo. """ repo = flask.g.repo reponame = flask.g.reponame repo_obj = flask.g.repo_obj 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, branchname=branchname, filename=filename, content=content, output_type=output_type, readme=readme, readme_ext=readme_ext, safe=safe, ), 200, headers ) @APP.route('//raw/',) @APP.route('///raw/',) @APP.route('//raw//f/') @APP.route('///raw//f/') @APP.route('/fork///raw/') @APP.route('/fork////raw/') @APP.route( '/fork///raw//f/') @APP.route( '/fork////raw//f/') def view_raw_file( repo, identifier, filename=None, username=None, namespace=None): """ Displays the raw content of a file of a commit for the specified repo. """ repo = flask.g.repo reponame = flask.g.reponame repo_obj = flask.g.repo_obj 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) @APP.route('//c//') @APP.route('//c/') @APP.route('///c//') @APP.route('///c/') @APP.route('/fork///c//') @APP.route('/fork///c/') @APP.route('/fork////c//') @APP.route('/fork////c/') def view_commit(repo, commitid, username=None, namespace=None): """ Render a commit in a repo """ repo = flask.g.repo reponame = flask.g.reponame repo_obj = flask.g.repo_obj 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, commitid=commitid, commit=commit, diff=diff, form=pagure.forms.ConfirmationForm(), ) @APP.route('//c/.patch') @APP.route('///c/.patch') @APP.route('/fork///c/.patch') @APP.route('/fork////c/.patch') def view_commit_patch(repo, commitid, username=None, namespace=None): """ Render a commit in a repo as patch """ repo = flask.g.repo reponame = flask.g.reponame repo_obj = flask.g.repo_obj 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('//tree/') @APP.route('//tree') @APP.route('///tree/') @APP.route('///tree') @APP.route('//tree/') @APP.route('///tree/') @APP.route('/fork///tree/') @APP.route('/fork///tree') @APP.route('/fork////tree/') @APP.route('/fork////tree') @APP.route('/fork///tree/') @APP.route('/fork////tree/') def view_tree(repo, identifier=None, username=None, namespace=None): """ Render the tree of the repo """ repo = flask.g.repo reponame = flask.g.reponame repo_obj = flask.g.repo_obj 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 not repo_obj.head_is_unborn: commit = repo_obj[repo_obj.head.target] branchname = repo_obj.head.shorthand # 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 and not isinstance(commit, pygit2.Blob): 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', origin='view_tree', repo=repo, username=username, branchname=branchname, filename='', content=content, output_type=output_type, readme=readme, readme_ext=readme_ext, safe=safe, ) @APP.route('//forks/') @APP.route('//forks') @APP.route('///forks/') @APP.route('///forks') @APP.route('/fork///forks/') @APP.route('/fork///forks') @APP.route('/fork////forks/') @APP.route('/fork////forks') def view_forks(repo, username=None, namespace=None): """ Presents all the forks of the project. """ repo = flask.g.repo reponame = flask.g.reponame repo_obj = flask.g.repo_obj return flask.render_template( 'forks.html', select='forks', username=username, repo=repo, ) @APP.route('//releases/') @APP.route('//releases') @APP.route('///releases/') @APP.route('///releases') @APP.route('/fork///releases/') @APP.route('/fork///releases') @APP.route('/fork////releases/') @APP.route('/fork////releases') def view_tags(repo, username=None, namespace=None): """ Presents all the tags of the project. """ repo = flask.g.repo reponame = flask.g.reponame repo_obj = flask.g.repo_obj tags = pagure.lib.git.get_git_tags_objects(repo) return flask.render_template( 'releases.html', select='tags', username=username, repo=repo, tags=tags, ) @APP.route('//upload/', methods=('GET', 'POST')) @APP.route('//upload', methods=('GET', 'POST')) @APP.route('///upload/', methods=('GET', 'POST')) @APP.route('///upload', methods=('GET', 'POST')) @APP.route('/fork///upload/', methods=('GET', 'POST')) @APP.route('/fork///upload', methods=('GET', 'POST')) @APP.route( '/fork////upload/', methods=('GET', 'POST')) @APP.route( '/fork////upload', methods=('GET', 'POST')) @login_required def new_release(repo, username=None, namespace=None): """ Upload a new release. """ if not APP.config.get('UPLOAD_FOLDER_PATH') \ and not APP.config.get('UPLOAD_FOLDER'): flask.abort(404) repo = flask.g.repo if not flask.g.repo_admin: 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'], 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, namespace=repo.namespace)) return flask.render_template( 'new_release.html', select='tags', username=username, repo=repo, form=form, ) @APP.route('//settings/', methods=('GET', 'POST')) @APP.route('//settings', methods=('GET', 'POST')) @APP.route('///settings/', methods=('GET', 'POST')) @APP.route('///settings', methods=('GET', 'POST')) @APP.route('/fork///settings/', methods=('GET', 'POST')) @APP.route('/fork///settings', methods=('GET', 'POST')) @APP.route( '/fork////settings/', methods=('GET', 'POST')) @APP.route( '/fork////settings', methods=('GET', 'POST')) @login_required def view_settings(repo, username=None, namespace=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 = flask.g.repo reponame = flask.g.reponame repo_obj = flask.g.repo_obj if not flask.g.repo_admin: flask.abort( 403, 'You are not allowed to change the settings for this project') 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, namespace=repo.namespace)) 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, form=form, tag_form=tag_form, branches_form=branches_form, tags=tags, plugins=plugins, branchname=branchname, ) @APP.route('//update', methods=['POST']) @APP.route('///update', methods=['POST']) @APP.route('/fork///update', methods=['POST']) @APP.route('/fork////update', methods=['POST']) @login_required def update_project(repo, username=None, namespace=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, namespace=namespace) return flask.redirect( flask.url_for('auth_login', next=url)) repo = flask.g.repo if not flask.g.repo_admin: 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, namespace=repo.namespace)) @APP.route('//update/priorities', methods=['POST']) @APP.route('///update/priorities', methods=['POST']) @APP.route('/fork///update/priorities', methods=['POST']) @APP.route( '/fork////update/priorities', methods=['POST']) @login_required def update_priorities(repo, username=None, namespace=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, namespace=namespace) return flask.redirect( flask.url_for('auth_login', next=url)) repo = flask.g.repo if not repo.settings.get('issue_tracker', True): flask.abort(404, 'No issue tracker found for this project') if not flask.g.repo_admin: 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, namespace=repo.namespace)) @APP.route('//update/milestones', methods=['POST']) @APP.route('///update/milestones', methods=['POST']) @APP.route('/fork///update/milestones', methods=['POST']) @APP.route( '/fork////update/milestones', methods=['POST']) @login_required def update_milestones(repo, username=None, namespace=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, namespace=namespace) return flask.redirect( flask.url_for('auth_login', next=url)) repo = flask.g.repo if not repo.settings.get('issue_tracker', True): flask.abort(404, 'No issue tracker found for this project') if not flask.g.repo_admin: 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, namespace=namespace)) @APP.route('//default/branch/', methods=['POST']) @APP.route('///default/branch/', methods=['POST']) @APP.route('/fork///default/branch/', methods=['POST']) @APP.route( '/fork////default/branch/', methods=['POST']) @login_required def change_ref_head(repo, username=None, namespace=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, namespace=namespace) return flask.redirect( flask.url_for('auth_login', next=url)) repo = flask.g.repo reponame = flask.g.reponame repo_obj = flask.g.repo_obj if not flask.g.repo_admin: flask.abort( 403, 'You are not allowed to change the settings for this project') 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, namespace=namespace)) @APP.route('//delete', methods=['POST']) @APP.route('///delete', methods=['POST']) @APP.route('/fork///delete', methods=['POST']) @APP.route('/fork////delete', methods=['POST']) @login_required def delete_repo(repo, username=None, namespace=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, namespace=namespace) return flask.redirect( flask.url_for('auth_login', next=url)) repo = flask.g.repo if not flask.g.repo_admin: 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('//hook_token', methods=['POST']) @APP.route('///hook_token', methods=['POST']) @APP.route('/fork///hook_token', methods=['POST']) @APP.route( '/fork////hook_token', methods=['POST']) @login_required def new_repo_hook_token(repo, username=None, namespace=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, namespace=namespace) return flask.redirect( flask.url_for('auth_login', next=url)) repo = flask.g.repo if not flask.g.repo_admin: 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, namespace=namespace)) @APP.route('//dropuser/', methods=['POST']) @APP.route('///dropuser/', methods=['POST']) @APP.route('/fork///dropuser/', methods=['POST']) @APP.route('/fork////dropuser/', methods=['POST']) @login_required def remove_user(repo, userid, username=None, namespace=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, namespace=namespace) return flask.redirect( flask.url_for('auth_login', next=url)) repo = flask.g.repo if not flask.g.repo_admin: 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, namespace=repo.namespace,) ) 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, namespace=namespace)) @APP.route('//adduser/', methods=('GET', 'POST')) @APP.route('//adduser', methods=('GET', 'POST')) @APP.route('///adduser/', methods=('GET', 'POST')) @APP.route('///adduser', methods=('GET', 'POST')) @APP.route('/fork///adduser/', methods=('GET', 'POST')) @APP.route('/fork///adduser', methods=('GET', 'POST')) @APP.route( '/fork////adduser/', methods=('GET', 'POST')) @APP.route( '/fork////adduser', methods=('GET', 'POST')) @login_required def add_user(repo, username=None, namespace=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 = flask.g.repo if not flask.g.repo_admin: 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, namespace=namespace)) 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('//dropgroup/', methods=['POST']) @APP.route('///dropgroup/', methods=['POST']) @APP.route( '/fork///dropgroup/', methods=['POST']) @APP.route( '/fork////dropgroup/', methods=['POST']) @login_required def remove_group_project(repo, groupid, username=None, namespace=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, namespace=namespace) return flask.redirect( flask.url_for('auth_login', next=url)) repo = flask.g.repo if not flask.g.repo_admin: 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, namespace=namespace)) 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, namespace=namespace)) @APP.route('//addgroup/', methods=('GET', 'POST')) @APP.route('//addgroup', methods=('GET', 'POST')) @APP.route('///addgroup/', methods=('GET', 'POST')) @APP.route('///addgroup', methods=('GET', 'POST')) @APP.route('/fork///addgroup/', methods=('GET', 'POST')) @APP.route('/fork///addgroup', methods=('GET', 'POST')) @APP.route( '/fork////addgroup/', methods=('GET', 'POST')) @APP.route( '/fork////addgroup', methods=('GET', 'POST')) @login_required def add_group_project(repo, username=None, namespace=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 = flask.g.repo if not flask.g.repo_admin: 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, namespace=namespace)) 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('//regenerate', methods=['POST']) @APP.route('///regenerate', methods=['POST']) @APP.route('/fork///regenerate', methods=['POST']) @APP.route('/fork////regenerate', methods=['POST']) @login_required def regenerate_git(repo, username=None, namespace=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, namespace=namespace) return flask.redirect( flask.url_for('auth_login', next=url)) repo = flask.g.repo if not flask.g.repo_admin: 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, namespace=namespace)) @APP.route('//token/new/', methods=('GET', 'POST')) @APP.route('//token/new', methods=('GET', 'POST')) @APP.route('///token/new/', methods=('GET', 'POST')) @APP.route('///token/new', methods=('GET', 'POST')) @APP.route('/fork///token/new/', methods=('GET', 'POST')) @APP.route('/fork///token/new', methods=('GET', 'POST')) @APP.route( '/fork////token/new/', methods=('GET', 'POST')) @APP.route( '/fork////token/new', methods=('GET', 'POST')) @login_required def add_token(repo, username=None, namespace=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 = flask.g.repo if not flask.g.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, namespace=namespace)) 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, username=username, repo=repo, ) @APP.route('//token/revoke/', methods=['POST']) @APP.route('///token/revoke/', methods=['POST']) @APP.route( '/fork///token/revoke/', methods=['POST']) @APP.route( '/fork////token/revoke/', methods=['POST']) @login_required def revoke_api_token(repo, token_id, username=None, namespace=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, namespace=namespace) return flask.redirect( flask.url_for('auth_login', next=url)) repo = flask.g.repo if not flask.g.repo_admin: 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, namespace=namespace)) @APP.route( '//edit//f/', methods=('GET', 'POST')) @APP.route( '///edit//f/', methods=('GET', 'POST')) @APP.route( '/fork///edit//f/', methods=('GET', 'POST')) @APP.route( '/fork////edit//f/', methods=('GET', 'POST')) @login_required def edit_file(repo, branchname, filename, username=None, namespace=None): """ Edit a file online. """ repo = flask.g.repo reponame = flask.g.reponame repo_obj = flask.g.repo_obj if not flask.g.repo_admin: 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) 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, namespace=namespace, 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, ) @APP.route('//b//delete', methods=['POST']) @APP.route('///b//delete', methods=['POST']) @APP.route('/fork///b//delete', methods=['POST']) @APP.route('/fork////b//delete', methods=['POST']) @login_required def delete_branch(repo, branchname, username=None, namespace=None): """ Delete the branch of a project. """ reponame = flask.g.reponame repo_obj = flask.g.repo_obj if not flask.g.repo_admin: 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') if branchname not in repo_obj.listall_branches(): flask.abort(404, 'Branch no found') try: branch = repo_obj.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, namespace=namespace)) @APP.route('/docs//') @APP.route('/docs//') @APP.route('/docs///') @APP.route('/docs///') @APP.route('/docs/fork///') @APP.route('/docs/fork////') @APP.route('/docs/fork///') @APP.route('/docs/fork////') def view_docs(repo, username=None, filename=None, namespace=None): """ Display the documentation """ repo = flask.g.repo 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, username=username, filename=filename, endpoint='view_docs', ) @APP.route('//activity/') @APP.route('//activity') @APP.route('///activity/') @APP.route('///activity') def view_project_activity(repo, namespace=None): """ Display the activity feed """ if not APP.config.get('DATAGREPPER_URL'): flask.abort(404) repo = flask.g.repo return flask.render_template( 'activity.html', repo=repo, ) @APP.route('//watch/settings/', methods=['POST']) @APP.route('///watch/settings/', methods=['POST']) @APP.route( '/fork///watch/settings/', methods=['POST']) @APP.route( '/fork////watch/settings/', methods=['POST']) @login_required def watch_repo(repo, watch, username=None, namespace=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) try: msg = pagure.lib.update_watch_status( SESSION, flask.g.repo, flask.g.fas_user.username, watch) SESSION.commit() flask.flash(msg) except pagure.exceptions.PagureException as msg: flask.flash(msg, 'error') return flask.redirect(return_point) @APP.route('//update/public_notif', methods=['POST']) @APP.route('///public_notif', methods=['POST']) @APP.route('/fork///public_notif', methods=['POST']) @APP.route( '/fork////public_notif', methods=['POST']) @login_required def update_public_notifications(repo, username=None, namespace=None): """ Update the public notification settings 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, namespace=namespace) return flask.redirect( flask.url_for('auth_login', next=url)) repo = flask.g.repo if not flask.g.repo_admin: flask.abort( 403, 'You are not allowed to change the settings for this project') form = pagure.forms.PublicNotificationForm() error = False if form.validate_on_submit(): issue_notifs = [ w.strip() for w in form.issue_notifs.data.split(',') if w.strip() ] pr_notifs = [ w.strip() for w in form.pr_notifs.data.split(',') if w.strip() ] try: notifs = repo.notifications notifs['issues'] = issue_notifs notifs['requests'] = pr_notifs repo.notifications = notifs SESSION.add(repo) SESSION.commit() flask.flash('Project updated') except SQLAlchemyError as err: # pragma: no cover SESSION.rollback() flask.flash(str(err), 'error') else: flask.flash( 'Unable to adjust one or more of the email provided', 'error') return flask.redirect(flask.url_for( 'view_settings', username=username, repo=repo.name, namespace=repo.namespace))