Changeset - 5e46f73f0d1c
[Not reviewed]
kallithea/alembic/versions/a020f7044fd6_rename_hooks.py
Show inline comments
 
@@ -8,56 +8,56 @@
 
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
# GNU General Public License for more details.
 
#
 
# You should have received a copy of the GNU General Public License
 
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
 

	
 
"""rename hooks
 

	
 
Revision ID: a020f7044fd6
 
Revises: 9358dc3d6828
 
Create Date: 2017-11-24 13:35:14.374000
 

	
 
"""
 

	
 
# The following opaque hexadecimal identifiers ("revisions") are used
 
# by Alembic to track this migration script and its relations to others.
 
revision = 'a020f7044fd6'
 
down_revision = '9358dc3d6828'
 
branch_labels = None
 
depends_on = None
 

	
 
from alembic import op
 
from sqlalchemy import MetaData, Table
 

	
 
from kallithea.model.db import Ui
 
from kallithea.model import db
 

	
 

	
 
meta = MetaData()
 

	
 

	
 
def upgrade():
 
    meta.bind = op.get_bind()
 
    ui = Table(Ui.__tablename__, meta, autoload=True)
 
    ui = Table(db.Ui.__tablename__, meta, autoload=True)
 

	
 
    ui.update(values={
 
        'ui_key': 'prechangegroup.push_lock_handling',
 
        'ui_value': 'python:kallithea.lib.hooks.push_lock_handling',
 
    }).where(ui.c.ui_key == 'prechangegroup.pre_push').execute()
 
    ui.update(values={
 
        'ui_key': 'preoutgoing.pull_lock_handling',
 
        'ui_value': 'python:kallithea.lib.hooks.pull_lock_handling',
 
    }).where(ui.c.ui_key == 'preoutgoing.pre_pull').execute()
 

	
 

	
 
def downgrade():
 
    meta.bind = op.get_bind()
 
    ui = Table(Ui.__tablename__, meta, autoload=True)
 
    ui = Table(db.Ui.__tablename__, meta, autoload=True)
 

	
 
    ui.update(values={
 
        'ui_key': 'prechangegroup.pre_push',
 
        'ui_value': 'python:kallithea.lib.hooks.pre_push',
 
    }).where(ui.c.ui_key == 'prechangegroup.push_lock_handling').execute()
 
    ui.update(values={
 
        'ui_key': 'preoutgoing.pre_pull',
 
        'ui_value': 'python:kallithea.lib.hooks.pre_pull',
 
    }).where(ui.c.ui_key == 'preoutgoing.pull_lock_handling').execute()
kallithea/alembic/versions/ad357ccd9521_drop_locking.py
Show inline comments
 
@@ -9,53 +9,53 @@
 
# GNU General Public License for more details.
 
#
 
# You should have received a copy of the GNU General Public License
 
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
 

	
 
"""Drop locking
 

	
 
Revision ID: ad357ccd9521
 
Revises: a020f7044fd6
 
Create Date: 2019-01-08
 

	
 
"""
 

	
 
# The following opaque hexadecimal identifiers ("revisions") are used
 
# by Alembic to track this migration script and its relations to others.
 
revision = 'ad357ccd9521'
 
down_revision = 'a020f7044fd6'
 
branch_labels = None
 
depends_on = None
 

	
 
import sqlalchemy as sa
 
from alembic import op
 
from sqlalchemy import MetaData, Table
 

	
 
from kallithea.model.db import Ui
 
from kallithea.model import db
 

	
 

	
 
meta = MetaData()
 

	
 

	
 
def upgrade():
 
    with op.batch_alter_table('groups', schema=None) as batch_op:
 
        batch_op.drop_column('enable_locking')
 

	
 
    with op.batch_alter_table('repositories', schema=None) as batch_op:
 
        batch_op.drop_column('locked')
 
        batch_op.drop_column('enable_locking')
 

	
 
    meta.bind = op.get_bind()
 
    ui = Table(Ui.__tablename__, meta, autoload=True)
 
    ui = Table(db.Ui.__tablename__, meta, autoload=True)
 
    ui.delete().where(ui.c.ui_key == 'prechangegroup.push_lock_handling').execute()
 
    ui.delete().where(ui.c.ui_key == 'preoutgoing.pull_lock_handling').execute()
 

	
 

	
 
def downgrade():
 
    with op.batch_alter_table('repositories', schema=None) as batch_op:
 
        batch_op.add_column(sa.Column('enable_locking', sa.BOOLEAN(), nullable=False, default=False))
 
        batch_op.add_column(sa.Column('locked', sa.VARCHAR(length=255), nullable=True, default=False))
 

	
 
    with op.batch_alter_table('groups', schema=None) as batch_op:
 
        batch_op.add_column(sa.Column('enable_locking', sa.BOOLEAN(), nullable=False, default=False))
 

	
 
    # Note: not restoring hooks
kallithea/alembic/versions/f62826179f39_add_unique_constraint_on_.py
Show inline comments
 
@@ -8,66 +8,66 @@
 
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
# GNU General Public License for more details.
 
#
 
# You should have received a copy of the GNU General Public License
 
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
 

	
 
"""add unique constraint on PullRequestReviewer
 

	
 
Revision ID: f62826179f39
 
Revises: a0a1bf09c143
 
Create Date: 2020-06-15 12:30:37.420321
 

	
 
"""
 

	
 
# The following opaque hexadecimal identifiers ("revisions") are used
 
# by Alembic to track this migration script and its relations to others.
 
revision = 'f62826179f39'
 
down_revision = 'a0a1bf09c143'
 
branch_labels = None
 
depends_on = None
 

	
 
import sqlalchemy as sa
 
from alembic import op
 

	
 
from kallithea.model.db import PullRequestReviewer
 
from kallithea.model import db
 

	
 

	
 
def upgrade():
 
    session = sa.orm.session.Session(bind=op.get_bind())
 

	
 
    # there may be existing duplicates in the database, remove them first
 

	
 
    seen = set()
 
    # duplicate_values contains one copy of each duplicated pair
 
    duplicate_values = (
 
        session
 
        .query(PullRequestReviewer.pull_request_id, PullRequestReviewer.user_id)
 
        .group_by(PullRequestReviewer.pull_request_id, PullRequestReviewer.user_id)
 
        .having(sa.func.count(PullRequestReviewer.pull_request_reviewers_id) > 1)
 
        .query(db.PullRequestReviewer.pull_request_id, db.PullRequestReviewer.user_id)
 
        .group_by(db.PullRequestReviewer.pull_request_id, db.PullRequestReviewer.user_id)
 
        .having(sa.func.count(db.PullRequestReviewer.pull_request_reviewers_id) > 1)
 
    )
 

	
 
    for pull_request_id, user_id in duplicate_values:
 
        # duplicate_occurrences contains all db records of the duplicate_value
 
        # currently being processed
 
        duplicate_occurrences = (
 
            session
 
            .query(PullRequestReviewer)
 
            .filter(PullRequestReviewer.pull_request_id == pull_request_id)
 
            .filter(PullRequestReviewer.user_id == user_id)
 
            .query(db.PullRequestReviewer)
 
            .filter(db.PullRequestReviewer.pull_request_id == pull_request_id)
 
            .filter(db.PullRequestReviewer.user_id == user_id)
 
        )
 
        for prr in duplicate_occurrences:
 
            if (pull_request_id, user_id) in seen:
 
                session.delete(prr)
 
            else:
 
                seen.add((pull_request_id, user_id))
 

	
 
    session.commit()
 

	
 
    # after deleting all duplicates, add the unique constraint
 
    with op.batch_alter_table('pull_request_reviewers', schema=None) as batch_op:
 
        batch_op.create_unique_constraint(batch_op.f('uq_pull_request_reviewers_pull_request_id'), ['pull_request_id', 'user_id'])
 

	
 

	
 
def downgrade():
 
    with op.batch_alter_table('pull_request_reviewers', schema=None) as batch_op:
 
        batch_op.drop_constraint(batch_op.f('uq_pull_request_reviewers_pull_request_id'), type_='unique')
kallithea/bin/kallithea_cli_repo.py
Show inline comments
 
@@ -9,96 +9,95 @@
 
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
# GNU General Public License for more details.
 
#
 
# You should have received a copy of the GNU General Public License
 
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
"""
 
This file was forked by the Kallithea project in July 2014 and later moved.
 
Original author and date, and relevant copyright and licensing information is below:
 
:created_on: Feb 9, 2013
 
:author: marcink
 
:copyright: (c) 2013 RhodeCode GmbH, and others.
 
:license: GPLv3, see LICENSE.md for more details.
 
"""
 
import datetime
 
import os
 
import re
 
import shutil
 

	
 
import click
 

	
 
import kallithea
 
import kallithea.bin.kallithea_cli_base as cli_base
 
from kallithea.lib.utils import REMOVED_REPO_PAT, repo2db_mapper
 
from kallithea.lib.utils2 import ask_ok
 
from kallithea.model import meta
 
from kallithea.model.db import Repository
 
from kallithea.model import db, meta
 
from kallithea.model.scm import ScmModel
 

	
 

	
 
@cli_base.register_command(config_file_initialize_app=True)
 
@click.option('--remove-missing', is_flag=True,
 
        help='Remove missing repositories from the Kallithea database.')
 
def repo_scan(remove_missing):
 
    """Scan filesystem for repositories.
 

	
 
    Search the configured repository root for new repositories and add them
 
    into Kallithea.
 
    Additionally, report repositories that were previously known to Kallithea
 
    but are no longer present on the filesystem. If option --remove-missing is
 
    given, remove the missing repositories from the Kallithea database.
 
    """
 
    click.echo('Now scanning root location for new repos ...')
 
    added, removed = repo2db_mapper(ScmModel().repo_scan(),
 
                                    remove_obsolete=remove_missing)
 
    click.echo('Scan completed.')
 
    if added:
 
        click.echo('Added: %s' % ', '.join(added))
 
    if removed:
 
        click.echo('%s: %s' % ('Removed' if remove_missing else 'Missing',
 
                          ', '.join(removed)))
 

	
 
@cli_base.register_command(config_file_initialize_app=True)
 
@click.argument('repositories', nargs=-1)
 
def repo_update_metadata(repositories):
 
    """
 
    Update repository metadata in database from repository content.
 

	
 
    In normal operation, Kallithea will keep caches up-to-date
 
    automatically. However, if repositories are externally modified, e.g. by
 
    a direct push via the filesystem rather than via a Kallithea URL,
 
    Kallithea is not aware of it. In this case, you should manually run this
 
    command to update the repository cache.
 

	
 
    If no repositories are specified, the caches of all repositories are
 
    updated.
 
    """
 
    if not repositories:
 
        repo_list = Repository.query().all()
 
        repo_list = db.Repository.query().all()
 
    else:
 
        repo_names = [n.strip() for n in repositories]
 
        repo_list = list(Repository.query()
 
                        .filter(Repository.repo_name.in_(repo_names)))
 
        repo_list = list(db.Repository.query()
 
                        .filter(db.Repository.repo_name.in_(repo_names)))
 

	
 
    for repo in repo_list:
 
        # update latest revision metadata in database
 
        repo.update_changeset_cache()
 
        # invalidate in-memory VCS object cache... will be repopulated on
 
        # first access
 
        repo.set_invalidate()
 

	
 
    meta.Session().commit()
 

	
 
    click.echo('Updated database with information about latest change in the following %s repositories:' % (len(repo_list)))
 
    click.echo('\n'.join(repo.repo_name for repo in repo_list))
 

	
 
@cli_base.register_command(config_file_initialize_app=True)
 
@click.option('--ask/--no-ask', default=True, help='Ask for confirmation or not. Default is --ask.')
 
@click.option('--older-than',
 
        help="""Only purge repositories that have been removed at least the given time ago.
 
        For example, '--older-than=30d' purges repositories deleted 30 days ago or longer.
 
        Possible suffixes: d (days), h (hours), m (minutes), s (seconds).""")
 
def repo_purge_deleted(ask, older_than):
 
    """Purge backups of deleted repositories.
 

	
 
    When a repository is deleted via the Kallithea web interface, the actual
 
    data is still present on the filesystem but set aside using a special name.
kallithea/config/middleware/simplegit.py
Show inline comments
 
@@ -14,49 +14,49 @@
 
"""
 
kallithea.lib.middleware.simplegit
 
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 

	
 
SimpleGit middleware for handling Git protocol requests (push/clone etc.)
 
It's implemented with basic auth function
 

	
 
This file was forked by the Kallithea project in July 2014.
 
Original author and date, and relevant copyright and licensing information is below:
 
:created_on: Apr 28, 2010
 
:author: marcink
 
:copyright: (c) 2013 RhodeCode GmbH, and others.
 
:license: GPLv3, see LICENSE.md for more details.
 

	
 
"""
 

	
 

	
 
import logging
 
import re
 

	
 
from kallithea.config.middleware.pygrack import make_wsgi_app
 
from kallithea.lib.base import BaseVCSController, get_path_info
 
from kallithea.lib.hooks import log_pull_action
 
from kallithea.lib.utils import make_ui
 
from kallithea.model.db import Repository
 
from kallithea.model import db
 

	
 

	
 
log = logging.getLogger(__name__)
 

	
 

	
 
GIT_PROTO_PAT = re.compile(r'^/(.+)/(info/refs|git-upload-pack|git-receive-pack)$')
 

	
 

	
 
cmd_mapping = {
 
    'git-receive-pack': 'push',
 
    'git-upload-pack': 'pull',
 
}
 

	
 

	
 
class SimpleGit(BaseVCSController):
 

	
 
    scm_alias = 'git'
 

	
 
    @classmethod
 
    def parse_request(cls, environ):
 
        path_info = get_path_info(environ)
 
        m = GIT_PROTO_PAT.match(path_info)
 
        if m is None:
 
            return None
 
@@ -66,33 +66,33 @@ class SimpleGit(BaseVCSController):
 
            repo_name = m.group(1).rstrip('/')
 
            cmd = m.group(2)
 

	
 
            query_string = environ['QUERY_STRING']
 
            if cmd == 'info/refs' and query_string.startswith('service='):
 
                service = query_string.split('=', 1)[1]
 
                action = cmd_mapping.get(service)
 
            else:
 
                service = None
 
                action = cmd_mapping.get(cmd)
 

	
 
        return parsed_request
 

	
 
    def _make_app(self, parsed_request):
 
        """
 
        Return a pygrack wsgi application.
 
        """
 
        pygrack_app = make_wsgi_app(parsed_request.repo_name, self.basepath)
 

	
 
        def wrapper_app(environ, start_response):
 
            if (parsed_request.cmd == 'info/refs' and
 
                parsed_request.service == 'git-upload-pack'
 
            ):
 
                baseui = make_ui()
 
                repo = Repository.get_by_repo_name(parsed_request.repo_name)
 
                repo = db.Repository.get_by_repo_name(parsed_request.repo_name)
 
                scm_repo = repo.scm_instance
 
                # Run hooks, like Mercurial outgoing.pull_logger does
 
                log_pull_action(ui=baseui, repo=scm_repo._repo)
 
            # Note: push hooks are handled by post-receive hook
 

	
 
            return pygrack_app(environ, start_response)
 

	
 
        return wrapper_app
kallithea/controllers/admin/admin.py
Show inline comments
 
@@ -20,128 +20,128 @@ Controller for Admin panel of Kallithea
 
This file was forked by the Kallithea project in July 2014.
 
Original author and date, and relevant copyright and licensing information is below:
 
:created_on: Apr 7, 2010
 
:author: marcink
 
:copyright: (c) 2013 RhodeCode GmbH, and others.
 
:license: GPLv3, see LICENSE.md for more details.
 
"""
 

	
 

	
 
import logging
 

	
 
from sqlalchemy.orm import joinedload
 
from sqlalchemy.sql.expression import and_, func, or_
 
from tg import request
 
from tg import tmpl_context as c
 
from whoosh import query
 
from whoosh.qparser.dateparse import DateParserPlugin
 
from whoosh.qparser.default import QueryParser
 

	
 
from kallithea.lib.auth import HasPermissionAnyDecorator, LoginRequired
 
from kallithea.lib.base import BaseController, render
 
from kallithea.lib.indexers import JOURNAL_SCHEMA
 
from kallithea.lib.page import Page
 
from kallithea.lib.utils2 import remove_prefix, remove_suffix, safe_int
 
from kallithea.model.db import UserLog
 
from kallithea.model import db
 

	
 

	
 
log = logging.getLogger(__name__)
 

	
 

	
 
def _journal_filter(user_log, search_term):
 
    """
 
    Filters sqlalchemy user_log based on search_term with whoosh Query language
 
    http://packages.python.org/Whoosh/querylang.html
 

	
 
    :param user_log:
 
    :param search_term:
 
    """
 
    log.debug('Initial search term: %r', search_term)
 
    qry = None
 
    if search_term:
 
        qp = QueryParser('repository', schema=JOURNAL_SCHEMA)
 
        qp.add_plugin(DateParserPlugin())
 
        qry = qp.parse(search_term)
 
        log.debug('Filtering using parsed query %r', qry)
 

	
 
    def wildcard_handler(col, wc_term):
 
        if wc_term.startswith('*') and not wc_term.endswith('*'):
 
            # postfix == endswith
 
            wc_term = remove_prefix(wc_term, prefix='*')
 
            return func.lower(col).endswith(func.lower(wc_term))
 
        elif wc_term.startswith('*') and wc_term.endswith('*'):
 
            # wildcard == ilike
 
            wc_term = remove_prefix(wc_term, prefix='*')
 
            wc_term = remove_suffix(wc_term, suffix='*')
 
            return func.lower(col).contains(func.lower(wc_term))
 

	
 
    def get_filterion(field, val, term):
 

	
 
        if field == 'repository':
 
            field = getattr(UserLog, 'repository_name')
 
            field = getattr(db.UserLog, 'repository_name')
 
        elif field == 'ip':
 
            field = getattr(UserLog, 'user_ip')
 
            field = getattr(db.UserLog, 'user_ip')
 
        elif field == 'date':
 
            field = getattr(UserLog, 'action_date')
 
            field = getattr(db.UserLog, 'action_date')
 
        elif field == 'username':
 
            field = getattr(UserLog, 'username')
 
            field = getattr(db.UserLog, 'username')
 
        else:
 
            field = getattr(UserLog, field)
 
            field = getattr(db.UserLog, field)
 
        log.debug('filter field: %s val=>%s', field, val)
 

	
 
        # sql filtering
 
        if isinstance(term, query.Wildcard):
 
            return wildcard_handler(field, val)
 
        elif isinstance(term, query.Prefix):
 
            return func.lower(field).startswith(func.lower(val))
 
        elif isinstance(term, query.DateRange):
 
            return and_(field >= val[0], field <= val[1])
 
        return func.lower(field) == func.lower(val)
 

	
 
    if isinstance(qry, (query.And, query.Term, query.Prefix, query.Wildcard,
 
                        query.DateRange)):
 
        if not isinstance(qry, query.And):
 
            qry = [qry]
 
        for term in qry:
 
            field = term.fieldname
 
            val = (term.text if not isinstance(term, query.DateRange)
 
                   else [term.startdate, term.enddate])
 
            user_log = user_log.filter(get_filterion(field, val, term))
 
    elif isinstance(qry, query.Or):
 
        filters = []
 
        for term in qry:
 
            field = term.fieldname
 
            val = (term.text if not isinstance(term, query.DateRange)
 
                   else [term.startdate, term.enddate])
 
            filters.append(get_filterion(field, val, term))
 
        user_log = user_log.filter(or_(*filters))
 

	
 
    return user_log
 

	
 

	
 
class AdminController(BaseController):
 

	
 
    @LoginRequired(allow_default_user=True)
 
    def _before(self, *args, **kwargs):
 
        super(AdminController, self)._before(*args, **kwargs)
 

	
 
    @HasPermissionAnyDecorator('hg.admin')
 
    def index(self):
 
        users_log = UserLog.query() \
 
                .options(joinedload(UserLog.user)) \
 
                .options(joinedload(UserLog.repository))
 
        users_log = db.UserLog.query() \
 
                .options(joinedload(db.UserLog.user)) \
 
                .options(joinedload(db.UserLog.repository))
 

	
 
        # FILTERING
 
        c.search_term = request.GET.get('filter')
 
        users_log = _journal_filter(users_log, c.search_term)
 

	
 
        users_log = users_log.order_by(UserLog.action_date.desc())
 
        users_log = users_log.order_by(db.UserLog.action_date.desc())
 

	
 
        p = safe_int(request.GET.get('page'), 1)
 

	
 
        c.users_log = Page(users_log, page=p, items_per_page=10,
 
                           filter=c.search_term)
 

	
 
        if request.environ.get('HTTP_X_PARTIAL_XHR'):
 
            return render('admin/admin_log.html')
 

	
 
        return render('admin/admin.html')
kallithea/controllers/admin/auth_settings.py
Show inline comments
 
@@ -15,90 +15,89 @@
 
kallithea.controllers.admin.auth_settings
 
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 

	
 
pluggable authentication controller for Kallithea
 

	
 
This file was forked by the Kallithea project in July 2014.
 
Original author and date, and relevant copyright and licensing information is below:
 
:created_on: Nov 26, 2010
 
:author: akesterson
 
"""
 

	
 
import logging
 
import traceback
 

	
 
import formencode.htmlfill
 
from tg import request
 
from tg import tmpl_context as c
 
from tg.i18n import ugettext as _
 
from webob.exc import HTTPFound
 

	
 
from kallithea.lib import auth_modules
 
from kallithea.lib import helpers as h
 
from kallithea.lib.auth import HasPermissionAnyDecorator, LoginRequired
 
from kallithea.lib.base import BaseController, render
 
from kallithea.lib.webutils import url
 
from kallithea.model import meta
 
from kallithea.model.db import Setting
 
from kallithea.lib.utils3 import url
 
from kallithea.model import db, meta
 
from kallithea.model.forms import AuthSettingsForm
 

	
 

	
 
log = logging.getLogger(__name__)
 

	
 

	
 
class AuthSettingsController(BaseController):
 

	
 
    @LoginRequired()
 
    @HasPermissionAnyDecorator('hg.admin')
 
    def _before(self, *args, **kwargs):
 
        super(AuthSettingsController, self)._before(*args, **kwargs)
 

	
 
    def __load_defaults(self):
 
        c.available_plugins = [
 
            'kallithea.lib.auth_modules.auth_internal',
 
            'kallithea.lib.auth_modules.auth_container',
 
            'kallithea.lib.auth_modules.auth_ldap',
 
            'kallithea.lib.auth_modules.auth_crowd',
 
            'kallithea.lib.auth_modules.auth_pam'
 
        ]
 
        self.enabled_plugins = auth_modules.get_auth_plugins()
 
        c.enabled_plugin_names = [plugin.__class__.__module__ for plugin in self.enabled_plugins]
 

	
 
    def __render(self, defaults, errors):
 
        c.defaults = {}
 
        c.plugin_settings = {}
 
        c.plugin_shortnames = {}
 

	
 
        for plugin in self.enabled_plugins:
 
            module = plugin.__class__.__module__
 
            c.plugin_shortnames[module] = plugin.name
 
            c.plugin_settings[module] = plugin.plugin_settings()
 
            for v in c.plugin_settings[module]:
 
                fullname = "auth_%s_%s" % (plugin.name, v["name"])
 
                if "default" in v:
 
                    c.defaults[fullname] = v["default"]
 
                # Current values will be the default on the form, if there are any
 
                setting = Setting.get_by_name(fullname)
 
                setting = db.Setting.get_by_name(fullname)
 
                if setting is not None:
 
                    c.defaults[fullname] = setting.app_settings_value
 
        if defaults:
 
            c.defaults.update(defaults)
 

	
 
        # we want to show , separated list of enabled plugins
 
        c.defaults['auth_plugins'] = ','.join(c.enabled_plugin_names)
 

	
 
        log.debug('defaults: %s', defaults)
 
        return formencode.htmlfill.render(
 
            render('admin/auth/auth_settings.html'),
 
            defaults=c.defaults,
 
            errors=errors,
 
            prefix_error=False,
 
            encoding="UTF-8",
 
            force_defaults=False)
 

	
 
    def index(self):
 
        self.__load_defaults()
 
        return self.__render(defaults=None, errors=None)
 

	
 
    def auth_settings(self):
 
        """POST create and store auth settings"""
 
        self.__load_defaults()
 
@@ -110,41 +109,41 @@ class AuthSettingsController(BaseControl
 
            new_enabled_plugins = _auth_plugins_validator.to_python(request.POST.get('auth_plugins'))
 
        except formencode.Invalid:
 
            # User provided an invalid plugin list. Just fall back to
 
            # the list of currently enabled plugins. (We'll re-validate
 
            # and show an error message to the user, below.)
 
            pass
 
        else:
 
            # Hide plugins that the user has asked to be disabled, but
 
            # do not show plugins that the user has asked to be enabled
 
            # (yet), since that'll cause validation errors and/or wrong
 
            # settings being applied (e.g. checkboxes being cleared),
 
            # since the plugin settings will not be in the POST data.
 
            c.enabled_plugin_names = [p for p in c.enabled_plugin_names if p in new_enabled_plugins]
 

	
 
        # Next, parse everything including plugin settings.
 
        _form = AuthSettingsForm(c.enabled_plugin_names)()
 

	
 
        try:
 
            form_result = _form.to_python(dict(request.POST))
 
            for k, v in form_result.items():
 
                if k == 'auth_plugins':
 
                    # we want to store it comma separated inside our settings
 
                    v = ','.join(v)
 
                log.debug("%s = %s", k, str(v))
 
                setting = Setting.create_or_update(k, v)
 
                setting = db.Setting.create_or_update(k, v)
 
            meta.Session().commit()
 
            h.flash(_('Auth settings updated successfully'),
 
                       category='success')
 
        except formencode.Invalid as errors:
 
            log.error(traceback.format_exc())
 
            e = errors.error_dict or {}
 
            return self.__render(
 
                defaults=errors.value,
 
                errors=e,
 
            )
 
        except Exception:
 
            log.error(traceback.format_exc())
 
            h.flash(_('error occurred during update of auth settings'),
 
                    category='error')
 

	
 
        raise HTTPFound(location=url('auth_home'))
kallithea/controllers/admin/defaults.py
Show inline comments
 
@@ -16,77 +16,76 @@ kallithea.controllers.admin.defaults
 
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 

	
 
default settings controller for Kallithea
 

	
 
This file was forked by the Kallithea project in July 2014.
 
Original author and date, and relevant copyright and licensing information is below:
 
:created_on: Apr 27, 2010
 
:author: marcink
 
:copyright: (c) 2013 RhodeCode GmbH, and others.
 
:license: GPLv3, see LICENSE.md for more details.
 
"""
 

	
 
import logging
 
import traceback
 

	
 
import formencode
 
from formencode import htmlfill
 
from tg import request
 
from tg.i18n import ugettext as _
 
from webob.exc import HTTPFound
 

	
 
from kallithea.lib import helpers as h
 
from kallithea.lib.auth import HasPermissionAnyDecorator, LoginRequired
 
from kallithea.lib.base import BaseController, render
 
from kallithea.lib.webutils import url
 
from kallithea.model import meta
 
from kallithea.model.db import Setting
 
from kallithea.lib.utils3 import url
 
from kallithea.model import db, meta
 
from kallithea.model.forms import DefaultsForm
 

	
 

	
 
log = logging.getLogger(__name__)
 

	
 

	
 
class DefaultsController(BaseController):
 

	
 
    @LoginRequired()
 
    @HasPermissionAnyDecorator('hg.admin')
 
    def _before(self, *args, **kwargs):
 
        super(DefaultsController, self)._before(*args, **kwargs)
 

	
 
    def index(self, format='html'):
 
        defaults = Setting.get_default_repo_settings()
 
        defaults = db.Setting.get_default_repo_settings()
 

	
 
        return htmlfill.render(
 
            render('admin/defaults/defaults.html'),
 
            defaults=defaults,
 
            encoding="UTF-8",
 
            force_defaults=False
 
        )
 

	
 
    def update(self, id):
 
        _form = DefaultsForm()()
 

	
 
        try:
 
            form_result = _form.to_python(dict(request.POST))
 
            for k, v in form_result.items():
 
                setting = Setting.create_or_update(k, v)
 
                setting = db.Setting.create_or_update(k, v)
 
            meta.Session().commit()
 
            h.flash(_('Default settings updated successfully'),
 
                    category='success')
 

	
 
        except formencode.Invalid as errors:
 
            defaults = errors.value
 

	
 
            return htmlfill.render(
 
                render('admin/defaults/defaults.html'),
 
                defaults=defaults,
 
                errors=errors.error_dict or {},
 
                prefix_error=False,
 
                encoding="UTF-8",
 
                force_defaults=False)
 
        except Exception:
 
            log.error(traceback.format_exc())
 
            h.flash(_('Error occurred during update of defaults'),
 
                    category='error')
 

	
 
        raise HTTPFound(location=url('defaults'))
kallithea/controllers/admin/gists.py
Show inline comments
 
@@ -21,198 +21,197 @@ This file was forked by the Kallithea pr
 
Original author and date, and relevant copyright and licensing information is below:
 
:created_on: May 9, 2013
 
:author: marcink
 
:copyright: (c) 2013 RhodeCode GmbH, and others.
 
:license: GPLv3, see LICENSE.md for more details.
 
