Changeset - 7b9d4f6bb04e
[Not reviewed]
beta
0 5 0
Marcin Kuzminski - 13 years ago 2013-03-24 20:24:17
marcin@python-works.com
show only open pull requests in the counter, and use repo context bar in pull requests view
5 files changed with 5 insertions and 7 deletions:
0 comments (0 inline, 0 general)
rhodecode/lib/base.py
Show inline comments
 
@@ -77,262 +77,261 @@ class BasicAuth(AuthBasicAuthenticator):
 
        if self._rc_auth_http_code and self._rc_auth_http_code == '403':
 
            # return 403 if alternative http return code is specified in
 
            # RhodeCode config
 
            return HTTPForbidden(headers=head)
 
        return HTTPUnauthorized(headers=head)
 

	
 
    def authenticate(self, environ):
 
        authorization = AUTHORIZATION(environ)
 
        if not authorization:
 
            return self.build_authentication()
 
        (authmeth, auth) = authorization.split(' ', 1)
 
        if 'basic' != authmeth.lower():
 
            return self.build_authentication()
 
        auth = auth.strip().decode('base64')
 
        _parts = auth.split(':', 1)
 
        if len(_parts) == 2:
 
            username, password = _parts
 
            if self.authfunc(environ, username, password):
 
                return username
 
        return self.build_authentication()
 

	
 
    __call__ = authenticate
 

	
 

	
 
class BaseVCSController(object):
 

	
 
    def __init__(self, application, config):
 
        self.application = application
 
        self.config = config
 
        # base path of repo locations
 
        self.basepath = self.config['base_path']
 
        #authenticate this mercurial request using authfunc
 
        self.authenticate = BasicAuth('', authfunc,
 
                                      config.get('auth_ret_code'))
 
        self.ip_addr = '0.0.0.0'
 

	
 
    def _handle_request(self, environ, start_response):
 
        raise NotImplementedError()
 

	
 
    def _get_by_id(self, repo_name):
 
        """
 
        Get's a special pattern _<ID> from clone url and tries to replace it
 
        with a repository_name for support of _<ID> non changable urls
 

	
 
        :param repo_name:
 
        """
 
        try:
 
            data = repo_name.split('/')
 
            if len(data) >= 2:
 
                by_id = data[1].split('_')
 
                if len(by_id) == 2 and by_id[1].isdigit():
 
                    _repo_name = Repository.get(by_id[1]).repo_name
 
                    data[1] = _repo_name
 
        except:
 
            log.debug('Failed to extract repo_name from id %s' % (
 
                      traceback.format_exc()
 
                      )
 
            )
 

	
 
        return '/'.join(data)
 

	
 
    def _invalidate_cache(self, repo_name):
 
        """
 
        Set's cache for this repository for invalidation on next access
 

	
 
        :param repo_name: full repo name, also a cache key
 
        """
 
        invalidate_cache('get_repo_cached_%s' % repo_name)
 

	
 
    def _check_permission(self, action, user, repo_name, ip_addr=None):
 
        """
 
        Checks permissions using action (push/pull) user and repository
 
        name
 

	
 
        :param action: push or pull action
 
        :param user: user instance
 
        :param repo_name: repository name
 
        """
 
        #check IP
 
        authuser = AuthUser(user_id=user.user_id, ip_addr=ip_addr)
 
        if not authuser.ip_allowed:
 
            return False
 
        else:
 
            log.info('Access for IP:%s allowed' % (ip_addr))
 
        if action == 'push':
 
            if not HasPermissionAnyMiddleware('repository.write',
 
                                              'repository.admin')(user,
 
                                                                  repo_name):
 
                return False
 

	
 
        else:
 
            #any other action need at least read permission
 
            if not HasPermissionAnyMiddleware('repository.read',
 
                                              'repository.write',
 
                                              'repository.admin')(user,
 
                                                                  repo_name):
 
                return False
 

	
 
        return True
 

	
 
    def _get_ip_addr(self, environ):
 
        return _get_ip_addr(environ)
 

	
 
    def _check_ssl(self, environ, start_response):
 
        """
 
        Checks the SSL check flag and returns False if SSL is not present
 
        and required True otherwise
 
        """
 
        org_proto = environ['wsgi._org_proto']
 
        #check if we have SSL required  ! if not it's a bad request !
 
        require_ssl = str2bool(RhodeCodeUi.get_by_key('push_ssl').ui_value)
 
        if require_ssl and org_proto == 'http':
 
            log.debug('proto is %s and SSL is required BAD REQUEST !'
 
                      % org_proto)
 
            return False
 
        return True
 

	
 
    def _check_locking_state(self, environ, action, repo, user_id):
 
        """
 
        Checks locking on this repository, if locking is enabled and lock is
 
        present returns a tuple of make_lock, locked, locked_by.
 
        make_lock can have 3 states None (do nothing) True, make lock
 
        False release lock, This value is later propagated to hooks, which
 
        do the locking. Think about this as signals passed to hooks what to do.
 

	
 
        """
 
        locked = False  # defines that locked error should be thrown to user
 
        make_lock = None
 
        repo = Repository.get_by_repo_name(repo)
 
        user = User.get(user_id)
 

	
 
        # this is kind of hacky, but due to how mercurial handles client-server
 
        # server see all operation on changeset; bookmarks, phases and
 
        # obsolescence marker in different transaction, we don't want to check
 
        # locking on those
 
        obsolete_call = environ['QUERY_STRING'] in ['cmd=listkeys',]
 
        locked_by = repo.locked
 
        if repo and repo.enable_locking and not obsolete_call:
 
            if action == 'push':
 
                #check if it's already locked !, if it is compare users
 
                user_id, _date = repo.locked
 
                if user.user_id == user_id:
 
                    log.debug('Got push from user %s, now unlocking' % (user))
 
                    # unlock if we have push from user who locked
 
                    make_lock = False
 
                else:
 
                    # we're not the same user who locked, ban with 423 !
 
                    locked = True
 
            if action == 'pull':
 
                if repo.locked[0] and repo.locked[1]:
 
                    locked = True
 
                else:
 
                    log.debug('Setting lock on repo %s by %s' % (repo, user))
 
                    make_lock = True
 

	
 
        else:
 
            log.debug('Repository %s do not have locking enabled' % (repo))
 
        log.debug('FINAL locking values make_lock:%s,locked:%s,locked_by:%s'
 
                  % (make_lock, locked, locked_by))
 
        return make_lock, locked, locked_by
 

	
 
    def __call__(self, environ, start_response):
 
        start = time.time()
 
        try:
 
            return self._handle_request(environ, start_response)
 
        finally:
 
            log = logging.getLogger('rhodecode.' + self.__class__.__name__)
 
            log.debug('Request time: %.3fs' % (time.time() - start))
 
            meta.Session.remove()
 

	
 

	
 
