diff --git a/rhodecode/apps/_base/__init__.py b/rhodecode/apps/_base/__init__.py --- a/rhodecode/apps/_base/__init__.py +++ b/rhodecode/apps/_base/__init__.py @@ -268,6 +268,34 @@ class RepoRoutePredicate(object): return False +class RepoTypeRoutePredicate(object): + def __init__(self, val, config): + self.val = val or ['hg', 'git', 'svn'] + + def text(self): + return 'repo_accepted_type = %s' % self.val + + phash = text + + def __call__(self, info, request): + + rhodecode_db_repo = request.db_repo + + log.debug( + '%s checking repo type for %s in %s', + self.__class__.__name__, rhodecode_db_repo.repo_type, self.val) + + if rhodecode_db_repo.repo_type in self.val: + return True + else: + log.warning('Current view is not supported for repo type:%s', + rhodecode_db_repo.repo_type) + return False + + + def includeme(config): config.add_route_predicate( 'repo_route', RepoRoutePredicate) + config.add_route_predicate( + 'repo_accepted_types', RepoTypeRoutePredicate) \ No newline at end of file diff --git a/rhodecode/apps/repository/__init__.py b/rhodecode/apps/repository/__init__.py --- a/rhodecode/apps/repository/__init__.py +++ b/rhodecode/apps/repository/__init__.py @@ -47,6 +47,16 @@ def includeme(config): pattern='/{repo_name:.*?[^/]}/pull-request/{pull_request_id}', repo_route=True) + config.add_route( + name='pullrequest_show_all', + pattern='/{repo_name:.*?[^/]}/pull-request', + repo_route=True, repo_accepted_types=['hg', 'git']) + + config.add_route( + name='pullrequest_show_all_data', + pattern='/{repo_name:.*?[^/]}/pull-request-data', + repo_route=True, repo_accepted_types=['hg', 'git']) + # Settings config.add_route( name='edit_repo', diff --git a/rhodecode/apps/repository/views/repo_pull_requests.py b/rhodecode/apps/repository/views/repo_pull_requests.py new file mode 100644 --- /dev/null +++ b/rhodecode/apps/repository/views/repo_pull_requests.py @@ -0,0 +1,184 @@ +# -*- coding: utf-8 -*- + +# Copyright (C) 2011-2017 RhodeCode GmbH +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License, version 3 +# (only), as published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# 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 Affero General Public License +# along with this program. If not, see . +# +# This program is dual-licensed. If you wish to learn more about the +# RhodeCode Enterprise Edition, including its added features, Support services, +# and proprietary license terms, please see https://rhodecode.com/licenses/ + +import logging + +from pyramid.view import view_config + +from rhodecode.apps._base import RepoAppView, DataGridAppView +from rhodecode.lib import helpers as h +from rhodecode.lib import audit_logger +from rhodecode.lib.auth import ( + LoginRequired, HasRepoPermissionAnyDecorator) +from rhodecode.lib.utils import PartialRenderer +from rhodecode.lib.utils2 import str2bool +from rhodecode.model.comment import CommentsModel +from rhodecode.model.db import PullRequest +from rhodecode.model.pull_request import PullRequestModel + +log = logging.getLogger(__name__) + + +class RepoPullRequestsView(RepoAppView, DataGridAppView): + + def load_default_context(self): + c = self._get_local_tmpl_context() + + # TODO(marcink): remove repo_info and use c.rhodecode_db_repo instead + c.repo_info = self.db_repo + + self._register_global_c(c) + return c + + def _get_pull_requests_list( + self, repo_name, source, filter_type, opened_by, statuses): + + draw, start, limit = self._extract_chunk(self.request) + search_q, order_by, order_dir = self._extract_ordering(self.request) + _render = PartialRenderer('data_table/_dt_elements.mako') + + # pagination + + if filter_type == 'awaiting_review': + pull_requests = PullRequestModel().get_awaiting_review( + repo_name, source=source, opened_by=opened_by, + statuses=statuses, offset=start, length=limit, + order_by=order_by, order_dir=order_dir) + pull_requests_total_count = PullRequestModel().count_awaiting_review( + repo_name, source=source, statuses=statuses, + opened_by=opened_by) + elif filter_type == 'awaiting_my_review': + pull_requests = PullRequestModel().get_awaiting_my_review( + repo_name, source=source, opened_by=opened_by, + user_id=self._rhodecode_user.user_id, statuses=statuses, + offset=start, length=limit, order_by=order_by, + order_dir=order_dir) + pull_requests_total_count = PullRequestModel().count_awaiting_my_review( + repo_name, source=source, user_id=self._rhodecode_user.user_id, + statuses=statuses, opened_by=opened_by) + else: + pull_requests = PullRequestModel().get_all( + repo_name, source=source, opened_by=opened_by, + statuses=statuses, offset=start, length=limit, + order_by=order_by, order_dir=order_dir) + pull_requests_total_count = PullRequestModel().count_all( + repo_name, source=source, statuses=statuses, + opened_by=opened_by) + + data = [] + comments_model = CommentsModel() + for pr in pull_requests: + comments = comments_model.get_all_comments( + self.db_repo.repo_id, pull_request=pr) + + data.append({ + 'name': _render('pullrequest_name', + pr.pull_request_id, pr.target_repo.repo_name), + 'name_raw': pr.pull_request_id, + 'status': _render('pullrequest_status', + pr.calculated_review_status()), + 'title': _render( + 'pullrequest_title', pr.title, pr.description), + 'description': h.escape(pr.description), + 'updated_on': _render('pullrequest_updated_on', + h.datetime_to_time(pr.updated_on)), + 'updated_on_raw': h.datetime_to_time(pr.updated_on), + 'created_on': _render('pullrequest_updated_on', + h.datetime_to_time(pr.created_on)), + 'created_on_raw': h.datetime_to_time(pr.created_on), + 'author': _render('pullrequest_author', + pr.author.full_contact, ), + 'author_raw': pr.author.full_name, + 'comments': _render('pullrequest_comments', len(comments)), + 'comments_raw': len(comments), + 'closed': pr.is_closed(), + }) + + data = ({ + 'draw': draw, + 'data': data, + 'recordsTotal': pull_requests_total_count, + 'recordsFiltered': pull_requests_total_count, + }) + return data + + @LoginRequired() + @HasRepoPermissionAnyDecorator( + 'repository.read', 'repository.write', 'repository.admin') + @view_config( + route_name='pullrequest_show_all', request_method='GET', + renderer='rhodecode:templates/pullrequests/pullrequests.mako') + def pull_request_list(self): + c = self.load_default_context() + + req_get = self.request.GET + c.source = str2bool(req_get.get('source')) + c.closed = str2bool(req_get.get('closed')) + c.my = str2bool(req_get.get('my')) + c.awaiting_review = str2bool(req_get.get('awaiting_review')) + c.awaiting_my_review = str2bool(req_get.get('awaiting_my_review')) + + c.active = 'open' + if c.my: + c.active = 'my' + if c.closed: + c.active = 'closed' + if c.awaiting_review and not c.source: + c.active = 'awaiting' + if c.source and not c.awaiting_review: + c.active = 'source' + if c.awaiting_my_review: + c.active = 'awaiting_my' + + return self._get_template_context(c) + + @LoginRequired() + @HasRepoPermissionAnyDecorator( + 'repository.read', 'repository.write', 'repository.admin') + @view_config( + route_name='pullrequest_show_all_data', request_method='GET', + renderer='json_ext', xhr=True) + def pull_request_list_data(self): + + # additional filters + req_get = self.request.GET + source = str2bool(req_get.get('source')) + closed = str2bool(req_get.get('closed')) + my = str2bool(req_get.get('my')) + awaiting_review = str2bool(req_get.get('awaiting_review')) + awaiting_my_review = str2bool(req_get.get('awaiting_my_review')) + + filter_type = 'awaiting_review' if awaiting_review \ + else 'awaiting_my_review' if awaiting_my_review \ + else None + + opened_by = None + if my: + opened_by = [self._rhodecode_user.user_id] + + statuses = [PullRequest.STATUS_NEW, PullRequest.STATUS_OPEN] + if closed: + statuses = [PullRequest.STATUS_CLOSED] + + data = self._get_pull_requests_list( + repo_name=self.db_repo_name, source=source, + filter_type=filter_type, opened_by=opened_by, statuses=statuses) + + return data diff --git a/rhodecode/config/routing.py b/rhodecode/config/routing.py --- a/rhodecode/config/routing.py +++ b/rhodecode/config/routing.py @@ -827,13 +827,6 @@ def make_map(config): 'method': ['DELETE']}, requirements=URL_NAME_REQUIREMENTS) - rmap.connect('pullrequest_show_all', - '/{repo_name}/pull-request', - controller='pullrequests', - action='show_all', conditions={'function': check_repo, - 'method': ['GET']}, - requirements=URL_NAME_REQUIREMENTS, jsroute=True) - rmap.connect('pullrequest_comment', '/{repo_name}/pull-request-comment/{pull_request_id}', controller='pullrequests', diff --git a/rhodecode/controllers/pullrequests.py b/rhodecode/controllers/pullrequests.py --- a/rhodecode/controllers/pullrequests.py +++ b/rhodecode/controllers/pullrequests.py @@ -72,124 +72,6 @@ class PullrequestsController(BaseRepoCon c.REVIEW_STATUS_APPROVED = ChangesetStatus.STATUS_APPROVED c.REVIEW_STATUS_REJECTED = ChangesetStatus.STATUS_REJECTED - def _extract_ordering(self, request): - column_index = safe_int(request.GET.get('order[0][column]')) - order_dir = request.GET.get('order[0][dir]', 'desc') - order_by = request.GET.get( - 'columns[%s][data][sort]' % column_index, 'name_raw') - return order_by, order_dir - - @LoginRequired() - @HasRepoPermissionAnyDecorator('repository.read', 'repository.write', - 'repository.admin') - @HasAcceptedRepoType('git', 'hg') - def show_all(self, repo_name): - # filter types - c.active = 'open' - c.source = str2bool(request.GET.get('source')) - c.closed = str2bool(request.GET.get('closed')) - c.my = str2bool(request.GET.get('my')) - c.awaiting_review = str2bool(request.GET.get('awaiting_review')) - c.awaiting_my_review = str2bool(request.GET.get('awaiting_my_review')) - c.repo_name = repo_name - - opened_by = None - if c.my: - c.active = 'my' - opened_by = [c.rhodecode_user.user_id] - - statuses = [PullRequest.STATUS_NEW, PullRequest.STATUS_OPEN] - if c.closed: - c.active = 'closed' - statuses = [PullRequest.STATUS_CLOSED] - - if c.awaiting_review and not c.source: - c.active = 'awaiting' - if c.source and not c.awaiting_review: - c.active = 'source' - if c.awaiting_my_review: - c.active = 'awaiting_my' - - data = self._get_pull_requests_list( - repo_name=repo_name, opened_by=opened_by, statuses=statuses) - if not request.is_xhr: - c.data = json.dumps(data['data']) - c.records_total = data['recordsTotal'] - return render('/pullrequests/pullrequests.mako') - else: - return json.dumps(data) - - def _get_pull_requests_list(self, repo_name, opened_by, statuses): - # pagination - start = safe_int(request.GET.get('start'), 0) - length = safe_int(request.GET.get('length'), c.visual.dashboard_items) - order_by, order_dir = self._extract_ordering(request) - - if c.awaiting_review: - pull_requests = PullRequestModel().get_awaiting_review( - repo_name, source=c.source, opened_by=opened_by, - statuses=statuses, offset=start, length=length, - order_by=order_by, order_dir=order_dir) - pull_requests_total_count = PullRequestModel( - ).count_awaiting_review( - repo_name, source=c.source, statuses=statuses, - opened_by=opened_by) - elif c.awaiting_my_review: - pull_requests = PullRequestModel().get_awaiting_my_review( - repo_name, source=c.source, opened_by=opened_by, - user_id=c.rhodecode_user.user_id, statuses=statuses, - offset=start, length=length, order_by=order_by, - order_dir=order_dir) - pull_requests_total_count = PullRequestModel( - ).count_awaiting_my_review( - repo_name, source=c.source, user_id=c.rhodecode_user.user_id, - statuses=statuses, opened_by=opened_by) - else: - pull_requests = PullRequestModel().get_all( - repo_name, source=c.source, opened_by=opened_by, - statuses=statuses, offset=start, length=length, - order_by=order_by, order_dir=order_dir) - pull_requests_total_count = PullRequestModel().count_all( - repo_name, source=c.source, statuses=statuses, - opened_by=opened_by) - - from rhodecode.lib.utils import PartialRenderer - _render = PartialRenderer('data_table/_dt_elements.mako') - data = [] - for pr in pull_requests: - comments = CommentsModel().get_all_comments( - c.rhodecode_db_repo.repo_id, pull_request=pr) - - data.append({ - 'name': _render('pullrequest_name', - pr.pull_request_id, pr.target_repo.repo_name), - 'name_raw': pr.pull_request_id, - 'status': _render('pullrequest_status', - pr.calculated_review_status()), - 'title': _render( - 'pullrequest_title', pr.title, pr.description), - 'description': h.escape(pr.description), - 'updated_on': _render('pullrequest_updated_on', - h.datetime_to_time(pr.updated_on)), - 'updated_on_raw': h.datetime_to_time(pr.updated_on), - 'created_on': _render('pullrequest_updated_on', - h.datetime_to_time(pr.created_on)), - 'created_on_raw': h.datetime_to_time(pr.created_on), - 'author': _render('pullrequest_author', - pr.author.full_contact, ), - 'author_raw': pr.author.full_name, - 'comments': _render('pullrequest_comments', len(comments)), - 'comments_raw': len(comments), - 'closed': pr.is_closed(), - }) - # json used to render the grid - data = ({ - 'data': data, - 'recordsTotal': pull_requests_total_count, - 'recordsFiltered': pull_requests_total_count, - }) - return data - @LoginRequired() @NotAnonymous() @HasRepoPermissionAnyDecorator('repository.read', 'repository.write', diff --git a/rhodecode/public/css/panels.less b/rhodecode/public/css/panels.less --- a/rhodecode/public/css/panels.less +++ b/rhodecode/public/css/panels.less @@ -42,6 +42,10 @@ .panel-body { padding: @panel-padding; + + &.panel-body-min-height { + min-height: 150px + } } .panel-footer { diff --git a/rhodecode/public/js/rhodecode/routes.js b/rhodecode/public/js/rhodecode/routes.js --- a/rhodecode/public/js/rhodecode/routes.js +++ b/rhodecode/public/js/rhodecode/routes.js @@ -34,7 +34,6 @@ function registerRCRoutes() { pyroutes.register('pullrequest_repo_destinations', '/%(repo_name)s/pull-request/repo-destinations', ['repo_name']); pyroutes.register('pullrequest_show', '/%(repo_name)s/pull-request/%(pull_request_id)s', ['repo_name', 'pull_request_id']); pyroutes.register('pullrequest_update', '/%(repo_name)s/pull-request/%(pull_request_id)s', ['repo_name', 'pull_request_id']); - pyroutes.register('pullrequest_show_all', '/%(repo_name)s/pull-request', ['repo_name']); pyroutes.register('pullrequest_comment', '/%(repo_name)s/pull-request-comment/%(pull_request_id)s', ['repo_name', 'pull_request_id']); pyroutes.register('pullrequest_comment_delete', '/%(repo_name)s/pull-request-comment/%(comment_id)s/delete', ['repo_name', 'comment_id']); pyroutes.register('changelog_home', '/%(repo_name)s/changelog', ['repo_name']); @@ -106,6 +105,8 @@ function registerRCRoutes() { pyroutes.register('branches_home', '/%(repo_name)s/branches', ['repo_name']); pyroutes.register('bookmarks_home', '/%(repo_name)s/bookmarks', ['repo_name']); pyroutes.register('pullrequest_show', '/%(repo_name)s/pull-request/%(pull_request_id)s', ['repo_name', 'pull_request_id']); + pyroutes.register('pullrequest_show_all', '/%(repo_name)s/pull-request', ['repo_name']); + pyroutes.register('pullrequest_show_all_data', '/%(repo_name)s/pull-request-data', ['repo_name']); pyroutes.register('edit_repo', '/%(repo_name)s/settings', ['repo_name']); pyroutes.register('edit_repo_advanced', '/%(repo_name)s/settings/advanced', ['repo_name']); pyroutes.register('edit_repo_advanced_delete', '/%(repo_name)s/settings/advanced/delete', ['repo_name']); diff --git a/rhodecode/templates/base/base.mako b/rhodecode/templates/base/base.mako --- a/rhodecode/templates/base/base.mako +++ b/rhodecode/templates/base/base.mako @@ -234,7 +234,7 @@ ## TODO: anderson: ideally it would have a function on the scm_instance "enable_pullrequest() and enable_fork()" %if c.rhodecode_db_repo.repo_type in ['git','hg']:
  • - + %if c.repository_pull_requests: ${c.repository_pull_requests} %endif diff --git a/rhodecode/templates/pullrequests/pullrequests.mako b/rhodecode/templates/pullrequests/pullrequests.mako --- a/rhodecode/templates/pullrequests/pullrequests.mako +++ b/rhodecode/templates/pullrequests/pullrequests.mako @@ -45,12 +45,12 @@ ##main @@ -73,7 +73,7 @@ %endif -
    +
    @@ -83,14 +83,24 @@