"""
 

	
 
import logging
 
import traceback
 

	
 
import formencode.htmlfill
 
from sqlalchemy.sql.expression import or_
 
from tg import request, response
 
from tg import tmpl_context as c
 
from tg.i18n import ugettext as _
 
from webob.exc import HTTPForbidden, HTTPFound, HTTPNotFound
 

	
 
from kallithea.lib import helpers as h
 
from kallithea.lib.auth import LoginRequired
 
from kallithea.lib.base import BaseController, jsonify, render
 
from kallithea.lib.page import Page
 
from kallithea.lib.utils2 import safe_int, safe_str, time_to_datetime
 
from kallithea.lib.vcs.exceptions import NodeNotChangedError, VCSError
 
from kallithea.lib.webutils import url
 
from kallithea.model import meta
 
from kallithea.model.db import Gist
 
from kallithea.model import db, meta
 
from kallithea.model.forms import GistForm
 
from kallithea.model.gist import GistModel
 

	
 

	
 
log = logging.getLogger(__name__)
 

	
 

	
 
class GistsController(BaseController):
 

	
 
    def __load_defaults(self, extra_values=None):
 
        c.lifetime_values = [
 
            (str(-1), _('Forever')),
 
            (str(5), _('5 minutes')),
 
            (str(60), _('1 hour')),
 
            (str(60 * 24), _('1 day')),
 
            (str(60 * 24 * 30), _('1 month')),
 
        ]
 
        if extra_values:
 
            c.lifetime_values.append(extra_values)
 
        c.lifetime_options = [(c.lifetime_values, _("Lifetime"))]
 

	
 
    @LoginRequired(allow_default_user=True)
 
    def index(self):
 
        not_default_user = not request.authuser.is_default_user
 
        c.show_private = request.GET.get('private') and not_default_user
 
        c.show_public = request.GET.get('public') and not_default_user
 
        url_params = {}
 
        if c.show_public:
 
            url_params['public'] = 1
 
        elif c.show_private:
 
            url_params['private'] = 1
 

	
 
        gists = Gist().query() \
 
        gists = db.Gist().query() \
 
            .filter_by(is_expired=False) \
 
            .order_by(Gist.created_on.desc())
 
            .order_by(db.Gist.created_on.desc())
 

	
 
        # MY private
 
        if c.show_private and not c.show_public:
 
            gists = gists.filter(Gist.gist_type == Gist.GIST_PRIVATE) \
 
                             .filter(Gist.owner_id == request.authuser.user_id)
 
            gists = gists.filter(db.Gist.gist_type == db.Gist.GIST_PRIVATE) \
 
                             .filter(db.Gist.owner_id == request.authuser.user_id)
 
        # MY public
 
        elif c.show_public and not c.show_private:
 
            gists = gists.filter(Gist.gist_type == Gist.GIST_PUBLIC) \
 
                             .filter(Gist.owner_id == request.authuser.user_id)
 
            gists = gists.filter(db.Gist.gist_type == db.Gist.GIST_PUBLIC) \
 
                             .filter(db.Gist.owner_id == request.authuser.user_id)
 

	
 
        # MY public+private
 
        elif c.show_private and c.show_public:
 
            gists = gists.filter(or_(Gist.gist_type == Gist.GIST_PUBLIC,
 
                                     Gist.gist_type == Gist.GIST_PRIVATE)) \
 
                             .filter(Gist.owner_id == request.authuser.user_id)
 
            gists = gists.filter(or_(db.Gist.gist_type == db.Gist.GIST_PUBLIC,
 
                                     db.Gist.gist_type == db.Gist.GIST_PRIVATE)) \
 
                             .filter(db.Gist.owner_id == request.authuser.user_id)
 

	
 
        # default show ALL public gists
 
        if not c.show_public and not c.show_private:
 
            gists = gists.filter(Gist.gist_type == Gist.GIST_PUBLIC)
 
            gists = gists.filter(db.Gist.gist_type == db.Gist.GIST_PUBLIC)
 

	
 
        c.gists = gists
 
        p = safe_int(request.GET.get('page'), 1)
 
        c.gists_pager = Page(c.gists, page=p, items_per_page=10,
 
                             **url_params)
 
        return render('admin/gists/index.html')
 

	
 
    @LoginRequired()
 
    def create(self):
 
        self.__load_defaults()
 
        gist_form = GistForm([x[0] for x in c.lifetime_values])()
 
        try:
 
            form_result = gist_form.to_python(dict(request.POST))
 
            # TODO: multiple files support, from the form
 
            filename = form_result['filename'] or Gist.DEFAULT_FILENAME
 
            filename = form_result['filename'] or db.Gist.DEFAULT_FILENAME
 
            nodes = {
 
                filename: {
 
                    'content': form_result['content'],
 
                    'lexer': form_result['mimetype']  # None is autodetect
 
                }
 
            }
 
            _public = form_result['public']
 
            gist_type = Gist.GIST_PUBLIC if _public else Gist.GIST_PRIVATE
 
            gist_type = db.Gist.GIST_PUBLIC if _public else db.Gist.GIST_PRIVATE
 
            gist = GistModel().create(
 
                description=form_result['description'],
 
                owner=request.authuser.user_id,
 
                ip_addr=request.ip_addr,
 
                gist_mapping=nodes,
 
                gist_type=gist_type,
 
                lifetime=form_result['lifetime']
 
            )
 
            meta.Session().commit()
 
            new_gist_id = gist.gist_access_id
 
        except formencode.Invalid as errors:
 
            defaults = errors.value
 

	
 
            return formencode.htmlfill.render(
 
                render('admin/gists/new.html'),
 
                defaults=defaults,
 
                errors=errors.error_dict or {},
 
                prefix_error=False,
 
                encoding="UTF-8",
 
                force_defaults=False)
 

	
 
        except Exception as e:
 
            log.error(traceback.format_exc())
 
            h.flash(_('Error occurred during gist creation'), category='error')
 
            raise HTTPFound(location=url('new_gist'))
 
        raise HTTPFound(location=url('gist', gist_id=new_gist_id))
 

	
 
    @LoginRequired()
 
    def new(self, format='html'):
 
        self.__load_defaults()
 
        return render('admin/gists/new.html')
 

	
 
    @LoginRequired()
 
    def delete(self, gist_id):
 
        gist = GistModel().get_gist(gist_id)
 
        owner = gist.owner_id == request.authuser.user_id
 
        if h.HasPermissionAny('hg.admin')() or owner:
 
            GistModel().delete(gist)
 
            meta.Session().commit()
 
            h.flash(_('Deleted gist %s') % gist.gist_access_id, category='success')
 
        else:
 
            raise HTTPForbidden()
 

	
 
        raise HTTPFound(location=url('gists'))
 

	
 
    @LoginRequired(allow_default_user=True)
 
    def show(self, gist_id, revision='tip', format='html', f_path=None):
 
        c.gist = Gist.get_or_404(gist_id)
 
        c.gist = db.Gist.get_or_404(gist_id)
 

	
 
        if c.gist.is_expired:
 
            log.error('Gist expired at %s',
 
                      time_to_datetime(c.gist.gist_expires))
 
            raise HTTPNotFound()
 
        try:
 
            c.file_changeset, c.files = GistModel().get_gist_files(gist_id,
 
                                                            revision=revision)
 
        except VCSError:
 
            log.error(traceback.format_exc())
 
            raise HTTPNotFound()
 
        if format == 'raw':
 
            content = '\n\n'.join(
 
                safe_str(f.content)
 
                for f in c.files if (f_path is None or f.path == f_path)
 
            )
 
            response.content_type = 'text/plain'
 
            return content
 
        return render('admin/gists/show.html')
 

	
 
    @LoginRequired()
 
    def edit(self, gist_id, format='html'):
 
        c.gist = Gist.get_or_404(gist_id)
 
        c.gist = db.Gist.get_or_404(gist_id)
 

	
 
        if c.gist.is_expired:
 
            log.error('Gist expired at %s',
 
                      time_to_datetime(c.gist.gist_expires))
 
            raise HTTPNotFound()
 
        try:
 
            c.file_changeset, c.files = GistModel().get_gist_files(gist_id)
 
        except VCSError:
 
            log.error(traceback.format_exc())
 
            raise HTTPNotFound()
 

	
 
        self.__load_defaults(extra_values=('0', _('Unmodified')))
 
        rendered = render('admin/gists/edit.html')
 

	
 
        if request.POST:
 
            rpost = request.POST
 
            nodes = {}
 
            for org_filename, filename, mimetype, content in zip(
 
                                                    rpost.getall('org_files'),
 
                                                    rpost.getall('files'),
 
                                                    rpost.getall('mimetypes'),
 
                                                    rpost.getall('contents')):
 

	
 
                nodes[org_filename] = {
 
@@ -230,37 +229,37 @@ class GistsController(BaseController):
 
                    gist_mapping=nodes,
 
                    gist_type=c.gist.gist_type,
 
                    lifetime=rpost['lifetime']
 
                )
 

	
 
                meta.Session().commit()
 
                h.flash(_('Successfully updated gist content'), category='success')
 
            except NodeNotChangedError:
 
                # raised if nothing was changed in repo itself. We anyway then
 
                # store only DB stuff for gist
 
                meta.Session().commit()
 
                h.flash(_('Successfully updated gist data'), category='success')
 
            except Exception:
 
                log.error(traceback.format_exc())
 
                h.flash(_('Error occurred during update of gist %s') % gist_id,
 
                        category='error')
 

	
 
            raise HTTPFound(location=url('gist', gist_id=gist_id))
 

	
 
        return rendered
 

	
 
    @LoginRequired()
 
    @jsonify
 
    def check_revision(self, gist_id):
 
        c.gist = Gist.get_or_404(gist_id)
 
        c.gist = db.Gist.get_or_404(gist_id)
 
        last_rev = c.gist.scm_instance.get_changeset()
 
        success = True
 
        revision = request.POST.get('revision')
 

	
 
        # TODO: maybe move this to model ?
 
        if revision != last_rev.raw_id:
 
            log.error('Last revision %s is different than submitted %s',
 
                      revision, last_rev)
 
            # our gist has newer version than we
 
            success = False
 

	
 
        return {'success': success}
kallithea/controllers/admin/my_account.py
Show inline comments
 
@@ -19,95 +19,94 @@ my account controller for Kallithea admi
 

	
 
This file was forked by the Kallithea project in July 2014.
 
Original author and date, and relevant copyright and licensing information is below:
 
:created_on: August 20, 2013
 
:author: marcink
 
:copyright: (c) 2013 RhodeCode GmbH, and others.
 
:license: GPLv3, see LICENSE.md for more details.
 
"""
 

	
 
import logging
 
import traceback
 

	
 
import formencode
 
from formencode import htmlfill
 
from tg import request
 
from tg import tmpl_context as c
 
from tg.i18n import ugettext as _
 
from webob.exc import HTTPFound
 

	
 
from kallithea.lib import auth_modules
 
from kallithea.lib import helpers as h
 
from kallithea.lib.auth import AuthUser, LoginRequired
 
from kallithea.lib.base import BaseController, IfSshEnabled, render
 
from kallithea.lib.utils2 import generate_api_key, safe_int
 
from kallithea.lib.webutils import url
 
from kallithea.model import meta
 
from kallithea.lib.utils3 import url
 
from kallithea.model import db, meta
 
from kallithea.model.api_key import ApiKeyModel
 
from kallithea.model.db import Repository, User, UserEmailMap, UserFollowing
 
from kallithea.model.forms import PasswordChangeForm, UserForm
 
from kallithea.model.repo import RepoModel
 
from kallithea.model.ssh_key import SshKeyModel, SshKeyModelException
 
from kallithea.model.user import UserModel
 

	
 

	
 
log = logging.getLogger(__name__)
 

	
 

	
 