class BaseController(WSGIController):
 

	
 
    def __before__(self):
 
        """
 
        __before__ is called before controller methods and after __call__
 
        """
 
        c.rhodecode_version = __version__
 
        c.rhodecode_instanceid = config.get('instance_id')
 
        c.rhodecode_name = config.get('rhodecode_title')
 
        c.use_gravatar = str2bool(config.get('use_gravatar'))
 
        c.ga_code = config.get('rhodecode_ga_code')
 
        # Visual options
 
        c.visual = AttributeDict({})
 
        rc_config = RhodeCodeSetting.get_app_settings()
 

	
 
        c.visual.show_public_icon = str2bool(rc_config.get('rhodecode_show_public_icon'))
 
        c.visual.show_private_icon = str2bool(rc_config.get('rhodecode_show_private_icon'))
 
        c.visual.stylify_metatags = str2bool(rc_config.get('rhodecode_stylify_metatags'))
 
        c.visual.lightweight_dashboard = str2bool(rc_config.get('rhodecode_lightweight_dashboard'))
 
        c.visual.lightweight_dashboard_items = safe_int(config.get('dashboard_items', 100))
 
        c.visual.repository_fields = str2bool(rc_config.get('rhodecode_repository_fields'))
 

	
 
        c.repo_name = get_repo_slug(request)
 
        c.repo_name = get_repo_slug(request)  # can be empty
 
        c.backends = BACKENDS.keys()
 
        c.unread_notifications = NotificationModel()\
 
                        .get_unread_cnt_for_user(c.rhodecode_user.user_id)
 
        self.cut_off_limit = int(config.get('cut_off_limit'))
 

	
 
        self.sa = meta.Session
 
        self.scm_model = ScmModel(self.sa)
 

	
 
    def __call__(self, environ, start_response):
 
        """Invoke the Controller"""
 
        # WSGIController.__call__ dispatches to the Controller method
 
        # the request is routed to. This routing information is
 
        # available in environ['pylons.routes_dict']
 
        try:
 
            self.ip_addr = _get_ip_addr(environ)
 
            # make sure that we update permissions each time we call controller
 
            api_key = request.GET.get('api_key')
 
            cookie_store = CookieStoreWrapper(session.get('rhodecode_user'))
 
            user_id = cookie_store.get('user_id', None)
 
            username = get_container_username(environ, config)
 
            auth_user = AuthUser(user_id, api_key, username, self.ip_addr)
 
            request.user = auth_user
 
            self.rhodecode_user = c.rhodecode_user = auth_user
 
            if not self.rhodecode_user.is_authenticated and \
 
                       self.rhodecode_user.user_id is not None:
 
                self.rhodecode_user.set_authenticated(
 
                    cookie_store.get('is_authenticated')
 
                )
 
            log.info('IP: %s User: %s accessed %s' % (
 
               self.ip_addr, auth_user, safe_unicode(_get_access_path(environ)))
 
            )
 
            return WSGIController.__call__(self, environ, start_response)
 
        finally:
 
            meta.Session.remove()
 

	
 

	
 
class BaseRepoController(BaseController):
 
    """
 
    Base class for controllers responsible for loading all needed data for
 
    repository loaded items are
 

	
 
    c.rhodecode_repo: instance of scm repository
 
    c.rhodecode_db_repo: instance of db
 
    c.repository_followers: number of followers
 
    c.repository_forks: number of forks
 
    c.repository_following: weather the current user is following the current repo
 
    """
 

	
 
    def __before__(self):
 
        super(BaseRepoController, self).__before__()
 
        if c.repo_name:
 

	
 
            dbr = c.rhodecode_db_repo = Repository.get_by_repo_name(c.repo_name)
 
            c.rhodecode_repo = c.rhodecode_db_repo.scm_instance
 
            # update last change according to VCS data
 
            dbr.update_changeset_cache(dbr.get_changeset())
 
            if c.rhodecode_repo is None:
 
                log.error('%s this repository is present in database but it '
 
                          'cannot be created as an scm instance', c.repo_name)
 

	
 
                redirect(url('home'))
 

	
 
            # some globals counter for menu
 
            c.repository_followers = self.scm_model.get_followers(dbr)
 
            c.repository_forks = self.scm_model.get_forks(dbr)
 
            c.repository_pull_requests = self.scm_model.get_pull_requests(dbr)
 
            c.repository_following = self.scm_model.is_following_repo(c.repo_name,
 
                                                self.rhodecode_user.user_id)
rhodecode/model/scm.py
Show inline comments
 
@@ -204,385 +204,386 @@ class ScmModel(BaseModel):
 

	
 
    def __get_repo(self, instance):
 
        cls = Repository
 
        if isinstance(instance, cls):
 
            return instance
 
        elif isinstance(instance, int) or safe_str(instance).isdigit():
 
            return cls.get(instance)
 
        elif isinstance(instance, basestring):
 
            return cls.get_by_repo_name(instance)
 
        elif instance:
 
            raise Exception('given object must be int, basestr or Instance'
 
                            ' of %s got %s' % (type(cls), type(instance)))
 

	
 
    @LazyProperty
 
    def repos_path(self):
 
        """
 
        Get's the repositories root path from database
 
        """
 

	
 
        q = self.sa.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/').one()
 

	
 
        return q.ui_value
 

	
 
    def repo_scan(self, repos_path=None):
 
        """
 
        Listing of repositories in given path. This path should not be a
 
        repository itself. Return a dictionary of repository objects
 

	
 
        :param repos_path: path to directory containing repositories
 
        """
 

	
 
        if repos_path is None:
 
            repos_path = self.repos_path
 

	
 
        log.info('scanning for repositories in %s' % repos_path)
 

	
 
        baseui = make_ui('db')
 
        repos = {}
 

	
 
        for name, path in get_filesystem_repos(repos_path, recursive=True):
 
            # name need to be decomposed and put back together using the /
 
            # since this is internal storage separator for rhodecode
 
            name = Repository.normalize_repo_name(name)
 

	
 
            try:
 
                if name in repos:
 
                    raise RepositoryError('Duplicate repository name %s '
 
                                          'found in %s' % (name, path))
 
                else:
 

	
 
                    klass = get_backend(path[0])
 

	
 
                    if path[0] == 'hg' and path[0] in BACKENDS.keys():
 
                        repos[name] = klass(safe_str(path[1]), baseui=baseui)
 

	
 
                    if path[0] == 'git' and path[0] in BACKENDS.keys():
 
                        repos[name] = klass(path[1])
 
            except OSError:
 
                continue
 
        log.debug('found %s paths with repositories' % (len(repos)))
 
        return repos
 

	
 
    def get_repos(self, all_repos=None, sort_key=None, simple=False):
 
        """
 
        Get all repos from db and for each repo create it's
 
        backend instance and fill that backed with information from database
 

	
 
        :param all_repos: list of repository names as strings
 
            give specific repositories list, good for filtering
 

	
 
        :param sort_key: initial sorting of repos
 
        :param simple: use SimpleCachedList - one without the SCM info
 
        """
 
        if all_repos is None:
 
            all_repos = self.sa.query(Repository)\
 
                        .filter(Repository.group_id == None)\
 
                        .order_by(func.lower(Repository.repo_name)).all()
 
        if simple:
 
            repo_iter = SimpleCachedRepoList(all_repos,
 
                                             repos_path=self.repos_path,
 
                                             order_by=sort_key)
 
        else:
 
            repo_iter = CachedRepoList(all_repos,
 
                                       repos_path=self.repos_path,
 
                                       order_by=sort_key)
 

	
 
        return repo_iter
 

	
 
    def get_repos_groups(self, all_groups=None):
 
        if all_groups is None:
 
            all_groups = RepoGroup.query()\
 
                .filter(RepoGroup.group_parent_id == None).all()
 
        return [x for x in GroupList(all_groups)]
 

	
 
    def mark_for_invalidation(self, repo_name):
 
        """
 
        Puts cache invalidation task into db for
 
        further global cache invalidation
 

	
 
        :param repo_name: this repo that should invalidation take place
 
        """
 
        invalidated_keys = CacheInvalidation.set_invalidate(repo_name=repo_name)
 
        repo = Repository.get_by_repo_name(repo_name)
 
        if repo:
 
            repo.update_changeset_cache()
 
        return invalidated_keys
 

	
 
    def toggle_following_repo(self, follow_repo_id, user_id):
 

	
 
        f = self.sa.query(UserFollowing)\
 
            .filter(UserFollowing.follows_repo_id == follow_repo_id)\
 
            .filter(UserFollowing.user_id == user_id).scalar()
 

	
 
        if f is not None:
 
            try:
 
                self.sa.delete(f)
 
                action_logger(UserTemp(user_id),
 
                              'stopped_following_repo',
 
                              RepoTemp(follow_repo_id))
 
                return
 
            except:
 
                log.error(traceback.format_exc())
 
                raise
 

	
 
        try:
 
            f = UserFollowing()
 
            f.user_id = user_id
 
            f.follows_repo_id = follow_repo_id
 
            self.sa.add(f)
 

	
 
            action_logger(UserTemp(user_id),
 
                          'started_following_repo',
 
                          RepoTemp(follow_repo_id))
 
        except:
 
            log.error(traceback.format_exc())
 
            raise
 

	
 
    def toggle_following_user(self, follow_user_id, user_id):
 
        f = self.sa.query(UserFollowing)\
 
            .filter(UserFollowing.follows_user_id == follow_user_id)\
 
            .filter(UserFollowing.user_id == user_id).scalar()
 

	
 
        if f is not None:
 
            try:
 
                self.sa.delete(f)
 
                return
 
            except:
 
                log.error(traceback.format_exc())
 
                raise
 

	
 
        try:
 
            f = UserFollowing()
 
            f.user_id = user_id
 
            f.follows_user_id = follow_user_id
 
            self.sa.add(f)
 
        except:
 
            log.error(traceback.format_exc())
 
            raise
 

	
 
    def is_following_repo(self, repo_name, user_id, cache=False):
 
        r = self.sa.query(Repository)\
 
            .filter(Repository.repo_name == repo_name).scalar()
 

	
 
        f = self.sa.query(UserFollowing)\
 
            .filter(UserFollowing.follows_repository == r)\
 
            .filter(UserFollowing.user_id == user_id).scalar()
 

	
 
        return f is not None
 

	
 
    def is_following_user(self, username, user_id, cache=False):
 
        u = User.get_by_username(username)
 

	
 
        f = self.sa.query(UserFollowing)\
 
            .filter(UserFollowing.follows_user == u)\
 
            .filter(UserFollowing.user_id == user_id).scalar()
 

	
 
        return f is not None
 

	
 
    def get_followers(self, repo):
 
        repo = self._get_repo(repo)
 

	
 
        return self.sa.query(UserFollowing)\
 
                .filter(UserFollowing.follows_repository == repo).count()
 

	
 
    def get_forks(self, repo):
 
        repo = self._get_repo(repo)
 
        return self.sa.query(Repository)\
 
                .filter(Repository.fork == repo).count()
 

	
 
    def get_pull_requests(self, repo):
 
        repo = self._get_repo(repo)
 
        return self.sa.query(PullRequest)\
 
                .filter(PullRequest.other_repo == repo).count()
 
                .filter(PullRequest.other_repo == repo)\
 
                .filter(PullRequest.status != PullRequest.STATUS_CLOSED).count()
 

	
 
    def mark_as_fork(self, repo, fork, user):
 
        repo = self.__get_repo(repo)
 
        fork = self.__get_repo(fork)
 
        if fork and repo.repo_id == fork.repo_id:
 
            raise Exception("Cannot set repository as fork of itself")
 
        repo.fork = fork
 
        self.sa.add(repo)
 
        return repo
 

	
 
    def _handle_push(self, repo, username, action, repo_name, revisions):
 
        """
 
        Triggers push action hooks
 

	
 
        :param repo: SCM repo
 
        :param username: username who pushes
 
        :param action: push/push_loca/push_remote
 
        :param repo_name: name of repo
 
        :param revisions: list of revisions that we pushed
 
        """
 
        from rhodecode import CONFIG
 
        from rhodecode.lib.base import _get_ip_addr
 
        try:
 
            from pylons import request
 
            environ = request.environ
 
        except TypeError:
 
            # we might use this outside of request context, let's fake the
 
            # environ data
 
            from webob import Request
 
            environ = Request.blank('').environ
 

	
 
        #trigger push hook
 
        extras = {
 
            'ip': _get_ip_addr(environ),
 
            'username': username,
 
            'action': 'push_local',
 
            'repository': repo_name,
 
            'scm': repo.alias,
 
            'config': CONFIG['__file__'],
 
            'server_url': get_server_url(environ),
 
            'make_lock': None,
 
            'locked_by': [None, None]
 
        }
 
        _scm_repo = repo._repo
 
        _set_extras(extras)
 
        if repo.alias == 'hg':
 
            log_push_action(_scm_repo.ui, _scm_repo, node=revisions[0])
 
        elif repo.alias == 'git':
 
            log_push_action(_scm_repo.ui, _scm_repo, _git_revs=revisions)
 

	
 
    def _get_IMC_module(self, scm_type):
 
        """
 
        Returns InMemoryCommit class based on scm_type
 

	
 
        :param scm_type:
 
        """
 
        if scm_type == 'hg':
 
            from rhodecode.lib.vcs.backends.hg import \
 
                MercurialInMemoryChangeset as IMC
 
        elif scm_type == 'git':
 
            from rhodecode.lib.vcs.backends.git import \
 
                GitInMemoryChangeset as IMC
 
        return IMC
 

	
 
    def pull_changes(self, repo, username):
 
        dbrepo = self.__get_repo(repo)
 
        clone_uri = dbrepo.clone_uri
 
        if not clone_uri:
 
            raise Exception("This repository doesn't have a clone uri")
 

	
 
        repo = dbrepo.scm_instance
 
        repo_name = dbrepo.repo_name
 
        try:
 
            if repo.alias == 'git':
 
                repo.fetch(clone_uri)
 
            else:
 
                repo.pull(clone_uri)
 
            self.mark_for_invalidation(repo_name)
 
        except:
 
            log.error(traceback.format_exc())
 
            raise
 

	
 
    def commit_change(self, repo, repo_name, cs, user, author, message,
 
                      content, f_path):
 
        """
 
        Commits changes
 

	
 
        :param repo: SCM instance
 

	
 
        """
 
        user = self._get_user(user)
 
        IMC = self._get_IMC_module(repo.alias)
 

	
 
        # decoding here will force that we have proper encoded values
 
        # in any other case this will throw exceptions and deny commit
 
        content = safe_str(content)
 
        path = safe_str(f_path)
 
        # message and author needs to be unicode
 
        # proper backend should then translate that into required type
 
        message = safe_unicode(message)
 
        author = safe_unicode(author)
 
        m = IMC(repo)
 
        m.change(FileNode(path, content))
 
        tip = m.commit(message=message,
 
                       author=author,
 
                       parents=[cs], branch=cs.branch)
 

	
 
        self.mark_for_invalidation(repo_name)
 
        self._handle_push(repo,
 
                          username=user.username,
 
                          action='push_local',
 
                          repo_name=repo_name,
 
                          revisions=[tip.raw_id])
 
        return tip
 

	
 
    def create_node(self, repo, repo_name, cs, user, author, message, content,
 
                      f_path):
 
        user = self._get_user(user)
 
        IMC = self._get_IMC_module(repo.alias)
 

	
 
        # decoding here will force that we have proper encoded values
 
        # in any other case this will throw exceptions and deny commit
 
        if isinstance(content, (basestring,)):
 
            content = safe_str(content)
 
        elif isinstance(content, (file, cStringIO.OutputType,)):
 
            content = content.read()
 
        else:
 
            raise Exception('Content is of unrecognized type %s' % (
 
                type(content)
 
            ))
 

	
 
        message = safe_unicode(message)
 
        author = safe_unicode(author)
 
        path = safe_str(f_path)
 
        m = IMC(repo)
 

	
 
        if isinstance(cs, EmptyChangeset):
 
            # EmptyChangeset means we we're editing empty repository
 
            parents = None
 
        else:
 
            parents = [cs]
 

	
 
        m.add(FileNode(path, content=content))
 
        tip = m.commit(message=message,
 
                       author=author,
 
                       parents=parents, branch=cs.branch)
 

	
 
        self.mark_for_invalidation(repo_name)
 
        self._handle_push(repo,
 
                          username=user.username,
 
                          action='push_local',
 
                          repo_name=repo_name,
 
                          revisions=[tip.raw_id])
 
        return tip
 

	
 
    def get_nodes(self, repo_name, revision, root_path='/', flat=True):
 
        """
 
        recursive walk in root dir and return a set of all path in that dir
 
        based on repository walk function
 

	
 
        :param repo_name: name of repository
 
        :param revision: revision for which to list nodes
 
        :param root_path: root path to list
 
        :param flat: return as a list, if False returns a dict with decription
 

	
 
        """
 
        _files = list()
 
        _dirs = list()
 
        try:
 
            _repo = self.__get_repo(repo_name)
 
            changeset = _repo.scm_instance.get_changeset(revision)
 
            root_path = root_path.lstrip('/')
 
            for topnode, dirs, files in changeset.walk(root_path):
 
                for f in files:
 
                    _files.append(f.path if flat else {"name": f.path,
 
                                                       "type": "file"})
 
                for d in dirs:
 
                    _dirs.append(d.path if flat else {"name": d.path,
 
                                                      "type": "dir"})
 
        except RepositoryError:
 
            log.debug(traceback.format_exc())
 
            raise
 

	
 
        return _dirs, _files
 

	
 
    def get_unread_journal(self):
 
        return self.sa.query(UserLog).count()
 

	
 
    def get_repo_landing_revs(self, repo=None):
 
        """
 
        Generates select option with tags branches and bookmarks (for hg only)
 
        grouped by type
rhodecode/templates/pullrequests/pullrequest.html
Show inline comments
 
<%inherit file="/base/base.html"/>
 

	
 
<%def name="title()">
 
    ${c.repo_name} ${_('New pull request')}
 
</%def>
 

	
 
<%def name="breadcrumbs_links()">
 
    ${h.link_to(_(u'Home'),h.url('/'))}
 
    &raquo;
 
    ${h.repo_link(c.rhodecode_db_repo.groups_and_repo)}
 
    &raquo;
 
    ${_('new pull request')}
 
</%def>
 

	
 
<%def name="main()">
 

	
 
${self.context_bar('showpullrequest')}
 
<div class="box">
 
    <!-- box / title -->
 
    <div class="title">
 
        ${self.breadcrumbs()}
 
    </div>
 
    ${h.form(url('pullrequest', repo_name=c.repo_name), method='post', id='pull_request_form')}
 
    <div style="float:left;padding:0px 30px 30px 30px">
 
        ##ORG
 
        <div style="float:left">
 
            <div>
 
                <span style="font-size: 20px">
 
                ${h.select('org_repo','',c.org_repos,class_='refs')}:${h.select('org_ref',c.default_org_ref,c.org_refs,class_='refs')}
 
                </span>
 
                 <div style="padding:5px 3px 3px 20px;">${c.rhodecode_db_repo.description}</div>
 
            </div>
 
            <div style="clear:both;padding-top: 10px"></div>
 
        </div>
 
          <div style="float:left;font-size:24px;padding:0px 20px">
 
          <img height=32 width=32 src="${h.url('/images/arrow_right_64.png')}"/>
 
          </div>
 

	
 
        ##OTHER, most Probably the PARENT OF THIS FORK
 
        <div style="float:left">
 
            <div>
 
                <span style="font-size: 20px">
 
                ${h.select('other_repo',c.default_other_repo,c.other_repos,class_='refs')}:${h.select('other_ref',c.default_other_ref,c.default_other_refs,class_='refs')}
 
                </span>
 
                 <div id="other_repo_desc" style="padding:5px 3px 3px 20px;"></div>
 
            </div>
 
            <div style="clear:both;padding-top: 10px"></div>
 
        </div>
 
       <div style="clear:both;padding-top: 10px"></div>
 
       ## overview pulled by ajax
 
       <div style="float:left" id="pull_request_overview"></div>
 
       <div style="float:left;clear:both;padding:10px 10px 10px 0px;display:none">
 
            <a id="pull_request_overview_url" href="#">${_('Detailed compare view')}</a>
 
       </div>
 
     </div>
 
    <div style="float:left; border-left:1px dashed #eee">
 
        <h4>${_('Pull request reviewers')}</h4>
 
        <div id="reviewers" style="padding:0px 0px 0px 15px">
 
          ## members goes here !
 
          <div class="group_members_wrap">
 
            <ul id="review_members" class="group_members">
 
            %for member in c.review_members:
 
              <li id="reviewer_${member.user_id}">
 
                <div class="reviewers_member">
 
                  <div class="gravatar"><img alt="gravatar" src="${h.gravatar_url(member.email,14)}"/> </div>
 
                  <div style="float:left">${member.full_name} (${_('owner')})</div>
 
                  <input type="hidden" value="${member.user_id}" name="review_members" />
 
                  <span class="delete_icon action_button" onclick="removeReviewMember(${member.user_id})"></span>
 
                </div>
 
              </li>
 
            %endfor
 
            </ul>
 
          </div>
 

	
 
          <div class='ac'>
 
            <div class="reviewer_ac">
 
               ${h.text('user', class_='yui-ac-input')}
 
               <span class="help-block">${_('Add reviewer to this pull request.')}</span>
 
               <div id="reviewers_container"></div>
 
            </div>
 
          </div>
 
        </div>
 
    </div>
 
    <h3>${_('Create new pull request')}</h3>
 

	
 
    <div class="form">
 
        <!-- fields -->
 

	
 
        <div class="fields">
 

	
 
             <div class="field">
 
                <div class="label">
 
                    <label for="pullrequest_title">${_('Title')}:</label>
 
                </div>
 
                <div class="input">
 
                    ${h.text('pullrequest_title',size=30)}
 
                </div>
 
             </div>
 

	
 
            <div class="field">
 
                <div class="label label-textarea">
 
                    <label for="pullrequest_desc">${_('Description')}:</label>
 
                </div>
 
                <div class="textarea text-area editor">
 
                    ${h.textarea('pullrequest_desc',size=30)}
 
                </div>
 
            </div>
 

	
 
            <div class="buttons">
 
                ${h.submit('save',_('Send pull request'),class_="ui-btn large")}
 
                ${h.reset('reset',_('Reset'),class_="ui-btn large")}
 
           </div>
 
        </div>
 
    </div>
 
    ${h.end_form()}
 

	
 
</div>
 

	
 
<script type="text/javascript">
 
  var _USERS_AC_DATA = ${c.users_array|n};
 
  var _GROUPS_AC_DATA = ${c.users_groups_array|n};
 
  PullRequestAutoComplete('user', 'reviewers_container', _USERS_AC_DATA, _GROUPS_AC_DATA);
 

	
 
  var other_repos_info = ${c.other_repos_info|n};
 

	
 
  var otherrepoChanged = function(){
 
      var sel_box = YUQ('#pull_request_form #other_repo')[0];
 
      var repo_name = sel_box.options[sel_box.selectedIndex].value;
 

	
 
      YUD.get('other_repo_desc').innerHTML = other_repos_info[repo_name]['description'];
 
      // replace options of other_ref with the ones for the current other_repo
 
      var other_ref_selector = YUD.get('other_ref');
 
      var new_select = YUD.createElementFromMarkup(other_repos_info[repo_name]['revs']);
 
      var new_selectedIndex = new_select.selectedIndex;
 
      other_ref_selector.innerHTML = ""; // clear old options
 
      while (new_select.length > 0){ // children will be popped when appened to other_ref_selector
 
          other_ref_selector.appendChild(new_select.children[0]);
 
      }
 
      // browsers lost track of selected when appendChild was used
 
      other_ref_selector.selectedIndex = new_selectedIndex;
 

	
 
      // reset && add the reviewer based on selected repo
 
      var _data = other_repos_info[repo_name];
 
      YUD.get('review_members').innerHTML = '';
 
      addReviewMember(_data.user.user_id, _data.user.firstname,
 
                      _data.user.lastname, _data.user.username,
 
                      _data.user.gravatar_link);
 
  }
 

	
 
  var loadPreview = function(){
 
      //url template
 
      var url = "${h.url('compare_url',
 
                         repo_name='__other_repo__',
 
                         org_ref_type='__other_ref_type__',
 
                         org_ref='__other_ref__',
 
                         other_repo='__org_repo__',
 
                         other_ref_type='__org_ref_type__',
 
                         other_ref='__org_ref__',
 
                         as_form=True,
 
                         merge=True,
 
                         )}";
 
      var org_repo = YUQ('#pull_request_form #org_repo')[0].value;
 
      var org_ref = YUQ('#pull_request_form #org_ref')[0].value.split(':');
 

	
 
      var other_repo = YUQ('#pull_request_form #other_repo')[0].value;
 
      var other_ref = YUQ('#pull_request_form #other_ref')[0].value.split(':');
 

	
 
      var select_refs = YUQ('#pull_request_form select.refs')
 
      var rev_data = {
 
          'org_repo': org_repo,
 
          'org_ref': org_ref[2],
 
          'org_ref_type': 'rev',
 
          'other_repo': other_repo,
 
          'other_ref': other_ref[2],
 
          'other_ref_type': 'rev',
 
      }; // gather the org/other ref and repo here
 

	
 
      for (k in rev_data){
 
          url = url.replace('__'+k+'__',rev_data[k]);
 
      }
 

	
 
      YUD.get('pull_request_overview').innerHTML = "${_('Loading ...')}";
 
      ypjax(url,'pull_request_overview');
 

	
 
      YUD.get('pull_request_overview_url').href = url; // shouldn't have as_form ... but ...
 
      YUD.setStyle(YUD.get('pull_request_overview_url').parentElement,'display','');
 
  }
 

	
 
  ## refresh automatically when something changes (org_repo can't change)
 

	
 
  YUE.on('org_ref', 'change', function(e){
 
     loadPreview();
 
  });
 

	
 
  YUE.on('other_repo', 'change', function(e){
 
      otherrepoChanged();
 
      loadPreview();
 
  });
 

	
 
  YUE.on('other_ref', 'change', function(e){
 
     loadPreview();
 
  });
 

	
 
  otherrepoChanged();
 
  //lazy load overview after 0.5s
 
  setTimeout(loadPreview, 500);
 

	
 
</script>
 

	
rhodecode/templates/pullrequests/pullrequest_show.html
Show inline comments
 
<%inherit file="/base/base.html"/>
 

	
 
<%def name="title()">
 
    ${c.repo_name} ${_('Pull request #%s') % c.pull_request.pull_request_id}
 
</%def>
 

	
 
<%def name="breadcrumbs_links()">
 
    ${h.link_to(_(u'Home'),h.url('/'))}
 
    &raquo;
 
    ${h.repo_link(c.rhodecode_db_repo.groups_and_repo)}
 
    &raquo;
 
    ${_('Pull request #%s') % c.pull_request.pull_request_id}
 
</%def>
 

	
 
<%def name="main()">
 

	
 
${self.context_bar('showpullrequest')}
 
<div class="box">
 
    <!-- box / title -->
 
    <div class="title">
 
        ${self.breadcrumbs()}
 
    </div>
 
        %if c.pull_request.is_closed():
 
        <div style="padding:10px; font-size:22px;width:100%;text-align: center; color:#88D882">${_('Closed %s') % (h.age(c.pull_request.updated_on))} ${_('with status %s') % h.changeset_status_lbl(c.current_changeset_status)}</div>
 
        %endif
 
    <h3>
 
    %if c.pull_request.is_closed():
 
        <img src="${h.url('/images/icons/lock_go.png')}" title="${_('Closed')}"/>
 
    %endif
 
    <img src="${h.url('/images/icons/flag_status_%s.png' % str(c.pull_request.last_review_status))}" />
 
    ${_('Title')}: ${c.pull_request.title}</h3>
 

	
 
    <div class="form">
 
      <div id="summary" class="fields">
 
         <div class="field">
 
          <div class="label-summary">
 
              <label>${_('Review status')}:</label>
 
          </div>
 
          <div class="input">
 
            <div class="changeset-status-container" style="float:none;clear:both">
 
            %if c.current_changeset_status:
 
              <div title="${_('Pull request status')}" class="changeset-status-lbl">${h.changeset_status_lbl(c.current_changeset_status)}</div>
 
              <div class="changeset-status-ico" style="padding:1px 4px"><img src="${h.url('/images/icons/flag_status_%s.png' % c.current_changeset_status)}" /></div>
 
            %endif
 
            </div>
 
          </div>
 
         </div>
 
         <div class="field">
 
          <div class="label-summary">
 
              <label>${_('Still not reviewed by')}:</label>
 
          </div>
 
          <div class="input">
 
            % if len(c.pull_request_pending_reviewers) > 0:
 
                <div class="tooltip" title="${h.tooltip(','.join([x.username for x in c.pull_request_pending_reviewers]))}">${ungettext('%d reviewer', '%d reviewers',len(c.pull_request_pending_reviewers)) % len(c.pull_request_pending_reviewers)}</div>
 
            %else:
 
                <div>${_('pull request was reviewed by all reviewers')}</div>
 
            %endif
 
          </div>
 
         </div>
 
         <div class="field">
 
          <div class="label-summary">
 
              <label>${_('Origin repository')}:</label>
 
          </div>
 
          <div class="input">
 
              <div>
 
             ##%if h.is_hg(c.pull_request.org_repo):
 
             ##  <img class="icon" title="${_('Mercurial repository')}" alt="${_('Mercurial repository')}" src="${h.url('/images/icons/hgicon.png')}"/>
 
             ##%elif h.is_git(c.pull_request.org_repo):
 
             ##  <img class="icon" title="${_('Git repository')}" alt="${_('Git repository')}" src="${h.url('/images/icons/giticon.png')}"/>
 
             ##%endif
 
              <span class="spantag">${c.pull_request.org_ref_parts[0]}: ${c.pull_request.org_ref_parts[1]}</span>
 
              <span><a href="${h.url('summary_home', repo_name=c.pull_request.org_repo.repo_name)}">${c.pull_request.org_repo.clone_url()}</a></span>
 
              </div>
 
          </div>
 
         </div>
 
         <div class="field">
 
          <div class="label-summary">
 
              <label>${_('Summary')}:</label>
 
          </div>
 
          <div class="input">
 
              <div style="white-space:pre-wrap">${h.literal(c.pull_request.description)}</div>
 
          </div>
 
         </div>
 
         <div class="field">
 
          <div class="label-summary">
 
              <label>${_('Created on')}:</label>
 
          </div>
 
          <div class="input">
 
              <div>${h.fmt_date(c.pull_request.created_on)}</div>
 
          </div>
 
         </div>
 
      </div>
 
    </div>
 

	
 
    <div style="overflow: auto;">
 
      ##DIFF
 
      <div class="table" style="float:left;clear:none">
 
          <div id="body" class="diffblock">
 
              <div style="white-space:pre-wrap;padding:5px">${_('Compare view')}</div>
 
          </div>
 
          <div id="changeset_compare_view_content">
 
              ##CS
 
              <div style="font-size:1.1em;font-weight: bold;clear:both;padding-top:10px">${ungettext('Showing %s commit','Showing %s commits', len(c.cs_ranges)) % len(c.cs_ranges)}</div>
 
              <%include file="/compare/compare_cs.html" />
 

	
 
              ## FILES
 
              <div style="font-size:1.1em;font-weight: bold;clear:both;padding-top:10px">
 

	
 
              % if c.limited_diff:
 
                  ${ungettext('%s file changed', '%s files changed', len(c.files)) % len(c.files)}
 
              % else:
 
                  ${ungettext('%s file changed with %s insertions and %s deletions','%s files changed with %s insertions and %s deletions', len(c.files)) % (len(c.files),c.lines_added,c.lines_deleted)}:
 
              %endif
 

	
 
              </div>
 
              <div class="cs_files">
 
                %if not c.files:
 
                   <span class="empty_data">${_('No files')}</span>
 
                %endif
 
                %for fid, change, f, stat in c.files:
 
                    <div class="cs_${change}">
 
                      <div class="node">${h.link_to(h.safe_unicode(f),h.url.current(anchor=fid))}</div>
 
                      <div class="changes">${h.fancy_file_stats(stat)}</div>
 
                    </div>
 
                %endfor
 
              </div>
 
              % if c.limited_diff:
 
                <h5>${_('Changeset was too big and was cut off...')} <a href="${h.url.current(fulldiff=1, **request.GET.mixed())}" onclick="return confirm('${_("confirm to show potentially huge diff")}')">${_('Show full diff')}</a></h5>
 
              % endif
 
          </div>
 
      </div>
 
      ## REVIEWERS
 
       <div style="float:left; border-left:1px dashed #eee">
 
       <h4>${_('Pull request reviewers')}</h4>
 
        <div id="reviewers" style="padding:0px 0px 5px 10px">
 
          ## members goes here !
 
          <div class="group_members_wrap" style="min-height:45px">
 
            <ul id="review_members" class="group_members">
 
            %for member,status in c.pull_request_reviewers:
 
              <li id="reviewer_${member.user_id}">
 
                <div class="reviewers_member">
 
                    <div style="float:left;padding:0px 3px 0px 0px" class="tooltip" title="${h.tooltip(h.changeset_status_lbl(status[0][1].status if status else 'not_reviewed'))}">
 
                      <img src="${h.url(str('/images/icons/flag_status_%s.png' % (status[0][1].status if status else 'not_reviewed')))}"/>
 
                    </div>
 
                  <div class="gravatar"><img alt="gravatar" src="${h.gravatar_url(member.email,14)}"/> </div>
 
                  <div style="float:left">${member.full_name} (${_('owner') if c.pull_request.user_id == member.user_id else _('reviewer')})</div>
 
                  <input type="hidden" value="${member.user_id}" name="review_members" />
 
                  %if not c.pull_request.is_closed() and (h.HasPermissionAny('hg.admin', 'repository.admin')() or c.pull_request.user_id == c.rhodecode_user.user_id):
 
                  <span class="delete_icon action_button" onclick="removeReviewMember(${member.user_id})"></span>
 
                  %endif
 
                </div>
 
              </li>
 
            %endfor
 
            </ul>
 
          </div>
 
          %if not c.pull_request.is_closed():
 
          <div class='ac'>
 
            %if h.HasPermissionAny('hg.admin', 'repository.admin')() or c.pull_request.author.user_id == c.rhodecode_user.user_id:
 
            <div class="reviewer_ac">
 
               ${h.text('user', class_='yui-ac-input')}
 
               <span class="help-block">${_('Add or remove reviewer to this pull request.')}</span>
 
               <div id="reviewers_container"></div>
 
            </div>
 
            <div style="padding:0px 10px">
 
             <span id="update_pull_request" class="ui-btn xsmall">${_('Save changes')}</span>
 
            </div>
 
            %endif
 
          </div>
 
          %endif
 
        </div>
 
       </div>
 
    </div>
 
    <script>
 
    var _USERS_AC_DATA = ${c.users_array|n};
 
    var _GROUPS_AC_DATA = ${c.users_groups_array|n};
 
    // TODO: switch this to pyroutes
 
    AJAX_COMMENT_URL = "${url('pullrequest_comment',repo_name=c.repo_name,pull_request_id=c.pull_request.pull_request_id)}";
 
    AJAX_COMMENT_DELETE_URL = "${url('pullrequest_comment_delete',repo_name=c.repo_name,comment_id='__COMMENT_ID__')}";
 

	
 
    pyroutes.register('pullrequest_comment', "${url('pullrequest_comment',repo_name='%(repo_name)s',pull_request_id='%(pull_request_id)s')}", ['repo_name', 'pull_request_id']);
 
    pyroutes.register('pullrequest_comment_delete', "${url('pullrequest_comment_delete',repo_name='%(repo_name)s',comment_id='%(comment_id)s')}", ['repo_name', 'comment_id']);
 
    pyroutes.register('pullrequest_update', "${url('pullrequest_update',repo_name='%(repo_name)s',pull_request_id='%(pull_request_id)s')}", ['repo_name', 'pull_request_id']);
 

	
 
    </script>
 

	
 
    ## diff block
 
    <%namespace name="diff_block" file="/changeset/diff_block.html"/>
 
    %for fid, change, f, stat in c.files:
 
      ${diff_block.diff_block_simple([c.changes[fid]])}
 
    %endfor
 
    % if c.limited_diff:
 
      <h4>${_('Changeset was too big and was cut off...')} <a href="${h.url.current(fulldiff=1, **request.GET.mixed())}" onclick="return confirm('${_("confirm to show potentially huge diff")}')">${_('Show full diff')}</a></h4>
 
    % endif
 

	
 

	
 
    ## template for inline comment form
 
    <%namespace name="comment" file="/changeset/changeset_file_comment.html"/>
 
    ${comment.comment_inline_form()}
 

	
 
    ## render comments and inlines
 
    ${comment.generate_comments(include_pr=True)}
 

	
 
    % if not c.pull_request.is_closed():
 
      ## main comment form and it status
 
      ${comment.comments(h.url('pullrequest_comment', repo_name=c.repo_name,
 
                                pull_request_id=c.pull_request.pull_request_id),
 
                                c.current_changeset_status,
 
                                close_btn=True, change_status=c.allowed_to_change_status)}
 
    %endif
rhodecode/templates/pullrequests/pullrequest_show_all.html
Show inline comments
 
<%inherit file="/base/base.html"/>
 

	
 
<%def name="title()">
 
    ${c.repo_name} ${_('all pull requests')}
 
</%def>
 

	
 
<%def name="breadcrumbs_links()">
 
    ${_('Pull requests')}
 
</%def>
 

	
 
<%def name="main()">
 
${self.context_bar('showpullrequest')}
 
<div class="box">
 
    <!-- box / title -->
 
    <div class="title">
 
        ${self.breadcrumbs()}
 
    </div>
 

	
 
    %for pr in c.pull_requests:
 
        <div>
 
          <h4  style="border:0px;padding:0px">
 
            %if pr.is_closed():
 
                <img src="${h.url('/images/icons/lock_go.png')}" title="${_('Closed')}"/>
 
            %endif
 
            <img src="${h.url('/images/icons/flag_status_%s.png' % str(pr.last_review_status))}" />
 
            <a href="${h.url('pullrequest_show',repo_name=c.repo_name,pull_request_id=pr.pull_request_id)}">
 
            ${_('Pull request #%s opened by %s on %s') % (pr.pull_request_id, pr.author.full_name, h.fmt_date(pr.created_on))}
 
            </a>
 
          </h4>
 
          <h5 style="border:0px;padding-bottom:0px">${_('Title')}: ${pr.title}</h5>
 
          <div style="padding:0px 24px">${pr.description}</div>
 
          <div style="border-bottom: 1px solid #DDD;margin:10px 20px;padding-bottom:10px"></div>
 
        </div>
 
    %endfor
 

	
 
</div>
 

	
 
<script type="text/javascript"></script>
 

	
 
</%def>
0 comments (0 inline, 0 general)