class MyAccountController(BaseController):
 

	
 
    @LoginRequired()
 
    def _before(self, *args, **kwargs):
 
        super(MyAccountController, self)._before(*args, **kwargs)
 

	
 
    def __load_data(self):
 
        c.user = User.get(request.authuser.user_id)
 
        c.user = db.User.get(request.authuser.user_id)
 
        if c.user.is_default_user:
 
            h.flash(_("You can't edit this user since it's"
 
                      " crucial for entire application"), category='warning')
 
            raise HTTPFound(location=url('users'))
 

	
 
    def _load_my_repos_data(self, watched=False):
 
        if watched:
 
            admin = False
 
            repos_list = meta.Session().query(Repository) \
 
                         .join(UserFollowing) \
 
                         .filter(UserFollowing.user_id ==
 
            repos_list = meta.Session().query(db.Repository) \
 
                         .join(db.UserFollowing) \
 
                         .filter(db.UserFollowing.user_id ==
 
                                 request.authuser.user_id).all()
 
        else:
 
            admin = True
 
            repos_list = meta.Session().query(Repository) \
 
                         .filter(Repository.owner_id ==
 
            repos_list = meta.Session().query(db.Repository) \
 
                         .filter(db.Repository.owner_id ==
 
                                 request.authuser.user_id).all()
 

	
 
        return RepoModel().get_repos_as_dict(repos_list, admin=admin)
 

	
 
    def my_account(self):
 
        c.active = 'profile'
 
        self.__load_data()
 
        c.perm_user = AuthUser(user_id=request.authuser.user_id)
 
        managed_fields = auth_modules.get_managed_fields(c.user)
 
        def_user_perms = AuthUser(dbuser=User.get_default_user()).global_permissions
 
        def_user_perms = AuthUser(dbuser=db.User.get_default_user()).global_permissions
 
        if 'hg.register.none' in def_user_perms:
 
            managed_fields.extend(['username', 'firstname', 'lastname', 'email'])
 

	
 
        c.readonly = lambda n: 'readonly' if n in managed_fields else None
 

	
 
        defaults = c.user.get_dict()
 
        update = False
 
        if request.POST:
 
            _form = UserForm(edit=True,
 
                             old_data={'user_id': request.authuser.user_id,
 
                                       'email': request.authuser.email})()
 
            form_result = {}
 
            try:
 
                post_data = dict(request.POST)
 
                post_data['new_password'] = ''
 
                post_data['password_confirmation'] = ''
 
                form_result = _form.to_python(post_data)
 
                # skip updating those attrs for my account
 
                skip_attrs = ['admin', 'active', 'extern_type', 'extern_name',
 
                              'new_password', 'password_confirmation',
 
                             ] + managed_fields
 

	
 
                UserModel().update(request.authuser.user_id, form_result,
 
                                   skip_attrs=skip_attrs)
 
@@ -170,50 +169,50 @@ class MyAccountController(BaseController
 

	
 
        # data used to render the grid
 
        c.data = self._load_my_repos_data()
 
        return render('admin/my_account/my_account.html')
 

	
 
    def my_account_watched(self):
 
        c.active = 'watched'
 
        self.__load_data()
 

	
 
        # data used to render the grid
 
        c.data = self._load_my_repos_data(watched=True)
 
        return render('admin/my_account/my_account.html')
 

	
 
    def my_account_perms(self):
 
        c.active = 'perms'
 
        self.__load_data()
 
        c.perm_user = AuthUser(user_id=request.authuser.user_id)
 

	
 
        return render('admin/my_account/my_account.html')
 

	
 
    def my_account_emails(self):
 
        c.active = 'emails'
 
        self.__load_data()
 

	
 
        c.user_email_map = UserEmailMap.query() \
 
            .filter(UserEmailMap.user == c.user).all()
 
        c.user_email_map = db.UserEmailMap.query() \
 
            .filter(db.UserEmailMap.user == c.user).all()
 
        return render('admin/my_account/my_account.html')
 

	
 
    def my_account_emails_add(self):
 
        email = request.POST.get('new_email')
 

	
 
        try:
 
            UserModel().add_extra_email(request.authuser.user_id, email)
 
            meta.Session().commit()
 
            h.flash(_("Added email %s to user") % email, category='success')
 
        except formencode.Invalid as error:
 
            msg = error.error_dict['email']
 
            h.flash(msg, category='error')
 
        except Exception:
 
            log.error(traceback.format_exc())
 
            h.flash(_('An error occurred during email saving'),
 
                    category='error')
 
        raise HTTPFound(location=url('my_account_emails'))
 

	
 
    def my_account_emails_delete(self):
 
        email_id = request.POST.get('del_email_id')
 
        user_model = UserModel()
 
        user_model.delete_extra_email(request.authuser.user_id, email_id)
 
        meta.Session().commit()
 
        h.flash(_("Removed email from user"), category='success')
 
@@ -225,49 +224,49 @@ class MyAccountController(BaseController
 
        show_expired = True
 
        c.lifetime_values = [
 
            (str(-1), _('Forever')),
 
            (str(5), _('5 minutes')),
 
            (str(60), _('1 hour')),
 
            (str(60 * 24), _('1 day')),
 
            (str(60 * 24 * 30), _('1 month')),
 
        ]
 
        c.lifetime_options = [(c.lifetime_values, _("Lifetime"))]
 
        c.user_api_keys = ApiKeyModel().get_api_keys(request.authuser.user_id,
 
                                                     show_expired=show_expired)
 
        return render('admin/my_account/my_account.html')
 

	
 
    def my_account_api_keys_add(self):
 
        lifetime = safe_int(request.POST.get('lifetime'), -1)
 
        description = request.POST.get('description')
 
        ApiKeyModel().create(request.authuser.user_id, description, lifetime)
 
        meta.Session().commit()
 
        h.flash(_("API key successfully created"), category='success')
 
        raise HTTPFound(location=url('my_account_api_keys'))
 

	
 
    def my_account_api_keys_delete(self):
 
        api_key = request.POST.get('del_api_key')
 
        if request.POST.get('del_api_key_builtin'):
 
            user = User.get(request.authuser.user_id)
 
            user = db.User.get(request.authuser.user_id)
 
            user.api_key = generate_api_key()
 
            meta.Session().commit()
 
            h.flash(_("API key successfully reset"), category='success')
 
        elif api_key:
 
            ApiKeyModel().delete(api_key, request.authuser.user_id)
 
            meta.Session().commit()
 
            h.flash(_("API key successfully deleted"), category='success')
 

	
 
        raise HTTPFound(location=url('my_account_api_keys'))
 

	
 
    @IfSshEnabled
 
    def my_account_ssh_keys(self):
 
        c.active = 'ssh_keys'
 
        self.__load_data()
 
        c.user_ssh_keys = SshKeyModel().get_ssh_keys(request.authuser.user_id)
 
        return render('admin/my_account/my_account.html')
 

	
 
    @IfSshEnabled
 
    def my_account_ssh_keys_add(self):
 
        description = request.POST.get('description')
 
        public_key = request.POST.get('public_key')
 
        try:
 
            new_ssh_key = SshKeyModel().create(request.authuser.user_id,
 
                                               description, public_key)
kallithea/controllers/admin/permissions.py
Show inline comments
 
@@ -18,51 +18,50 @@ kallithea.controllers.admin.permissions
 
permissions controller for Kallithea
 

	
 
This file was forked by the Kallithea project in July 2014.
 
Original author and date, and relevant copyright and licensing information is below:
 
:created_on: Apr 27, 2010
 
:author: marcink
 
:copyright: (c) 2013 RhodeCode GmbH, and others.
 
:license: GPLv3, see LICENSE.md for more details.
 
"""
 

	
 

	
 
import logging
 
import traceback
 

	
 
import formencode
 
from formencode import htmlfill
 
from tg import request
 
from tg import tmpl_context as c
 
from tg.i18n import ugettext as _
 
from webob.exc import HTTPFound
 

	
 
from kallithea.lib import helpers as h
 
from kallithea.lib.auth import AuthUser, HasPermissionAnyDecorator, LoginRequired
 
from kallithea.lib.base import BaseController, render
 
from kallithea.lib.webutils import url
 
from kallithea.model import meta
 
from kallithea.model.db import User, UserIpMap
 
from kallithea.lib.utils3 import url
 
from kallithea.model import db, meta
 
from kallithea.model.forms import DefaultPermissionsForm
 
from kallithea.model.permission import PermissionModel
 

	
 

	
 
log = logging.getLogger(__name__)
 

	
 

	
 
class PermissionsController(BaseController):
 

	
 
    @LoginRequired()
 
    @HasPermissionAnyDecorator('hg.admin')
 
    def _before(self, *args, **kwargs):
 
        super(PermissionsController, self)._before(*args, **kwargs)
 

	
 
    def __load_data(self):
 
        # Permissions for the Default user on new repositories
 
        c.repo_perms_choices = [('repository.none', _('None'),),
 
                                   ('repository.read', _('Read'),),
 
                                   ('repository.write', _('Write'),),
 
                                   ('repository.admin', _('Admin'),)]
 
        # Permissions for the Default user on new repository groups
 
        c.group_perms_choices = [('group.none', _('None'),),
 
                                 ('group.read', _('Read'),),
 
                                 ('group.write', _('Write'),),
 
@@ -113,71 +112,71 @@ class PermissionsController(BaseControll
 
                form_result = _form.to_python(dict(request.POST))
 
                form_result.update({'perm_user_name': 'default'})
 
                PermissionModel().update(form_result)
 
                meta.Session().commit()
 
                h.flash(_('Global permissions updated successfully'),
 
                        category='success')
 

	
 
            except formencode.Invalid as errors:
 
                defaults = errors.value
 

	
 
                return htmlfill.render(
 
                    render('admin/permissions/permissions.html'),
 
                    defaults=defaults,
 
                    errors=errors.error_dict or {},
 
                    prefix_error=False,
 
                    encoding="UTF-8",
 
                    force_defaults=False)
 
            except Exception:
 
                log.error(traceback.format_exc())
 
                h.flash(_('Error occurred during update of permissions'),
 
                        category='error')
 

	
 
            raise HTTPFound(location=url('admin_permissions'))
 

	
 
        c.user = User.get_default_user()
 
        c.user = db.User.get_default_user()
 
        defaults = {'anonymous': c.user.active}
 

	
 
        for p in c.user.user_perms:
 
            if p.permission.permission_name.startswith('repository.'):
 
                defaults['default_repo_perm'] = p.permission.permission_name
 

	
 
            if p.permission.permission_name.startswith('group.'):
 
                defaults['default_group_perm'] = p.permission.permission_name
 

	
 
            if p.permission.permission_name.startswith('usergroup.'):
 
                defaults['default_user_group_perm'] = p.permission.permission_name
 

	
 
            elif p.permission.permission_name.startswith('hg.create.'):
 
                defaults['default_repo_create'] = p.permission.permission_name
 

	
 
            if p.permission.permission_name.startswith('hg.usergroup.'):
 
                defaults['default_user_group_create'] = p.permission.permission_name
 

	
 
            if p.permission.permission_name.startswith('hg.register.'):
 
                defaults['default_register'] = p.permission.permission_name
 

	
 
            if p.permission.permission_name.startswith('hg.extern_activate.'):
 
                defaults['default_extern_activate'] = p.permission.permission_name
 

	
 
            if p.permission.permission_name.startswith('hg.fork.'):
 
                defaults['default_fork'] = p.permission.permission_name
 

	
 
        return htmlfill.render(
 
            render('admin/permissions/permissions.html'),
 
            defaults=defaults,
 
            encoding="UTF-8",
 
            force_defaults=False)
 

	
 
    def permission_ips(self):
 
        c.active = 'ips'
 
        c.user = User.get_default_user()
 
        c.user_ip_map = UserIpMap.query() \
 
                        .filter(UserIpMap.user == c.user).all()
 
        c.user = db.User.get_default_user()
 
        c.user_ip_map = db.UserIpMap.query() \
 
                        .filter(db.UserIpMap.user == c.user).all()
 

	
 
        return render('admin/permissions/permissions.html')
 

	
 
    def permission_perms(self):
 
        c.active = 'perms'
 
        c.user = User.get_default_user()
 
        c.user = db.User.get_default_user()
 
        c.perm_user = AuthUser(dbuser=c.user)
 
        return render('admin/permissions/permissions.html')
kallithea/controllers/admin/repo_groups.py
Show inline comments
 
@@ -19,107 +19,106 @@ Repository groups controller for Kallith
 

	
 
This file was forked by the Kallithea project in July 2014.
 
Original author and date, and relevant copyright and licensing information is below:
 
:created_on: Mar 23, 2010
 
:author: marcink
 
:copyright: (c) 2013 RhodeCode GmbH, and others.
 
:license: GPLv3, see LICENSE.md for more details.
 
"""
 

	
 
import logging
 
import traceback
 

	
 
import formencode
 
from formencode import htmlfill
 
from tg import app_globals, request
 
from tg import tmpl_context as c
 
from tg.i18n import ugettext as _
 
from tg.i18n import ungettext
 
from webob.exc import HTTPForbidden, HTTPFound, HTTPInternalServerError, HTTPNotFound
 

	
 
from kallithea.lib import helpers as h
 
from kallithea.lib.auth import HasPermissionAny, HasRepoGroupPermissionLevel, HasRepoGroupPermissionLevelDecorator, LoginRequired
 
from kallithea.lib.base import BaseController, render
 
from kallithea.lib.utils2 import safe_int
 
from kallithea.lib.webutils import url
 
from kallithea.model import meta
 
from kallithea.model.db import RepoGroup, Repository
 
from kallithea.lib.utils3 import url
 
from kallithea.model import db, meta
 
from kallithea.model.forms import RepoGroupForm, RepoGroupPermsForm
 
from kallithea.model.repo import RepoModel
 
from kallithea.model.repo_group import RepoGroupModel
 
from kallithea.model.scm import AvailableRepoGroupChoices, RepoGroupList
 

	
 

	
 
log = logging.getLogger(__name__)
 

	
 

	
 
class RepoGroupsController(BaseController):
 

	
 
    @LoginRequired(allow_default_user=True)
 
    def _before(self, *args, **kwargs):
 
        super(RepoGroupsController, self)._before(*args, **kwargs)
 

	
 
    def __load_defaults(self, extras=(), exclude=()):
 
        """extras is used for keeping current parent ignoring permissions
 
        exclude is used for not moving group to itself TODO: also exclude descendants
 
        Note: only admin can create top level groups
 
        """
 
        repo_groups = AvailableRepoGroupChoices('admin', extras)
 
        exclude_group_ids = set(rg.group_id for rg in exclude)
 
        c.repo_groups = [rg for rg in repo_groups
 
                         if rg[0] not in exclude_group_ids]
 

	
 
    def __load_data(self, group_id):
 
        """
 
        Load defaults settings for edit, and update
 

	
 
        :param group_id:
 
        """
 
        repo_group = RepoGroup.get_or_404(group_id)
 
        repo_group = db.RepoGroup.get_or_404(group_id)
 
        data = repo_group.get_dict()
 
        data['group_name'] = repo_group.name
 

	
 
        # fill repository group users
 
        for p in repo_group.repo_group_to_perm:
 
            data.update({'u_perm_%s' % p.user.username:
 
                             p.permission.permission_name})
 

	
 
        # fill repository group groups
 
        for p in repo_group.users_group_to_perm:
 
            data.update({'g_perm_%s' % p.users_group.users_group_name:
 
                             p.permission.permission_name})
 

	
 
        return data
 

	
 
    def _revoke_perms_on_yourself(self, form_result):
 
        _up = [u for u in form_result['perms_updates'] if request.authuser.username == u[0]]
 
        _new = [u for u in form_result['perms_new'] if request.authuser.username == u[0]]
 
        if _new and _new[0][1] != 'group.admin' or _up and _up[0][1] != 'group.admin':
 
            return True
 
        return False
 

	
 
    def index(self, format='html'):
 
        _list = RepoGroup.query(sorted=True).all()
 
        _list = db.RepoGroup.query(sorted=True).all()
 
        group_iter = RepoGroupList(_list, perm_level='admin')
 
        repo_groups_data = []
 
        _tmpl_lookup = app_globals.mako_lookup
 
        template = _tmpl_lookup.get_template('data_table/_dt_elements.html')
 

	
 
        def repo_group_name(repo_group_name, children_groups):
 
            return template.get_def("repo_group_name") \
 
                .render_unicode(repo_group_name, children_groups, _=_, h=h, c=c)
 

	
 
        def repo_group_actions(repo_group_id, repo_group_name, gr_count):
 
            return template.get_def("repo_group_actions") \
 
                .render_unicode(repo_group_id, repo_group_name, gr_count, _=_, h=h, c=c,
 
                        ungettext=ungettext)
 

	
 
        for repo_gr in group_iter:
 
            children_groups = [g.name for g in repo_gr.parents] + [repo_gr.name]
 
            repo_count = repo_gr.repositories.count()
 
            repo_groups_data.append({
 
                "raw_name": repo_gr.group_name,
 
                "group_name": repo_group_name(repo_gr.group_name, children_groups),
 
                "desc": h.escape(repo_gr.group_description),
 
                "repos": repo_count,
 
                "owner": h.person(repo_gr.owner),
 
                "action": repo_group_actions(repo_gr.group_id, repo_gr.group_name,
 
@@ -160,207 +159,207 @@ class RepoGroupsController(BaseControlle
 
                prefix_error=False,
 
                encoding="UTF-8",
 
                force_defaults=False)
 
        except Exception:
 
            log.error(traceback.format_exc())
 
            h.flash(_('Error occurred during creation of repository group %s')
 
                    % request.POST.get('group_name'), category='error')
 
            if form_result is None:
 
                raise
 
            parent_group_id = form_result['parent_group_id']
 
            # TODO: maybe we should get back to the main view, not the admin one
 
            raise HTTPFound(location=url('repos_groups', parent_group=parent_group_id))
 
        h.flash(_('Created repository group %s') % gr.group_name,
 
                category='success')
 
        raise HTTPFound(location=url('repos_group_home', group_name=gr.group_name))
 

	
 
    def new(self):
 
        if HasPermissionAny('hg.admin')('group create'):
 
            # we're global admin, we're ok and we can create TOP level groups
 
            pass
 
        else:
 
            # we pass in parent group into creation form, thus we know
 
            # what would be the group, we can check perms here !
 
            group_id = safe_int(request.GET.get('parent_group'))
 
            group = RepoGroup.get(group_id) if group_id else None
 
            group = db.RepoGroup.get(group_id) if group_id else None
 
            group_name = group.group_name if group else None
 
            if HasRepoGroupPermissionLevel('admin')(group_name, 'group create'):
 
                pass
 
            else:
 
                raise HTTPForbidden()
 

	
 
        self.__load_defaults()
 
        return render('admin/repo_groups/repo_group_add.html')
 

	
 
    @HasRepoGroupPermissionLevelDecorator('admin')
 
    def update(self, group_name):
 
        c.repo_group = RepoGroup.guess_instance(group_name)
 
        c.repo_group = db.RepoGroup.guess_instance(group_name)
 
        self.__load_defaults(extras=[c.repo_group.parent_group],
 
                             exclude=[c.repo_group])
 

	
 
        # TODO: kill allow_empty_group - it is only used for redundant form validation!
 
        if HasPermissionAny('hg.admin')('group edit'):
 
            # we're global admin, we're ok and we can create TOP level groups
 
            allow_empty_group = True
 
        elif not c.repo_group.parent_group:
 
            allow_empty_group = True
 
        else:
 
            allow_empty_group = False
 
        repo_group_form = RepoGroupForm(
 
            edit=True,
 
            old_data=c.repo_group.get_dict(),
 
            repo_groups=c.repo_groups,
 
            can_create_in_root=allow_empty_group,
 
        )()
 
        try:
 
            form_result = repo_group_form.to_python(dict(request.POST))
 

	
 
            new_gr = RepoGroupModel().update(group_name, form_result)
 
            meta.Session().commit()
 
            h.flash(_('Updated repository group %s')
 
                    % form_result['group_name'], category='success')
 
            # we now have new name !
 
            group_name = new_gr.group_name
 
            # TODO: in future action_logger(, '', '', '')
 
        except formencode.Invalid as errors:
 
            c.active = 'settings'
 
            return htmlfill.render(
 
                render('admin/repo_groups/repo_group_edit.html'),
 
                defaults=errors.value,
 
                errors=errors.error_dict or {},
 
                prefix_error=False,
 
                encoding="UTF-8",
 
                force_defaults=False)
 
        except Exception:
 
            log.error(traceback.format_exc())
 
            h.flash(_('Error occurred during update of repository group %s')
 
                    % request.POST.get('group_name'), category='error')
 

	
 
        raise HTTPFound(location=url('edit_repo_group', group_name=group_name))
 

	
 
    @HasRepoGroupPermissionLevelDecorator('admin')
 
    def delete(self, group_name):
 
        gr = c.repo_group = RepoGroup.guess_instance(group_name)
 
        gr = c.repo_group = db.RepoGroup.guess_instance(group_name)
 
        repos = gr.repositories.all()
 
        if repos:
 
            h.flash(_('This group contains %s repositories and cannot be '
 
                      'deleted') % len(repos), category='warning')
 
            raise HTTPFound(location=url('repos_groups'))
 

	
 
        children = gr.children.all()
 
        if children:
 
            h.flash(_('This group contains %s subgroups and cannot be deleted'
 
                      % (len(children))), category='warning')
 
            raise HTTPFound(location=url('repos_groups'))
 

	
 
        try:
 
            RepoGroupModel().delete(group_name)
 
            meta.Session().commit()
 
            h.flash(_('Removed repository group %s') % group_name,
 
                    category='success')
 
            # TODO: in future action_logger(, '', '', '')
 
        except Exception:
 
            log.error(traceback.format_exc())
 
            h.flash(_('Error occurred during deletion of repository group %s')
 
                    % group_name, category='error')
 

	
 
        if gr.parent_group:
 
            raise HTTPFound(location=url('repos_group_home', group_name=gr.parent_group.group_name))
 
        raise HTTPFound(location=url('repos_groups'))
 

	
 
    def show_by_name(self, group_name):
 
        """
 
        This is a proxy that does a lookup group_name -> id, and shows
 
        the group by id view instead
 
        """
 
        group_name = group_name.rstrip('/')
 
        id_ = RepoGroup.get_by_group_name(group_name)
 
        id_ = db.RepoGroup.get_by_group_name(group_name)
 
        if id_:
 
            return self.show(group_name)
 
        raise HTTPNotFound
 

	
 
    @HasRepoGroupPermissionLevelDecorator('read')
 
    def show(self, group_name):
 
        c.active = 'settings'
 

	
 
        c.group = c.repo_group = RepoGroup.guess_instance(group_name)
 
        c.group = c.repo_group = db.RepoGroup.guess_instance(group_name)
 

	
 
        groups = RepoGroup.query(sorted=True).filter_by(parent_group=c.group).all()
 
        groups = db.RepoGroup.query(sorted=True).filter_by(parent_group=c.group).all()
 
        repo_groups_list = self.scm_model.get_repo_groups(groups)
 

	
 
        repos_list = Repository.query(sorted=True).filter_by(group=c.group).all()
 
        repos_list = db.Repository.query(sorted=True).filter_by(group=c.group).all()
 
        c.data = RepoModel().get_repos_as_dict(repos_list,
 
                                               repo_groups_list=repo_groups_list,
 
                                               short_name=True)
 

	
 
        return render('admin/repo_groups/repo_group_show.html')
 

	
 
    @HasRepoGroupPermissionLevelDecorator('admin')
 
    def edit(self, group_name):
 
        c.active = 'settings'
 

	
 
        c.repo_group = RepoGroup.guess_instance(group_name)
 
        c.repo_group = db.RepoGroup.guess_instance(group_name)
 
        self.__load_defaults(extras=[c.repo_group.parent_group],
 
                             exclude=[c.repo_group])
 
        defaults = self.__load_data(c.repo_group.group_id)
 

	
 
        return htmlfill.render(
 
            render('admin/repo_groups/repo_group_edit.html'),
 
            defaults=defaults,
 
            encoding="UTF-8",
 
            force_defaults=False
 
        )
 

	
 
    @HasRepoGroupPermissionLevelDecorator('admin')
 
    def edit_repo_group_advanced(self, group_name):
 
        c.active = 'advanced'
 
        c.repo_group = RepoGroup.guess_instance(group_name)
 
        c.repo_group = db.RepoGroup.guess_instance(group_name)
 

	
 
        return render('admin/repo_groups/repo_group_edit.html')
 

	
 
    @HasRepoGroupPermissionLevelDecorator('admin')
 
    def edit_repo_group_perms(self, group_name):
 
        c.active = 'perms'
 
        c.repo_group = RepoGroup.guess_instance(group_name)
 
        c.repo_group = db.RepoGroup.guess_instance(group_name)
 
        self.__load_defaults()
 
        defaults = self.__load_data(c.repo_group.group_id)
 

	
 
        return htmlfill.render(
 
            render('admin/repo_groups/repo_group_edit.html'),
 
            defaults=defaults,
 
            encoding="UTF-8",
 
            force_defaults=False
 
        )
 

	
 
    @HasRepoGroupPermissionLevelDecorator('admin')
 
    def update_perms(self, group_name):
 
        """
 
        Update permissions for given repository group
 

	
 
        :param group_name:
 
        """
 

	
 
        c.repo_group = RepoGroup.guess_instance(group_name)
 
        c.repo_group = db.RepoGroup.guess_instance(group_name)
 
        valid_recursive_choices = ['none', 'repos', 'groups', 'all']
 
        form_result = RepoGroupPermsForm(valid_recursive_choices)().to_python(request.POST)
 
        if not request.authuser.is_admin:
 
            if self._revoke_perms_on_yourself(form_result):
 
                msg = _('Cannot revoke permission for yourself as admin')
 
                h.flash(msg, category='warning')
 
                raise HTTPFound(location=url('edit_repo_group_perms', group_name=group_name))
 
        recursive = form_result['recursive']
 
        # iterate over all members(if in recursive mode) of this groups and
 
        # set the permissions !
 
        # this can be potentially heavy operation
 
        RepoGroupModel()._update_permissions(c.repo_group,
 
                                             form_result['perms_new'],
 
                                             form_result['perms_updates'],
 
                                             recursive)
 
        # TODO: implement this
 
        #action_logger(request.authuser, 'admin_changed_repo_permissions',
 
        #              repo_name, request.ip_addr)
 
        meta.Session().commit()
 
        h.flash(_('Repository group permissions updated'), category='success')
 
        raise HTTPFound(location=url('edit_repo_group_perms', group_name=group_name))
 

	
 
    @HasRepoGroupPermissionLevelDecorator('admin')
 
    def delete_perms(self, group_name):
kallithea/controllers/admin/repos.py
Show inline comments
 
@@ -24,95 +24,94 @@ Original author and date, and relevant c
 
:copyright: (c) 2013 RhodeCode GmbH, and others.
 
:license: GPLv3, see LICENSE.md for more details.
 
"""
 

	
 
import logging
 
import traceback
 

	
 
import celery.result
 
import formencode
 
from formencode import htmlfill
 
from tg import request
 
from tg import tmpl_context as c
 
from tg.i18n import ugettext as _
 
from webob.exc import HTTPForbidden, HTTPFound, HTTPInternalServerError, HTTPNotFound
 

	
 
import kallithea
 
from kallithea.lib import helpers as h
 
from kallithea.lib.auth import HasRepoPermissionLevelDecorator, LoginRequired, NotAnonymous
 
from kallithea.lib.base import BaseRepoController, jsonify, render
 
from kallithea.lib.exceptions import AttachedForksError
 
from kallithea.lib.utils import action_logger
 
from kallithea.lib.utils2 import safe_int
 
from kallithea.lib.vcs import RepositoryError
 
from kallithea.lib.webutils import url
 
from kallithea.model import meta
 
from kallithea.model.db import RepoGroup, Repository, RepositoryField, Setting, UserFollowing
 
from kallithea.model import db, meta
 
from kallithea.model.forms import RepoFieldForm, RepoForm, RepoPermsForm
 
from kallithea.model.repo import RepoModel
 
from kallithea.model.scm import AvailableRepoGroupChoices, RepoList, ScmModel
 

	
 

	
 
log = logging.getLogger(__name__)
 

	
 

	
 
class ReposController(BaseRepoController):
 

	
 
    @LoginRequired(allow_default_user=True)
 
    def _before(self, *args, **kwargs):
 
        super(ReposController, self)._before(*args, **kwargs)
 

	
 
    def _load_repo(self):
 
        repo_obj = c.db_repo
 

	
 
        if repo_obj is None:
 
            h.not_mapped_error(c.repo_name)
 
            raise HTTPFound(location=url('repos'))
 

	
 
        return repo_obj
 

	
 
    def __load_defaults(self, repo=None):
 
        extras = [] if repo is None else [repo.group]
 

	
 
        c.repo_groups = AvailableRepoGroupChoices('write', extras)
 

	
 
        c.landing_revs_choices, c.landing_revs = ScmModel().get_repo_landing_revs(repo)
 

	
 
    def __load_data(self):
 
        """
 
        Load defaults settings for edit, and update
 
        """
 
        c.repo_info = self._load_repo()
 
        self.__load_defaults(c.repo_info)
 

	
 
        defaults = RepoModel()._get_defaults(c.repo_name)
 
        defaults['clone_uri'] = c.repo_info.clone_uri_hidden # don't show password
 
        defaults['permanent_url'] = c.repo_info.clone_url(clone_uri_tmpl=c.clone_uri_tmpl, with_id=True)
 

	
 
        return defaults
 

	
 
    def index(self, format='html'):
 
        repos_list = RepoList(Repository.query(sorted=True).all(), perm_level='admin')
 
        repos_list = RepoList(db.Repository.query(sorted=True).all(), perm_level='admin')
 
        # the repo list will be filtered to only show repos where the user has read permissions
 
        repos_data = RepoModel().get_repos_as_dict(repos_list, admin=True)
 
        # data used to render the grid
 
        c.data = repos_data
 

	
 
        return render('admin/repos/repos.html')
 

	
 
    @NotAnonymous()
 
    def create(self):
 
        self.__load_defaults()
 
        try:
 
            # CanWriteGroup validators checks permissions of this POST
 
            form_result = RepoForm(repo_groups=c.repo_groups,
 
                                   landing_revs=c.landing_revs_choices)() \
 
                            .to_python(dict(request.POST))
 
        except formencode.Invalid as errors:
 
            log.info(errors)
 
            return htmlfill.render(
 
                render('admin/repos/repo_add.html'),
 
                defaults=errors.value,
 
                errors=errors.error_dict or {},
 
                prefix_error=False,
 
                force_defaults=False,
 
                encoding="UTF-8")
 
@@ -120,115 +119,115 @@ class ReposController(BaseRepoController
 
        try:
 
            # create is done sometimes async on celery, db transaction
 
            # management is handled there.
 
            task = RepoModel().create(form_result, request.authuser.user_id)
 
            task_id = task.task_id
 
        except Exception:
 
            log.error(traceback.format_exc())
 
            msg = (_('Error creating repository %s')
 
                   % form_result.get('repo_name'))
 
            h.flash(msg, category='error')
 
            raise HTTPFound(location=url('home'))
 

	
 
        raise HTTPFound(location=h.url('repo_creating_home',
 
                              repo_name=form_result['repo_name_full'],
 
                              task_id=task_id))
 

	
 
    @NotAnonymous()
 
    def create_repository(self):
 
        self.__load_defaults()
 
        if not c.repo_groups:
 
            raise HTTPForbidden
 
        parent_group = request.GET.get('parent_group')
 

	
 
        ## apply the defaults from defaults page
 
        defaults = Setting.get_default_repo_settings(strip_prefix=True)
 
        defaults = db.Setting.get_default_repo_settings(strip_prefix=True)
 
        if parent_group:
 
            prg = RepoGroup.get(parent_group)
 
            prg = db.RepoGroup.get(parent_group)
 
            if prg is None or not any(rgc[0] == prg.group_id
 
                                      for rgc in c.repo_groups):
 
                raise HTTPForbidden
 
            defaults.update({'repo_group': parent_group})
 

	
 
        return htmlfill.render(
 
            render('admin/repos/repo_add.html'),
 
            defaults=defaults,
 
            errors={},
 
            prefix_error=False,
 
            encoding="UTF-8",
 
            force_defaults=False)
 

	
 
    @LoginRequired()
 
    def repo_creating(self, repo_name):
 
        c.repo = repo_name
 
        c.task_id = request.GET.get('task_id')
 
        if not c.repo:
 
            raise HTTPNotFound()
 
        return render('admin/repos/repo_creating.html')
 

	
 
    @LoginRequired()
 
    @jsonify
 
    def repo_check(self, repo_name):
 
        c.repo = repo_name
 
        task_id = request.GET.get('task_id')
 

	
 
        if task_id and task_id not in ['None']:
 
            if kallithea.CELERY_APP:
 
                task_result = celery.result.AsyncResult(task_id, app=kallithea.CELERY_APP)
 
                if task_result.failed():
 
                    raise HTTPInternalServerError(task_result.traceback)
 

	
 
        repo = Repository.get_by_repo_name(repo_name)
 
        if repo and repo.repo_state == Repository.STATE_CREATED:
 
        repo = db.Repository.get_by_repo_name(repo_name)
 
        if repo and repo.repo_state == db.Repository.STATE_CREATED:
 
            if repo.clone_uri:
 
                h.flash(_('Created repository %s from %s')
 
                        % (repo.repo_name, repo.clone_uri_hidden), category='success')
 
            else:
 
                repo_url = h.link_to(repo.repo_name,
 
                                     h.url('summary_home',
 
                                           repo_name=repo.repo_name))
 
                fork = repo.fork
 
                if fork is not None:
 
                    fork_name = fork.repo_name
 
                    h.flash(h.HTML(_('Forked repository %s as %s'))
 
                            % (fork_name, repo_url), category='success')
 
                else:
 
                    h.flash(h.HTML(_('Created repository %s')) % repo_url,
 
                            category='success')
 
            return {'result': True}
 
        return {'result': False}
 

	
 
    @HasRepoPermissionLevelDecorator('admin')
 
    def update(self, repo_name):
 
        c.repo_info = self._load_repo()
 
        self.__load_defaults(c.repo_info)
 
        c.active = 'settings'
 
        c.repo_fields = RepositoryField.query() \
 
            .filter(RepositoryField.repository == c.repo_info).all()
 
        c.repo_fields = db.RepositoryField.query() \
 
            .filter(db.RepositoryField.repository == c.repo_info).all()
 

	
 
        repo_model = RepoModel()
 
        changed_name = repo_name
 
        repo = Repository.get_by_repo_name(repo_name)
 
        repo = db.Repository.get_by_repo_name(repo_name)
 
        old_data = {
 
            'repo_name': repo_name,
 
            'repo_group': repo.group.get_dict() if repo.group else {},
 
            'repo_type': repo.repo_type,
 
        }
 
        _form = RepoForm(edit=True, old_data=old_data,
 
                         repo_groups=c.repo_groups,
 
                         landing_revs=c.landing_revs_choices)()
 

	
 
        try:
 
            form_result = _form.to_python(dict(request.POST))
 
            repo = repo_model.update(repo_name, **form_result)
 
            ScmModel().mark_for_invalidation(repo_name)
 
            h.flash(_('Repository %s updated successfully') % repo_name,
 
                    category='success')
 
            changed_name = repo.repo_name
 
            action_logger(request.authuser, 'admin_updated_repo',
 
                changed_name, request.ip_addr)
 
            meta.Session().commit()
 
        except formencode.Invalid as errors:
 
            log.info(errors)
 
            defaults = self.__load_data()
 
            defaults.update(errors.value)
 
            return htmlfill.render(
 
@@ -264,50 +263,50 @@ class ReposController(BaseRepoController
 
                    handle_forks = 'delete'
 
                    h.flash(_('Deleted %s forks') % _forks, category='success')
 
            repo_model.delete(repo, forks=handle_forks)
 
            action_logger(request.authuser, 'admin_deleted_repo',
 
                repo_name, request.ip_addr)
 
            ScmModel().mark_for_invalidation(repo_name)
 
            h.flash(_('Deleted repository %s') % repo_name, category='success')
 
            meta.Session().commit()
 
        except AttachedForksError:
 
            h.flash(_('Cannot delete repository %s which still has forks')
 
                        % repo_name, category='warning')
 

	
 
        except Exception:
 
            log.error(traceback.format_exc())
 
            h.flash(_('An error occurred during deletion of %s') % repo_name,
 
                    category='error')
 

	
 
        if repo.group:
 
            raise HTTPFound(location=url('repos_group_home', group_name=repo.group.group_name))
 
        raise HTTPFound(location=url('repos'))
 

	
 
    @HasRepoPermissionLevelDecorator('admin')
 
    def edit(self, repo_name):
 
        defaults = self.__load_data()
 
        c.repo_fields = RepositoryField.query() \
 
            .filter(RepositoryField.repository == c.repo_info).all()
 
        c.repo_fields = db.RepositoryField.query() \
 
            .filter(db.RepositoryField.repository == c.repo_info).all()
 
        c.active = 'settings'
 
        return htmlfill.render(
 
            render('admin/repos/repo_edit.html'),
 
            defaults=defaults,
 
            encoding="UTF-8",
 
            force_defaults=False)
 

	
 
    @HasRepoPermissionLevelDecorator('admin')
 
    def edit_permissions(self, repo_name):
 
        c.repo_info = self._load_repo()
 
        c.active = 'permissions'
 
        defaults = RepoModel()._get_defaults(repo_name)
 

	
 
        return htmlfill.render(
 
            render('admin/repos/repo_edit.html'),
 
            defaults=defaults,
 
            encoding="UTF-8",
 
            force_defaults=False)
 

	
 
    @HasRepoPermissionLevelDecorator('admin')
 
    def edit_permissions_update(self, repo_name):
 
        form = RepoPermsForm()().to_python(request.POST)
 
        RepoModel()._update_permissions(repo_name, form['perms_new'],
 
                                        form['perms_updates'])
 
@@ -331,128 +330,128 @@ class ReposController(BaseRepoController
 
                assert False
 

	
 
            if obj_type == 'user':
 
                RepoModel().revoke_user_permission(repo=repo_name, user=obj_id)
 
            elif obj_type == 'user_group':
 
                RepoModel().revoke_user_group_permission(
 
                    repo=repo_name, group_name=obj_id
 
                )
 
            else:
 
                assert False
 
            # TODO: implement this
 
            #action_logger(request.authuser, 'admin_revoked_repo_permissions',
 
            #              repo_name, request.ip_addr)
 
            meta.Session().commit()
 
        except Exception:
 
            log.error(traceback.format_exc())
 
            h.flash(_('An error occurred during revoking of permission'),
 
                    category='error')
 
            raise HTTPInternalServerError()
 
        return []
 

	
 
    @HasRepoPermissionLevelDecorator('admin')
 
    def edit_fields(self, repo_name):
 
        c.repo_info = self._load_repo()
 
        c.repo_fields = RepositoryField.query() \
 
            .filter(RepositoryField.repository == c.repo_info).all()
 
        c.repo_fields = db.RepositoryField.query() \
 
            .filter(db.RepositoryField.repository == c.repo_info).all()
 
        c.active = 'fields'
 
        if request.POST:
 

	
 
            raise HTTPFound(location=url('repo_edit_fields'))
 
        return render('admin/repos/repo_edit.html')
 

	
 
    @HasRepoPermissionLevelDecorator('admin')
 
    def create_repo_field(self, repo_name):
 
        try:
 
            form_result = RepoFieldForm()().to_python(dict(request.POST))
 
            new_field = RepositoryField()
 
            new_field.repository = Repository.get_by_repo_name(repo_name)
 
            new_field = db.RepositoryField()
 
            new_field.repository = db.Repository.get_by_repo_name(repo_name)
 
            new_field.field_key = form_result['new_field_key']
 
            new_field.field_type = form_result['new_field_type']  # python type
 
            new_field.field_value = form_result['new_field_value']  # set initial blank value
 
            new_field.field_desc = form_result['new_field_desc']
 
            new_field.field_label = form_result['new_field_label']
 
            meta.Session().add(new_field)
 
            meta.Session().commit()
 
        except formencode.Invalid as e:
 
            h.flash(_('Field validation error: %s') % e.msg, category='error')
 
        except Exception as e:
 
            log.error(traceback.format_exc())
 
            h.flash(_('An error occurred during creation of field: %r') % e, category='error')
 
        raise HTTPFound(location=url('edit_repo_fields', repo_name=repo_name))
 

	
 
    @HasRepoPermissionLevelDecorator('admin')
 
    def delete_repo_field(self, repo_name, field_id):
 
        field = RepositoryField.get_or_404(field_id)
 
        field = db.RepositoryField.get_or_404(field_id)
 
        try:
 
            meta.Session().delete(field)
 
            meta.Session().commit()
 
        except Exception as e:
 
            log.error(traceback.format_exc())
 
            msg = _('An error occurred during removal of field')
 
            h.flash(msg, category='error')
 
        raise HTTPFound(location=url('edit_repo_fields', repo_name=repo_name))
 

	
 
    @HasRepoPermissionLevelDecorator('admin')
 
    def edit_advanced(self, repo_name):
 
        c.repo_info = self._load_repo()
 
        c.default_user_id = kallithea.DEFAULT_USER_ID
 
        c.in_public_journal = UserFollowing.query() \
 
            .filter(UserFollowing.user_id == c.default_user_id) \
 
            .filter(UserFollowing.follows_repository == c.repo_info).scalar()
 
        c.in_public_journal = db.UserFollowing.query() \
 
            .filter(db.UserFollowing.user_id == c.default_user_id) \
 
            .filter(db.UserFollowing.follows_repository == c.repo_info).scalar()
 

	
 
        _repos = Repository.query(sorted=True).all()
 
        _repos = db.Repository.query(sorted=True).all()
 
        read_access_repos = RepoList(_repos, perm_level='read')
 
        c.repos_list = [(None, _('-- Not a fork --'))]
 
        c.repos_list += [(x.repo_id, x.repo_name)
 
                         for x in read_access_repos
 
                         if x.repo_id != c.repo_info.repo_id
 
                         and x.repo_type == c.repo_info.repo_type]
 

	
 
        defaults = {
 
            'id_fork_of': c.repo_info.fork_id if c.repo_info.fork_id else ''
 
        }
 

	
 
        c.active = 'advanced'
 
        if request.POST:
 
            raise HTTPFound(location=url('repo_edit_advanced'))
 
        return htmlfill.render(
 
            render('admin/repos/repo_edit.html'),
 
            defaults=defaults,
 
            encoding="UTF-8",
 
            force_defaults=False)
 

	
 
    @HasRepoPermissionLevelDecorator('admin')
 
    def edit_advanced_journal(self, repo_name):
 
        """
 
        Sets this repository to be visible in public journal,
 
        in other words asking default user to follow this repo
 

	
 
        :param repo_name:
 
        """
 

	
 
        try:
 
            repo_id = Repository.get_by_repo_name(repo_name).repo_id
 
            repo_id = db.Repository.get_by_repo_name(repo_name).repo_id
 
            user_id = kallithea.DEFAULT_USER_ID
 
            self.scm_model.toggle_following_repo(repo_id, user_id)
 
            h.flash(_('Updated repository visibility in public journal'),
 
                    category='success')
 
            meta.Session().commit()
 
        except Exception:
 
            h.flash(_('An error occurred during setting this'
 
                      ' repository in public journal'),
 
                    category='error')
 
        raise HTTPFound(location=url('edit_repo_advanced', repo_name=repo_name))
 

	
 
    @HasRepoPermissionLevelDecorator('admin')
 
    def edit_advanced_fork(self, repo_name):
 
        """
 
        Mark given repository as a fork of another
 

	
 
        :param repo_name:
 
        """
 
        try:
 
            fork_id = request.POST.get('id_fork_of')
 
            repo = ScmModel().mark_as_fork(repo_name, fork_id,
 
                                           request.authuser.username)
 
            fork = repo.fork.repo_name if repo.fork else _('Nothing')
 
            meta.Session().commit()
kallithea/controllers/admin/settings.py
Show inline comments
 
@@ -22,392 +22,391 @@ Original author and date, and relevant c
 
:created_on: Jul 14, 2010
 
:author: marcink
 
:copyright: (c) 2013 RhodeCode GmbH, and others.
 
:license: GPLv3, see LICENSE.md for more details.
 
"""
 

	
 
import logging
 
import traceback
 

	
 
import formencode
 
from formencode import htmlfill
 
from tg import config, request
 
from tg import tmpl_context as c
 
from tg.i18n import ugettext as _
 
from webob.exc import HTTPFound
 

	
 
from kallithea.lib import helpers as h
 
from kallithea.lib.auth import HasPermissionAnyDecorator, LoginRequired
 
from kallithea.lib.base import BaseController, render
 
from kallithea.lib.celerylib import tasks
 
from kallithea.lib.utils import repo2db_mapper, set_app_settings
 
from kallithea.lib.utils2 import safe_str
 
from kallithea.lib.vcs import VCSError
 
from kallithea.lib.webutils import url
 
from kallithea.model import meta
 
from kallithea.model.db import Repository, Setting, Ui
 
from kallithea.model import db, meta
 
from kallithea.model.forms import ApplicationSettingsForm, ApplicationUiSettingsForm, ApplicationVisualisationForm
 
from kallithea.model.notification import EmailNotificationModel
 
from kallithea.model.scm import ScmModel
 

	
 

	
 
log = logging.getLogger(__name__)
 

	
 

	
 
class SettingsController(BaseController):
 

	
 
    @LoginRequired(allow_default_user=True)
 
    def _before(self, *args, **kwargs):
 
        super(SettingsController, self)._before(*args, **kwargs)
 

	
 
    def _get_hg_ui_settings(self):
 
        ret = Ui.query().all()
 
        ret = db.Ui.query().all()
 

	
 
        settings = {}
 
        for each in ret:
 
            k = each.ui_section + '_' + each.ui_key
 
            v = each.ui_value
 
            if k == 'paths_/':
 
                k = 'paths_root_path'
 

	
 
            k = k.replace('.', '_')
 

	
 
            if each.ui_section in ['hooks', 'extensions']:
 
                v = each.ui_active
 

	
 
            settings[k] = v
 
        return settings
 

	
 
    @HasPermissionAnyDecorator('hg.admin')
 
    def settings_vcs(self):
 
        c.active = 'vcs'
 
        if request.POST:
 
            application_form = ApplicationUiSettingsForm()()
 
            try:
 
                form_result = application_form.to_python(dict(request.POST))
 
            except formencode.Invalid as errors:
 
                return htmlfill.render(
 
                     render('admin/settings/settings.html'),
 
                     defaults=errors.value,
 
                     errors=errors.error_dict or {},
 
                     prefix_error=False,
 
                     encoding="UTF-8",
 
                     force_defaults=False)
 

	
 
            try:
 
                if c.visual.allow_repo_location_change:
 
                    sett = Ui.get_by_key('paths', '/')
 
                    sett = db.Ui.get_by_key('paths', '/')
 
                    sett.ui_value = form_result['paths_root_path']
 

	
 
                # HOOKS
 
                sett = Ui.get_by_key('hooks', Ui.HOOK_UPDATE)
 
                sett = db.Ui.get_by_key('hooks', db.Ui.HOOK_UPDATE)
 
                sett.ui_active = form_result['hooks_changegroup_update']
 

	
 
                sett = Ui.get_by_key('hooks', Ui.HOOK_REPO_SIZE)
 
                sett = db.Ui.get_by_key('hooks', db.Ui.HOOK_REPO_SIZE)
 
                sett.ui_active = form_result['hooks_changegroup_repo_size']
 

	
 
                ## EXTENSIONS
 
                sett = Ui.get_or_create('extensions', 'largefiles')
 
                sett = db.Ui.get_or_create('extensions', 'largefiles')
 
                sett.ui_active = form_result['extensions_largefiles']
 

	
 
#                sett = Ui.get_or_create('extensions', 'hggit')
 
#                sett = db.Ui.get_or_create('extensions', 'hggit')
 
#                sett.ui_active = form_result['extensions_hggit']
 

	
 
                meta.Session().commit()
 

	
 
                h.flash(_('Updated VCS settings'), category='success')
 

	
 
            except Exception:
 
                log.error(traceback.format_exc())
 
                h.flash(_('Error occurred while updating '
 
                          'application settings'), category='error')
 

	
 
        defaults = Setting.get_app_settings()
 
        defaults = db.Setting.get_app_settings()
 
        defaults.update(self._get_hg_ui_settings())
 

	
 
        return htmlfill.render(
 
            render('admin/settings/settings.html'),
 
            defaults=defaults,
 
            encoding="UTF-8",
 
            force_defaults=False)
 

	
 
    @HasPermissionAnyDecorator('hg.admin')
 
    def settings_mapping(self):
 
        c.active = 'mapping'
 
        if request.POST:
 
            rm_obsolete = request.POST.get('destroy', False)
 
            install_git_hooks = request.POST.get('hooks', False)
 
            overwrite_git_hooks = request.POST.get('hooks_overwrite', False)
 
            invalidate_cache = request.POST.get('invalidate', False)
 
            log.debug('rescanning repo location with destroy obsolete=%s, '
 
                      'install git hooks=%s and '
 
                      'overwrite git hooks=%s' % (rm_obsolete, install_git_hooks, overwrite_git_hooks))
 

	
 
            filesystem_repos = ScmModel().repo_scan()
 
            added, removed = repo2db_mapper(filesystem_repos, rm_obsolete,
 
                                            install_git_hooks=install_git_hooks,
 
                                            user=request.authuser.username,
 
                                            overwrite_git_hooks=overwrite_git_hooks)
 
            added_msg = h.HTML(', ').join(
 
                h.link_to(safe_str(repo_name), h.url('summary_home', repo_name=repo_name)) for repo_name in added
 
            ) or '-'
 
            removed_msg = h.HTML(', ').join(
 
                safe_str(repo_name) for repo_name in removed
 
            ) or '-'
 
            h.flash(h.HTML(_('Repositories successfully rescanned. Added: %s. Removed: %s.')) %
 
                    (added_msg, removed_msg), category='success')
 

	
 
            if invalidate_cache:
 
                log.debug('invalidating all repositories cache')
 
                i = 0
 
                for repo in Repository.query():
 
                for repo in db.Repository.query():
 
                    try:
 
                        ScmModel().mark_for_invalidation(repo.repo_name)
 
                        i += 1
 
                    except VCSError as e:
 
                        log.warning('VCS error invalidating %s: %s', repo.repo_name, e)
 
                h.flash(_('Invalidated %s repositories') % i, category='success')
 

	
 
            raise HTTPFound(location=url('admin_settings_mapping'))
 

	
 
        defaults = Setting.get_app_settings()
 
        defaults = db.Setting.get_app_settings()
 
        defaults.update(self._get_hg_ui_settings())
 

	
 
        return htmlfill.render(
 
            render('admin/settings/settings.html'),
 
            defaults=defaults,
 
            encoding="UTF-8",
 
            force_defaults=False)
 

	
 
    @HasPermissionAnyDecorator('hg.admin')
 
    def settings_global(self):
 
        c.active = 'global'
 
        if request.POST:
 
            application_form = ApplicationSettingsForm()()
 
            try:
 
                form_result = application_form.to_python(dict(request.POST))
 
            except formencode.Invalid as errors:
 
                return htmlfill.render(
 
                    render('admin/settings/settings.html'),
 
                    defaults=errors.value,
 
                    errors=errors.error_dict or {},
 
                    prefix_error=False,
 
                    encoding="UTF-8",
 
                    force_defaults=False)
 

	
 
            try:
 
                for setting in (
 
                    'title',
 
                    'realm',
 
                    'ga_code',
 
                    'captcha_public_key',
 
                    'captcha_private_key',
 
                ):
 
                    Setting.create_or_update(setting, form_result[setting])
 
                    db.Setting.create_or_update(setting, form_result[setting])
 

	
 
                meta.Session().commit()
 
                set_app_settings(config)
 
                h.flash(_('Updated application settings'), category='success')
 

	
 
            except Exception:
 
                log.error(traceback.format_exc())
 
                h.flash(_('Error occurred while updating '
 
                          'application settings'),
 
                          category='error')
 

	
 
            raise HTTPFound(location=url('admin_settings_global'))
 

	
 
        defaults = Setting.get_app_settings()
 
        defaults = db.Setting.get_app_settings()
 
        defaults.update(self._get_hg_ui_settings())
 

	
 
        return htmlfill.render(
 
            render('admin/settings/settings.html'),
 
            defaults=defaults,
 
            encoding="UTF-8",
 
            force_defaults=False)
 

	
 
    @HasPermissionAnyDecorator('hg.admin')
 
    def settings_visual(self):
 
        c.active = 'visual'
 
        if request.POST:
 
            application_form = ApplicationVisualisationForm()()
 
            try:
 
                form_result = application_form.to_python(dict(request.POST))
 
            except formencode.Invalid as errors:
 
                return htmlfill.render(
 
                    render('admin/settings/settings.html'),
 
                    defaults=errors.value,
 
                    errors=errors.error_dict or {},
 
                    prefix_error=False,
 
                    encoding="UTF-8",
 
                    force_defaults=False)
 

	
 
            try:
 
                settings = [
 
                    ('show_public_icon', 'show_public_icon', 'bool'),
 
                    ('show_private_icon', 'show_private_icon', 'bool'),
 
                    ('stylify_metalabels', 'stylify_metalabels', 'bool'),
 
                    ('repository_fields', 'repository_fields', 'bool'),
 
                    ('dashboard_items', 'dashboard_items', 'int'),
 
                    ('admin_grid_items', 'admin_grid_items', 'int'),
 
                    ('show_version', 'show_version', 'bool'),
 
                    ('use_gravatar', 'use_gravatar', 'bool'),
 
                    ('gravatar_url', 'gravatar_url', 'unicode'),
 
                    ('clone_uri_tmpl', 'clone_uri_tmpl', 'unicode'),
 
                    ('clone_ssh_tmpl', 'clone_ssh_tmpl', 'unicode'),
 
                ]
 
                for setting, form_key, type_ in settings:
 
                    Setting.create_or_update(setting, form_result[form_key], type_)
 
                    db.Setting.create_or_update(setting, form_result[form_key], type_)
 

	
 
                meta.Session().commit()
 
                set_app_settings(config)
 
                h.flash(_('Updated visualisation settings'),
 
                        category='success')
 

	
 
            except Exception:
 
                log.error(traceback.format_exc())
 
                h.flash(_('Error occurred during updating '
 
                          'visualisation settings'),
 
                        category='error')
 

	
 
            raise HTTPFound(location=url('admin_settings_visual'))
 

	
 
        defaults = Setting.get_app_settings()
 
        defaults = db.Setting.get_app_settings()
 
        defaults.update(self._get_hg_ui_settings())
 

	
 
        return htmlfill.render(
 
            render('admin/settings/settings.html'),
 
            defaults=defaults,
 
            encoding="UTF-8",
 
            force_defaults=False)
 

	
 
    @HasPermissionAnyDecorator('hg.admin')
 
    def settings_email(self):
 
        c.active = 'email'
 
        if request.POST:
 
            test_email = request.POST.get('test_email')
 
            test_email_subj = 'Kallithea test email'
 
            test_body = ('Kallithea Email test, '
 
                               'Kallithea version: %s' % c.kallithea_version)
 
            if not test_email:
 
                h.flash(_('Please enter email address'), category='error')
 
                raise HTTPFound(location=url('admin_settings_email'))
 

	
 
            test_email_txt_body = EmailNotificationModel() \
 
                .get_email_tmpl(EmailNotificationModel.TYPE_DEFAULT,
 
                                'txt', body=test_body)
 
            test_email_html_body = EmailNotificationModel() \
 
                .get_email_tmpl(EmailNotificationModel.TYPE_DEFAULT,
 
                                'html', body=test_body)
 

	
 
            recipients = [test_email] if test_email else None
 

	
 
            tasks.send_email(recipients, test_email_subj,
 
                             test_email_txt_body, test_email_html_body)
 

	
 
            h.flash(_('Send email task created'), category='success')
 
            raise HTTPFound(location=url('admin_settings_email'))
 

	
 
        defaults = Setting.get_app_settings()
 
        defaults = db.Setting.get_app_settings()
 
        defaults.update(self._get_hg_ui_settings())
 

	
 
        import kallithea
 
        c.ini = kallithea.CONFIG
 

	
 
        return htmlfill.render(
 
            render('admin/settings/settings.html'),
 
            defaults=defaults,
 
            encoding="UTF-8",
 
            force_defaults=False)
 

	
 
    @HasPermissionAnyDecorator('hg.admin')
 
    def settings_hooks(self):
 
        c.active = 'hooks'
 
        if request.POST:
 
            if c.visual.allow_custom_hooks_settings:
 
                ui_key = request.POST.get('new_hook_ui_key')
 
                ui_value = request.POST.get('new_hook_ui_value')
 

	
 
                hook_id = request.POST.get('hook_id')
 

	
 
                try:
 
                    ui_key = ui_key and ui_key.strip()
 
                    if ui_key in (x.ui_key for x in Ui.get_custom_hooks()):
 
                    if ui_key in (x.ui_key for x in db.Ui.get_custom_hooks()):
 
                        h.flash(_('Hook already exists'), category='error')
 
                    elif ui_key in (x.ui_key for x in Ui.get_builtin_hooks()):
 
                    elif ui_key in (x.ui_key for x in db.Ui.get_builtin_hooks()):
 
                        h.flash(_('Builtin hooks are read-only. Please use another hook name.'), category='error')
 
                    elif ui_value and ui_key:
 
                        Ui.create_or_update_hook(ui_key, ui_value)
 
                        db.Ui.create_or_update_hook(ui_key, ui_value)
 
                        h.flash(_('Added new hook'), category='success')
 
                    elif hook_id:
 
                        Ui.delete(hook_id)
 
                        db.Ui.delete(hook_id)
 
                        meta.Session().commit()
 

	
 
                    # check for edits
 
                    update = False
 
                    _d = request.POST.dict_of_lists()
 
                    for k, v, ov in zip(_d.get('hook_ui_key', []),
 
                                        _d.get('hook_ui_value_new', []),
 
                                        _d.get('hook_ui_value', [])):
 
                        if v != ov:
 
                            Ui.create_or_update_hook(k, v)
 
                            db.Ui.create_or_update_hook(k, v)
 
                            update = True
 

	
 
                    if update:
 
                        h.flash(_('Updated hooks'), category='success')
 
                    meta.Session().commit()
 
                except Exception:
 
                    log.error(traceback.format_exc())
 
                    h.flash(_('Error occurred during hook creation'),
 
                            category='error')
 

	
 
                raise HTTPFound(location=url('admin_settings_hooks'))
 

	
 
        defaults = Setting.get_app_settings()
 
        defaults = db.Setting.get_app_settings()
 
        defaults.update(self._get_hg_ui_settings())
 

	
 
        c.hooks = Ui.get_builtin_hooks()
 
        c.custom_hooks = Ui.get_custom_hooks()
 
        c.hooks = db.Ui.get_builtin_hooks()
 
        c.custom_hooks = db.Ui.get_custom_hooks()
 

	
 
        return htmlfill.render(
 
            render('admin/settings/settings.html'),
 
            defaults=defaults,
 
            encoding="UTF-8",
 
            force_defaults=False)
 

	
 
    @HasPermissionAnyDecorator('hg.admin')
 
    def settings_search(self):
 
        c.active = 'search'
 
        if request.POST:
 
            repo_location = self._get_hg_ui_settings()['paths_root_path']
 
            full_index = request.POST.get('full_index', False)
 
            tasks.whoosh_index(repo_location, full_index)
 
            h.flash(_('Whoosh reindex task scheduled'), category='success')
 
            raise HTTPFound(location=url('admin_settings_search'))
 

	
 
        defaults = Setting.get_app_settings()
 
        defaults = db.Setting.get_app_settings()
 
        defaults.update(self._get_hg_ui_settings())
 

	
 
        return htmlfill.render(
 
            render('admin/settings/settings.html'),
 
            defaults=defaults,
 
            encoding="UTF-8",
 
            force_defaults=False)
 

	
 
    @HasPermissionAnyDecorator('hg.admin')
 
    def settings_system(self):
 
        c.active = 'system'
 

	
 
        defaults = Setting.get_app_settings()
 
        defaults = db.Setting.get_app_settings()
 
        defaults.update(self._get_hg_ui_settings())
 

	
 
        import kallithea
 
        c.ini = kallithea.CONFIG
 
        server_info = Setting.get_server_info()
 
        server_info = db.Setting.get_server_info()
 
        for key, val in server_info.items():
 
            setattr(c, key, val)
 

	
 
        return htmlfill.render(
 
            render('admin/settings/settings.html'),
 
            defaults=defaults,
 
            encoding="UTF-8",
 
            force_defaults=False)
kallithea/controllers/admin/user_groups.py
Show inline comments
 
@@ -22,87 +22,86 @@ Original author and date, and relevant c
 
:created_on: Jan 25, 2011
 
:author: marcink
 
:copyright: (c) 2013 RhodeCode GmbH, and others.
 
:license: GPLv3, see LICENSE.md for more details.
 
"""
 

	
 
import logging
 
import traceback
 

	
 
import formencode
 
from formencode import htmlfill
 
from sqlalchemy.orm import joinedload
 
from sqlalchemy.sql.expression import func
 
from tg import app_globals, request
 
from tg import tmpl_context as c
 
from tg.i18n import ugettext as _
 
from webob.exc import HTTPFound, HTTPInternalServerError
 

	
 
from kallithea.lib import helpers as h
 
from kallithea.lib.auth import HasPermissionAnyDecorator, HasUserGroupPermissionLevelDecorator, LoginRequired
 
from kallithea.lib.base import BaseController, render
 
from kallithea.lib.exceptions import RepoGroupAssignmentError, UserGroupsAssignedException
 
from kallithea.lib.utils import action_logger
 
from kallithea.lib.utils2 import safe_int, safe_str
 
from kallithea.lib.webutils import url
 
from kallithea.model import meta
 
from kallithea.model.db import User, UserGroup, UserGroupRepoGroupToPerm, UserGroupRepoToPerm, UserGroupToPerm
 
from kallithea.lib.utils3 import url
 
from kallithea.model import db, meta
 
from kallithea.model.forms import CustomDefaultPermissionsForm, UserGroupForm, UserGroupPermsForm
 
from kallithea.model.scm import UserGroupList
 
from kallithea.model.user_group import UserGroupModel
 

	
 

	
 
log = logging.getLogger(__name__)
 

	
 

	
 
class UserGroupsController(BaseController):
 

	
 
    @LoginRequired(allow_default_user=True)
 
    def _before(self, *args, **kwargs):
 
        super(UserGroupsController, self)._before(*args, **kwargs)
 

	
 
    def __load_data(self, user_group_id):
 
        c.group_members_obj = sorted((x.user for x in c.user_group.members),
 
                                     key=lambda u: u.username.lower())
 

	
 
        c.group_members = [(x.user_id, x.username) for x in c.group_members_obj]
 
        c.available_members = sorted(((x.user_id, x.username) for x in
 
                                      User.query().all()),
 
                                      db.User.query().all()),
 
                                     key=lambda u: u[1].lower())
 

	
 
    def __load_defaults(self, user_group_id):
 
        """
 
        Load defaults settings for edit, and update
 

	
 
        :param user_group_id:
 
        """
 
        user_group = UserGroup.get_or_404(user_group_id)
 
        user_group = db.UserGroup.get_or_404(user_group_id)
 
        data = user_group.get_dict()
 
        return data
 

	
 
    def index(self, format='html'):
 
        _list = UserGroup.query() \
 
                        .order_by(func.lower(UserGroup.users_group_name)) \
 
        _list = db.UserGroup.query() \
 
                        .order_by(func.lower(db.UserGroup.users_group_name)) \
 
                        .all()
 
        group_iter = UserGroupList(_list, perm_level='admin')
 
        user_groups_data = []
 
        _tmpl_lookup = app_globals.mako_lookup
 
        template = _tmpl_lookup.get_template('data_table/_dt_elements.html')
 

	
 
        def user_group_name(user_group_id, user_group_name):
 
            return template.get_def("user_group_name") \
 
                .render_unicode(user_group_id, user_group_name, _=_, h=h, c=c)
 

	
 
        def user_group_actions(user_group_id, user_group_name):
 
            return template.get_def("user_group_actions") \
 
                .render_unicode(user_group_id, user_group_name, _=_, h=h, c=c)
 

	
 
        for user_gr in group_iter:
 
            user_groups_data.append({
 
                "raw_name": user_gr.users_group_name,
 
                "group_name": user_group_name(user_gr.users_group_id,
 
                                              user_gr.users_group_name),
 
                "desc": h.escape(user_gr.user_group_description),
 
                "members": len(user_gr.members),
 
                "active": h.boolicon(user_gr.users_group_active),
 
                "owner": h.person(user_gr.owner.username),
 
                "action": user_group_actions(user_gr.users_group_id, user_gr.users_group_name)
 
@@ -133,276 +132,276 @@ class UserGroupsController(BaseControlle
 
            h.flash(h.HTML(_('Created user group %s')) % h.link_to(gr, url('edit_users_group', id=ug.users_group_id)),
 
                category='success')
 
            meta.Session().commit()
 
        except formencode.Invalid as errors:
 
            return htmlfill.render(
 
                render('admin/user_groups/user_group_add.html'),
 
                defaults=errors.value,
 
                errors=errors.error_dict or {},
 
                prefix_error=False,
 
                encoding="UTF-8",
 
                force_defaults=False)
 
        except Exception:
 
            log.error(traceback.format_exc())
 
            h.flash(_('Error occurred during creation of user group %s')
 
                    % request.POST.get('users_group_name'), category='error')
 

	
 
        raise HTTPFound(location=url('users_groups'))
 

	
 
    @HasPermissionAnyDecorator('hg.admin', 'hg.usergroup.create.true')
 
    def new(self, format='html'):
 
        return render('admin/user_groups/user_group_add.html')
 

	
 
    @HasUserGroupPermissionLevelDecorator('admin')
 
    def update(self, id):
 
        c.user_group = UserGroup.get_or_404(id)
 
        c.user_group = db.UserGroup.get_or_404(id)
 
        c.active = 'settings'
 
        self.__load_data(id)
 

	
 
        available_members = [safe_str(x[0]) for x in c.available_members]
 

	
 
        users_group_form = UserGroupForm(edit=True,
 
                                         old_data=c.user_group.get_dict(),
 
                                         available_members=available_members)()
 

	
 
        try:
 
            form_result = users_group_form.to_python(request.POST)
 
            UserGroupModel().update(c.user_group, form_result)
 
            gr = form_result['users_group_name']
 
            action_logger(request.authuser,
 
                          'admin_updated_users_group:%s' % gr,
 
                          None, request.ip_addr)
 
            h.flash(_('Updated user group %s') % gr, category='success')
 
            meta.Session().commit()
 
        except formencode.Invalid as errors:
 
            ug_model = UserGroupModel()
 
            defaults = errors.value
 
            e = errors.error_dict or {}
 
            defaults.update({
 
                'create_repo_perm': ug_model.has_perm(id,
 
                                                      'hg.create.repository'),
 
                'fork_repo_perm': ug_model.has_perm(id,
 
                                                    'hg.fork.repository'),
 
            })
 

	
 
            return htmlfill.render(
 
                render('admin/user_groups/user_group_edit.html'),
 
                defaults=defaults,
 
                errors=e,
 
                prefix_error=False,
 
                encoding="UTF-8",
 
                force_defaults=False)
 
        except Exception:
 
            log.error(traceback.format_exc())
 
            h.flash(_('Error occurred during update of user group %s')
 
                    % request.POST.get('users_group_name'), category='error')
 

	
 
        raise HTTPFound(location=url('edit_users_group', id=id))
 

	
 
    @HasUserGroupPermissionLevelDecorator('admin')
 
    def delete(self, id):
 
        usr_gr = UserGroup.get_or_404(id)
 
        usr_gr = db.UserGroup.get_or_404(id)
 
        try:
 
            UserGroupModel().delete(usr_gr)
 
            meta.Session().commit()
 
            h.flash(_('Successfully deleted user group'), category='success')
 
        except UserGroupsAssignedException as e:
 
            h.flash(e, category='error')
 
        except Exception:
 
            log.error(traceback.format_exc())
 
            h.flash(_('An error occurred during deletion of user group'),
 
                    category='error')
 
        raise HTTPFound(location=url('users_groups'))
 

	
 
    @HasUserGroupPermissionLevelDecorator('admin')
 
    def edit(self, id, format='html'):
 
        c.user_group = UserGroup.get_or_404(id)
 
        c.user_group = db.UserGroup.get_or_404(id)
 
        c.active = 'settings'
 
        self.__load_data(id)
 

	
 
        defaults = self.__load_defaults(id)
 

	
 
        return htmlfill.render(
 
            render('admin/user_groups/user_group_edit.html'),
 
            defaults=defaults,
 
            encoding="UTF-8",
 
            force_defaults=False
 
        )
 

	
 
    @HasUserGroupPermissionLevelDecorator('admin')
 
    def edit_perms(self, id):
 
        c.user_group = UserGroup.get_or_404(id)
 
        c.user_group = db.UserGroup.get_or_404(id)
 
        c.active = 'perms'
 

	
 
        defaults = {}
 
        # fill user group users
 
        for p in c.user_group.user_user_group_to_perm:
 
            defaults.update({'u_perm_%s' % p.user.username:
 
                             p.permission.permission_name})
 

	
 
        for p in c.user_group.user_group_user_group_to_perm:
 
            defaults.update({'g_perm_%s' % p.user_group.users_group_name:
 
                             p.permission.permission_name})
 

	
 
        return htmlfill.render(
 
            render('admin/user_groups/user_group_edit.html'),
 
            defaults=defaults,
 
            encoding="UTF-8",
 
            force_defaults=False
 
        )
 

	
 
    @HasUserGroupPermissionLevelDecorator('admin')
 
    def update_perms(self, id):
 
        """
 
        grant permission for given usergroup
 

	
 
        :param id:
 
        """
 
        user_group = UserGroup.get_or_404(id)
 
        user_group = db.UserGroup.get_or_404(id)
 
        form = UserGroupPermsForm()().to_python(request.POST)
 

	
 
        # set the permissions !
 
        try:
 
            UserGroupModel()._update_permissions(user_group, form['perms_new'],
 
                                                 form['perms_updates'])
 
        except RepoGroupAssignmentError:
 
            h.flash(_('Target group cannot be the same'), category='error')
 
            raise HTTPFound(location=url('edit_user_group_perms', id=id))
 
        # TODO: implement this
 
        #action_logger(request.authuser, 'admin_changed_repo_permissions',
 
        #              repo_name, request.ip_addr)
 
        meta.Session().commit()
 
        h.flash(_('User group permissions updated'), category='success')
 
        raise HTTPFound(location=url('edit_user_group_perms', id=id))
 

	
 
    @HasUserGroupPermissionLevelDecorator('admin')
 
    def delete_perms(self, id):
 
        try:
 
            obj_type = request.POST.get('obj_type')
 
            obj_id = None
 
            if obj_type == 'user':
 
                obj_id = safe_int(request.POST.get('user_id'))
 
            elif obj_type == 'user_group':
 
                obj_id = safe_int(request.POST.get('user_group_id'))
 

	
 
            if not request.authuser.is_admin:
 
                if obj_type == 'user' and request.authuser.user_id == obj_id:
 
                    msg = _('Cannot revoke permission for yourself as admin')
 
                    h.flash(msg, category='warning')
 
                    raise Exception('revoke admin permission on self')
 
            if obj_type == 'user':
 
                UserGroupModel().revoke_user_permission(user_group=id,
 
                                                        user=obj_id)
 
            elif obj_type == 'user_group':
 
                UserGroupModel().revoke_user_group_permission(target_user_group=id,
 
                                                              user_group=obj_id)
 
            meta.Session().commit()
 
        except Exception:
 
            log.error(traceback.format_exc())
 
            h.flash(_('An error occurred during revoking of permission'),
 
                    category='error')
 
            raise HTTPInternalServerError()
 

	
 
    @HasUserGroupPermissionLevelDecorator('admin')
 
    def edit_default_perms(self, id):
 
        c.user_group = UserGroup.get_or_404(id)
 
        c.user_group = db.UserGroup.get_or_404(id)
 
        c.active = 'default_perms'
 

	
 
        permissions = {
 
            'repositories': {},
 
            'repositories_groups': {}
 
        }
 
        ugroup_repo_perms = UserGroupRepoToPerm.query() \
 
            .options(joinedload(UserGroupRepoToPerm.permission)) \
 
            .options(joinedload(UserGroupRepoToPerm.repository)) \
 
            .filter(UserGroupRepoToPerm.users_group_id == id) \
 
        ugroup_repo_perms = db.UserGroupRepoToPerm.query() \
 
            .options(joinedload(db.UserGroupRepoToPerm.permission)) \
 
            .options(joinedload(db.UserGroupRepoToPerm.repository)) \
 
            .filter(db.UserGroupRepoToPerm.users_group_id == id) \
 
            .all()
 

	
 
        for gr in ugroup_repo_perms:
 
            permissions['repositories'][gr.repository.repo_name]  \
 
                = gr.permission.permission_name
 

	
 
        ugroup_group_perms = UserGroupRepoGroupToPerm.query() \
 
            .options(joinedload(UserGroupRepoGroupToPerm.permission)) \
 
            .options(joinedload(UserGroupRepoGroupToPerm.group)) \
 
            .filter(UserGroupRepoGroupToPerm.users_group_id == id) \
 
        ugroup_group_perms = db.UserGroupRepoGroupToPerm.query() \
 
            .options(joinedload(db.UserGroupRepoGroupToPerm.permission)) \
 
            .options(joinedload(db.UserGroupRepoGroupToPerm.group)) \
 
            .filter(db.UserGroupRepoGroupToPerm.users_group_id == id) \
 
            .all()
 

	
 
        for gr in ugroup_group_perms:
 
            permissions['repositories_groups'][gr.group.group_name] \
 
                = gr.permission.permission_name
 
        c.permissions = permissions
 

	
 
        ug_model = UserGroupModel()
 

	
 
        defaults = c.user_group.get_dict()
 
        defaults.update({
 
            'create_repo_perm': ug_model.has_perm(c.user_group,
 
                                                  'hg.create.repository'),
 
            'create_user_group_perm': ug_model.has_perm(c.user_group,
 
                                                        'hg.usergroup.create.true'),
 
            'fork_repo_perm': ug_model.has_perm(c.user_group,
 
                                                'hg.fork.repository'),
 
        })
 

	
 
        return htmlfill.render(
 
            render('admin/user_groups/user_group_edit.html'),
 
            defaults=defaults,
 
            encoding="UTF-8",
 
            force_defaults=False
 
        )
 

	
 
    @HasUserGroupPermissionLevelDecorator('admin')
 
    def update_default_perms(self, id):
 
        user_group = UserGroup.get_or_404(id)
 
        user_group = db.UserGroup.get_or_404(id)
 

	
 
        try:
 
            form = CustomDefaultPermissionsForm()()
 
            form_result = form.to_python(request.POST)
 

	
 
            usergroup_model = UserGroupModel()
 

	
 
            defs = UserGroupToPerm.query() \
 
                .filter(UserGroupToPerm.users_group == user_group) \
 
            defs = db.UserGroupToPerm.query() \
 
                .filter(db.UserGroupToPerm.users_group == user_group) \
 
                .all()
 
            for ug in defs:
 
                meta.Session().delete(ug)
 

	
 
            if form_result['create_repo_perm']:
 
                usergroup_model.grant_perm(id, 'hg.create.repository')
 
            else:
 
                usergroup_model.grant_perm(id, 'hg.create.none')
 
            if form_result['create_user_group_perm']:
 
                usergroup_model.grant_perm(id, 'hg.usergroup.create.true')
 
            else:
 
                usergroup_model.grant_perm(id, 'hg.usergroup.create.false')
 
            if form_result['fork_repo_perm']:
 
                usergroup_model.grant_perm(id, 'hg.fork.repository')
 
            else:
 
                usergroup_model.grant_perm(id, 'hg.fork.none')
 

	
 
            h.flash(_("Updated permissions"), category='success')
 
            meta.Session().commit()
 
        except Exception:
 
            log.error(traceback.format_exc())
 
            h.flash(_('An error occurred during permissions saving'),
 
                    category='error')
 

	
 
        raise HTTPFound(location=url('edit_user_group_default_perms', id=id))
 

	
 
    @HasUserGroupPermissionLevelDecorator('admin')
 
    def edit_advanced(self, id):
 
        c.user_group = UserGroup.get_or_404(id)
 
        c.user_group = db.UserGroup.get_or_404(id)
 
        c.active = 'advanced'
 
        c.group_members_obj = sorted((x.user for x in c.user_group.members),
 
                                     key=lambda u: u.username.lower())
 
        return render('admin/user_groups/user_group_edit.html')
 

	
 
    @HasUserGroupPermissionLevelDecorator('admin')
 
    def edit_members(self, id):
 
        c.user_group = UserGroup.get_or_404(id)
 
        c.user_group = db.UserGroup.get_or_404(id)
 
        c.active = 'members'
 
        c.group_members_obj = sorted((x.user for x in c.user_group.members),
 
                                     key=lambda u: u.username.lower())
 

	
 
        c.group_members = [(x.user_id, x.username) for x in c.group_members_obj]
 
        return render('admin/user_groups/user_group_edit.html')
kallithea/controllers/admin/users.py
Show inline comments
 
@@ -23,204 +23,203 @@ Original author and date, and relevant c
 
:author: marcink
 
:copyright: (c) 2013 RhodeCode GmbH, and others.
 
:license: GPLv3, see LICENSE.md for more details.
 
"""
 

	
 
import logging
 
import traceback
 

	
 
import formencode
 
from formencode import htmlfill
 
from sqlalchemy.sql.expression import func
 
from tg import app_globals, request
 
from tg import tmpl_context as c
 
from tg.i18n import ugettext as _
 
from webob.exc import HTTPFound, HTTPNotFound
 

	
 
import kallithea
 
from kallithea.lib import auth_modules
 
from kallithea.lib import helpers as h
 
from kallithea.lib.auth import AuthUser, HasPermissionAnyDecorator, LoginRequired
 
from kallithea.lib.base import BaseController, IfSshEnabled, render
 
from kallithea.lib.exceptions import DefaultUserException, UserCreationError, UserOwnsReposException
 
from kallithea.lib.utils import action_logger
 
from kallithea.lib.utils2 import datetime_to_time, generate_api_key, safe_int
 
from kallithea.lib.webutils import url
 
from kallithea.model import meta
 
from kallithea.lib.utils3 import url
 
from kallithea.model import db, meta
 
from kallithea.model.api_key import ApiKeyModel
 
from kallithea.model.db import User, UserEmailMap, UserIpMap, UserToPerm
 
from kallithea.model.forms import CustomDefaultPermissionsForm, UserForm
 
from kallithea.model.ssh_key import SshKeyModel, SshKeyModelException
 
from kallithea.model.user import UserModel
 

	
 

	
 
log = logging.getLogger(__name__)
 

	
 

	
 
class UsersController(BaseController):
 

	
 
    @LoginRequired()
 
    @HasPermissionAnyDecorator('hg.admin')
 
    def _before(self, *args, **kwargs):
 
        super(UsersController, self)._before(*args, **kwargs)
 

	
 
    def index(self, format='html'):
 
        c.users_list = User.query().order_by(User.username) \
 
        c.users_list = db.User.query().order_by(db.User.username) \
 
                        .filter_by(is_default_user=False) \
 
                        .order_by(func.lower(User.username)) \
 
                        .order_by(func.lower(db.User.username)) \
 
                        .all()
 

	
 
        users_data = []
 
        _tmpl_lookup = app_globals.mako_lookup
 
        template = _tmpl_lookup.get_template('data_table/_dt_elements.html')
 

	
 
        grav_tmpl = '<div class="gravatar">%s</div>'
 

	
 
        def username(user_id, username):
 
            return template.get_def("user_name") \
 
                .render_unicode(user_id, username, _=_, h=h, c=c)
 

	
 
        def user_actions(user_id, username):
 
            return template.get_def("user_actions") \
 
                .render_unicode(user_id, username, _=_, h=h, c=c)
 

	
 
        for user in c.users_list:
 
            users_data.append({
 
                "gravatar": grav_tmpl % h.gravatar(user.email, size=20),
 
                "raw_name": user.username,
 
                "username": username(user.user_id, user.username),
 
                "firstname": h.escape(user.name),
 
                "lastname": h.escape(user.lastname),
 
                "last_login": h.fmt_date(user.last_login),
 
                "last_login_raw": datetime_to_time(user.last_login),
 
                "active": h.boolicon(user.active),
 
                "admin": h.boolicon(user.admin),
 
                "extern_type": user.extern_type,
 
                "extern_name": user.extern_name,
 
                "action": user_actions(user.user_id, user.username),
 
            })
 

	
 
        c.data = {
 
            "sort": None,
 
            "dir": "asc",
 
            "records": users_data
 
        }
 

	
 
        return render('admin/users/users.html')
 

	
 
    def create(self):
 
        c.default_extern_type = User.DEFAULT_AUTH_TYPE
 
        c.default_extern_type = db.User.DEFAULT_AUTH_TYPE
 
        c.default_extern_name = ''
 
        user_model = UserModel()
 
        user_form = UserForm()()
 
        try:
 
            form_result = user_form.to_python(dict(request.POST))
 
            user = user_model.create(form_result)
 
            action_logger(request.authuser, 'admin_created_user:%s' % user.username,
 
                          None, request.ip_addr)
 
            h.flash(_('Created user %s') % user.username,
 
                    category='success')
 
            meta.Session().commit()
 
        except formencode.Invalid as errors:
 
            return htmlfill.render(
 
                render('admin/users/user_add.html'),
 
                defaults=errors.value,
 
                errors=errors.error_dict or {},
 
                prefix_error=False,
 
                encoding="UTF-8",
 
                force_defaults=False)
 
        except UserCreationError as e:
 
            h.flash(e, 'error')
 
        except Exception:
 
            log.error(traceback.format_exc())
 
            h.flash(_('Error occurred during creation of user %s')
 
                    % request.POST.get('username'), category='error')
 
        raise HTTPFound(location=url('edit_user', id=user.user_id))
 

	
 
    def new(self, format='html'):
 
        c.default_extern_type = User.DEFAULT_AUTH_TYPE
 
        c.default_extern_type = db.User.DEFAULT_AUTH_TYPE
 
        c.default_extern_name = ''
 
        return render('admin/users/user_add.html')
 

	
 
    def update(self, id):
 
        user_model = UserModel()
 
        user = user_model.get(id)
 
        _form = UserForm(edit=True, old_data={'user_id': id,
 
                                              'email': user.email})()
 
        form_result = {}
 
        try:
 
            form_result = _form.to_python(dict(request.POST))
 
            skip_attrs = ['extern_type', 'extern_name',
 
                         ] + auth_modules.get_managed_fields(user)
 

	
 
            user_model.update(id, form_result, skip_attrs=skip_attrs)
 
            usr = form_result['username']
 
            action_logger(request.authuser, 'admin_updated_user:%s' % usr,
 
                          None, request.ip_addr)
 
            h.flash(_('User updated successfully'), category='success')
 
            meta.Session().commit()
 
        except formencode.Invalid as errors:
 
            defaults = errors.value
 
            e = errors.error_dict or {}
 
            defaults.update({
 
                'create_repo_perm': user_model.has_perm(id,
 
                                                        'hg.create.repository'),
 
                'fork_repo_perm': user_model.has_perm(id, 'hg.fork.repository'),
 
            })
 
            return htmlfill.render(
 
                self._render_edit_profile(user),
 
                defaults=defaults,
 
                errors=e,
 
                prefix_error=False,
 
                encoding="UTF-8",
 
                force_defaults=False)
 
        except Exception:
 
            log.error(traceback.format_exc())
 
            h.flash(_('Error occurred during update of user %s')
 
                    % form_result.get('username'), category='error')
 
        raise HTTPFound(location=url('edit_user', id=id))
 

	
 
    def delete(self, id):
 
        usr = User.get_or_404(id)
 
        usr = db.User.get_or_404(id)
 
        has_ssh_keys = bool(usr.ssh_keys)
 
        try:
 
            UserModel().delete(usr)
 
            meta.Session().commit()
 
            h.flash(_('Successfully deleted user'), category='success')
 
        except (UserOwnsReposException, DefaultUserException) as e:
 
            h.flash(e, category='warning')
 
        except Exception:
 
            log.error(traceback.format_exc())
 
            h.flash(_('An error occurred during deletion of user'),
 
                    category='error')
 
        else:
 
            if has_ssh_keys:
 
                SshKeyModel().write_authorized_keys()
 
        raise HTTPFound(location=url('users'))
 

	
 
    def _get_user_or_raise_if_default(self, id):
 
        try:
 
            return User.get_or_404(id, allow_default=False)
 
            return db.User.get_or_404(id, allow_default=False)
 
        except DefaultUserException:
 
            h.flash(_("The default user cannot be edited"), category='warning')
 
            raise HTTPNotFound
 

	
 
    def _render_edit_profile(self, user):
 
        c.user = user
 
        c.active = 'profile'
 
        c.perm_user = AuthUser(dbuser=user)
 
        managed_fields = auth_modules.get_managed_fields(user)
 
        c.readonly = lambda n: 'readonly' if n in managed_fields else None
 
        return render('admin/users/user_edit.html')
 

	
 
    def edit(self, id, format='html'):
 
        user = self._get_user_or_raise_if_default(id)
 
        defaults = user.get_dict()
 

	
 
        return htmlfill.render(
 
            self._render_edit_profile(user),
 
            defaults=defaults,
 
            encoding="UTF-8",
 
            force_defaults=False)
 

	
 
    def edit_advanced(self, id):
 
        c.user = self._get_user_or_raise_if_default(id)
 
@@ -297,122 +296,122 @@ class UsersController(BaseController):
 

	
 
        umodel = UserModel()
 
        defaults = c.user.get_dict()
 
        defaults.update({
 
            'create_repo_perm': umodel.has_perm(c.user, 'hg.create.repository'),
 
            'create_user_group_perm': umodel.has_perm(c.user,
 
                                                      'hg.usergroup.create.true'),
 
            'fork_repo_perm': umodel.has_perm(c.user, 'hg.fork.repository'),
 
        })
 
        return htmlfill.render(
 
            render('admin/users/user_edit.html'),
 
            defaults=defaults,
 
            encoding="UTF-8",
 
            force_defaults=False)
 

	
 
    def update_perms(self, id):
 
        user = self._get_user_or_raise_if_default(id)
 

	
 
        try:
 
            form = CustomDefaultPermissionsForm()()
 
            form_result = form.to_python(request.POST)
 

	
 
            user_model = UserModel()
 

	
 
            defs = UserToPerm.query() \
 
                .filter(UserToPerm.user == user) \
 
            defs = db.UserToPerm.query() \
 
                .filter(db.UserToPerm.user == user) \
 
                .all()
 
            for ug in defs:
 
                meta.Session().delete(ug)
 

	
 
            if form_result['create_repo_perm']:
 
                user_model.grant_perm(id, 'hg.create.repository')
 
            else:
 
                user_model.grant_perm(id, 'hg.create.none')
 
            if form_result['create_user_group_perm']:
 
                user_model.grant_perm(id, 'hg.usergroup.create.true')
 
            else:
 
                user_model.grant_perm(id, 'hg.usergroup.create.false')
 
            if form_result['fork_repo_perm']:
 
                user_model.grant_perm(id, 'hg.fork.repository')
 
            else:
 
                user_model.grant_perm(id, 'hg.fork.none')
 
            h.flash(_("Updated permissions"), category='success')
 
            meta.Session().commit()
 
        except Exception:
 
            log.error(traceback.format_exc())
 
            h.flash(_('An error occurred during permissions saving'),
 
                    category='error')
 
        raise HTTPFound(location=url('edit_user_perms', id=id))
 

	
 
    def edit_emails(self, id):
 
        c.user = self._get_user_or_raise_if_default(id)
 
        c.active = 'emails'
 
        c.user_email_map = UserEmailMap.query() \
 
            .filter(UserEmailMap.user == c.user).all()
 
        c.user_email_map = db.UserEmailMap.query() \
 
            .filter(db.UserEmailMap.user == c.user).all()
 

	
 
        defaults = c.user.get_dict()
 
        return htmlfill.render(
 
            render('admin/users/user_edit.html'),
 
            defaults=defaults,
 
            encoding="UTF-8",
 
            force_defaults=False)
 

	
 
    def add_email(self, id):
 
        user = self._get_user_or_raise_if_default(id)
 
        email = request.POST.get('new_email')
 
        user_model = UserModel()
 

	
 
        try:
 
            user_model.add_extra_email(id, email)
 
            meta.Session().commit()
 
            h.flash(_("Added email %s to user") % email, category='success')
 
        except formencode.Invalid as error:
 
            msg = error.error_dict['email']
 
            h.flash(msg, category='error')
 
        except Exception:
 
            log.error(traceback.format_exc())
 
            h.flash(_('An error occurred during email saving'),
 
                    category='error')
 
        raise HTTPFound(location=url('edit_user_emails', id=id))
 

	
 
    def delete_email(self, id):
 
        user = self._get_user_or_raise_if_default(id)
 
        email_id = request.POST.get('del_email_id')
 
        user_model = UserModel()
 
        user_model.delete_extra_email(id, email_id)
 
        meta.Session().commit()
 
        h.flash(_("Removed email from user"), category='success')
 
        raise HTTPFound(location=url('edit_user_emails', id=id))
 

	
 
    def edit_ips(self, id):
 
        c.user = self._get_user_or_raise_if_default(id)
 
        c.active = 'ips'
 
        c.user_ip_map = UserIpMap.query() \
 
            .filter(UserIpMap.user == c.user).all()
 
        c.user_ip_map = db.UserIpMap.query() \
 
            .filter(db.UserIpMap.user == c.user).all()
 

	
 
        c.default_user_ip_map = UserIpMap.query() \
 
            .filter(UserIpMap.user_id == kallithea.DEFAULT_USER_ID).all()
 
        c.default_user_ip_map = db.UserIpMap.query() \
 
            .filter(db.UserIpMap.user_id == kallithea.DEFAULT_USER_ID).all()
 

	
 
        defaults = c.user.get_dict()
 
        return htmlfill.render(
 
            render('admin/users/user_edit.html'),
 
            defaults=defaults,
 
            encoding="UTF-8",
 
            force_defaults=False)
 

	
 
    def add_ip(self, id):
 
        ip = request.POST.get('new_ip')
 
        user_model = UserModel()
 

	
 
        try:
 
            user_model.add_extra_ip(id, ip)
 
            meta.Session().commit()
 
            h.flash(_("Added IP address %s to user whitelist") % ip, category='success')
 
        except formencode.Invalid as error:
 
            msg = error.error_dict['ip']
 
            h.flash(msg, category='error')
 
        except Exception:
 
            log.error(traceback.format_exc())
 
            h.flash(_('An error occurred while adding IP address'),
 
                    category='error')
 

	
kallithea/controllers/api/__init__.py
Show inline comments
 
@@ -19,49 +19,49 @@ JSON RPC controller
 

	
 
This file was forked by the Kallithea project in July 2014.
 
Original author and date, and relevant copyright and licensing information is below:
 
:created_on: Aug 20, 2011
 
:author: marcink
 
:copyright: (c) 2013 RhodeCode GmbH, and others.
 
:license: GPLv3, see LICENSE.md for more details.
 
"""
 

	
 
import inspect
 
import itertools
 
import logging
 
import time
 
import traceback
 
import types
 

	
 
from tg import Response, TGController, request, response
 
from webob.exc import HTTPError, HTTPException
 

	
 
from kallithea.lib import ext_json
 
from kallithea.lib.auth import AuthUser
 
from kallithea.lib.base import _get_ip_addr as _get_ip
 
from kallithea.lib.base import get_path_info
 
from kallithea.lib.utils2 import ascii_bytes
 
from kallithea.model.db import User
 
from kallithea.model import db
 

	
 

	
 
log = logging.getLogger('JSONRPC')
 

	
 

	
 
class JSONRPCError(BaseException):
 

	
 
    def __init__(self, message):
 
        self.message = message
 
        super(JSONRPCError, self).__init__()
 

	
 
    def __str__(self):
 
        return self.message
 

	
 

	
 
class JSONRPCErrorResponse(Response, HTTPException):
 
    """
 
    Generate a Response object with a JSON-RPC error body
 
    """
 

	
 
    def __init__(self, message=None, retid=None, code=None):
 
        HTTPException.__init__(self, message, self)
 
        Response.__init__(self,
 
                          json_body=dict(id=retid, result=None, error=message),
 
@@ -124,49 +124,49 @@ class JSONRPCController(TGController):
 
            json_body = ext_json.loads(raw_body)
 
        except ValueError as e:
 
            # catch JSON errors Here
 
            raise JSONRPCErrorResponse(retid=self._req_id,
 
                                       message="JSON parse error ERR:%s RAW:%r"
 
                                                % (e, raw_body))
 

	
 
        # check AUTH based on API key
 
        try:
 
            self._req_api_key = json_body['api_key']
 
            self._req_id = json_body['id']
 
            self._req_method = json_body['method']
 
            self._request_params = json_body['args']
 
            if not isinstance(self._request_params, dict):
 
                self._request_params = {}
 

	
 
            log.debug('method: %s, params: %s',
 
                      self._req_method, self._request_params)
 
        except KeyError as e:
 
            raise JSONRPCErrorResponse(retid=self._req_id,
 
                                       message='Incorrect JSON query missing %s' % e)
 

	
 
        # check if we can find this session using api_key
 
        try:
 
            u = User.get_by_api_key(self._req_api_key)
 
            u = db.User.get_by_api_key(self._req_api_key)
 
            auth_user = AuthUser.make(dbuser=u, ip_addr=ip_addr)
 
            if auth_user is None:
 
                raise JSONRPCErrorResponse(retid=self._req_id,
 
                                           message='Invalid API key')
 
        except Exception as e:
 
            raise JSONRPCErrorResponse(retid=self._req_id,
 
                                       message='Invalid API key')
 

	
 
        request.authuser = auth_user
 
        request.ip_addr = ip_addr
 

	
 
        self._error = None
 
        try:
 
            self._func = self._find_method()
 
        except AttributeError as e:
 
            raise JSONRPCErrorResponse(retid=self._req_id,
 
                                       message=str(e))
 

	
 
        # now that we have a method, add self._req_params to
 
        # self.kargs and dispatch control to WGIController
 
        argspec = inspect.getfullargspec(self._func)
 
        arglist = argspec.args[1:]
 
        argtypes = [type(arg) for arg in argspec.defaults or []]
 
        default_empty = type(NotImplemented)
kallithea/controllers/api/api.py
Show inline comments
 
@@ -17,52 +17,51 @@ kallithea.controllers.api.api
 

	
 
API controller for Kallithea
 

	
 
This file was forked by the Kallithea project in July 2014.
 
Original author and date, and relevant copyright and licensing information is below:
 
:created_on: Aug 20, 2011
 
:author: marcink
 
:copyright: (c) 2013 RhodeCode GmbH, and others.
 
:license: GPLv3, see LICENSE.md for more details.
 
"""
 

	
 
import logging
 
import traceback
 
from datetime import datetime
 

	
 
from tg import request
 

	
 
from kallithea.controllers.api import JSONRPCController, JSONRPCError
 
from kallithea.lib.auth import (AuthUser, HasPermissionAny, HasPermissionAnyDecorator, HasRepoGroupPermissionLevel, HasRepoPermissionLevel,
 
                                HasUserGroupPermissionLevel)
 
from kallithea.lib.exceptions import DefaultUserException, UserGroupsAssignedException
 
from kallithea.lib.utils import action_logger, repo2db_mapper
 
from kallithea.lib.vcs.backends.base import EmptyChangeset
 
from kallithea.lib.vcs.exceptions import EmptyRepositoryError
 
from kallithea.model import meta
 
from kallithea.model import db, meta
 
from kallithea.model.changeset_status import ChangesetStatusModel
 
from kallithea.model.comment import ChangesetCommentsModel
 
from kallithea.model.db import ChangesetStatus, Gist, Permission, PullRequest, RepoGroup, Repository, Setting, User, UserGroup, UserIpMap
 
from kallithea.model.gist import GistModel
 
from kallithea.model.pull_request import PullRequestModel
 
from kallithea.model.repo import RepoModel
 
from kallithea.model.repo_group import RepoGroupModel
 
from kallithea.model.scm import ScmModel, UserGroupList
 
from kallithea.model.user import UserModel
 
from kallithea.model.user_group import UserGroupModel
 

	
 

	
 
log = logging.getLogger(__name__)
 

	
 

	
 
def store_update(updates, attr, name):
 
    """
 
    Stores param in updates dict if it's not None (i.e. if user explicitly set
 
    a parameter). This allows easy updates of passed in params.
 
    """
 
    if attr is not None:
 
        updates[name] = attr
 

	
 

	
 
def get_user_or_error(userid):
 
    """
 
    Get user by id or name or return JsonRPCError if not found
 
@@ -72,74 +71,74 @@ def get_user_or_error(userid):
 
    user = UserModel().get_user(userid)
 
    if user is None:
 
        raise JSONRPCError("user `%s` does not exist" % (userid,))
 
    return user
 

	
 

	
 
def get_repo_or_error(repoid):
 
    """
 
    Get repo by id or name or return JsonRPCError if not found
 

	
 
    :param repoid:
 
    """
 
    repo = RepoModel().get_repo(repoid)
 
    if repo is None:
 
        raise JSONRPCError('repository `%s` does not exist' % (repoid,))
 
    return repo
 

	
 

	
 
def get_repo_group_or_error(repogroupid):
 
    """
 
    Get repo group by id or name or return JsonRPCError if not found
 

	
 
    :param repogroupid:
 
    """
 
    repo_group = RepoGroup.guess_instance(repogroupid)
 
    repo_group = db.RepoGroup.guess_instance(repogroupid)
 
    if repo_group is None:
 
        raise JSONRPCError(
 
            'repository group `%s` does not exist' % (repogroupid,))
 
    return repo_group
 

	
 

	
 
def get_user_group_or_error(usergroupid):
 
    """
 
    Get user group by id or name or return JsonRPCError if not found
 

	
 
    :param usergroupid:
 
    """
 
    user_group = UserGroupModel().get_group(usergroupid)
 
    if user_group is None:
 
        raise JSONRPCError('user group `%s` does not exist' % (usergroupid,))
 
    return user_group
 

	
 

	
 
def get_perm_or_error(permid, prefix=None):
 
    """
 
    Get permission by id or name or return JsonRPCError if not found
 

	
 
    :param permid:
 
    """
 
    perm = Permission.get_by_key(permid)
 
    perm = db.Permission.get_by_key(permid)
 
    if perm is None:
 
        raise JSONRPCError('permission `%s` does not exist' % (permid,))
 
    if prefix:
 
        if not perm.permission_name.startswith(prefix):
 
            raise JSONRPCError('permission `%s` is invalid, '
 
                               'should start with %s' % (permid, prefix))
 
    return perm
 

	
 

	
 
def get_gist_or_error(gistid):
 
    """
 
    Get gist by id or gist_access_id or return JsonRPCError if not found
 

	
 
    :param gistid:
 
    """
 
    gist = GistModel().get_gist(gistid)
 
    if gist is None:
 
        raise JSONRPCError('gist `%s` does not exist' % (gistid,))
 
    return gist
 

	
 

	
 
class ApiController(JSONRPCController):
 
    """
 
    API Controller
 
@@ -302,75 +301,75 @@ class ApiController(JSONRPCController):
 
        This command can be executed only using api_key belonging to user with
 
        admin rights.
 

	
 
        :param userid: username to show ips for
 
        :type userid: Optional(str or int)
 

	
 
        OUTPUT::
 

	
 
            id : <id_given_in_input>
 
            result : {
 
                         "server_ip_addr": "<ip_from_clien>",
 
                         "user_ips": [
 
                                        {
 
                                           "ip_addr": "<ip_with_mask>",
 
                                           "ip_range": ["<start_ip>", "<end_ip>"],
 
                                        },
 
                                        ...
 
                                     ]
 
            }
 

	
 
        """
 
        if userid is None:
 
            userid = request.authuser.user_id
 
        user = get_user_or_error(userid)
 
        ips = UserIpMap.query().filter(UserIpMap.user == user).all()
 
        ips = db.UserIpMap.query().filter(db.UserIpMap.user == user).all()
 
        return dict(
 
            server_ip_addr=request.ip_addr,
 
            user_ips=ips
 
        )
 

	
 
    # alias for old
 
    show_ip = get_ip
 

	
 
    @HasPermissionAnyDecorator('hg.admin')
 
    def get_server_info(self):
 
        """
 
        return server info, including Kallithea version and installed packages
 

	
 

	
 
        OUTPUT::
 

	
 
          id : <id_given_in_input>
 
          result : {
 
            'modules': [<module name>,...]
 
            'py_version': <python version>,
 
            'platform': <platform type>,
 
            'kallithea_version': <kallithea version>
 
          }
 
          error :  null
 
        """
 
        return Setting.get_server_info()
 
        return db.Setting.get_server_info()
 

	
 
    def get_user(self, userid=None):
 
        """
 
        Gets a user by username or user_id, Returns empty result if user is
 
        not found. If userid param is skipped it is set to id of user who is
 
        calling this method. This command can be executed only using api_key
 
        belonging to user with admin rights, or regular users that cannot
 
        specify different userid than theirs
 

	
 
        :param userid: user to get data for
 
        :type userid: Optional(str or int)
 

	
 
        OUTPUT::
 

	
 
            id : <id_given_in_input>
 
            result: None if user does not exist or
 
                    {
 
                        "user_id" :     "<user_id>",
 
                        "api_key" :     "<api_key>",
 
                        "api_keys":     "[<list of all API keys including additional ones>]"
 
                        "username" :    "<username>",
 
                        "firstname":    "<firstname>",
 
                        "lastname" :    "<lastname>",
 
                        "email" :       "<email>",
 
@@ -404,110 +403,110 @@ class ApiController(JSONRPCController):
 
        if userid is None:
 
            userid = request.authuser.user_id
 

	
 
        user = get_user_or_error(userid)
 
        data = user.get_api_data()
 
        data['permissions'] = AuthUser(user_id=user.user_id).permissions
 
        return data
 

	
 
    @HasPermissionAnyDecorator('hg.admin')
 
    def get_users(self):
 
        """
 
        Lists all existing users. This command can be executed only using api_key
 
        belonging to user with admin rights.
 

	
 

	
 
        OUTPUT::
 

	
 
            id : <id_given_in_input>
 
            result: [<user_object>, ...]
 
            error:  null
 
        """
 

	
 
        return [
 
            user.get_api_data()
 
            for user in User.query()
 
                .order_by(User.username)
 
            for user in db.User.query()
 
                .order_by(db.User.username)
 
                .filter_by(is_default_user=False)
 
        ]
 

	
 
    @HasPermissionAnyDecorator('hg.admin')
 
    def create_user(self, username, email, password='',
 
                    firstname='', lastname='',
 
                    active=True, admin=False,
 
                    extern_type=User.DEFAULT_AUTH_TYPE,
 
                    extern_type=db.User.DEFAULT_AUTH_TYPE,
 
                    extern_name=''):
 
        """
 
        Creates new user. Returns new user object. This command can
 
        be executed only using api_key belonging to user with admin rights.
 

	
 
        :param username: new username
 
        :type username: str or int
 
        :param email: email
 
        :type email: str
 
        :param password: password
 
        :type password: Optional(str)
 
        :param firstname: firstname
 
        :type firstname: Optional(str)
 
        :param lastname: lastname
 
        :type lastname: Optional(str)
 
        :param active: active
 
        :type active: Optional(bool)
 
        :param admin: admin
 
        :type admin: Optional(bool)
 
        :param extern_name: name of extern
 
        :type extern_name: Optional(str)
 
        :param extern_type: extern_type
 
        :type extern_type: Optional(str)
 

	
 

	
 
        OUTPUT::
 

	
 
            id : <id_given_in_input>
 
            result: {
 
                      "msg" : "created new user `<username>`",
 
                      "user": <user_obj>
 
                    }
 
            error:  null
 

	
 
        ERROR OUTPUT::
 

	
 
          id : <id_given_in_input>
 
          result : null
 
          error :  {
 
            "user `<username>` already exist"
 
            or
 
            "email `<email>` already exist"
 
            or
 
            "failed to create user `<username>`"
 
          }
 

	
 
        """
 

	
 
        if User.get_by_username(username):
 
        if db.User.get_by_username(username):
 
            raise JSONRPCError("user `%s` already exist" % (username,))
 

	
 
        if User.get_by_email(email):
 
        if db.User.get_by_email(email):
 
            raise JSONRPCError("email `%s` already exist" % (email,))
 

	
 
        try:
 
            user = UserModel().create_or_update(
 
                username=username,
 
                password=password,
 
                email=email,
 
                firstname=firstname,
 
                lastname=lastname,
 
                active=active,
 
                admin=admin,
 
                extern_type=extern_type,
 
                extern_name=extern_name
 
            )
 
            meta.Session().commit()
 
            return dict(
 
                msg='created new user `%s`' % username,
 
                user=user.get_api_data()
 
            )
 
        except Exception:
 
            log.error(traceback.format_exc())
 
            raise JSONRPCError('failed to create user `%s`' % (username,))
 

	
 
    @HasPermissionAnyDecorator('hg.admin')
 
@@ -660,49 +659,49 @@ class ApiController(JSONRPCController):
 
        if not HasPermissionAny('hg.admin')():
 
            if not HasUserGroupPermissionLevel('read')(user_group.users_group_name):
 
                raise JSONRPCError('user group `%s` does not exist' % (usergroupid,))
 

	
 
        data = user_group.get_api_data()
 
        return data
 

	
 
    # permission check inside
 
    def get_user_groups(self):
 
        """
 
        Lists all existing user groups. This command can be executed only using
 
        api_key belonging to user with admin rights or user who has at least
 
        read access to user group.
 

	
 

	
 
        OUTPUT::
 

	
 
            id : <id_given_in_input>
 
            result : [<user_group_obj>,...]
 
            error : null
 
        """
 

	
 
        return [
 
            user_group.get_api_data()
 
            for user_group in UserGroupList(UserGroup.query().all(), perm_level='read')
 
            for user_group in UserGroupList(db.UserGroup.query().all(), perm_level='read')
 
        ]
 

	
 
    @HasPermissionAnyDecorator('hg.admin', 'hg.usergroup.create.true')
 
    def create_user_group(self, group_name, description='',
 
                          owner=None, active=True):
 
        """
 
        Creates new user group. This command can be executed only using api_key
 
        belonging to user with admin rights or an user who has create user group
 
        permission
 

	
 
        :param group_name: name of new user group
 
        :type group_name: str
 
        :param description: group description
 
        :type description: str
 
        :param owner: owner of group. If not passed apiuser is the owner
 
        :type owner: Optional(str or int)
 
        :param active: group is active
 
        :type active: Optional(bool)
 

	
 
        OUTPUT::
 

	
 
            id : <id_given_in_input>
 
            result: {
 
                      "msg": "created new user group `<groupname>`",
 
@@ -1079,49 +1078,49 @@ class ApiController(JSONRPCController):
 

	
 
            id : <id_given_in_input>
 
            result: [
 
                      {
 
                        "repo_id" :          "<repo_id>",
 
                        "repo_name" :        "<reponame>"
 
                        "repo_type" :        "<repo_type>",
 
                        "clone_uri" :        "<clone_uri>",
 
                        "private": :         "<bool>",
 
                        "created_on" :       "<datetimecreated>",
 
                        "description" :      "<description>",
 
                        "landing_rev":       "<landing_rev>",
 
                        "owner":             "<repo_owner>",
 
                        "fork_of":           "<name_of_fork_parent>",
 
                        "enable_downloads":  "<bool>",
 
                        "enable_statistics": "<bool>",
 
                      },
 
                      …
 
                    ]
 
            error:  null
 
        """
 
        if not HasPermissionAny('hg.admin')():
 
            repos = RepoModel().get_all_user_repos(user=request.authuser.user_id)
 
        else:
 
            repos = Repository.query()
 
            repos = db.Repository.query()
 

	
 
        return [
 
            repo.get_api_data()
 
            for repo in repos
 
        ]
 

	
 
    # permission check inside
 
    def get_repo_nodes(self, repoid, revision, root_path,
 
                       ret_type='all'):
 
        """
 
        returns a list of nodes and it's children in a flat list for a given path
 
        at given revision. It's possible to specify ret_type to show only `files` or
 
        `dirs`.  This command can be executed only using api_key belonging to
 
        user with admin rights or regular user that have at least read access to repository.
 

	
 
        :param repoid: repository name or repository id
 
        :type repoid: str or int
 
        :param revision: revision for which listing should be done
 
        :type revision: str
 
        :param root_path: path from which start displaying
 
        :type root_path: str
 
        :param ret_type: return type 'all|files|dirs' nodes
 
        :type ret_type: Optional(str)
 

	
 
@@ -1214,64 +1213,64 @@ class ApiController(JSONRPCController):
 

	
 
        ERROR OUTPUT::
 

	
 
          id : <id_given_in_input>
 
          result : null
 
          error :  {
 
             'failed to create repository `<repo_name>`
 
          }
 

	
 
        """
 
        if not HasPermissionAny('hg.admin')():
 
            if owner is not None:
 
                # forbid setting owner for non-admins
 
                raise JSONRPCError(
 
                    'Only Kallithea admin can specify `owner` param'
 
                )
 
        if owner is None:
 
            owner = request.authuser.user_id
 

	
 
        owner = get_user_or_error(owner)
 

	
 
        if RepoModel().get_by_repo_name(repo_name):
 
            raise JSONRPCError("repo `%s` already exist" % repo_name)
 

	
 
        defs = Setting.get_default_repo_settings(strip_prefix=True)
 
        defs = db.Setting.get_default_repo_settings(strip_prefix=True)
 
        if private is None:
 
            private = defs.get('repo_private') or False
 
        if repo_type is None:
 
            repo_type = defs.get('repo_type')
 
        if enable_statistics is None:
 
            enable_statistics = defs.get('repo_enable_statistics')
 
        if enable_downloads is None:
 
            enable_downloads = defs.get('repo_enable_downloads')
 

	
 
        try:
 
            repo_name_parts = repo_name.split('/')
 
            repo_group = None
 
            if len(repo_name_parts) > 1:
 
                group_name = '/'.join(repo_name_parts[:-1])
 
                repo_group = RepoGroup.get_by_group_name(group_name)
 
                repo_group = db.RepoGroup.get_by_group_name(group_name)
 
                if repo_group is None:
 
                    raise JSONRPCError("repo group `%s` not found" % group_name)
 
            data = dict(
 
                repo_name=repo_name_parts[-1],
 
                repo_name_full=repo_name,
 
                repo_type=repo_type,
 
                repo_description=description,
 
                owner=owner,
 
                repo_private=private,
 
                clone_uri=clone_uri,
 
                repo_group=repo_group,
 
                repo_landing_rev=landing_rev,
 
                enable_statistics=enable_statistics,
 
                enable_downloads=enable_downloads,
 
                repo_copy_permissions=copy_permissions,
 
            )
 

	
 
            task = RepoModel().create(form_data=data, cur_user=owner)
 
            task_id = task.task_id
 
            # no commit, it's done in RepoModel, or async via celery
 
            return dict(
 
                msg="Created new repository `%s`" % (repo_name,),
 
                success=True,  # cannot return the repo data here since fork
 
                               # can be done async
 
@@ -1405,49 +1404,49 @@ class ApiController(JSONRPCController):
 
        if HasPermissionAny('hg.admin')():
 
            pass
 
        elif HasRepoPermissionLevel('read')(repo.repo_name):
 
            if owner is not None:
 
                # forbid setting owner for non-admins
 
                raise JSONRPCError(
 
                    'Only Kallithea admin can specify `owner` param'
 
                )
 

	
 
            if not HasPermissionAny('hg.create.repository')():
 
                raise JSONRPCError('no permission to create repositories')
 
        else:
 
            raise JSONRPCError('repository `%s` does not exist' % (repoid,))
 

	
 
        if owner is None:
 
            owner = request.authuser.user_id
 

	
 
        owner = get_user_or_error(owner)
 

	
 
        try:
 
            fork_name_parts = fork_name.split('/')
 
            repo_group = None
 
            if len(fork_name_parts) > 1:
 
                group_name = '/'.join(fork_name_parts[:-1])
 
                repo_group = RepoGroup.get_by_group_name(group_name)
 
                repo_group = db.RepoGroup.get_by_group_name(group_name)
 
                if repo_group is None:
 
                    raise JSONRPCError("repo group `%s` not found" % group_name)
 

	
 
            form_data = dict(
 
                repo_name=fork_name_parts[-1],
 
                repo_name_full=fork_name,
 
                repo_group=repo_group,
 
                repo_type=repo.repo_type,
 
                description=description,
 
                private=private,
 
                copy_permissions=copy_permissions,
 
                landing_rev=landing_rev,
 
                update_after_clone=False,
 
                fork_parent_id=repo.repo_id,
 
            )
 
            task = RepoModel().create_fork(form_data, cur_user=owner)
 
            # no commit, it's done in RepoModel, or async via celery
 
            task_id = task.task_id
 
            return dict(
 
                msg='Created fork of `%s` as `%s`' % (repo.repo_name,
 
                                                      fork_name),
 
                success=True,  # cannot return the repo data here since fork
 
                               # can be done async
 
                task=task_id
 
@@ -1735,90 +1734,90 @@ class ApiController(JSONRPCController):
 
            members.append(user_data)
 

	
 
        for user_group in repo_group.users_group_to_perm:
 
            perm = user_group.permission.permission_name
 
            user_group = user_group.users_group
 
            user_group_data = {
 
                'name': user_group.users_group_name,
 
                'type': "user_group",
 
                'permission': perm
 
            }
 
            members.append(user_group_data)
 

	
 
        data = repo_group.get_api_data()
 
        data["members"] = members
 
        return data
 

	
 
    @HasPermissionAnyDecorator('hg.admin')
 
    def get_repo_groups(self):
 
        """
 
        Returns all repository groups
 

	
 
        """
 
        return [
 
            repo_group.get_api_data()
 
            for repo_group in RepoGroup.query()
 
            for repo_group in db.RepoGroup.query()
 
        ]
 

	
 
    @HasPermissionAnyDecorator('hg.admin')
 
    def create_repo_group(self, group_name, description='',
 
                          owner=None,
 
                          parent=None,
 
                          copy_permissions=False):
 
        """
 
        Creates a repository group. This command can be executed only using
 
        api_key belonging to user with admin rights.
 

	
 
        :param group_name:
 
        :type group_name:
 
        :param description:
 
        :type description:
 
        :param owner:
 
        :type owner:
 
        :param parent:
 
        :type parent:
 
        :param copy_permissions:
 
        :type copy_permissions:
 

	
 
        OUTPUT::
 

	
 
          id : <id_given_in_input>
 
          result : {
 
              "msg": "created new repo group `<repo_group_name>`"
 
              "repo_group": <repogroup_object>
 
          }
 
          error :  null
 

	
 
        ERROR OUTPUT::
 

	
 
          id : <id_given_in_input>
 
          result : null
 
          error :  {
 
            failed to create repo group `<repogroupid>`
 
          }
 

	
 
        """
 
        if RepoGroup.get_by_group_name(group_name):
 
        if db.RepoGroup.get_by_group_name(group_name):
 
            raise JSONRPCError("repo group `%s` already exist" % (group_name,))
 

	
 
        if owner is None:
 
            owner = request.authuser.user_id
 
        group_description = description
 
        parent_group = None
 
        if parent is not None:
 
            parent_group = get_repo_group_or_error(parent)
 

	
 
        try:
 
            repo_group = RepoGroupModel().create(
 
                group_name=group_name,
 
                group_description=group_description,
 
                owner=owner,
 
                parent=parent_group,
 
                copy_permissions=copy_permissions
 
            )
 
            meta.Session().commit()
 
            return dict(
 
                msg='created new repo group `%s`' % group_name,
 
                repo_group=repo_group.get_api_data()
 
            )
 
        except Exception:
 

	
 
@@ -2169,56 +2168,56 @@ class ApiController(JSONRPCController):
 

	
 
    def get_gists(self, userid=None):
 
        """
 
        Get all gists for given user. If userid is empty returned gists
 
        are for user who called the api
 

	
 
        :param userid: user to get gists for
 
        :type userid: Optional(str or int)
 
        """
 
        if not HasPermissionAny('hg.admin')():
 
            # make sure normal user does not pass someone else userid,
 
            # he is not allowed to do that
 
            if userid is not None and userid != request.authuser.user_id:
 
                raise JSONRPCError(
 
                    'userid is not the same as your user'
 
                )
 

	
 
        if userid is None:
 
            user_id = request.authuser.user_id
 
        else:
 
            user_id = get_user_or_error(userid).user_id
 

	
 
        return [
 
            gist.get_api_data()
 
            for gist in Gist().query()
 
            for gist in db.Gist().query()
 
                .filter_by(is_expired=False)
 
                .filter(Gist.owner_id == user_id)
 
                .order_by(Gist.created_on.desc())
 
                .filter(db.Gist.owner_id == user_id)
 
                .order_by(db.Gist.created_on.desc())
 
        ]
 

	
 
    def create_gist(self, files, owner=None,
 
                    gist_type=Gist.GIST_PUBLIC, lifetime=-1,
 
                    gist_type=db.Gist.GIST_PUBLIC, lifetime=-1,
 
                    description=''):
 

	
 
        """
 
        Creates new Gist
 

	
 
        :param files: files to be added to gist
 
            {'filename': {'content':'...', 'lexer': null},
 
             'filename2': {'content':'...', 'lexer': null}}
 
        :type files: dict
 
        :param owner: gist owner, defaults to api method caller
 
        :type owner: Optional(str or int)
 
        :param gist_type: type of gist 'public' or 'private'
 
        :type gist_type: Optional(str)
 
        :param lifetime: time in minutes of gist lifetime
 
        :type lifetime: Optional(int)
 
        :param description: gist description
 
        :type description: Optional(str)
 

	
 
        OUTPUT::
 

	
 
          id : <id_given_in_input>
 
          result : {
 
            "msg": "created new gist",
 
            "gist": {}
 
@@ -2319,116 +2318,116 @@ class ApiController(JSONRPCController):
 

	
 
    # permission check inside
 
    def get_changeset(self, repoid, raw_id, with_reviews=False):
 
        repo = get_repo_or_error(repoid)
 
        if not HasRepoPermissionLevel('read')(repo.repo_name):
 
            raise JSONRPCError('Access denied to repo %s' % repo.repo_name)
 
        changeset = repo.get_changeset(raw_id)
 
        if isinstance(changeset, EmptyChangeset):
 
            raise JSONRPCError('Changeset %s does not exist' % raw_id)
 

	
 
        info = dict(changeset.as_dict())
 

	
 
        if with_reviews:
 
            reviews = ChangesetStatusModel().get_statuses(
 
                                repo.repo_name, raw_id)
 
            info["reviews"] = reviews
 

	
 
        return info
 

	
 
    # permission check inside
 
    def get_pullrequest(self, pullrequest_id):
 
        """
 
        Get given pull request by id
 
        """
 
        pull_request = PullRequest.get(pullrequest_id)
 
        pull_request = db.PullRequest.get(pullrequest_id)
 
        if pull_request is None:
 
            raise JSONRPCError('pull request `%s` does not exist' % (pullrequest_id,))
 
        if not HasRepoPermissionLevel('read')(pull_request.org_repo.repo_name):
 
            raise JSONRPCError('not allowed')
 
        return pull_request.get_api_data()
 

	
 
    # permission check inside
 
    def comment_pullrequest(self, pull_request_id, comment_msg='', status=None, close_pr=False):
 
        """
 
        Add comment, close and change status of pull request.
 
        """
 
        apiuser = get_user_or_error(request.authuser.user_id)
 
        pull_request = PullRequest.get(pull_request_id)
 
        pull_request = db.PullRequest.get(pull_request_id)
 
        if pull_request is None:
 
            raise JSONRPCError('pull request `%s` does not exist' % (pull_request_id,))
 
        if (not HasRepoPermissionLevel('read')(pull_request.org_repo.repo_name)):
 
            raise JSONRPCError('No permission to add comment. User needs at least reading permissions'
 
                               ' to the source repository.')
 
        owner = apiuser.user_id == pull_request.owner_id
 
        reviewer = apiuser.user_id in [reviewer.user_id for reviewer in pull_request.reviewers]
 
        if close_pr and not (apiuser.admin or owner):
 
            raise JSONRPCError('No permission to close pull request. User needs to be admin or owner.')
 
        if status and not (apiuser.admin or owner or reviewer):
 
            raise JSONRPCError('No permission to change pull request status. User needs to be admin, owner or reviewer.')
 
        if pull_request.is_closed():
 
            raise JSONRPCError('pull request is already closed')
 

	
 
        comment = ChangesetCommentsModel().create(
 
            text=comment_msg,
 
            repo=pull_request.org_repo.repo_id,
 
            author=apiuser.user_id,
 
            pull_request=pull_request.pull_request_id,
 
            f_path=None,
 
            line_no=None,
 
            status_change=ChangesetStatus.get_status_lbl(status),
 
            status_change=db.ChangesetStatus.get_status_lbl(status),
 
            closing_pr=close_pr
 
        )
 
        action_logger(apiuser,
 
                      'user_commented_pull_request:%s' % pull_request_id,
 
                      pull_request.org_repo, request.ip_addr)
 
        if status:
 
            ChangesetStatusModel().set_status(
 
                pull_request.org_repo_id,
 
                status,
 
                apiuser.user_id,
 
                comment,
 
                pull_request=pull_request_id
 
            )
 
        if close_pr:
 
            PullRequestModel().close_pull_request(pull_request_id)
 
            action_logger(apiuser,
 
                          'user_closed_pull_request:%s' % pull_request_id,
 
                          pull_request.org_repo, request.ip_addr)
 
        meta.Session().commit()
 
        return True
 

	
 
    # permission check inside
 
    def edit_reviewers(self, pull_request_id, add=None, remove=None):
 
        """
 
        Add and/or remove one or more reviewers to a pull request, by username
 
        or user ID. Reviewers are specified either as a single-user string or
 
        as a JSON list of one or more strings.
 
        """
 
        if add is None and remove is None:
 
            raise JSONRPCError('''Invalid request. Neither 'add' nor 'remove' is specified.''')
 

	
 
        pull_request = PullRequest.get(pull_request_id)
 
        pull_request = db.PullRequest.get(pull_request_id)
 
        if pull_request is None:
 
            raise JSONRPCError('pull request `%s` does not exist' % (pull_request_id,))
 

	
 
        apiuser = get_user_or_error(request.authuser.user_id)
 
        is_owner = apiuser.user_id == pull_request.owner_id
 
        is_repo_admin = HasRepoPermissionLevel('admin')(pull_request.other_repo.repo_name)
 
        if not (apiuser.admin or is_repo_admin or is_owner):
 
            raise JSONRPCError('No permission to edit reviewers of this pull request. User needs to be admin or pull request owner.')
 
        if pull_request.is_closed():
 
            raise JSONRPCError('Cannot edit reviewers of a closed pull request.')
 

	
 
        if not isinstance(add, list):
 
            add = [add]
 
        if not isinstance(remove, list):
 
            remove = [remove]
 

	
 
        # look up actual user objects from given name or id. Bail out if unknown.
 
        add_objs = set(get_user_or_error(user) for user in add if user is not None)
 
        remove_objs = set(get_user_or_error(user) for user in remove if user is not None)
 

	
 
        new_reviewers = redundant_reviewers = set()
 
        if add_objs:
 
            new_reviewers, redundant_reviewers = PullRequestModel().add_reviewers(apiuser, pull_request, add_objs)
 
        if remove_objs:
kallithea/controllers/changeset.py
Show inline comments
 
@@ -23,52 +23,51 @@ Original author and date, and relevant c
 
:author: marcink
 
:copyright: (c) 2013 RhodeCode GmbH, and others.
 
:license: GPLv3, see LICENSE.md for more details.
 
"""
 

	
 
import binascii
 
import logging
 
import traceback
 
from collections import OrderedDict
 

	
 
from tg import request, response
 
from tg import tmpl_context as c
 
from tg.i18n import ugettext as _
 
from webob.exc import HTTPBadRequest, HTTPForbidden, HTTPNotFound
 

	
 
import kallithea.lib.helpers as h
 
from kallithea.lib import diffs
 
from kallithea.lib.auth import HasRepoPermissionLevelDecorator, LoginRequired
 
from kallithea.lib.base import BaseRepoController, jsonify, render
 
from kallithea.lib.graphmod import graph_data
 
from kallithea.lib.utils import action_logger
 
from kallithea.lib.utils2 import ascii_str, safe_str
 
from kallithea.lib.vcs.backends.base import EmptyChangeset
 
from kallithea.lib.vcs.exceptions import ChangesetDoesNotExistError, EmptyRepositoryError, RepositoryError
 
from kallithea.model import meta
 
from kallithea.model import db, meta
 
from kallithea.model.changeset_status import ChangesetStatusModel
 
from kallithea.model.comment import ChangesetCommentsModel
 
from kallithea.model.db import ChangesetComment, ChangesetStatus
 
from kallithea.model.pull_request import PullRequestModel
 

	
 

	
 
log = logging.getLogger(__name__)
 

	
 

	
 
def create_cs_pr_comment(repo_name, revision=None, pull_request=None, allowed_to_change_status=True):
 
    """
 
    Add a comment to the specified changeset or pull request, using POST values
 
    from the request.
 

	
 
    Comments can be inline (when a file path and line number is specified in
 
    POST) or general comments.
 
    A comment can be accompanied by a review status change (accepted, rejected,
 
    etc.). Pull requests can be closed or deleted.
 

	
 
    Parameter 'allowed_to_change_status' is used for both status changes and
 
    closing of pull requests. For deleting of pull requests, more specific
 
    checks are done.
 
    """
 

	
 
    assert request.environ.get('HTTP_X_PARTIAL_XHR')
 
    if pull_request:
 
        pull_request_id = pull_request.pull_request_id
 
@@ -94,90 +93,90 @@ def create_cs_pr_comment(repo_name, revi
 
        if (pull_request.owner_id == request.authuser.user_id or
 
            h.HasPermissionAny('hg.admin')() or
 
            h.HasRepoPermissionLevel('admin')(pull_request.org_repo.repo_name) or
 
            h.HasRepoPermissionLevel('admin')(pull_request.other_repo.repo_name)
 
        ) and not pull_request.is_closed():
 
            PullRequestModel().delete(pull_request)
 
            meta.Session().commit()
 
            h.flash(_('Successfully deleted pull request %s') % pull_request_id,
 
                    category='success')
 
            return {
 
               'location': h.url('my_pullrequests'), # or repo pr list?
 
            }
 
        raise HTTPForbidden()
 

	
 
    text = request.POST.get('text', '').strip()
 

	
 
    comment = ChangesetCommentsModel().create(
 
        text=text,
 
        repo=c.db_repo.repo_id,
 
        author=request.authuser.user_id,
 
        revision=revision,
 
        pull_request=pull_request_id,
 
        f_path=f_path or None,
 
        line_no=line_no or None,
 
        status_change=ChangesetStatus.get_status_lbl(status) if status else None,
 
        status_change=db.ChangesetStatus.get_status_lbl(status) if status else None,
 
        closing_pr=close_pr,
 
    )
 

	
 
    if status:
 
        ChangesetStatusModel().set_status(
 
            c.db_repo.repo_id,
 
            status,
 
            request.authuser.user_id,
 
            comment,
 
            revision=revision,
 
            pull_request=pull_request_id,
 
        )
 

	
 
    if pull_request:
 
        action = 'user_commented_pull_request:%s' % pull_request_id
 
    else:
 
        action = 'user_commented_revision:%s' % revision
 
    action_logger(request.authuser, action, c.db_repo, request.ip_addr)
 

	
 
    if pull_request and close_pr:
 
        PullRequestModel().close_pull_request(pull_request_id)
 
        action_logger(request.authuser,
 
                      'user_closed_pull_request:%s' % pull_request_id,
 
                      c.db_repo, request.ip_addr)
 

	
 
    meta.Session().commit()
 

	
 
    data = {
 
       'target_id': h.safeid(request.POST.get('f_path')),
 
    }
 
    if comment is not None:
 
        c.comment = comment
 
        data.update(comment.get_dict())
 
        data.update({'rendered_text':
 
                     render('changeset/changeset_comment_block.html')})
 

	
 
    return data
 

	
 
def delete_cs_pr_comment(repo_name, comment_id):
 
    """Delete a comment from a changeset or pull request"""
 
    co = ChangesetComment.get_or_404(comment_id)
 
    co = db.ChangesetComment.get_or_404(comment_id)
 
    if co.repo.repo_name != repo_name:
 
        raise HTTPNotFound()
 
    if co.pull_request and co.pull_request.is_closed():
 
        # don't allow deleting comments on closed pull request
 
        raise HTTPForbidden()
 

	
 
    owner = co.author_id == request.authuser.user_id
 
    repo_admin = h.HasRepoPermissionLevel('admin')(repo_name)
 
    if h.HasPermissionAny('hg.admin')() or repo_admin or owner:
 
        ChangesetCommentsModel().delete(comment=co)
 
        meta.Session().commit()
 
        return True
 
    else:
 
        raise HTTPForbidden()
 

	
 
class ChangesetController(BaseRepoController):
 

	
 
    def _before(self, *args, **kwargs):
 
        super(ChangesetController, self)._before(*args, **kwargs)
 
        c.affected_files_cut_off = 60
 

	
 
    def _index(self, revision, method):
 
        c.pull_request = None
 
        c.fulldiff = request.GET.get('fulldiff') # for reporting number of changed files
 
@@ -189,49 +188,49 @@ class ChangesetController(BaseRepoContro
 
            if len(rev_range) == 2:
 
                enable_comments = False
 
                rev_start = rev_range[0]
 
                rev_end = rev_range[1]
 
                rev_ranges = c.db_repo_scm_instance.get_changesets(start=rev_start,
 
                                                             end=rev_end)
 
            else:
 
                rev_ranges = [c.db_repo_scm_instance.get_changeset(revision)]
 

	
 
            c.cs_ranges = list(rev_ranges)
 
            if not c.cs_ranges:
 
                raise RepositoryError('Changeset range returned empty result')
 

	
 
        except (ChangesetDoesNotExistError, EmptyRepositoryError):
 
            log.debug(traceback.format_exc())
 
            msg = _('Such revision does not exist for this repository')
 
            h.flash(msg, category='error')
 
            raise HTTPNotFound()
 

	
 
        c.changes = OrderedDict()
 

	
 
        c.lines_added = 0  # count of lines added
 
        c.lines_deleted = 0  # count of lines removes
 

	
 
        c.changeset_statuses = ChangesetStatus.STATUSES
 
        c.changeset_statuses = db.ChangesetStatus.STATUSES
 
        comments = dict()
 
        c.statuses = []
 
        c.inline_comments = []
 
        c.inline_cnt = 0
 

	
 
        # Iterate over ranges (default changeset view is always one changeset)
 
        for changeset in c.cs_ranges:
 
            if method == 'show':
 
                c.statuses.extend([ChangesetStatusModel().get_status(
 
                            c.db_repo.repo_id, changeset.raw_id)])
 

	
 
                # Changeset comments
 
                comments.update((com.comment_id, com)
 
                                for com in ChangesetCommentsModel()
 
                                .get_comments(c.db_repo.repo_id,
 
                                              revision=changeset.raw_id))
 

	
 
                # Status change comments - mostly from pull requests
 
                comments.update((st.comment_id, st.comment)
 
                                for st in ChangesetStatusModel()
 
                                .get_statuses(c.db_repo.repo_id,
 
                                              changeset.raw_id, with_revisions=True)
 
                                if st.comment_id is not None)
 

	
kallithea/controllers/compare.py
Show inline comments
 
@@ -18,69 +18,69 @@ kallithea.controllers.compare
 
compare controller showing differences between two
 
repos, branches, bookmarks or tips
 

	
 
This file was forked by the Kallithea project in July 2014.
 
Original author and date, and relevant copyright and licensing information is below:
 
:created_on: May 6, 2012
 
:author: marcink
 
:copyright: (c) 2013 RhodeCode GmbH, and others.
 
:license: GPLv3, see LICENSE.md for more details.
 
"""
 

	
 

	
 
import logging
 

	
 
from tg import request
 
from tg import tmpl_context as c
 
from tg.i18n import ugettext as _
 
from webob.exc import HTTPBadRequest, HTTPFound, HTTPNotFound
 

	
 
from kallithea.lib import diffs
 
from kallithea.lib import helpers as h
 
from kallithea.lib.auth import HasRepoPermissionLevelDecorator, LoginRequired
 
from kallithea.lib.base import BaseRepoController, render
 
from kallithea.lib.graphmod import graph_data
 
from kallithea.lib.webutils import url
 
from kallithea.model.db import Repository
 
from kallithea.lib.utils3 import url
 
from kallithea.model import db
 

	
 

	
 
log = logging.getLogger(__name__)
 

	
 

	
 
class CompareController(BaseRepoController):
 

	
 
    def _before(self, *args, **kwargs):
 
        super(CompareController, self)._before(*args, **kwargs)
 

	
 
        # The base repository has already been retrieved.
 
        c.a_repo = c.db_repo
 

	
 
        # Retrieve the "changeset" repository (default: same as base).
 
        other_repo = request.GET.get('other_repo', None)
 
        if other_repo is None:
 
            c.cs_repo = c.a_repo
 
        else:
 
            c.cs_repo = Repository.get_by_repo_name(other_repo)
 
            c.cs_repo = db.Repository.get_by_repo_name(other_repo)
 
            if c.cs_repo is None:
 
                msg = _('Could not find other repository %s') % other_repo
 
                h.flash(msg, category='error')
 
                raise HTTPFound(location=url('compare_home', repo_name=c.a_repo.repo_name))
 

	
 
        # Verify that it's even possible to compare these two repositories.
 
        if c.a_repo.scm_instance.alias != c.cs_repo.scm_instance.alias:
 
            msg = _('Cannot compare repositories of different types')
 
            h.flash(msg, category='error')
 
            raise HTTPFound(location=url('compare_home', repo_name=c.a_repo.repo_name))
 

	
 
    @LoginRequired(allow_default_user=True)
 
    @HasRepoPermissionLevelDecorator('read')
 
    def index(self, repo_name):
 
        c.compare_home = True
 
        c.a_ref_name = c.cs_ref_name = None
 
        return render('compare/compare_diff.html')
 

	
 
    @LoginRequired(allow_default_user=True)
 
    @HasRepoPermissionLevelDecorator('read')
 
    def compare(self, repo_name, org_ref_type, org_ref_name, other_ref_type, other_ref_name):
 
        org_ref_name = org_ref_name.strip()
 
        other_ref_name = other_ref_name.strip()
 

	
kallithea/controllers/followers.py
Show inline comments
 
@@ -13,45 +13,45 @@
 
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
"""
 
kallithea.controllers.followers
 
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 

	
 
Followers controller for Kallithea
 

	
 
This file was forked by the Kallithea project in July 2014.
 
Original author and date, and relevant copyright and licensing information is below:
 
:created_on: Apr 23, 2011
 
:author: marcink
 
:copyright: (c) 2013 RhodeCode GmbH, and others.
 
:license: GPLv3, see LICENSE.md for more details.
 
"""
 

	
 
import logging
 

	
 
from tg import request
 
from tg import tmpl_context as c
 

	
 
from kallithea.lib.auth import HasRepoPermissionLevelDecorator, LoginRequired
 
from kallithea.lib.base import BaseRepoController, render
 
from kallithea.lib.page import Page
 
from kallithea.lib.utils2 import safe_int
 
from kallithea.model.db import UserFollowing
 
from kallithea.model import db
 

	
 

	
 
log = logging.getLogger(__name__)
 

	
 

	
 
class FollowersController(BaseRepoController):
 

	
 
    @LoginRequired(allow_default_user=True)
 
    @HasRepoPermissionLevelDecorator('read')
 
    def followers(self, repo_name):
 
        p = safe_int(request.GET.get('page'), 1)
 
        repo_id = c.db_repo.repo_id
 
        d = UserFollowing.get_repo_followers(repo_id) \
 
            .order_by(UserFollowing.follows_from)
 
        d = db.UserFollowing.get_repo_followers(repo_id) \
 
            .order_by(db.UserFollowing.follows_from)
 
        c.followers_pager = Page(d, page=p, items_per_page=20)
 

	
 
        if request.environ.get('HTTP_X_PARTIAL_XHR'):
 
            return render('/followers/followers_data.html')
 

	
 
        return render('/followers/followers.html')
kallithea/controllers/forks.py
Show inline comments
 
@@ -20,157 +20,157 @@ forks controller for Kallithea
 
This file was forked by the Kallithea project in July 2014.
 
Original author and date, and relevant copyright and licensing information is below:
 
:created_on: Apr 23, 2011
 
:author: marcink
 
:copyright: (c) 2013 RhodeCode GmbH, and others.
 
:license: GPLv3, see LICENSE.md for more details.
 
"""
 

	
 
import logging
 
import traceback
 

	
 
import formencode
 
from formencode import htmlfill
 
from tg import request
 
from tg import tmpl_context as c
 
from tg.i18n import ugettext as _
 
from webob.exc import HTTPFound
 

	
 
import kallithea
 
import kallithea.lib.helpers as h
 
from kallithea.lib.auth import HasPermissionAnyDecorator, HasRepoPermissionLevel, HasRepoPermissionLevelDecorator, LoginRequired
 
from kallithea.lib.base import BaseRepoController, render
 
from kallithea.lib.page import Page
 
from kallithea.lib.utils2 import safe_int
 
from kallithea.lib.webutils import url
 
from kallithea.model.db import Repository, Ui, UserFollowing
 
from kallithea.lib.utils3 import url
 
from kallithea.model import db
 
from kallithea.model.forms import RepoForkForm
 
from kallithea.model.repo import RepoModel
 
from kallithea.model.scm import AvailableRepoGroupChoices, ScmModel
 

	
 

	
 
log = logging.getLogger(__name__)
 

	
 

	
 
class ForksController(BaseRepoController):
 

	
 
    def __load_defaults(self):
 
        c.repo_groups = AvailableRepoGroupChoices('write')
 

	
 
        c.landing_revs_choices, c.landing_revs = ScmModel().get_repo_landing_revs()
 

	
 
        c.can_update = Ui.get_by_key('hooks', Ui.HOOK_UPDATE).ui_active
 
        c.can_update = db.Ui.get_by_key('hooks', db.Ui.HOOK_UPDATE).ui_active
 

	
 
    def __load_data(self):
 
        """
 
        Load defaults settings for edit, and update
 
        """
 
        self.__load_defaults()
 

	
 
        c.repo_info = c.db_repo
 
        repo = c.db_repo.scm_instance
 

	
 
        if c.repo_info is None:
 
            h.not_mapped_error(c.repo_name)
 
            raise HTTPFound(location=url('repos'))
 

	
 
        c.default_user_id = kallithea.DEFAULT_USER_ID
 
        c.in_public_journal = UserFollowing.query() \
 
            .filter(UserFollowing.user_id == c.default_user_id) \
 
            .filter(UserFollowing.follows_repository == c.repo_info).scalar()
 
        c.in_public_journal = db.UserFollowing.query() \
 
            .filter(db.UserFollowing.user_id == c.default_user_id) \
 
            .filter(db.UserFollowing.follows_repository == c.repo_info).scalar()
 

	
 
        if c.repo_info.stats:
 
            last_rev = c.repo_info.stats.stat_on_revision + 1
 
        else:
 
            last_rev = 0
 
        c.stats_revision = last_rev
 

	
 
        c.repo_last_rev = repo.count() if repo.revisions else 0
 

	
 
        if last_rev == 0 or c.repo_last_rev == 0:
 
            c.stats_percentage = 0
 
        else:
 
            c.stats_percentage = '%.2f' % ((float((last_rev)) /
 
                                            c.repo_last_rev) * 100)
 

	
 
        defaults = RepoModel()._get_defaults(c.repo_name)
 
        # alter the description to indicate a fork
 
        defaults['description'] = ('fork of repository: %s \n%s'
 
                                   % (defaults['repo_name'],
 
                                      defaults['description']))
 
        # add suffix to fork
 
        defaults['repo_name'] = '%s-fork' % defaults['repo_name']
 

	
 
        return defaults
 

	
 
    @LoginRequired(allow_default_user=True)
 
    @HasRepoPermissionLevelDecorator('read')
 
    def forks(self, repo_name):
 
        p = safe_int(request.GET.get('page'), 1)
 
        repo_id = c.db_repo.repo_id
 
        d = []
 
        for r in Repository.get_repo_forks(repo_id):
 
        for r in db.Repository.get_repo_forks(repo_id):
 
            if not HasRepoPermissionLevel('read')(r.repo_name, 'get forks check'):
 
                continue
 
            d.append(r)
 
        c.forks_pager = Page(d, page=p, items_per_page=20)
 

	
 
        if request.environ.get('HTTP_X_PARTIAL_XHR'):
 
            return render('/forks/forks_data.html')
 

	
 
        return render('/forks/forks.html')
 

	
 
    @LoginRequired()
 
    @HasPermissionAnyDecorator('hg.admin', 'hg.fork.repository')
 
    @HasRepoPermissionLevelDecorator('read')
 
    def fork(self, repo_name):
 
        c.repo_info = Repository.get_by_repo_name(repo_name)
 
        c.repo_info = db.Repository.get_by_repo_name(repo_name)
 
        if not c.repo_info:
 
            h.not_mapped_error(repo_name)
 
            raise HTTPFound(location=url('home'))
 

	
 
        defaults = self.__load_data()
 

	
 
        return htmlfill.render(
 
            render('forks/fork.html'),
 
            defaults=defaults,
 
            encoding="UTF-8",
 
            force_defaults=False)
 

	
 
    @LoginRequired()
 
    @HasPermissionAnyDecorator('hg.admin', 'hg.fork.repository')
 
    @HasRepoPermissionLevelDecorator('read')
 
    def fork_create(self, repo_name):
 
        self.__load_defaults()
 
        c.repo_info = Repository.get_by_repo_name(repo_name)
 
        c.repo_info = db.Repository.get_by_repo_name(repo_name)
 
        _form = RepoForkForm(old_data={'repo_type': c.repo_info.repo_type},
 
                             repo_groups=c.repo_groups,
 
                             landing_revs=c.landing_revs_choices)()
 
        form_result = {}
 
        task_id = None
 
        try:
 
            form_result = _form.to_python(dict(request.POST))
 

	
 
            # an approximation that is better than nothing
 
            if not Ui.get_by_key('hooks', Ui.HOOK_UPDATE).ui_active:
 
            if not db.Ui.get_by_key('hooks', db.Ui.HOOK_UPDATE).ui_active:
 
                form_result['update_after_clone'] = False
 

	
 
            # create fork is done sometimes async on celery, db transaction
 
            # management is handled there.
 
            task = RepoModel().create_fork(form_result, request.authuser.user_id)
 
            task_id = task.task_id
 
        except formencode.Invalid as errors:
 
            return htmlfill.render(
 
                render('forks/fork.html'),
 
                defaults=errors.value,
 
                errors=errors.error_dict or {},
 
                prefix_error=False,
 
                encoding="UTF-8",
 
                force_defaults=False)
 
        except Exception:
 
            log.error(traceback.format_exc())
 
            h.flash(_('An error occurred during repository forking %s') %
 
                    repo_name, category='error')
 

	
 
        raise HTTPFound(location=h.url('repo_creating_home',
 
                              repo_name=form_result['repo_name_full'],
 
                              task_id=task_id))
kallithea/controllers/home.py
Show inline comments
 
@@ -17,122 +17,122 @@ kallithea.controllers.home
 

	
 
Home controller for Kallithea
 

	
 
This file was forked by the Kallithea project in July 2014.
 
Original author and date, and relevant copyright and licensing information is below:
 
:created_on: Feb 18, 2010
 
:author: marcink
 
:copyright: (c) 2013 RhodeCode GmbH, and others.
 
:license: GPLv3, see LICENSE.md for more details.
 

	
 
"""
 

	
 
import logging
 

	
 
from sqlalchemy import or_
 
from tg import request
 
from tg import tmpl_context as c
 
from tg.i18n import ugettext as _
 
from webob.exc import HTTPBadRequest
 

	
 
from kallithea.lib import helpers as h
 
from kallithea.lib.auth import HasRepoPermissionLevelDecorator, LoginRequired
 
from kallithea.lib.base import BaseController, jsonify, render
 
from kallithea.lib.utils2 import safe_str
 
from kallithea.model.db import RepoGroup, Repository, User, UserGroup
 
from kallithea.model import db
 
from kallithea.model.repo import RepoModel
 
from kallithea.model.scm import UserGroupList
 

	
 

	
 
log = logging.getLogger(__name__)
 

	
 

	
 
class HomeController(BaseController):
 

	
 
    def about(self):
 
        return render('/about.html')
 

	
 
    @LoginRequired(allow_default_user=True)
 
    def index(self):
 
        c.group = None
 

	
 
        repo_groups_list = self.scm_model.get_repo_groups()
 
        repos_list = Repository.query(sorted=True).filter_by(group=None).all()
 
        repos_list = db.Repository.query(sorted=True).filter_by(group=None).all()
 

	
 
        c.data = RepoModel().get_repos_as_dict(repos_list,
 
                                               repo_groups_list=repo_groups_list,
 
                                               short_name=True)
 

	
 
        return render('/index.html')
 

	
 
    @LoginRequired(allow_default_user=True)
 
    @jsonify
 
    def repo_switcher_data(self):
 
        if request.is_xhr:
 
            all_repos = Repository.query(sorted=True).all()
 
            all_repos = db.Repository.query(sorted=True).all()
 
            repo_iter = self.scm_model.get_repos(all_repos)
 
            all_groups = RepoGroup.query(sorted=True).all()
 
            all_groups = db.RepoGroup.query(sorted=True).all()
 
            repo_groups_iter = self.scm_model.get_repo_groups(all_groups)
 

	
 
            res = [{
 
                    'text': _('Groups'),
 
                    'children': [
 
                       {'id': obj.group_name,
 
                        'text': obj.group_name,
 
                        'type': 'group',
 
                        'obj': {}}
 
                       for obj in repo_groups_iter
 
                    ],
 
                   },
 
                   {
 
                    'text': _('Repositories'),
 
                    'children': [
 
                       {'id': obj.repo_name,
 
                        'text': obj.repo_name,
 
                        'type': 'repo',
 
                        'obj': obj.get_dict()}
 
                       for obj in repo_iter
 
                    ],
 
                   }]
 

	
 
            for res_dict in res:
 
                for child in (res_dict['children']):
 
                    child['obj'].pop('_changeset_cache', None)  # bytes cannot be encoded in json ... but this value isn't relevant on client side at all ...
 

	
 
            data = {
 
                'more': False,
 
                'results': res,
 
            }
 
            return data
 

	
 
        else:
 
            raise HTTPBadRequest()
 

	
 
    @LoginRequired(allow_default_user=True)
 
    @HasRepoPermissionLevelDecorator('read')
 
    @jsonify
 
    def repo_refs_data(self, repo_name):
 
        repo = Repository.get_by_repo_name(repo_name).scm_instance
 
        repo = db.Repository.get_by_repo_name(repo_name).scm_instance
 
        res = []
 
        _branches = repo.branches.items()
 
        if _branches:
 
            res.append({
 
                'text': _('Branch'),
 
                'children': [{'id': safe_str(rev), 'text': safe_str(name), 'type': 'branch'} for name, rev in _branches]
 
            })
 
        _closed_branches = repo.closed_branches.items()
 
        if _closed_branches:
 
            res.append({
 
                'text': _('Closed Branches'),
 
                'children': [{'id': safe_str(rev), 'text': safe_str(name), 'type': 'closed-branch'} for name, rev in _closed_branches]
 
            })
 
        _tags = repo.tags.items()
 
        if _tags:
 
            res.append({
 
                'text': _('Tag'),
 
                'children': [{'id': safe_str(rev), 'text': safe_str(name), 'type': 'tag'} for name, rev in _tags]
 
            })
 
        _bookmarks = repo.bookmarks.items()
 
        if _bookmarks:
 
            res.append({
 
                'text': _('Bookmark'),
 
                'children': [{'id': safe_str(rev), 'text': safe_str(name), 'type': 'book'} for name, rev in _bookmarks]
 
@@ -142,70 +142,70 @@ class HomeController(BaseController):
 
            'results': res
 
        }
 
        return data
 

	
 
    @LoginRequired()
 
    @jsonify
 
    def users_and_groups_data(self):
 
        """
 
        Returns 'results' with a list of users and user groups.
 

	
 
        You can either use the 'key' GET parameter to get a user by providing
 
        the exact user key or you can use the 'query' parameter to
 
        search for users by user key, first name and last name.
 
        'types' defaults to just 'users' but can be set to 'users,groups' to
 
        get both users and groups.
 
        No more than 500 results (of each kind) will be returned.
 
        """
 
        types = request.GET.get('types', 'users').split(',')
 
        key = request.GET.get('key', '')
 
        query = request.GET.get('query', '')
 
        results = []
 
        if 'users' in types:
 
            user_list = []
 
            if key:
 
                u = User.get_by_username(key)
 
                u = db.User.get_by_username(key)
 
                if u:
 
                    user_list = [u]
 
            elif query:
 
                user_list = User.query() \
 
                    .filter(User.is_default_user == False) \
 
                    .filter(User.active == True) \
 
                user_list = db.User.query() \
 
                    .filter(db.User.is_default_user == False) \
 
                    .filter(db.User.active == True) \
 
                    .filter(or_(
 
                        User.username.ilike("%%" + query + "%%"),
 
                        User.name.concat(' ').concat(User.lastname).ilike("%%" + query + "%%"),
 
                        User.lastname.concat(' ').concat(User.name).ilike("%%" + query + "%%"),
 
                        User.email.ilike("%%" + query + "%%"),
 
                        db.User.username.ilike("%%" + query + "%%"),
 
                        db.User.name.concat(' ').concat(db.User.lastname).ilike("%%" + query + "%%"),
 
                        db.User.lastname.concat(' ').concat(db.User.name).ilike("%%" + query + "%%"),
 
                        db.User.email.ilike("%%" + query + "%%"),
 
                    )) \
 
                    .order_by(User.username) \
 
                    .order_by(db.User.username) \
 
                    .limit(500) \
 
                    .all()
 
            for u in user_list:
 
                results.append({
 
                    'type': 'user',
 
                    'id': u.user_id,
 
                    'nname': u.username,
 
                    'fname': u.name,
 
                    'lname': u.lastname,
 
                    'gravatar_lnk': h.gravatar_url(u.email, size=28, default='default'),
 
                    'gravatar_size': 14,
 
                })
 
        if 'groups' in types:
 
            grp_list = []
 
            if key:
 
                grp = UserGroup.get_by_group_name(key)
 
                grp = db.UserGroup.get_by_group_name(key)
 
                if grp:
 
                    grp_list = [grp]
 
            elif query:
 
                grp_list = UserGroup.query() \
 
                    .filter(UserGroup.users_group_name.ilike("%%" + query + "%%")) \
 
                    .filter(UserGroup.users_group_active == True) \
 
                    .order_by(UserGroup.users_group_name) \
 
                grp_list = db.UserGroup.query() \
 
                    .filter(db.UserGroup.users_group_name.ilike("%%" + query + "%%")) \
 
                    .filter(db.UserGroup.users_group_active == True) \
 
                    .order_by(db.UserGroup.users_group_name) \
 
                    .limit(500) \
 
                    .all()
 
            for g in UserGroupList(grp_list, perm_level='read'):
 
                results.append({
 
                    'type': 'group',
 
                    'id': g.users_group_id,
 
                    'grname': g.users_group_name,
 
                })
 
        return dict(results=results)
kallithea/controllers/journal.py
Show inline comments
 
@@ -22,103 +22,102 @@ Original author and date, and relevant c
 
:created_on: Nov 21, 2010
 
:author: marcink
 
:copyright: (c) 2013 RhodeCode GmbH, and others.
 
:license: GPLv3, see LICENSE.md for more details.
 
"""
 

	
 
import logging
 
import traceback
 
from itertools import groupby
 

	
 
from sqlalchemy import or_
 
from sqlalchemy.orm import joinedload
 
from tg import request, response
 
from tg import tmpl_context as c
 
from tg.i18n import ugettext as _
 
from webob.exc import HTTPBadRequest
 

	
 
import kallithea.lib.helpers as h
 
from kallithea.controllers.admin.admin import _journal_filter
 
from kallithea.lib import feeds
 
from kallithea.lib.auth import LoginRequired
 
from kallithea.lib.base import BaseController, render
 
from kallithea.lib.page import Page
 
from kallithea.lib.utils2 import AttributeDict, safe_int
 
from kallithea.model import meta
 
from kallithea.model.db import Repository, User, UserFollowing, UserLog
 
from kallithea.model import db, meta
 
from kallithea.model.repo import RepoModel
 

	
 

	
 
log = logging.getLogger(__name__)
 

	
 

	
 
language = 'en-us'
 
ttl = "5"
 
feed_nr = 20
 

	
 

	
 
class JournalController(BaseController):
 

	
 
    def _before(self, *args, **kwargs):
 
        super(JournalController, self)._before(*args, **kwargs)
 
        c.search_term = request.GET.get('filter')
 

	
 
    def _get_daily_aggregate(self, journal):
 
        groups = []
 
        for k, g in groupby(journal, lambda x: x.action_as_day):
 
            user_group = []
 
            # groupby username if it's a present value, else fallback to journal username
 
            for _unused, g2 in groupby(list(g), lambda x: x.user.username if x.user else x.username):
 
                l = list(g2)
 
                user_group.append((l[0].user, l))
 

	
 
            groups.append((k, user_group,))
 

	
 
        return groups
 

	
 
    def _get_journal_data(self, following_repos):
 
        repo_ids = [x.follows_repository_id for x in following_repos
 
                    if x.follows_repository_id is not None]
 
        user_ids = [x.follows_user_id for x in following_repos
 
                    if x.follows_user_id is not None]
 

	
 
        filtering_criterion = None
 

	
 
        if repo_ids and user_ids:
 
            filtering_criterion = or_(UserLog.repository_id.in_(repo_ids),
 
                        UserLog.user_id.in_(user_ids))
 
            filtering_criterion = or_(db.UserLog.repository_id.in_(repo_ids),
 
                        db.UserLog.user_id.in_(user_ids))
 
        if repo_ids and not user_ids:
 
            filtering_criterion = UserLog.repository_id.in_(repo_ids)
 
            filtering_criterion = db.UserLog.repository_id.in_(repo_ids)
 
        if not repo_ids and user_ids:
 
            filtering_criterion = UserLog.user_id.in_(user_ids)
 
            filtering_criterion = db.UserLog.user_id.in_(user_ids)
 
        if filtering_criterion is not None:
 
            journal = UserLog.query() \
 
                .options(joinedload(UserLog.user)) \
 
                .options(joinedload(UserLog.repository))
 
            journal = db.UserLog.query() \
 
                .options(joinedload(db.UserLog.user)) \
 
                .options(joinedload(db.UserLog.repository))
 
            # filter
 
            journal = _journal_filter(journal, c.search_term)
 
            journal = journal.filter(filtering_criterion) \
 
                        .order_by(UserLog.action_date.desc())
 
                        .order_by(db.UserLog.action_date.desc())
 
        else:
 
            journal = []
 

	
 
        return journal
 

	
 
    def _feed(self, repos, feeder, link, desc):
 
        response.content_type = feeder.content_type
 
        journal = self._get_journal_data(repos)
 

	
 
        header = dict(
 
            title=desc,
 
            link=link,
 
            description=desc,
 
        )
 

	
 
        entries=[]
 
        for entry in journal[:feed_nr]:
 
            user = entry.user
 
            if user is None:
 
                # fix deleted users
 
                user = AttributeDict({'short_contact': entry.username,
 
                                      'email': '',
 
                                      'full_contact': ''})
 
            action, action_extra, ico = h.action_parser(entry, feed=True)
 
@@ -145,132 +144,132 @@ class JournalController(BaseController):
 
            link = h.canonical_url('public_journal_atom')
 
            desc = '%s %s %s' % (c.site_name, _('Public Journal'),
 
                                  'atom feed')
 
        else:
 
            link = h.canonical_url('journal_atom')
 
            desc = '%s %s %s' % (c.site_name, _('Journal'), 'atom feed')
 

	
 
        return self._feed(repos, feeds.AtomFeed, link, desc)
 

	
 
    def _rss_feed(self, repos, public=True):
 
        if public:
 
            link = h.canonical_url('public_journal_atom')
 
            desc = '%s %s %s' % (c.site_name, _('Public Journal'),
 
                                  'rss feed')
 
        else:
 
            link = h.canonical_url('journal_atom')
 
            desc = '%s %s %s' % (c.site_name, _('Journal'), 'rss feed')
 

	
 
        return self._feed(repos, feeds.RssFeed, link, desc)
 

	
 
    @LoginRequired()
 
    def index(self):
 
        # Return a rendered template
 
        p = safe_int(request.GET.get('page'), 1)
 
        c.user = User.get(request.authuser.user_id)
 
        c.following = UserFollowing.query() \
 
            .filter(UserFollowing.user_id == request.authuser.user_id) \
 
            .options(joinedload(UserFollowing.follows_repository)) \
 
        c.user = db.User.get(request.authuser.user_id)
 
        c.following = db.UserFollowing.query() \
 
            .filter(db.UserFollowing.user_id == request.authuser.user_id) \
 
            .options(joinedload(db.UserFollowing.follows_repository)) \
 
            .all()
 

	
 
        journal = self._get_journal_data(c.following)
 

	
 
        c.journal_pager = Page(journal, page=p, items_per_page=20,
 
                               filter=c.search_term)
 
        c.journal_day_aggregate = self._get_daily_aggregate(c.journal_pager)
 

	
 
        if request.environ.get('HTTP_X_PARTIAL_XHR'):
 
            return render('journal/journal_data.html')
 

	
 
        repos_list = Repository.query(sorted=True) \
 
        repos_list = db.Repository.query(sorted=True) \
 
            .filter_by(owner_id=request.authuser.user_id).all()
 

	
 
        repos_data = RepoModel().get_repos_as_dict(repos_list, admin=True)
 
        # data used to render the grid
 
        c.data = repos_data
 

	
 
        return render('journal/journal.html')
 

	
 
    @LoginRequired()
 
    def journal_atom(self):
 
        """Produce a simple atom-1.0 feed"""
 
        following = UserFollowing.query() \
 
            .filter(UserFollowing.user_id == request.authuser.user_id) \
 
            .options(joinedload(UserFollowing.follows_repository)) \
 
        following = db.UserFollowing.query() \
 
            .filter(db.UserFollowing.user_id == request.authuser.user_id) \
 
            .options(joinedload(db.UserFollowing.follows_repository)) \
 
            .all()
 
        return self._atom_feed(following, public=False)
 

	
 
    @LoginRequired()
 
    def journal_rss(self):
 
        """Produce a simple rss2 feed"""
 
        following = UserFollowing.query() \
 
            .filter(UserFollowing.user_id == request.authuser.user_id) \
 
            .options(joinedload(UserFollowing.follows_repository)) \
 
        following = db.UserFollowing.query() \
 
            .filter(db.UserFollowing.user_id == request.authuser.user_id) \
 
            .options(joinedload(db.UserFollowing.follows_repository)) \
 
            .all()
 
        return self._rss_feed(following, public=False)
 

	
 
    @LoginRequired()
 
    def toggle_following(self):
 
        user_id = request.POST.get('follows_user_id')
 
        if user_id:
 
            try:
 
                self.scm_model.toggle_following_user(user_id,
 
                                            request.authuser.user_id)
 
                meta.Session().commit()
 
                return 'ok'
 
            except Exception:
 
                log.error(traceback.format_exc())
 
                raise HTTPBadRequest()
 

	
 
        repo_id = request.POST.get('follows_repository_id')
 
        if repo_id:
 
            try:
 
                self.scm_model.toggle_following_repo(repo_id,
 
                                            request.authuser.user_id)
 
                meta.Session().commit()
 
                return 'ok'
 
            except Exception:
 
                log.error(traceback.format_exc())
 
                raise HTTPBadRequest()
 

	
 
        raise HTTPBadRequest()
 

	
 
    @LoginRequired(allow_default_user=True)
 
    def public_journal(self):
 
        # Return a rendered template
 
        p = safe_int(request.GET.get('page'), 1)
 

	
 
        c.following = UserFollowing.query() \
 
            .filter(UserFollowing.user_id == request.authuser.user_id) \
 
            .options(joinedload(UserFollowing.follows_repository)) \
 
        c.following = db.UserFollowing.query() \
 
            .filter(db.UserFollowing.user_id == request.authuser.user_id) \
 
            .options(joinedload(db.UserFollowing.follows_repository)) \
 
            .all()
 

	
 
        journal = self._get_journal_data(c.following)
 

	
 
        c.journal_pager = Page(journal, page=p, items_per_page=20)
 

	
 
        c.journal_day_aggregate = self._get_daily_aggregate(c.journal_pager)
 

	
 
        if request.environ.get('HTTP_X_PARTIAL_XHR'):
 
            return render('journal/journal_data.html')
 

	
 
        return render('journal/public_journal.html')
 

	
 
    @LoginRequired(allow_default_user=True)
 
    def public_journal_atom(self):
 
        """Produce a simple atom-1.0 feed"""
 
        c.following = UserFollowing.query() \
 
            .filter(UserFollowing.user_id == request.authuser.user_id) \
 
            .options(joinedload(UserFollowing.follows_repository)) \
 
        c.following = db.UserFollowing.query() \
 
            .filter(db.UserFollowing.user_id == request.authuser.user_id) \
 
            .options(joinedload(db.UserFollowing.follows_repository)) \
 
            .all()
 

	
 
        return self._atom_feed(c.following)
 

	
 
    @LoginRequired(allow_default_user=True)
 
    def public_journal_rss(self):
 
        """Produce a simple rss2 feed"""
 
        c.following = UserFollowing.query() \
 
            .filter(UserFollowing.user_id == request.authuser.user_id) \
 
            .options(joinedload(UserFollowing.follows_repository)) \
 
        c.following = db.UserFollowing.query() \
 
            .filter(db.UserFollowing.user_id == request.authuser.user_id) \
 
            .options(joinedload(db.UserFollowing.follows_repository)) \
 
            .all()
 

	
 
        return self._rss_feed(c.following)
kallithea/controllers/login.py
Show inline comments
 
@@ -19,177 +19,176 @@ Login controller for Kallithea
 

	
 
This file was forked by the Kallithea project in July 2014.
 
Original author and date, and relevant copyright and licensing information is below:
 
:created_on: Apr 22, 2010
 
:author: marcink
 
:copyright: (c) 2013 RhodeCode GmbH, and others.
 
:license: GPLv3, see LICENSE.md for more details.
 
"""
 

	
 

	
 
import logging
 
import re
 

	
 
import formencode
 
from formencode import htmlfill
 
from tg import request, session
 
from tg import tmpl_context as c
 
from tg.i18n import ugettext as _
 
from webob.exc import HTTPBadRequest, HTTPFound
 

	
 
import kallithea.lib.helpers as h
 
from kallithea.lib.auth import AuthUser, HasPermissionAnyDecorator
 
from kallithea.lib.base import BaseController, log_in_user, render
 
from kallithea.lib.exceptions import UserCreationError
 
from kallithea.lib.webutils import url
 
from kallithea.model import meta
 
from kallithea.model.db import Setting, User
 
from kallithea.lib.utils3 import url
 
from kallithea.model import db, meta
 
from kallithea.model.forms import LoginForm, PasswordResetConfirmationForm, PasswordResetRequestForm, RegisterForm
 
from kallithea.model.user import UserModel
 

	
 

	
 
log = logging.getLogger(__name__)
 

	
 

	
 
class LoginController(BaseController):
 

	
 
    def _validate_came_from(self, came_from,
 
            _re=re.compile(r"/(?!/)[-!#$%&'()*+,./:;=?@_~0-9A-Za-z]*$")):
 
        """Return True if came_from is valid and can and should be used.
 

	
 
        Determines if a URI reference is valid and relative to the origin;
 
        or in RFC 3986 terms, whether it matches this production:
 

	
 
          origin-relative-ref = path-absolute [ "?" query ] [ "#" fragment ]
 

	
 
        with the exception that '%' escapes are not validated and '#' is
 
        allowed inside the fragment part.
 
        """
 
        return _re.match(came_from) is not None
 

	
 
    def index(self):
 
        c.came_from = request.GET.get('came_from', '')
 
        if c.came_from:
 
            if not self._validate_came_from(c.came_from):
 
                log.error('Invalid came_from (not server-relative): %r', c.came_from)
 
                raise HTTPBadRequest()
 
        else:
 
            c.came_from = url('home')
 

	
 
        if request.POST:
 
            # import Login Form validator class
 
            login_form = LoginForm()()
 
            try:
 
                # login_form will check username/password using ValidAuth and report failure to the user
 
                c.form_result = login_form.to_python(dict(request.POST))
 
                username = c.form_result['username']
 
                user = User.get_by_username_or_email(username)
 
                user = db.User.get_by_username_or_email(username)
 
                assert user is not None  # the same user get just passed in the form validation
 
            except formencode.Invalid as errors:
 
                defaults = errors.value
 
                # remove password from filling in form again
 
                defaults.pop('password', None)
 
                return htmlfill.render(
 
                    render('/login.html'),
 
                    defaults=errors.value,
 
                    errors=errors.error_dict or {},
 
                    prefix_error=False,
 
                    encoding="UTF-8",
 
                    force_defaults=False)
 
            except UserCreationError as e:
 
                # container auth or other auth functions that create users on
 
                # the fly can throw this exception signaling that there's issue
 
                # with user creation, explanation should be provided in
 
                # Exception itself
 
                h.flash(e, 'error')
 
            else:
 
                # login_form already validated the password - now set the session cookie accordingly
 
                auth_user = log_in_user(user, c.form_result['remember'], is_external_auth=False, ip_addr=request.ip_addr)
 
                if auth_user:
 
                    raise HTTPFound(location=c.came_from)
 
                h.flash(_('Authentication failed.'), 'error')
 
        else:
 
            # redirect if already logged in
 
            if not request.authuser.is_anonymous:
 
                raise HTTPFound(location=c.came_from)
 
            # continue to show login to default user
 

	
 
        return render('/login.html')
 

	
 
    @HasPermissionAnyDecorator('hg.admin', 'hg.register.auto_activate',
 
                               'hg.register.manual_activate')
 
    def register(self):
 
        def_user_perms = AuthUser(dbuser=User.get_default_user()).global_permissions
 
        def_user_perms = AuthUser(dbuser=db.User.get_default_user()).global_permissions
 
        c.auto_active = 'hg.register.auto_activate' in def_user_perms
 

	
 
        settings = Setting.get_app_settings()
 
        settings = db.Setting.get_app_settings()
 
        captcha_private_key = settings.get('captcha_private_key')
 
        c.captcha_active = bool(captcha_private_key)
 
        c.captcha_public_key = settings.get('captcha_public_key')
 

	
 
        if request.POST:
 
            register_form = RegisterForm()()
 
            try:
 
                form_result = register_form.to_python(dict(request.POST))
 
                form_result['active'] = c.auto_active
 

	
 
                if c.captcha_active:
 
                    from kallithea.lib.recaptcha import submit
 
                    response = submit(request.POST.get('g-recaptcha-response'),
 
                                      private_key=captcha_private_key,
 
                                      remoteip=request.ip_addr)
 
                    if not response.is_valid:
 
                        _value = form_result
 
                        _msg = _('Bad captcha')
 
                        error_dict = {'recaptcha_field': _msg}
 
                        raise formencode.Invalid(_msg, _value, None,
 
                                                 error_dict=error_dict)
 

	
 
                UserModel().create_registration(form_result)
 
                h.flash(_('You have successfully registered with %s') % (c.site_name or 'Kallithea'),
 
                        category='success')
 
                meta.Session().commit()
 
                raise HTTPFound(location=url('login_home'))
 

	
 
            except formencode.Invalid as errors:
 
                return htmlfill.render(
 
                    render('/register.html'),
 
                    defaults=errors.value,
 
                    errors=errors.error_dict or {},
 
                    prefix_error=False,
 
                    encoding="UTF-8",
 
                    force_defaults=False)
 
            except UserCreationError as e:
 
                # container auth or other auth functions that create users on
 
                # the fly can throw this exception signaling that there's issue
 
                # with user creation, explanation should be provided in
 
                # Exception itself
 
                h.flash(e, 'error')
 

	
 
        return render('/register.html')
 

	
 
    def password_reset(self):
 
        settings = Setting.get_app_settings()
 
        settings = db.Setting.get_app_settings()
 
        captcha_private_key = settings.get('captcha_private_key')
 
        c.captcha_active = bool(captcha_private_key)
 
        c.captcha_public_key = settings.get('captcha_public_key')
 

	
 
        if request.POST:
 
            password_reset_form = PasswordResetRequestForm()()
 
            try:
 
                form_result = password_reset_form.to_python(dict(request.POST))
 
                if c.captcha_active:
 
                    from kallithea.lib.recaptcha import submit
 
                    response = submit(request.POST.get('g-recaptcha-response'),
 
                                      private_key=captcha_private_key,
 
                                      remoteip=request.ip_addr)
 
                    if not response.is_valid:
 
                        _value = form_result
 
                        _msg = _('Bad captcha')
 
                        error_dict = {'recaptcha_field': _msg}
 
                        raise formencode.Invalid(_msg, _value, None,
 
                                                 error_dict=error_dict)
 
                redirect_link = UserModel().send_reset_password_email(form_result)
 
                h.flash(_('A password reset confirmation code has been sent'),
 
                            category='success')
 
                raise HTTPFound(location=redirect_link)
 

	
kallithea/controllers/pullrequests.py
Show inline comments
 
@@ -24,63 +24,62 @@ Original author and date, and relevant c
 
:copyright: (c) 2013 RhodeCode GmbH, and others.
 
:license: GPLv3, see LICENSE.md for more details.
 
"""
 

	
 
import logging
 
import traceback
 

	
 
import formencode
 
import mercurial.unionrepo
 
from tg import request
 
from tg import tmpl_context as c
 
from tg.i18n import ugettext as _
 
from webob.exc import HTTPBadRequest, HTTPForbidden, HTTPFound, HTTPNotFound
 

	
 
from kallithea.controllers.changeset import create_cs_pr_comment, delete_cs_pr_comment
 
from kallithea.lib import diffs
 
from kallithea.lib import helpers as h
 
from kallithea.lib.auth import HasRepoPermissionLevelDecorator, LoginRequired
 
from kallithea.lib.base import BaseRepoController, jsonify, render
 
from kallithea.lib.graphmod import graph_data
 
from kallithea.lib.page import Page
 
from kallithea.lib.utils2 import ascii_bytes, safe_bytes, safe_int
 
from kallithea.lib.vcs.exceptions import ChangesetDoesNotExistError, EmptyRepositoryError
 
from kallithea.lib.webutils import url
 
from kallithea.model import meta
 
from kallithea.model import db, meta
 
from kallithea.model.changeset_status import ChangesetStatusModel
 
from kallithea.model.comment import ChangesetCommentsModel
 
from kallithea.model.db import ChangesetStatus, PullRequest, PullRequestReviewer, Repository, User
 
from kallithea.model.forms import PullRequestForm, PullRequestPostForm
 
from kallithea.model.pull_request import CreatePullRequestAction, CreatePullRequestIterationAction, PullRequestModel
 

	
 

	
 
log = logging.getLogger(__name__)
 

	
 

	
 
def _get_reviewer(user_id):
 
    """Look up user by ID and validate it as a potential reviewer."""
 
    try:
 
        user = User.get(int(user_id))
 
        user = db.User.get(int(user_id))
 
    except ValueError:
 
        user = None
 

	
 
    if user is None or user.is_default_user:
 
        h.flash(_('Invalid reviewer "%s" specified') % user_id, category='error')
 
        raise HTTPBadRequest()
 

	
 
    return user
 

	
 

	
 
class PullrequestsController(BaseRepoController):
 

	
 
    def _get_repo_refs(self, repo, rev=None, branch=None, branch_rev=None):
 
        """return a structure with repo's interesting changesets, suitable for
 
        the selectors in pullrequest.html
 

	
 
        rev: a revision that must be in the list somehow and selected by default
 
        branch: a branch that must be in the list and selected by default - even if closed
 
        branch_rev: a revision of which peers should be preferred and available."""
 
        # list named branches that has been merged to this named branch - it should probably merge back
 
        peers = []
 

	
 
        if branch_rev:
 
            # a revset not restricting to merge() would be better
 
@@ -162,91 +161,91 @@ class PullrequestsController(BaseRepoCon
 
                    selected = 'tag:null:' + repo.EMPTY_CHANGESET
 
                    tags.append((selected, 'null'))
 
            else:  # Git
 
                if not repo.branches:
 
                    selected = ''  # doesn't make sense, but better than nothing
 
                elif 'master' in repo.branches:
 
                    selected = 'branch:master:%s' % repo.branches['master']
 
                else:
 
                    k, v = list(repo.branches.items())[0]
 
                    selected = 'branch:%s:%s' % (k, v)
 

	
 
        groups = [(specials, _("Special")),
 
                  (peers, _("Peer branches")),
 
                  (bookmarks, _("Bookmarks")),
 
                  (branches, _("Branches")),
 
                  (tags, _("Tags")),
 
                  ]
 
        return [g for g in groups if g[0]], selected
 

	
 
    def _is_allowed_to_change_status(self, pull_request):
 
        if pull_request.is_closed():
 
            return False
 

	
 
        owner = request.authuser.user_id == pull_request.owner_id
 
        reviewer = PullRequestReviewer.query() \
 
            .filter(PullRequestReviewer.pull_request == pull_request) \
 
            .filter(PullRequestReviewer.user_id == request.authuser.user_id) \
 
        reviewer = db.PullRequestReviewer.query() \
 
            .filter(db.PullRequestReviewer.pull_request == pull_request) \
 
            .filter(db.PullRequestReviewer.user_id == request.authuser.user_id) \
 
            .count() != 0
 

	
 
        return request.authuser.admin or owner or reviewer
 

	
 
    @LoginRequired(allow_default_user=True)
 
    @HasRepoPermissionLevelDecorator('read')
 
    def show_all(self, repo_name):
 
        c.from_ = request.GET.get('from_') or ''
 
        c.closed = request.GET.get('closed') or ''
 
        url_params = {}
 
        if c.from_:
 
            url_params['from_'] = 1
 
        if c.closed:
 
            url_params['closed'] = 1
 
        p = safe_int(request.GET.get('page'), 1)
 

	
 
        q = PullRequest.query(include_closed=c.closed, sorted=True)
 
        q = db.PullRequest.query(include_closed=c.closed, sorted=True)
 
        if c.from_:
 
            q = q.filter_by(org_repo=c.db_repo)
 
        else:
 
            q = q.filter_by(other_repo=c.db_repo)
 
        c.pull_requests = q.all()
 

	
 
        c.pullrequests_pager = Page(c.pull_requests, page=p, items_per_page=100, **url_params)
 

	
 
        return render('/pullrequests/pullrequest_show_all.html')
 

	
 
    @LoginRequired()
 
    def show_my(self):
 
        c.closed = request.GET.get('closed') or ''
 

	
 
        c.my_pull_requests = PullRequest.query(
 
        c.my_pull_requests = db.PullRequest.query(
 
            include_closed=c.closed,
 
            sorted=True,
 
        ).filter_by(owner_id=request.authuser.user_id).all()
 

	
 
        c.participate_in_pull_requests = []
 
        c.participate_in_pull_requests_todo = []
 
        done_status = set([ChangesetStatus.STATUS_APPROVED, ChangesetStatus.STATUS_REJECTED])
 
        for pr in PullRequest.query(
 
        done_status = set([db.ChangesetStatus.STATUS_APPROVED, db.ChangesetStatus.STATUS_REJECTED])
 
        for pr in db.PullRequest.query(
 
            include_closed=c.closed,
 
            reviewer_id=request.authuser.user_id,
 
            sorted=True,
 
        ):
 
            status = pr.user_review_status(request.authuser.user_id) # very inefficient!!!
 
            if status in done_status:
 
                c.participate_in_pull_requests.append(pr)
 
            else:
 
                c.participate_in_pull_requests_todo.append(pr)
 

	
 
        return render('/pullrequests/pullrequest_show_my.html')
 

	
 
    @LoginRequired()
 
    @HasRepoPermissionLevelDecorator('read')
 
    def index(self):
 
        org_repo = c.db_repo
 
        org_scm_instance = org_repo.scm_instance
 
        try:
 
            org_scm_instance.get_changeset()
 
        except EmptyRepositoryError as e:
 
            h.flash(_('There are no changesets yet'),
 
                    category='warning')
 
            raise HTTPFound(location=url('summary_home', repo_name=org_repo.repo_name))
 

	
 
@@ -299,176 +298,176 @@ class PullrequestsController(BaseRepoCon
 
    def repo_info(self, repo_name):
 
        repo = c.db_repo
 
        refs, selected_ref = self._get_repo_refs(repo.scm_instance)
 
        return {
 
            'description': repo.description.split('\n', 1)[0],
 
            'selected_ref': selected_ref,
 
            'refs': refs,
 
            }
 

	
 
    @LoginRequired()
 
    @HasRepoPermissionLevelDecorator('read')
 
    def create(self, repo_name):
 
        repo = c.db_repo
 
        try:
 
            _form = PullRequestForm(repo.repo_id)().to_python(request.POST)
 
        except formencode.Invalid as errors:
 
            log.error(traceback.format_exc())
 
            log.error(str(errors))
 
            msg = _('Error creating pull request: %s') % errors.msg
 
            h.flash(msg, 'error')
 
            raise HTTPBadRequest
 

	
 
        # heads up: org and other might seem backward here ...
 
        org_ref = _form['org_ref'] # will have merge_rev as rev but symbolic name
 
        org_repo = Repository.guess_instance(_form['org_repo'])
 
        org_repo = db.Repository.guess_instance(_form['org_repo'])
 

	
 
        other_ref = _form['other_ref'] # will have symbolic name and head revision
 
        other_repo = Repository.guess_instance(_form['other_repo'])
 
        other_repo = db.Repository.guess_instance(_form['other_repo'])
 

	
 
        reviewers = []
 

	
 
        title = _form['pullrequest_title']
 
        description = _form['pullrequest_desc'].strip()
 
        owner = User.get(request.authuser.user_id)
 
        owner = db.User.get(request.authuser.user_id)
 

	
 
        try:
 
            cmd = CreatePullRequestAction(org_repo, other_repo, org_ref, other_ref, title, description, owner, reviewers)
 
        except CreatePullRequestAction.ValidationError as e:
 
            h.flash(e, category='error', logf=log.error)
 
            raise HTTPNotFound
 

	
 
        try:
 
            pull_request = cmd.execute()
 
            meta.Session().commit()
 
        except Exception:
 
            h.flash(_('Error occurred while creating pull request'),
 
                    category='error')
 
            log.error(traceback.format_exc())
 
            raise HTTPFound(location=url('pullrequest_home', repo_name=repo_name))
 

	
 
        h.flash(_('Successfully opened new pull request'),
 
                category='success')
 
        raise HTTPFound(location=pull_request.url())
 

	
 
    def create_new_iteration(self, old_pull_request, new_rev, title, description, reviewers):
 
        owner = User.get(request.authuser.user_id)
 
        owner = db.User.get(request.authuser.user_id)
 
        new_org_rev = self._get_ref_rev(old_pull_request.org_repo, 'rev', new_rev)
 
        new_other_rev = self._get_ref_rev(old_pull_request.other_repo, old_pull_request.other_ref_parts[0], old_pull_request.other_ref_parts[1])
 
        try:
 
            cmd = CreatePullRequestIterationAction(old_pull_request, new_org_rev, new_other_rev, title, description, owner, reviewers)
 
        except CreatePullRequestAction.ValidationError as e:
 
            h.flash(e, category='error', logf=log.error)
 
            raise HTTPNotFound
 

	
 
        try:
 
            pull_request = cmd.execute()
 
            meta.Session().commit()
 
        except Exception:
 
            h.flash(_('Error occurred while creating pull request'),
 
                    category='error')
 
            log.error(traceback.format_exc())
 
            raise HTTPFound(location=old_pull_request.url())
 

	
 
        h.flash(_('New pull request iteration created'),
 
                category='success')
 
        raise HTTPFound(location=pull_request.url())
 

	
 
    # pullrequest_post for PR editing
 
    @LoginRequired()
 
    @HasRepoPermissionLevelDecorator('read')
 
    def post(self, repo_name, pull_request_id):
 
        pull_request = PullRequest.get_or_404(pull_request_id)
 
        pull_request = db.PullRequest.get_or_404(pull_request_id)
 
        if pull_request.is_closed():
 
            raise HTTPForbidden()
 
        assert pull_request.other_repo.repo_name == repo_name
 
        # only owner or admin can update it
 
        owner = pull_request.owner_id == request.authuser.user_id
 
        repo_admin = h.HasRepoPermissionLevel('admin')(c.repo_name)
 
        if not (h.HasPermissionAny('hg.admin')() or repo_admin or owner):
 
            raise HTTPForbidden()
 

	
 
        _form = PullRequestPostForm()().to_python(request.POST)
 

	
 
        cur_reviewers = set(pull_request.get_reviewer_users())
 
        new_reviewers = set(_get_reviewer(s) for s in _form['review_members'])
 
        old_reviewers = set(_get_reviewer(s) for s in _form['org_review_members'])
 

	
 
        other_added = cur_reviewers - old_reviewers
 
        other_removed = old_reviewers - cur_reviewers
 

	
 
        if other_added:
 
            h.flash(_('Meanwhile, the following reviewers have been added: %s') %
 
                    (', '.join(u.username for u in other_added)),
 
                    category='warning')
 
        if other_removed:
 
            h.flash(_('Meanwhile, the following reviewers have been removed: %s') %
 
                    (', '.join(u.username for u in other_removed)),
 
                    category='warning')
 

	
 
        if _form['updaterev']:
 
            return self.create_new_iteration(pull_request,
 
                                      _form['updaterev'],
 
                                      _form['pullrequest_title'],
 
                                      _form['pullrequest_desc'],
 
                                      new_reviewers)
 

	
 
        added_reviewers = new_reviewers - old_reviewers - cur_reviewers
 
        removed_reviewers = (old_reviewers - new_reviewers) & cur_reviewers
 

	
 
        old_description = pull_request.description
 
        pull_request.title = _form['pullrequest_title']
 
        pull_request.description = _form['pullrequest_desc'].strip() or _('No description')
 
        pull_request.owner = User.get_by_username(_form['owner'])
 
        user = User.get(request.authuser.user_id)
 
        pull_request.owner = db.User.get_by_username(_form['owner'])
 
        user = db.User.get(request.authuser.user_id)
 

	
 
        PullRequestModel().mention_from_description(user, pull_request, old_description)
 
        PullRequestModel().add_reviewers(user, pull_request, added_reviewers)
 
        PullRequestModel().remove_reviewers(user, pull_request, removed_reviewers)
 

	
 
        meta.Session().commit()
 
        h.flash(_('Pull request updated'), category='success')
 

	
 
        raise HTTPFound(location=pull_request.url())
 

	
 
    @LoginRequired()
 
    @HasRepoPermissionLevelDecorator('read')
 
    @jsonify
 
    def delete(self, repo_name, pull_request_id):
 
        pull_request = PullRequest.get_or_404(pull_request_id)
 
        pull_request = db.PullRequest.get_or_404(pull_request_id)
 
        # only owner can delete it !
 
        if pull_request.owner_id == request.authuser.user_id:
 
            PullRequestModel().delete(pull_request)
 
            meta.Session().commit()
 
            h.flash(_('Successfully deleted pull request'),
 
                    category='success')
 
            raise HTTPFound(location=url('my_pullrequests'))
 
        raise HTTPForbidden()
 

	
 
    @LoginRequired(allow_default_user=True)
 
    @HasRepoPermissionLevelDecorator('read')
 
    def show(self, repo_name, pull_request_id, extra=None):
 
        c.pull_request = PullRequest.get_or_404(pull_request_id)
 
        c.pull_request = db.PullRequest.get_or_404(pull_request_id)
 
        c.allowed_to_change_status = self._is_allowed_to_change_status(c.pull_request)
 
        cc_model = ChangesetCommentsModel()
 
        cs_model = ChangesetStatusModel()
 

	
 
        # pull_requests repo_name we opened it against
 
        # ie. other_repo must match
 
        if repo_name != c.pull_request.other_repo.repo_name:
 
            raise HTTPNotFound
 

	
 
        # load compare data into template context
 
        c.cs_repo = c.pull_request.org_repo
 
        (c.cs_ref_type,
 
         c.cs_ref_name,
 
         c.cs_rev) = c.pull_request.org_ref.split(':')
 

	
 
        c.a_repo = c.pull_request.other_repo
 
        (c.a_ref_type,
 
         c.a_ref_name,
 
         c.a_rev) = c.pull_request.other_ref.split(':') # a_rev is ancestor
 

	
 
        org_scm_instance = c.cs_repo.scm_instance # property with expensive cache invalidation check!!!
 
        c.cs_ranges = []
 
        for x in c.pull_request.revisions:
 
            try:
 
@@ -595,44 +594,44 @@ class PullrequestsController(BaseRepoCon
 
            c.lines_added += st['added']
 
            c.lines_deleted += st['deleted']
 
            filename = f['filename']
 
            fid = h.FID('', filename)
 
            html_diff = diffs.as_html(enable_comments=True, parsed_lines=[f])
 
            c.file_diff_data.append((fid, None, f['operation'], f['old_filename'], filename, html_diff, st))
 

	
 
        # inline comments
 
        c.inline_cnt = 0
 
        c.inline_comments = cc_model.get_inline_comments(
 
                                c.db_repo.repo_id,
 
                                pull_request=pull_request_id)
 
        # count inline comments
 
        for __, lines in c.inline_comments:
 
            for comments in lines.values():
 
                c.inline_cnt += len(comments)
 
        # comments
 
        c.comments = cc_model.get_comments(c.db_repo.repo_id, pull_request=pull_request_id)
 

	
 
        # (badly named) pull-request status calculation based on reviewer votes
 
        (c.pull_request_reviewers,
 
         c.pull_request_pending_reviewers,
 
         c.current_voting_result,
 
         ) = cs_model.calculate_pull_request_result(c.pull_request)
 
        c.changeset_statuses = ChangesetStatus.STATUSES
 
        c.changeset_statuses = db.ChangesetStatus.STATUSES
 

	
 
        c.is_ajax_preview = False
 
        c.ancestors = None # [c.a_rev] ... but that is shown in an other way
 
        return render('/pullrequests/pullrequest_show.html')
 

	
 
    @LoginRequired()
 
    @HasRepoPermissionLevelDecorator('read')
 
    @jsonify
 
    def comment(self, repo_name, pull_request_id):
 
        pull_request = PullRequest.get_or_404(pull_request_id)
 
        pull_request = db.PullRequest.get_or_404(pull_request_id)
 
        allowed_to_change_status = self._is_allowed_to_change_status(pull_request)
 
        return create_cs_pr_comment(repo_name, pull_request=pull_request,
 
                allowed_to_change_status=allowed_to_change_status)
 

	
 
    @LoginRequired()
 
    @HasRepoPermissionLevelDecorator('read')
 
    @jsonify
 
    def delete_comment(self, repo_name, comment_id):
 
        return delete_cs_pr_comment(repo_name, comment_id)
kallithea/controllers/summary.py
Show inline comments
 
@@ -29,49 +29,49 @@ import calendar
 
import itertools
 
import logging
 
import traceback
 
from datetime import date, timedelta
 
from time import mktime
 

	
 
from beaker.cache import cache_region
 
from tg import request
 
from tg import tmpl_context as c
 
from tg.i18n import ugettext as _
 
from webob.exc import HTTPBadRequest
 

	
 
import kallithea.lib.helpers as h
 
from kallithea.lib import ext_json
 
from kallithea.lib.auth import HasRepoPermissionLevelDecorator, LoginRequired
 
from kallithea.lib.base import BaseRepoController, jsonify, render
 
from kallithea.lib.celerylib.tasks import get_commits_stats
 
from kallithea.lib.conf import ALL_EXTS, ALL_READMES, LANGUAGES_EXTENSIONS_MAP
 
from kallithea.lib.markup_renderer import MarkupRenderer
 
from kallithea.lib.page import Page
 
from kallithea.lib.utils2 import safe_int, safe_str
 
from kallithea.lib.vcs.backends.base import EmptyChangeset
 
from kallithea.lib.vcs.exceptions import ChangesetError, EmptyRepositoryError, NodeDoesNotExistError
 
from kallithea.lib.vcs.nodes import FileNode
 
from kallithea.model.db import Statistics
 
from kallithea.model import db
 

	
 

	
 
log = logging.getLogger(__name__)
 

	
 
README_FILES = [''.join([x[0][0], x[1][0]]) for x in
 
                    sorted(list(itertools.product(ALL_READMES, ALL_EXTS)),
 
                           key=lambda y:y[0][1] + y[1][1])]
 

	
 

	
 
class SummaryController(BaseRepoController):
 

	
 
    def __get_readme_data(self, db_repo):
 
        repo_name = db_repo.repo_name
 
        log.debug('Looking for README file')
 

	
 
        @cache_region('long_term_file', '_get_readme_from_cache')
 
        def _get_readme_from_cache(*_cache_keys):  # parameters are not really used - only as caching key
 
            readme_data = None
 
            readme_file = None
 
            try:
 
                # gets the landing revision! or tip if fails
 
                cs = db_repo.get_landing_changeset()
 
                if isinstance(cs, EmptyChangeset):
 
                    raise EmptyRepositoryError()
 
@@ -110,100 +110,100 @@ class SummaryController(BaseRepoControll
 
        except EmptyRepositoryError as e:
 
            h.flash(e, category='warning')
 
            collection = []
 
        c.cs_pagination = Page(collection, page=p, items_per_page=size)
 
        page_revisions = [x.raw_id for x in list(c.cs_pagination)]
 
        c.cs_comments = c.db_repo.get_comments(page_revisions)
 
        c.cs_statuses = c.db_repo.statuses(page_revisions)
 

	
 
        c.ssh_repo_url = None
 
        if request.authuser.is_default_user:
 
            username = None
 
        else:
 
            username = request.authuser.username
 
            if c.ssh_enabled:
 
                c.ssh_repo_url = c.db_repo.clone_url(clone_uri_tmpl=c.clone_ssh_tmpl)
 

	
 
        c.clone_repo_url = c.db_repo.clone_url(clone_uri_tmpl=c.clone_uri_tmpl, with_id=False, username=username)
 
        c.clone_repo_url_id = c.db_repo.clone_url(clone_uri_tmpl=c.clone_uri_tmpl, with_id=True, username=username)
 

	
 
        if c.db_repo.enable_statistics:
 
            c.show_stats = True
 
        else:
 
            c.show_stats = False
 

	
 
        stats = Statistics.query() \
 
            .filter(Statistics.repository == c.db_repo) \
 
        stats = db.Statistics.query() \
 
            .filter(db.Statistics.repository == c.db_repo) \
 
            .scalar()
 

	
 
        c.stats_percentage = 0
 

	
 
        if stats and stats.languages:
 
            lang_stats_d = ext_json.loads(stats.languages)
 
            lang_stats = [(x, {"count": y,
 
                               "desc": LANGUAGES_EXTENSIONS_MAP.get(x, '?')})
 
                          for x, y in lang_stats_d.items()]
 
            lang_stats.sort(key=lambda k: (-k[1]['count'], k[0]))
 
            c.trending_languages = lang_stats[:10]
 
        else:
 
            c.trending_languages = []
 

	
 
        c.enable_downloads = c.db_repo.enable_downloads
 
        c.readme_data, c.readme_file = \
 
            self.__get_readme_data(c.db_repo)
 
        return render('summary/summary.html')
 

	
 
    @LoginRequired()
 
    @HasRepoPermissionLevelDecorator('read')
 
    @jsonify
 
    def repo_size(self, repo_name):
 
        if request.is_xhr:
 
            return c.db_repo._repo_size()
 
        else:
 
            raise HTTPBadRequest()
 

	
 
    @LoginRequired(allow_default_user=True)
 
    @HasRepoPermissionLevelDecorator('read')
 
    def statistics(self, repo_name):
 
        if c.db_repo.enable_statistics:
 
            c.show_stats = True
 
            c.no_data_msg = _('No data ready yet')
 
        else:
 
            c.show_stats = False
 
            c.no_data_msg = _('Statistics are disabled for this repository')
 

	
 
        td = date.today() + timedelta(days=1)
 
        td_1m = td - timedelta(days=calendar.monthrange(td.year, td.month)[1])
 
        td_1y = td - timedelta(days=365)
 

	
 
        ts_min_m = mktime(td_1m.timetuple())
 
        ts_min_y = mktime(td_1y.timetuple())
 
        ts_max_y = mktime(td.timetuple())
 
        c.ts_min = ts_min_m
 
        c.ts_max = ts_max_y
 

	
 
        stats = Statistics.query() \
 
            .filter(Statistics.repository == c.db_repo) \
 
        stats = db.Statistics.query() \
 
            .filter(db.Statistics.repository == c.db_repo) \
 
            .scalar()
 
        c.stats_percentage = 0
 
        if stats and stats.languages:
 
            c.commit_data = ext_json.loads(stats.commit_activity)
 
            c.overview_data = ext_json.loads(stats.commit_activity_combined)
 

	
 
            lang_stats_d = ext_json.loads(stats.languages)
 
            lang_stats = [(x, {"count": y,
 
                               "desc": LANGUAGES_EXTENSIONS_MAP.get(x, '?')})
 
                          for x, y in lang_stats_d.items()]
 
            lang_stats.sort(key=lambda k: (-k[1]['count'], k[0]))
 
            c.trending_languages = lang_stats[:10]
 

	
 
            last_rev = stats.stat_on_revision + 1
 
            c.repo_last_rev = c.db_repo_scm_instance.count() \
 
                if c.db_repo_scm_instance.revisions else 0
 
            if last_rev == 0 or c.repo_last_rev == 0:
 
                pass
 
            else:
 
                c.stats_percentage = '%.2f' % ((float((last_rev)) /
 
                                                c.repo_last_rev) * 100)
 
        else:
 
            c.commit_data = {}
 
            c.overview_data = ([[ts_min_y, 0], [ts_max_y, 10]])

Changeset was too big and was cut off... Show full diff anyway

0 comments (0 inline, 0 general)