Changeset - cd50d1b5f35b
[Not reviewed]
Merge default
1 88 3
Marcin Kuzminski - 13 years ago 2013-01-21 00:03:44
marcin@python-works.com
merged with beta
91 files changed with 6564 insertions and 1469 deletions:
0 comments (0 inline, 0 general)
CONTRIBUTORS
Show inline comments
 
@@ -32,3 +32,4 @@ List of contributors to RhodeCode projec
 
    Raoul Thill <raoul.thill@gmail.com>
 
    Philip Jameson <philip.j@hostdime.com>
 
    Mads Kiilerich <madski@unity3d.com>
 
    Dan Sheridan <djs@adelard.com>
docs/api/api.rst
Show inline comments
 
@@ -155,9 +155,10 @@ OUTPUT::
 
lock
 
----
 

	
 
Set locking state on given repository by given user.
 
Set locking state on given repository by given user. If userid param is skipped
 
, then it is set to id of user whos calling this method.
 
This command can be executed only using api_key belonging to user with admin 
 
rights.
 
rights or regular user that have admin or write access to repository.
 

	
 
INPUT::
 

	
 
@@ -166,9 +167,8 @@ INPUT::
 
    method :  "lock"
 
    args :    {
 
                "repoid" : "<reponame or repo_id>"
 
                "userid" : "<user_id or username>",
 
                "userid" : "<user_id or username = Optional(=apiuser)>",
 
                "locked" : "<bool true|false>"
 
                
 
              }
 

	
 
OUTPUT::
 
@@ -178,12 +178,47 @@ OUTPUT::
 
    error :  null
 

	
 

	
 
show_ip
 
-------
 

	
 
Shows IP address as seen from RhodeCode server, together with all
 
defined IP addresses for given user.
 
This command can be executed only using api_key belonging to user with admin 
 
rights.
 

	
 
INPUT::
 

	
 
    id : <id_for_response>
 
    api_key : "<api_key>"
 
    method :  "show_ip"
 
    args :    {
 
                "userid" : "<user_id or username>",
 
              }
 

	
 
OUTPUT::
 

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

	
 

	
 
get_user
 
--------
 

	
 
Get's an 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.
 
rights, or regular users that cannot specify different userid than theirs
 

	
 

	
 
INPUT::
 
@@ -192,7 +227,7 @@ INPUT::
 
    api_key : "<api_key>"
 
    method :  "get_user"
 
    args :    { 
 
                "userid" : "<username or user_id>"
 
                "userid" : "<username or user_id Optional(=apiuser)>"
 
              }
 

	
 
OUTPUT::
 
@@ -200,16 +235,17 @@ OUTPUT::
 
    id : <id_given_in_input>
 
    result: None if user does not exist or 
 
            {
 
                "user_id" :  "<user_id>",
 
                "username" : "<username>",
 
                "firstname": "<firstname>",
 
                "lastname" : "<lastname>",
 
                "email" :    "<email>",
 
                "emails":    "<list_of_all_additional_emails>",
 
                "active" :   "<bool>",
 
                "admin" :    "<bool>",
 
                "ldap_dn" :  "<ldap_dn>",
 
                "last_login": "<last_login>",
 
                "user_id" :     "<user_id>",
 
                "username" :    "<username>",
 
                "firstname":    "<firstname>",
 
                "lastname" :    "<lastname>",
 
                "email" :       "<email>",
 
                "emails":       "<list_of_all_additional_emails>",
 
                "ip_addresses": "<list_of_ip_addresses_for_user>",
 
                "active" :      "<bool>",
 
                "admin" :       "<bool>",
 
                "ldap_dn" :     "<ldap_dn>",
 
                "last_login":   "<last_login>",
 
                "permissions": {
 
                    "global": ["hg.create.repository",
 
                               "repository.read",
 
@@ -241,16 +277,17 @@ OUTPUT::
 
    id : <id_given_in_input>
 
    result: [
 
              {
 
                "user_id" :  "<user_id>",
 
                "username" : "<username>",
 
                "firstname": "<firstname>",
 
                "lastname" : "<lastname>",
 
                "email" :    "<email>",
 
                "emails":    "<list_of_all_additional_emails>",
 
                "active" :   "<bool>",
 
                "admin" :    "<bool>",
 
                "ldap_dn" :  "<ldap_dn>",
 
                "last_login": "<last_login>",
 
                "user_id" :     "<user_id>",
 
                "username" :    "<username>",
 
                "firstname":    "<firstname>",
 
                "lastname" :    "<lastname>",
 
                "email" :       "<email>",
 
                "emails":       "<list_of_all_additional_emails>",
 
                "ip_addresses": "<list_of_ip_addresses_for_user>",
 
                "active" :      "<bool>",
 
                "admin" :       "<bool>",
 
                "ldap_dn" :     "<ldap_dn>",
 
                "last_login":   "<last_login>",
 
              },
 
    	
 
            ]
 
@@ -315,14 +352,14 @@ INPUT::
 
    method :  "update_user"
 
    args :    {
 
                "userid" : "<user_id or username>",
 
                "username" :  "<username> = Optional",
 
                "email" :     "<useremail> = Optional",
 
                "password" :  "<password> = Optional",
 
                "firstname" : "<firstname> = Optional",
 
                "lastname" :  "<lastname> = Optional",
 
                "active" :    "<bool> = Optional",
 
                "admin" :     "<bool> = Optional",
 
                "ldap_dn" :   "<ldap_dn> = Optional"
 
                "username" :  "<username> = Optional(None)",
 
                "email" :     "<useremail> = Optional(None)",
 
                "password" :  "<password> = Optional(None)",
 
                "firstname" : "<firstname> = Optional(None)",
 
                "lastname" :  "<lastname> = Optional(None)",
 
                "active" :    "<bool> = Optional(None)",
 
                "admin" :     "<bool> = Optional(None)",
 
                "ldap_dn" :   "<ldap_dn> = Optional(None)"
 
              }
 

	
 
OUTPUT::
 
@@ -537,8 +574,9 @@ get_repo
 
--------
 

	
 
Gets an existing repository by it's name or repository_id. Members will return
 
either users_group or user associated to that repository. This command can 
 
be executed only using api_key belonging to user with admin rights.
 
either users_group or user associated to that repository. 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.
 

	
 

	
 
INPUT::
 
@@ -555,29 +593,40 @@ OUTPUT::
 
    id : <id_given_in_input>
 
    result: None if repository does not exist or
 
            {
 
                "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>",
 
                "repo_id" :          "<repo_id>",
 
                "repo_name" :        "<reponame>"
 
                "repo_type" :        "<repo_type>",
 
                "clone_uri" :        "<clone_uri>",
 
                "enable_downloads":  "<bool>",
 
                "enable_locking":    "<bool>",
 
                "enable_statistics": "<bool>",                
 
                "private":           "<bool>",
 
                "created_on" :       "<date_time_created>",                
 
                "description" :      "<description>",
 
                "landing_rev":       "<landing_rev>",
 
                "last_changeset":    {
 
                                       "author":   "<full_author>",
 
                                       "date":     "<date_time_of_commit>",
 
                                       "message":  "<commit_message>",
 
                                       "raw_id":   "<raw_id>",
 
                                       "revision": "<numeric_revision>",
 
                                       "short_id": "<short_id>"
 
                                     }
 
                "owner":             "<repo_owner>",
 
                "fork_of":           "<name_of_fork_parent>",
 
                "members" :     [
 
                                  { 
 
                                    "type": "user",
 
                                    "user_id" :  "<user_id>",
 
                                    "username" : "<username>",
 
                                    "firstname": "<firstname>",
 
                                    "lastname" : "<lastname>",
 
                                    "email" :    "<email>",
 
                                    "emails":    "<list_of_all_additional_emails>",
 
                                    "active" :   "<bool>",
 
                                    "admin" :    "<bool>",
 
                                    "ldap_dn" :  "<ldap_dn>",
 
                                    "last_login": "<last_login>",
 
                                    "user_id" :    "<user_id>",
 
                                    "username" :   "<username>",
 
                                    "firstname":   "<firstname>",
 
                                    "lastname" :   "<lastname>",
 
                                    "email" :      "<email>",
 
                                    "emails":      "<list_of_all_additional_emails>",
 
                                    "active" :     "<bool>",
 
                                    "admin" :      "<bool>",
 
                                    "ldap_dn" :    "<ldap_dn>",
 
                                    "last_login":  "<last_login>",
 
                                    "permission" : "repository.(read|write|admin)"
 
                                  },
 
 
@@ -597,8 +646,9 @@ OUTPUT::
 
get_repos
 
---------
 

	
 
Lists all existing repositories. This command can be executed only using api_key
 
belonging to user with admin rights
 
Lists all existing repositories. This command can be executed only using 
 
api_key belonging to user with admin rights or regular user that have 
 
admin, write or read access to repository.
 

	
 

	
 
INPUT::
 
@@ -613,16 +663,19 @@ OUTPUT::
 
    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>",
 
                "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_locking":    "<bool>",
 
                "enable_statistics": "<bool>",                   
 
              },
 
 
            ]
 
@@ -666,11 +719,12 @@ OUTPUT::
 
create_repo
 
-----------
 

	
 
Creates a repository. This command can be executed only using api_key
 
belonging to user with admin rights.
 
If repository name contains "/", all needed repository groups will be created.
 
For example "foo/bar/baz" will create groups "foo", "bar" (with "foo" as parent),
 
and create "baz" repository with "bar" as group.
 
Creates a repository. If repository name contains "/", all needed repository
 
groups will be created. For example "foo/bar/baz" will create groups 
 
"foo", "bar" (with "foo" as parent), and create "baz" repository with 
 
"bar" as group. This command can be executed only using api_key belonging to user with admin 
 
rights or regular user that have create repository permission. Regular users
 
cannot specify owner parameter
 

	
 

	
 
INPUT::
 
@@ -679,13 +733,16 @@ INPUT::
 
    api_key : "<api_key>"
 
    method :  "create_repo"
 
    args:     {
 
                "repo_name" :   "<reponame>",
 
                "owner" :       "<onwer_name_or_id>",
 
                "repo_type" :   "<repo_type>",
 
                "description" : "<description> = Optional('')",
 
                "private" :     "<bool> = Optional(False)",
 
                "clone_uri" :   "<clone_uri> = Optional(None)",
 
                "landing_rev" : "<landing_rev> = Optional('tip')",
 
                "repo_name" :        "<reponame>",
 
                "owner" :            "<onwer_name_or_id = Optional(=apiuser)>",
 
                "repo_type" :        "<repo_type> = Optional('hg')",
 
                "description" :      "<description> = Optional('')",
 
                "private" :          "<bool> = Optional(False)",
 
                "clone_uri" :        "<clone_uri> = Optional(None)",
 
                "landing_rev" :      "<landing_rev> = Optional('tip')",
 
                "enable_downloads":  "<bool> = Optional(False)",
 
                "enable_locking":    "<bool> = Optional(False)",
 
                "enable_statistics": "<bool> = Optional(False)",
 
              }
 

	
 
OUTPUT::
 
@@ -694,26 +751,65 @@ OUTPUT::
 
    result: {
 
              "msg": "Created new repository `<reponame>`",
 
              "repo": {
 
                "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>",
 
                "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":             "<username or user_id>",
 
                "fork_of":           "<name_of_fork_parent>",
 
                "enable_downloads":  "<bool>",
 
                "enable_locking":    "<bool>",
 
                "enable_statistics": "<bool>",                     
 
              },
 
            }
 
    error:  null
 

	
 

	
 
fork_repo
 
---------
 

	
 
Creates a fork of given repo. In case of using celery this will
 
immidiatelly return success message, while fork is going to be created
 
asynchronous. This command can be executed only using api_key belonging to
 
user with admin rights or regular user that have fork permission, and at least
 
read access to forking repository. Regular users cannot specify owner parameter.
 

	
 

	
 
INPUT::
 

	
 
    id : <id_for_response>
 
    api_key : "<api_key>"
 
    method :  "fork_repo"
 
    args:     {
 
                "repoid" :          "<reponame or repo_id>",
 
                "fork_name":        "<forkname>",
 
                "owner":            "<username or user_id = Optional(=apiuser)>",
 
                "description":      "<description>",
 
                "copy_permissions": "<bool>",
 
                "private":          "<bool>",
 
                "landing_rev":      "<landing_rev>"
 
                                
 
              }
 

	
 
OUTPUT::
 

	
 
    id : <id_given_in_input>
 
    result: {
 
              "msg": "Created fork of `<reponame>` as `<forkname>`",
 
              "success": true
 
            }
 
    error:  null
 

	
 

	
 
delete_repo
 
-----------
 

	
 
Deletes a repository. This command can be executed only using api_key
 
belonging to user with admin rights.
 
Deletes a repository. This command can be executed only using api_key belonging to user with admin 
 
rights or regular user that have admin access to repository.
 

	
 

	
 
INPUT::
docs/changelog.rst
Show inline comments
 
@@ -4,6 +4,38 @@
 
Changelog
 
=========
 

	
 
1.5.2 (**2013-01-14**)
 
----------------------
 

	
 
news
 
++++
 

	
 
- IP restrictions for users. Each user can get a set of whitelist IP+mask for
 
  extra protection. Useful for buildbots etc.
 
- added full last changeset info to lightweight dashboard. lightweight dashboard
 
  is now fully functional replacement of original dashboard.
 
- implemented certain API calls for non-admin users.
 
- enabled all Markdown Extra plugins
 
- implemented #725 Pull Request View - Show origin repo URL
 
- show comments from pull requests into associated changesets
 

	
 
fixes
 
+++++
 

	
 
- update repoinfo script is more failsafe
 
- fixed #687  Lazy loaded tooltip bug with simultaneous ajax requests
 
- fixed #691: Notifications for pull requests: move link to top for better
 
  readability
 
- fixed #699: fix missing fork docs for API
 
- fixed #693 Opening changeset from pull request fails
 
- fixed #710 File view stripping empty lines from beginning and end of file
 
- fixed issues with getting repos by path on windows, caused GIT hooks to fail
 
- fixed issues with groups paginator on main dashboard
 
- improved fetch/pull command for git repos, now pulling all refs
 
- fixed issue #719 Journal revision ID tooltip AJAX query path is incorrect
 
  when running in a subdir
 
- fixed issue #702 API methods without arguments fail when "args":null
 
- set the status of changesets initially on pull request. Fixes issues #690 and #587
 

	
 
1.5.1 (**2012-12-13**)
 
----------------------
docs/installation.rst
Show inline comments
 
@@ -43,6 +43,10 @@ For installing RhodeCode i highly recomm
 
way many required by RhodeCode libraries will remain sandboxed from your main
 
python and making things less problematic when doing system python updates. 
 

	
 
Alternative very detailed installation instructions for Ubuntu Server with
 
celery, indexer and daemon scripts: https://gist.github.com/4546398
 

	
 

	
 
- Assuming you have installed virtualenv_ create a new virtual environment 
 
  using virtualenv command:: 
 

	
rhodecode/__init__.py
Show inline comments
 
@@ -26,7 +26,7 @@
 
import sys
 
import platform
 

	
 
VERSION = (1, 5, 1)
 
VERSION = (1, 5, 2)
 

	
 
try:
 
    from rhodecode.lib import get_current_revision
 
@@ -38,7 +38,7 @@ except ImportError:
 

	
 
__version__ = ('.'.join((str(each) for each in VERSION[:3])) +
 
               '.'.join(VERSION[3:]))
 
__dbversion__ = 9  # defines current db version for migrations
 
__dbversion__ = 10  # defines current db version for migrations
 
__platform__ = platform.system()
 
__license__ = 'GPLv3'
 
__py_version__ = sys.version_info
rhodecode/config/routing.py
Show inline comments
 
@@ -222,6 +222,10 @@ def make_map(config):
 
                  action="add_email", conditions=dict(method=["PUT"]))
 
        m.connect("user_emails_delete", "/users_emails/{id}",
 
                  action="delete_email", conditions=dict(method=["DELETE"]))
 
        m.connect("user_ips", "/users_ips/{id}",
 
                  action="add_ip", conditions=dict(method=["PUT"]))
 
        m.connect("user_ips_delete", "/users_ips/{id}",
 
                  action="delete_ip", conditions=dict(method=["DELETE"]))
 

	
 
    #ADMIN USERS GROUPS REST ROUTES
 
    with rmap.submapper(path_prefix=ADMIN_PREFIX,
 
@@ -355,8 +359,6 @@ def make_map(config):
 
        m.connect('api', '/api')
 

	
 
    #USER JOURNAL
 
    rmap.connect('journal_my_repos', '%s/journal_my_repos' % ADMIN_PREFIX,
 
                 controller='journal', action='index_my_repos')
 
    rmap.connect('journal', '%s/journal' % ADMIN_PREFIX,
 
                 controller='journal', action='index')
 
    rmap.connect('journal_rss', '%s/journal/rss' % ADMIN_PREFIX,
rhodecode/controllers/admin/notifications.py
Show inline comments
 
@@ -110,8 +110,8 @@ class NotificationsController(BaseContro
 
        # url('notification', notification_id=ID)
 
        try:
 
            no = Notification.get(notification_id)
 
            owner = lambda: (no.notifications_to_users.user.user_id
 
                             == c.rhodecode_user.user_id)
 
            owner = all(un.user.user_id == c.rhodecode_user.user_id
 
                        for un in no.notifications_to_users)
 
            if h.HasPermissionAny('hg.admin')() or owner:
 
                    NotificationModel().mark_read(c.rhodecode_user.user_id, no)
 
                    Session().commit()
 
@@ -132,8 +132,8 @@ class NotificationsController(BaseContro
 

	
 
        try:
 
            no = Notification.get(notification_id)
 
            owner = lambda: (no.notifications_to_users.user.user_id
 
                             == c.rhodecode_user.user_id)
 
            owner = all(un.user.user_id == c.rhodecode_user.user_id
 
                        for un in no.notifications_to_users)
 
            if h.HasPermissionAny('hg.admin')() or owner:
 
                    NotificationModel().delete(c.rhodecode_user.user_id, no)
 
                    Session().commit()
 
@@ -149,8 +149,8 @@ class NotificationsController(BaseContro
 
        c.user = self.rhodecode_user
 
        no = Notification.get(notification_id)
 

	
 
        owner = lambda: (no.notifications_to_users.user.user_id
 
                         == c.user.user_id)
 
        owner = all(un.user.user_id == c.rhodecode_user.user_id
 
                    for un in no.notifications_to_users)
 
        if no and (h.HasPermissionAny('hg.admin', 'repository.admin')() or owner):
 
            unotification = NotificationModel()\
 
                            .get_user_notification(c.user.user_id, no)
rhodecode/controllers/admin/permissions.py
Show inline comments
 
@@ -33,11 +33,12 @@ from pylons.controllers.util import abor
 
from pylons.i18n.translation import _
 

	
 
from rhodecode.lib import helpers as h
 
from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator
 
from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator,\
 
    AuthUser
 
from rhodecode.lib.base import BaseController, render
 
from rhodecode.model.forms import DefaultPermissionsForm
 
from rhodecode.model.permission import PermissionModel
 
from rhodecode.model.db import User
 
from rhodecode.model.db import User, UserIpMap
 
from rhodecode.model.meta import Session
 

	
 
log = logging.getLogger(__name__)
 
@@ -105,36 +106,41 @@ class PermissionsController(BaseControll
 
        #    h.form(url('permission', id=ID),
 
        #           method='put')
 
        # url('permission', id=ID)
 

	
 
        permission_model = PermissionModel()
 
        if id == 'default':
 
            c.user = default_user = User.get_by_username('default')
 
            c.perm_user = AuthUser(user_id=default_user.user_id)
 
            c.user_ip_map = UserIpMap.query()\
 
                            .filter(UserIpMap.user == default_user).all()
 
            permission_model = PermissionModel()
 

	
 
        _form = DefaultPermissionsForm([x[0] for x in self.repo_perms_choices],
 
                                       [x[0] for x in self.group_perms_choices],
 
                                       [x[0] for x in self.register_choices],
 
                                       [x[0] for x in self.create_choices],
 
                                       [x[0] for x in self.fork_choices])()
 
            _form = DefaultPermissionsForm(
 
                    [x[0] for x in self.repo_perms_choices],
 
                    [x[0] for x in self.group_perms_choices],
 
                    [x[0] for x in self.register_choices],
 
                    [x[0] for x in self.create_choices],
 
                    [x[0] for x in self.fork_choices])()
 

	
 
        try:
 
            form_result = _form.to_python(dict(request.POST))
 
            form_result.update({'perm_user_name': id})
 
            permission_model.update(form_result)
 
            Session().commit()
 
            h.flash(_('Default permissions updated successfully'),
 
                    category='success')
 
            try:
 
                form_result = _form.to_python(dict(request.POST))
 
                form_result.update({'perm_user_name': id})
 
                permission_model.update(form_result)
 
                Session().commit()
 
                h.flash(_('Default permissions updated successfully'),
 
                        category='success')
 

	
 
        except formencode.Invalid, errors:
 
            defaults = errors.value
 
            except formencode.Invalid, 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")
 
        except Exception:
 
            log.error(traceback.format_exc())
 
            h.flash(_('error occurred during update of permissions'),
 
                    category='error')
 
                return htmlfill.render(
 
                    render('admin/permissions/permissions.html'),
 
                    defaults=defaults,
 
                    errors=errors.error_dict or {},
 
                    prefix_error=False,
 
                    encoding="UTF-8")
 
            except Exception:
 
                log.error(traceback.format_exc())
 
                h.flash(_('error occurred during update of permissions'),
 
                        category='error')
 

	
 
        return redirect(url('edit_permission', id=id))
 

	
 
@@ -157,10 +163,11 @@ class PermissionsController(BaseControll
 

	
 
        #this form can only edit default user permissions
 
        if id == 'default':
 
            default_user = User.get_by_username('default')
 
            defaults = {'_method': 'put',
 
                        'anonymous': default_user.active}
 

	
 
            c.user = default_user = User.get_by_username('default')
 
            defaults = {'anonymous': default_user.active}
 
            c.perm_user = AuthUser(user_id=default_user.user_id)
 
            c.user_ip_map = UserIpMap.query()\
 
                            .filter(UserIpMap.user == default_user).all()
 
            for p in default_user.user_perms:
 
                if p.permission.permission_name.startswith('repository.'):
 
                    defaults['default_repo_perm'] = p.permission.permission_name
 
@@ -181,7 +188,7 @@ class PermissionsController(BaseControll
 
                render('admin/permissions/permissions.html'),
 
                defaults=defaults,
 
                encoding="UTF-8",
 
                force_defaults=True,
 
                force_defaults=False
 
            )
 
        else:
 
            return redirect(url('admin_home'))
rhodecode/controllers/admin/repos.py
Show inline comments
 
@@ -135,40 +135,10 @@ class ReposController(BaseController):
 
                        .order_by(func.lower(Repository.repo_name))\
 
                        .all()
 

	
 
        repos_data = []
 
        total_records = len(c.repos_list)
 

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

	
 
        quick_menu = lambda repo_name: (template.get_def("quick_menu")
 
                                        .render(repo_name, _=_, h=h, c=c))
 
        repo_lnk = lambda name, rtype, private, fork_of: (
 
            template.get_def("repo_name")
 
            .render(name, rtype, private, fork_of, short_name=False,
 
                    admin=True, _=_, h=h, c=c))
 

	
 
        repo_actions = lambda repo_name: (template.get_def("repo_actions")
 
                                       .render(repo_name, _=_, h=h, c=c))
 

	
 
        for repo in c.repos_list:
 
            repos_data.append({
 
                "menu": quick_menu(repo.repo_name),
 
                "raw_name": repo.repo_name.lower(),
 
                "name": repo_lnk(repo.repo_name, repo.repo_type,
 
                                 repo.private, repo.fork),
 
                "desc": repo.description,
 
                "owner": repo.user.username,
 
                "action": repo_actions(repo.repo_name),
 
            })
 

	
 
        c.data = json.dumps({
 
            "totalRecords": total_records,
 
            "startIndex": 0,
 
            "sort": "name",
 
            "dir": "asc",
 
            "records": repos_data
 
        })
 
        repos_data = RepoModel().get_repos_as_dict(repos_list=c.repos_list,
 
                                                   admin=True)
 
        #json used to render the grid
 
        c.data = json.dumps(repos_data)
 

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

	
rhodecode/controllers/admin/repos_groups.py
Show inline comments
 
@@ -295,54 +295,18 @@ class ReposGroupsController(BaseControll
 
        c.groups = self.scm_model.get_repos_groups(groups)
 

	
 
        if c.visual.lightweight_dashboard is False:
 
            c.cached_repo_list = self.scm_model.get_repos(all_repos=gr_filter)
 

	
 
            c.repos_list = c.cached_repo_list
 
            c.repos_list = self.scm_model.get_repos(all_repos=gr_filter)
 
        ## lightweight version of dashboard
 
        else:
 
            c.repos_list = Repository.query()\
 
                            .filter(Repository.group_id == id)\
 
                            .order_by(func.lower(Repository.repo_name))\
 
                            .all()
 
            repos_data = []
 
            total_records = len(c.repos_list)
 

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

	
 
            quick_menu = lambda repo_name: (template.get_def("quick_menu")
 
                                            .render(repo_name, _=_, h=h, c=c))
 
            repo_lnk = lambda name, rtype, private, fork_of: (
 
                template.get_def("repo_name")
 
                .render(name, rtype, private, fork_of, short_name=False,
 
                        admin=False, _=_, h=h, c=c))
 
            last_change = lambda last_change:  (template.get_def("last_change")
 
                                           .render(last_change, _=_, h=h, c=c))
 
            rss_lnk = lambda repo_name: (template.get_def("rss")
 
                                           .render(repo_name, _=_, h=h, c=c))
 
            atom_lnk = lambda repo_name: (template.get_def("atom")
 
                                           .render(repo_name, _=_, h=h, c=c))
 

	
 
            for repo in c.repos_list:
 
                repos_data.append({
 
                    "menu": quick_menu(repo.repo_name),
 
                    "raw_name": repo.repo_name.lower(),
 
                    "name": repo_lnk(repo.repo_name, repo.repo_type,
 
                                     repo.private, repo.fork),
 
                    "last_change": last_change(repo.last_db_change),
 
                    "desc": repo.description,
 
                    "owner": h.person(repo.user.username),
 
                    "rss": rss_lnk(repo.repo_name),
 
                    "atom": atom_lnk(repo.repo_name),
 
                })
 

	
 
            c.data = json.dumps({
 
                "totalRecords": total_records,
 
                "startIndex": 0,
 
                "sort": "name",
 
                "dir": "asc",
 
                "records": repos_data
 
            })
 
            repos_data = RepoModel().get_repos_as_dict(repos_list=c.repos_list,
 
                                                       admin=False)
 
            #json used to render the grid
 
            c.data = json.dumps(repos_data)
 

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

	
rhodecode/controllers/admin/settings.py
Show inline comments
 
@@ -48,11 +48,12 @@ from rhodecode.model.forms import UserFo
 
    ApplicationUiSettingsForm, ApplicationVisualisationForm
 
from rhodecode.model.scm import ScmModel
 
from rhodecode.model.user import UserModel
 
from rhodecode.model.repo import RepoModel
 
from rhodecode.model.db import User
 
from rhodecode.model.notification import EmailNotificationModel
 
from rhodecode.model.meta import Session
 
from rhodecode.lib.utils2 import str2bool
 

	
 
from rhodecode.lib.utils2 import str2bool, safe_unicode
 
from rhodecode.lib.compat import json
 
log = logging.getLogger(__name__)
 

	
 

	
 
@@ -119,10 +120,11 @@ class SettingsController(BaseController)
 
                invalidate_cache('get_repo_cached_%s' % repo_name)
 

	
 
            added, removed = repo2db_mapper(initial, rm_obsolete)
 

	
 
            h.flash(_('Repositories successfully'
 
                      ' rescanned added: %s,removed: %s') % (added, removed),
 
                      category='success')
 
            _repr = lambda l: ', '.join(map(safe_unicode, l)) or '-'
 
            h.flash(_('Repositories successfully '
 
                      'rescanned added: %s ; removed: %s') %
 
                    (_repr(added), _repr(removed)),
 
                    category='success')
 

	
 
        if setting_id == 'whoosh':
 
            repo_location = self._get_hg_ui_settings()['paths_root_path']
 
@@ -336,7 +338,7 @@ class SettingsController(BaseController)
 
                .get_email_tmpl(EmailNotificationModel.TYPE_DEFAULT,
 
                                body=test_email_body)
 

	
 
            recipients = [test_email] if [test_email] else None
 
            recipients = [test_email] if test_email else None
 

	
 
            run_task(tasks.send_email, recipients, test_email_subj,
 
                     test_email_body, test_email_html_body)
 
@@ -381,6 +383,17 @@ class SettingsController(BaseController)
 
            force_defaults=False
 
        )
 

	
 
    def _load_my_repos_data(self):
 
        repos_list = Session().query(Repository)\
 
                     .filter(Repository.user_id ==
 
                             self.rhodecode_user.user_id)\
 
                     .order_by(func.lower(Repository.repo_name)).all()
 

	
 
        repos_data = RepoModel().get_repos_as_dict(repos_list=repos_list,
 
                                                   admin=True)
 
        #json used to render the grid
 
        return json.dumps(repos_data)
 

	
 
    @NotAnonymous()
 
    def my_account(self):
 
        """
 
@@ -389,17 +402,16 @@ class SettingsController(BaseController)
 
        # url('admin_settings_my_account')
 

	
 
        c.user = User.get(self.rhodecode_user.user_id)
 
        all_repos = Session().query(Repository)\
 
                     .filter(Repository.user_id == c.user.user_id)\
 
                     .order_by(func.lower(Repository.repo_name)).all()
 

	
 
        c.user_repos = ScmModel().get_repos(all_repos)
 
        c.ldap_dn = c.user.ldap_dn
 

	
 
        if c.user.username == 'default':
 
            h.flash(_("You can't edit this user since it's"
 
              " crucial for entire application"), category='warning')
 
            return redirect(url('users'))
 

	
 
        #json used to render the grid
 
        c.data = self._load_my_repos_data()
 

	
 
        defaults = c.user.get_dict()
 

	
 
        c.form = htmlfill.render(
 
@@ -420,19 +432,25 @@ class SettingsController(BaseController)
 
        #           method='put')
 
        # url('admin_settings_my_account_update', id=ID)
 
        uid = self.rhodecode_user.user_id
 
        c.user = User.get(self.rhodecode_user.user_id)
 
        c.ldap_dn = c.user.ldap_dn
 
        email = self.rhodecode_user.email
 
        _form = UserForm(edit=True,
 
                         old_data={'user_id': uid, 'email': email})()
 
        form_result = {}
 
        try:
 
            form_result = _form.to_python(dict(request.POST))
 
            UserModel().update_my_account(uid, form_result)
 
            skip_attrs = ['admin', 'active']  # skip attr for my account
 
            if c.ldap_dn:
 
                #forbid updating username for ldap accounts
 
                skip_attrs.append('username')
 
            UserModel().update(uid, form_result, skip_attrs=skip_attrs)
 
            h.flash(_('Your account was updated successfully'),
 
                    category='success')
 
            Session().commit()
 
        except formencode.Invalid, errors:
 
            c.user = User.get(self.rhodecode_user.user_id)
 

	
 
            #json used to render the grid
 
            c.data = self._load_my_repos_data()
 
            c.form = htmlfill.render(
 
                render('admin/users/user_edit_my_account_form.html'),
 
                defaults=errors.value,
 
@@ -448,23 +466,14 @@ class SettingsController(BaseController)
 
        return redirect(url('my_account'))
 

	
 
    @NotAnonymous()
 
    def my_account_my_repos(self):
 
        all_repos = Session().query(Repository)\
 
            .filter(Repository.user_id == self.rhodecode_user.user_id)\
 
            .order_by(func.lower(Repository.repo_name))\
 
            .all()
 
        c.user_repos = ScmModel().get_repos(all_repos)
 
        return render('admin/users/user_edit_my_account_repos.html')
 

	
 
    @NotAnonymous()
 
    def my_account_my_pullrequests(self):
 
        c.my_pull_requests = PullRequest.query()\
 
                                .filter(PullRequest.user_id==
 
                                .filter(PullRequest.user_id ==
 
                                        self.rhodecode_user.user_id)\
 
                                .all()
 
        c.participate_in_pull_requests = \
 
            [x.pull_request for x in PullRequestReviewers.query()\
 
                                    .filter(PullRequestReviewers.user_id==
 
                                    .filter(PullRequestReviewers.user_id ==
 
                                            self.rhodecode_user.user_id)\
 
                                    .all()]
 
        return render('admin/users/user_edit_my_account_pullrequests.html')
rhodecode/controllers/admin/users.py
Show inline comments
 
@@ -41,7 +41,7 @@ from rhodecode.lib.auth import LoginRequ
 
    AuthUser
 
from rhodecode.lib.base import BaseController, render
 

	
 
from rhodecode.model.db import User, UserEmailMap
 
from rhodecode.model.db import User, UserEmailMap, UserIpMap
 
from rhodecode.model.forms import UserForm
 
from rhodecode.model.user import UserModel
 
from rhodecode.model.meta import Session
 
@@ -159,7 +159,7 @@ class UsersController(BaseController):
 
        user_model = UserModel()
 
        c.user = user_model.get(id)
 
        c.ldap_dn = c.user.ldap_dn
 
        c.perm_user = AuthUser(user_id=id)
 
        c.perm_user = AuthUser(user_id=id, ip_addr=self.ip_addr)
 
        _form = UserForm(edit=True, old_data={'user_id': id,
 
                                              'email': c.user.email})()
 
        form_result = {}
 
@@ -178,6 +178,8 @@ class UsersController(BaseController):
 
        except formencode.Invalid, errors:
 
            c.user_email_map = UserEmailMap.query()\
 
                            .filter(UserEmailMap.user == c.user).all()
 
            c.user_ip_map = UserIpMap.query()\
 
                            .filter(UserIpMap.user == c.user).all()
 
            defaults = errors.value
 
            e = errors.error_dict or {}
 
            defaults.update({
 
@@ -231,12 +233,14 @@ class UsersController(BaseController):
 
            h.flash(_("You can't edit this user"), category='warning')
 
            return redirect(url('users'))
 

	
 
        c.perm_user = AuthUser(user_id=id)
 
        c.perm_user = AuthUser(user_id=id, ip_addr=self.ip_addr)
 
        c.user.permissions = {}
 
        c.granted_permissions = UserModel().fill_perms(c.user)\
 
            .permissions['global']
 
        c.user_email_map = UserEmailMap.query()\
 
                        .filter(UserEmailMap.user == c.user).all()
 
        c.user_ip_map = UserIpMap.query()\
 
                        .filter(UserIpMap.user == c.user).all()
 
        user_model = UserModel()
 
        c.ldap_dn = c.user.ldap_dn
 
        defaults = c.user.get_dict()
 
@@ -299,7 +303,6 @@ class UsersController(BaseController):
 
        """POST /user_emails:Add an existing item"""
 
        # url('user_emails', id=ID, method='put')
 

	
 
        #TODO: validation and form !!!
 
        email = request.POST.get('new_email')
 
        user_model = UserModel()
 

	
 
@@ -324,3 +327,36 @@ class UsersController(BaseController):
 
        Session().commit()
 
        h.flash(_("Removed email from user"), category='success')
 
        return redirect(url('edit_user', id=id))
 

	
 
    def add_ip(self, id):
 
        """POST /user_ips:Add an existing item"""
 
        # url('user_ips', id=ID, method='put')
 

	
 
        ip = request.POST.get('new_ip')
 
        user_model = UserModel()
 

	
 
        try:
 
            user_model.add_extra_ip(id, ip)
 
            Session().commit()
 
            h.flash(_("Added ip %s to user") % ip, category='success')
 
        except formencode.Invalid, error:
 
            msg = error.error_dict['ip']
 
            h.flash(msg, category='error')
 
        except Exception:
 
            log.error(traceback.format_exc())
 
            h.flash(_('An error occurred during ip saving'),
 
                    category='error')
 
        if 'default_user' in request.POST:
 
            return redirect(url('edit_permission', id='default'))
 
        return redirect(url('edit_user', id=id))
 

	
 
    def delete_ip(self, id):
 
        """DELETE /user_ips_delete/id: Delete an existing item"""
 
        # url('user_ips_delete', id=ID, method='delete')
 
        user_model = UserModel()
 
        user_model.delete_extra_ip(id, request.POST.get('del_ip'))
 
        Session().commit()
 
        h.flash(_("Removed ip from user"), category='success')
 
        if 'default_user' in request.POST:
 
            return redirect(url('edit_permission', id='default'))
 
        return redirect(url('edit_user', id=id))
rhodecode/controllers/api/__init__.py
Show inline comments
 
@@ -32,17 +32,15 @@ import urllib
 
import traceback
 
import time
 

	
 
from rhodecode.lib.compat import izip_longest, json
 

	
 
from paste.response import replace_header
 

	
 
from pylons.controllers import WSGIController
 

	
 

	
 
from webob.exc import HTTPNotFound, HTTPForbidden, HTTPInternalServerError, \
 
HTTPBadRequest, HTTPError
 

	
 
from rhodecode.model.db import User
 
from rhodecode.model import meta
 
from rhodecode.lib.compat import izip_longest, json
 
from rhodecode.lib.auth import AuthUser
 
from rhodecode.lib.base import _get_ip_addr, _get_access_path
 
from rhodecode.lib.utils2 import safe_unicode
 
@@ -86,6 +84,9 @@ class JSONRPCController(WSGIController):
 

	
 
     """
 

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

	
 
    def _get_method_args(self):
 
        """
 
        Return `self._rpc_args` to dispatched controller method
 
@@ -99,6 +100,7 @@ class JSONRPCController(WSGIController):
 
        controller and if it exists, dispatch to it.
 
        """
 
        start = time.time()
 
        ip_addr = self.ip_addr = self._get_ip_addr(environ)
 
        self._req_id = None
 
        if 'CONTENT_LENGTH' not in environ:
 
            log.debug("No Content-Length")
 
@@ -130,6 +132,9 @@ class JSONRPCController(WSGIController):
 
            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)
 
@@ -144,7 +149,15 @@ class JSONRPCController(WSGIController):
 
            if u is None:
 
                return jsonrpc_error(retid=self._req_id,
 
                                     message='Invalid API KEY')
 
            auth_u = AuthUser(u.user_id, self._req_api_key)
 

	
 
            #check if we are allowed to use this IP
 
            auth_u = AuthUser(u.user_id, self._req_api_key, ip_addr=ip_addr)
 
            if not auth_u.ip_allowed:
 
                return jsonrpc_error(retid=self._req_id,
 
                        message='request from IP:%s not allowed' % (ip_addr))
 
            else:
 
                log.info('Access for IP:%s allowed' % (ip_addr))
 

	
 
        except Exception, e:
 
            return jsonrpc_error(retid=self._req_id,
 
                                 message='Invalid API KEY')
 
@@ -202,6 +215,7 @@ class JSONRPCController(WSGIController):
 
                )
 

	
 
        self._rpc_args = {USER_SESSION_ATTR: u}
 

	
 
        self._rpc_args.update(self._request_params)
 

	
 
        self._rpc_args['action'] = self._req_method
rhodecode/controllers/api/api.py
Show inline comments
 
@@ -27,10 +27,12 @@
 

	
 
import traceback
 
import logging
 
from pylons.controllers.util import abort
 

	
 
from rhodecode.controllers.api import JSONRPCController, JSONRPCError
 
from rhodecode.lib.auth import HasPermissionAllDecorator, \
 
    HasPermissionAnyDecorator, PasswordGenerator, AuthUser
 
from rhodecode.lib.auth import PasswordGenerator, AuthUser, \
 
    HasPermissionAllDecorator, HasPermissionAnyDecorator, \
 
    HasPermissionAnyApi, HasRepoPermissionAnyApi
 
from rhodecode.lib.utils import map_groups, repo2db_mapper
 
from rhodecode.model.meta import Session
 
from rhodecode.model.scm import ScmModel
 
@@ -38,11 +40,27 @@ from rhodecode.model.repo import RepoMod
 
from rhodecode.model.user import UserModel
 
from rhodecode.model.users_group import UsersGroupModel
 
from rhodecode.model.permission import PermissionModel
 
from rhodecode.model.db import Repository
 
from rhodecode.model.db import Repository, RhodeCodeSetting, UserIpMap
 

	
 
log = logging.getLogger(__name__)
 

	
 

	
 
class OptionalAttr(object):
 
    """
 
    Special Optional Option that defines other attribute
 
    """
 
    def __init__(self, attr_name):
 
        self.attr_name = attr_name
 

	
 
    def __repr__(self):
 
        return '<OptionalAttr:%s>' % self.attr_name
 

	
 
    def __call__(self):
 
        return self
 
#alias
 
OAttr = OptionalAttr
 

	
 

	
 
class Optional(object):
 
    """
 
    Defines an optional parameter::
 
@@ -184,10 +202,11 @@ class ApiController(JSONRPCController):
 
                'Error occurred during rescan repositories action'
 
            )
 

	
 
    @HasPermissionAllDecorator('hg.admin')
 
    def lock(self, apiuser, repoid, userid, locked):
 
    def lock(self, apiuser, repoid, locked, userid=Optional(OAttr('apiuser'))):
 
        """
 
        Set locking state on particular repository by given user
 
        Set locking state on particular repository by given user, if
 
        this command is runned by non-admin account userid is set to user
 
        who is calling this method
 

	
 
        :param apiuser:
 
        :param repoid:
 
@@ -195,6 +214,22 @@ class ApiController(JSONRPCController):
 
        :param locked:
 
        """
 
        repo = get_repo_or_error(repoid)
 
        if HasPermissionAnyApi('hg.admin')(user=apiuser):
 
            pass
 
        elif HasRepoPermissionAnyApi('repository.admin',
 
                                     'repository.write')(user=apiuser,
 
                                                         repo_name=repo.repo_name):
 
            #make sure normal user does not pass someone else userid,
 
            #he is not allowed to do that
 
            if not isinstance(userid, Optional) and userid != apiuser.user_id:
 
                raise JSONRPCError(
 
                    'userid is not the same as your user'
 
                )
 
        else:
 
            raise JSONRPCError('repository `%s` does not exist' % (repoid))
 

	
 
        if isinstance(userid, Optional):
 
            userid = apiuser.user_id
 
        user = get_user_or_error(userid)
 
        locked = bool(locked)
 
        try:
 
@@ -212,13 +247,38 @@ class ApiController(JSONRPCController):
 
            )
 

	
 
    @HasPermissionAllDecorator('hg.admin')
 
    def get_user(self, apiuser, userid):
 
        """"
 
        Get a user by username
 
    def show_ip(self, apiuser, userid):
 
        """
 
        Shows IP address as seen from RhodeCode server, together with all
 
        defined IP addresses for given user
 

	
 
        :param apiuser:
 
        :param userid:
 
        """
 
        user = get_user_or_error(userid)
 
        ips = UserIpMap.query().filter(UserIpMap.user == user).all()
 
        return dict(
 
            ip_addr_server=self.ip_addr,
 
            user_ips=ips
 
        )
 

	
 
    def get_user(self, apiuser, userid=Optional(OAttr('apiuser'))):
 
        """"
 
        Get a user by username, or userid, if userid is given
 

	
 
        :param apiuser:
 
        :param userid:
 
        """
 
        if HasPermissionAnyApi('hg.admin')(user=apiuser) is False:
 
            #make sure normal user does not pass someone else userid,
 
            #he is not allowed to do that
 
            if not isinstance(userid, Optional) and userid != apiuser.user_id:
 
                raise JSONRPCError(
 
                    'userid is not the same as your user'
 
                )
 

	
 
        if isinstance(userid, Optional):
 
            userid = apiuser.user_id
 

	
 
        user = get_user_or_error(userid)
 
        data = user.get_api_data()
 
@@ -479,7 +539,6 @@ class ApiController(JSONRPCController):
 
                    )
 
            )
 

	
 
    @HasPermissionAnyDecorator('hg.admin')
 
    def get_repo(self, apiuser, repoid):
 
        """"
 
        Get repository by name
 
@@ -489,6 +548,12 @@ class ApiController(JSONRPCController):
 
        """
 
        repo = get_repo_or_error(repoid)
 

	
 
        if HasPermissionAnyApi('hg.admin')(user=apiuser) is False:
 
            # check if we have admin permission for this repo !
 
            if HasRepoPermissionAnyApi('repository.admin')(user=apiuser,
 
                                            repo_name=repo.repo_name) is False:
 
                raise JSONRPCError('repository `%s` does not exist' % (repoid))
 

	
 
        members = []
 
        for user in repo.repo_to_perm:
 
            perm = user.permission.permission_name
 
@@ -510,20 +575,23 @@ class ApiController(JSONRPCController):
 
        data['members'] = members
 
        return data
 

	
 
    @HasPermissionAnyDecorator('hg.admin')
 
    def get_repos(self, apiuser):
 
        """"
 
        Get all repositories
 

	
 
        :param apiuser:
 
        """
 
        result = []
 
        if HasPermissionAnyApi('hg.admin')(user=apiuser) is False:
 
            repos = RepoModel().get_all_user_repos(user=apiuser)
 
        else:
 
            repos = RepoModel().get_all()
 

	
 
        result = []
 
        for repo in RepoModel().get_all():
 
        for repo in repos:
 
            result.append(repo.get_api_data())
 
        return result
 

	
 
    @HasPermissionAnyDecorator('hg.admin')
 
    @HasPermissionAllDecorator('hg.admin')
 
    def get_repo_nodes(self, apiuser, repoid, revision, root_path,
 
                       ret_type='all'):
 
        """
 
@@ -556,12 +624,16 @@ class ApiController(JSONRPCController):
 
            )
 

	
 
    @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
 
    def create_repo(self, apiuser, repo_name, owner, repo_type,
 
    def create_repo(self, apiuser, repo_name, owner=Optional(OAttr('apiuser')),
 
                    repo_type=Optional('hg'),
 
                    description=Optional(''), private=Optional(False),
 
                    clone_uri=Optional(None), landing_rev=Optional('tip')):
 
                    clone_uri=Optional(None), landing_rev=Optional('tip'),
 
                    enable_statistics=Optional(False),
 
                    enable_locking=Optional(False),
 
                    enable_downloads=Optional(False)):
 
        """
 
        Create repository, if clone_url is given it makes a remote clone
 
        if repo_name is withina  group name the groups will be created
 
        if repo_name is within a group name the groups will be created
 
        automatically if they aren't present
 

	
 
        :param apiuser:
 
@@ -573,12 +645,32 @@ class ApiController(JSONRPCController):
 
        :param clone_uri:
 
        :param landing_rev:
 
        """
 
        if HasPermissionAnyApi('hg.admin')(user=apiuser) is False:
 
            if not isinstance(owner, Optional):
 
                #forbid setting owner for non-admins
 
                raise JSONRPCError(
 
                    'Only RhodeCode admin can specify `owner` param'
 
                )
 
        if isinstance(owner, Optional):
 
            owner = apiuser.user_id
 

	
 
        owner = get_user_or_error(owner)
 

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

	
 
        private = Optional.extract(private)
 
        defs = RhodeCodeSetting.get_default_repo_settings(strip_prefix=True)
 
        if isinstance(private, Optional):
 
            private = defs.get('repo_private') or Optional.extract(private)
 
        if isinstance(repo_type, Optional):
 
            repo_type = defs.get('repo_type')
 
        if isinstance(enable_statistics, Optional):
 
            enable_statistics = defs.get('repo_enable_statistics')
 
        if isinstance(enable_locking, Optional):
 
            enable_locking = defs.get('repo_enable_locking')
 
        if isinstance(enable_downloads, Optional):
 
            enable_downloads = defs.get('repo_enable_downloads')
 

	
 
        clone_uri = Optional.extract(clone_uri)
 
        description = Optional.extract(description)
 
        landing_rev = Optional.extract(landing_rev)
 
@@ -596,32 +688,51 @@ class ApiController(JSONRPCController):
 
                clone_uri=clone_uri,
 
                repos_group=group,
 
                landing_rev=landing_rev,
 
                enable_statistics=enable_statistics,
 
                enable_downloads=enable_downloads,
 
                enable_locking=enable_locking
 
            )
 

	
 
            Session().commit()
 

	
 
            return dict(
 
                msg="Created new repository `%s`" % (repo.repo_name),
 
                repo=repo.get_api_data()
 
            )
 

	
 
        except Exception:
 
            log.error(traceback.format_exc())
 
            raise JSONRPCError('failed to create repository `%s`' % repo_name)
 

	
 
    @HasPermissionAnyDecorator('hg.admin')
 
    def fork_repo(self, apiuser, repoid, fork_name, owner,
 
    @HasPermissionAnyDecorator('hg.admin', 'hg.fork.repository')
 
    def fork_repo(self, apiuser, repoid, fork_name, owner=Optional(OAttr('apiuser')),
 
                  description=Optional(''), copy_permissions=Optional(False),
 
                  private=Optional(False), landing_rev=Optional('tip')):
 
        repo = get_repo_or_error(repoid)
 
        repo_name = repo.repo_name
 
        owner = get_user_or_error(owner)
 

	
 
        _repo = RepoModel().get_by_repo_name(fork_name)
 
        if _repo:
 
            type_ = 'fork' if _repo.fork else 'repo'
 
            raise JSONRPCError("%s `%s` already exist" % (type_, fork_name))
 

	
 
        if HasPermissionAnyApi('hg.admin')(user=apiuser):
 
            pass
 
        elif HasRepoPermissionAnyApi('repository.admin',
 
                                     'repository.write',
 
                                     'repository.read')(user=apiuser,
 
                                                        repo_name=repo.repo_name):
 
            if not isinstance(owner, Optional):
 
                #forbid setting owner for non-admins
 
                raise JSONRPCError(
 
                    'Only RhodeCode admin can specify `owner` param'
 
                )
 
        else:
 
            raise JSONRPCError('repository `%s` does not exist' % (repoid))
 

	
 
        if isinstance(owner, Optional):
 
            owner = apiuser.user_id
 

	
 
        owner = get_user_or_error(owner)
 

	
 
        try:
 
            # create structure of groups and return the last group
 
            group = map_groups(fork_name)
 
@@ -652,7 +763,6 @@ class ApiController(JSONRPCController):
 
                                                            fork_name)
 
            )
 

	
 
    @HasPermissionAnyDecorator('hg.admin')
 
    def delete_repo(self, apiuser, repoid):
 
        """
 
        Deletes a given repository
 
@@ -662,6 +772,12 @@ class ApiController(JSONRPCController):
 
        """
 
        repo = get_repo_or_error(repoid)
 

	
 
        if HasPermissionAnyApi('hg.admin')(user=apiuser) is False:
 
            # check if we have admin permission for this repo !
 
            if HasRepoPermissionAnyApi('repository.admin')(user=apiuser,
 
                                            repo_name=repo.repo_name) is False:
 
                 raise JSONRPCError('repository `%s` does not exist' % (repoid))
 

	
 
        try:
 
            RepoModel().delete(repo)
 
            Session().commit()
 
@@ -675,7 +791,7 @@ class ApiController(JSONRPCController):
 
                'failed to delete repository `%s`' % repo.repo_name
 
            )
 

	
 
    @HasPermissionAnyDecorator('hg.admin')
 
    @HasPermissionAllDecorator('hg.admin')
 
    def grant_user_permission(self, apiuser, repoid, userid, perm):
 
        """
 
        Grant permission for user on given repository, or update existing one
 
@@ -708,7 +824,7 @@ class ApiController(JSONRPCController):
 
                )
 
            )
 

	
 
    @HasPermissionAnyDecorator('hg.admin')
 
    @HasPermissionAllDecorator('hg.admin')
 
    def revoke_user_permission(self, apiuser, repoid, userid):
 
        """
 
        Revoke permission for user on given repository
 
@@ -739,7 +855,7 @@ class ApiController(JSONRPCController):
 
                )
 
            )
 

	
 
    @HasPermissionAnyDecorator('hg.admin')
 
    @HasPermissionAllDecorator('hg.admin')
 
    def grant_users_group_permission(self, apiuser, repoid, usersgroupid,
 
                                     perm):
 
        """
 
@@ -778,7 +894,7 @@ class ApiController(JSONRPCController):
 
                )
 
            )
 

	
 
    @HasPermissionAnyDecorator('hg.admin')
 
    @HasPermissionAllDecorator('hg.admin')
 
    def revoke_users_group_permission(self, apiuser, repoid, usersgroupid):
 
        """
 
        Revoke permission for users group on given repository
rhodecode/controllers/changeset.py
Show inline comments
 
@@ -221,13 +221,25 @@ class ChangesetController(BaseRepoContro
 
        for changeset in c.cs_ranges:
 
            inlines = []
 
            if method == 'show':
 
                c.statuses.extend([ChangesetStatusModel()\
 
                                  .get_status(c.rhodecode_db_repo.repo_id,
 
                                              changeset.raw_id)])
 
                c.statuses.extend([ChangesetStatusModel().get_status(
 
                            c.rhodecode_db_repo.repo_id, changeset.raw_id)])
 

	
 
                c.comments.extend(ChangesetCommentsModel()\
 
                                  .get_comments(c.rhodecode_db_repo.repo_id,
 
                                                revision=changeset.raw_id))
 

	
 
                #comments from PR
 
                st = ChangesetStatusModel().get_statuses(
 
                            c.rhodecode_db_repo.repo_id, changeset.raw_id,
 
                            with_revisions=True)
 
                # from associated statuses, check the pull requests, and
 
                # show comments from them
 

	
 
                prs = set([x.pull_request for x in
 
                           filter(lambda x: x.pull_request != None, st)])
 

	
 
                for pr in prs:
 
                    c.comments.extend(pr.comments)
 
                inlines = ChangesetCommentsModel()\
 
                            .get_inline_comments(c.rhodecode_db_repo.repo_id,
 
                                                 revision=changeset.raw_id)
 
@@ -269,6 +281,9 @@ class ChangesetController(BaseRepoContro
 
                cs_changes[''] = [None, None, None, None, diff, None]
 
            c.changes[changeset.raw_id] = cs_changes
 

	
 
        #sort comments by how they were generated
 
        c.comments = sorted(c.comments, key=lambda x: x.comment_id)
 

	
 
        # count inline comments
 
        for __, lines in c.inline_comments:
 
            for comments in lines.values():
 
@@ -342,7 +357,7 @@ class ChangesetController(BaseRepoContro
 
                )
 
            except StatusChangeOnClosedPullRequestError:
 
                log.error(traceback.format_exc())
 
                msg = _('Changing status on a changeset associated with'
 
                msg = _('Changing status on a changeset associated with '
 
                        'a closed pull request is not allowed')
 
                h.flash(msg, category='warning')
 
                return redirect(h.url('changeset_home', repo_name=repo_name,
 
@@ -371,7 +386,7 @@ class ChangesetController(BaseRepoContro
 
    @jsonify
 
    def delete_comment(self, repo_name, comment_id):
 
        co = ChangesetComment.get(comment_id)
 
        owner = lambda: co.author.user_id == c.rhodecode_user.user_id
 
        owner = co.author.user_id == c.rhodecode_user.user_id
 
        if h.HasPermissionAny('hg.admin', 'repository.admin')() or owner:
 
            ChangesetCommentsModel().delete(comment=co)
 
            Session().commit()
rhodecode/controllers/compare.py
Show inline comments
 
@@ -103,8 +103,11 @@ class CompareController(BaseRepoControll
 
        c.org_repo = org_repo = Repository.get_by_repo_name(org_repo)
 
        c.other_repo = other_repo = Repository.get_by_repo_name(other_repo)
 

	
 
        if c.org_repo is None or c.other_repo is None:
 
            log.error('Could not found repo %s or %s' % (org_repo, other_repo))
 
        if c.org_repo is None:
 
            log.error('Could not find org repo %s' % org_repo)
 
            raise HTTPNotFound
 
        if c.other_repo is None:
 
            log.error('Could not find other repo %s' % other_repo)
 
            raise HTTPNotFound
 

	
 
        if c.org_repo != c.other_repo and h.is_git(c.rhodecode_repo):
rhodecode/controllers/home.py
Show inline comments
 
@@ -28,6 +28,7 @@ import logging
 
from pylons import tmpl_context as c, request
 
from pylons.i18n.translation import _
 
from webob.exc import HTTPBadRequest
 
from sqlalchemy.sql.expression import func
 

	
 
import rhodecode
 
from rhodecode.lib import helpers as h
 
@@ -35,7 +36,8 @@ from rhodecode.lib.ext_json import json
 
from rhodecode.lib.auth import LoginRequired
 
from rhodecode.lib.base import BaseController, render
 
from rhodecode.model.db import Repository
 
from sqlalchemy.sql.expression import func
 
from rhodecode.model.repo import RepoModel
 

	
 

	
 
log = logging.getLogger(__name__)
 

	
 
@@ -58,51 +60,11 @@ class HomeController(BaseController):
 
                            .filter(Repository.group_id == None)\
 
                            .order_by(func.lower(Repository.repo_name))\
 
                            .all()
 
            repos_data = []
 
            total_records = len(c.repos_list)
 

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

	
 
            quick_menu = lambda repo_name: (template.get_def("quick_menu")
 
                                            .render(repo_name, _=_, h=h, c=c))
 
            repo_lnk = lambda name, rtype, private, fork_of: (
 
                template.get_def("repo_name")
 
                .render(name, rtype, private, fork_of, short_name=False,
 
                        admin=False, _=_, h=h, c=c))
 
            last_change = lambda last_change:  (template.get_def("last_change")
 
                                           .render(last_change, _=_, h=h, c=c))
 
            rss_lnk = lambda repo_name: (template.get_def("rss")
 
                                           .render(repo_name, _=_, h=h, c=c))
 
            atom_lnk = lambda repo_name: (template.get_def("atom")
 
                                           .render(repo_name, _=_, h=h, c=c))
 

	
 
            def desc(desc):
 
                if c.visual.stylify_metatags:
 
                    return h.urlify_text(h.desc_stylize(h.truncate(desc, 60)))
 
                else:
 
                    return h.urlify_text(h.truncate(desc, 60))
 

	
 
            for repo in c.repos_list:
 
                repos_data.append({
 
                    "menu": quick_menu(repo.repo_name),
 
                    "raw_name": repo.repo_name.lower(),
 
                    "name": repo_lnk(repo.repo_name, repo.repo_type,
 
                                     repo.private, repo.fork),
 
                    "last_change": last_change(repo.last_db_change),
 
                    "desc": desc(repo.description),
 
                    "owner": h.person(repo.user.username),
 
                    "rss": rss_lnk(repo.repo_name),
 
                    "atom": atom_lnk(repo.repo_name),
 
                })
 

	
 
            c.data = json.dumps({
 
                "totalRecords": total_records,
 
                "startIndex": 0,
 
                "sort": "name",
 
                "dir": "asc",
 
                "records": repos_data
 
            })
 
            repos_data = RepoModel().get_repos_as_dict(repos_list=c.repos_list,
 
                                                       admin=False)
 
            #json used to render the grid
 
            c.data = json.dumps(repos_data)
 

	
 
        return render('/index.html')
 

	
rhodecode/controllers/journal.py
Show inline comments
 
@@ -27,6 +27,8 @@ from itertools import groupby
 

	
 
from sqlalchemy import or_
 
from sqlalchemy.orm import joinedload
 
from sqlalchemy.sql.expression import func
 

	
 
from webhelpers.paginate import Page
 
from webhelpers.feedgenerator import Atom1Feed, Rss201rev2Feed
 

	
 
@@ -39,10 +41,10 @@ from rhodecode.lib.auth import LoginRequ
 
from rhodecode.lib.base import BaseController, render
 
from rhodecode.model.db import UserLog, UserFollowing, Repository, User
 
from rhodecode.model.meta import Session
 
from sqlalchemy.sql.expression import func
 
from rhodecode.model.scm import ScmModel
 
from rhodecode.lib.utils2 import safe_int, AttributeDict
 
from rhodecode.controllers.admin.admin import _journal_filter
 
from rhodecode.model.repo import RepoModel
 
from rhodecode.lib.compat import json
 

	
 
log = logging.getLogger(__name__)
 

	
 
@@ -78,18 +80,73 @@ class JournalController(BaseController):
 
        c.journal_data = render('journal/journal_data.html')
 
        if request.environ.get('HTTP_X_PARTIAL_XHR'):
 
            return c.journal_data
 
        return render('journal/journal.html')
 

	
 
        repos_list = Session().query(Repository)\
 
                     .filter(Repository.user_id ==
 
                             self.rhodecode_user.user_id)\
 
                     .order_by(func.lower(Repository.repo_name)).all()
 

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

	
 
        watched_repos_data = []
 

	
 
        ## watched repos
 
        _render = RepoModel._render_datatable
 

	
 
        def quick_menu(repo_name):
 
            return _render('quick_menu', repo_name)
 

	
 
        def repo_lnk(name, rtype, private, fork_of):
 
            return _render('repo_name', name, rtype, private, fork_of,
 
                           short_name=False, admin=False)
 

	
 
        def last_rev(repo_name, cs_cache):
 
            return _render('revision', repo_name, cs_cache.get('revision'),
 
                           cs_cache.get('raw_id'), cs_cache.get('author'),
 
                           cs_cache.get('message'))
 

	
 
    @LoginRequired()
 
    @NotAnonymous()
 
    def index_my_repos(self):
 
        c.user = User.get(self.rhodecode_user.user_id)
 
        if request.environ.get('HTTP_X_PARTIAL_XHR'):
 
            all_repos = self.sa.query(Repository)\
 
                     .filter(Repository.user_id == c.user.user_id)\
 
                     .order_by(func.lower(Repository.repo_name)).all()
 
            c.user_repos = ScmModel().get_repos(all_repos)
 
            return render('journal/journal_page_repos.html')
 
        def desc(desc):
 
            from pylons import tmpl_context as c
 
            if c.visual.stylify_metatags:
 
                return h.urlify_text(h.desc_stylize(h.truncate(desc, 60)))
 
            else:
 
                return h.urlify_text(h.truncate(desc, 60))
 

	
 
        def repo_actions(repo_name):
 
            return _render('repo_actions', repo_name)
 

	
 
        def owner_actions(user_id, username):
 
            return _render('user_name', user_id, username)
 

	
 
        def toogle_follow(repo_id):
 
            return  _render('toggle_follow', repo_id)
 

	
 
        for entry in c.following:
 
            repo = entry.follows_repository
 
            cs_cache = repo.changeset_cache
 
            row = {
 
                "menu": quick_menu(repo.repo_name),
 
                "raw_name": repo.repo_name.lower(),
 
                "name": repo_lnk(repo.repo_name, repo.repo_type,
 
                                 repo.private, repo.fork),
 
                "last_changeset": last_rev(repo.repo_name, cs_cache),
 
                "raw_tip": cs_cache.get('revision'),
 
                "action": toogle_follow(repo.repo_id)
 
            }
 

	
 
            watched_repos_data.append(row)
 

	
 
        c.watched_data = json.dumps({
 
            "totalRecords": len(c.following),
 
            "startIndex": 0,
 
            "sort": "name",
 
            "dir": "asc",
 
            "records": watched_repos_data
 
        })
 
        return render('journal/journal.html')
 

	
 
    @LoginRequired(api_access=True)
 
    @NotAnonymous()
rhodecode/controllers/login.py
Show inline comments
 
@@ -54,10 +54,9 @@ class LoginController(BaseController):
 
    def index(self):
 
        # redirect if already logged in
 
        c.came_from = request.GET.get('came_from')
 

	
 
        if self.rhodecode_user.is_authenticated \
 
                            and self.rhodecode_user.username != 'default':
 

	
 
        not_default = self.rhodecode_user.username != 'default'
 
        ip_allowed = self.rhodecode_user.ip_allowed
 
        if self.rhodecode_user.is_authenticated and not_default and ip_allowed:
 
            return redirect(url('home'))
 

	
 
        if request.POST:
rhodecode/controllers/pullrequests.py
Show inline comments
 
@@ -97,7 +97,7 @@ class PullrequestsController(BaseRepoCon
 
            return repo.branches.keys()[0]
 

	
 
    def _get_is_allowed_change_status(self, pull_request):
 
        owner = self.rhodecode_user.user_id == pull_request.user_id 
 
        owner = self.rhodecode_user.user_id == pull_request.user_id
 
        reviewer = self.rhodecode_user.user_id in [x.user_id for x in
 
                                                   pull_request.reviewers]
 
        return (self.rhodecode_user.admin or owner or reviewer)
 
@@ -299,7 +299,7 @@ class PullrequestsController(BaseRepoCon
 
                                  else EmptyChangeset(), 'raw_id'))
 

	
 
        c.statuses = org_repo.statuses([x.raw_id for x in c.cs_ranges])
 
        c.target_repo = c.repo_name
 
        c.target_repo = other_repo.repo_name
 
        # defines that we need hidden inputs with changesets
 
        c.as_form = request.GET.get('as_form', False)
 

	
 
@@ -339,7 +339,6 @@ class PullrequestsController(BaseRepoCon
 
        c.users_array = repo_model.get_users_js()
 
        c.users_groups_array = repo_model.get_users_groups_js()
 
        c.pull_request = PullRequest.get_or_404(pull_request_id)
 
        c.target_repo = c.pull_request.org_repo.repo_name
 
        c.allowed_to_change_status = self._get_is_allowed_change_status(c.pull_request)
 
        cc_model = ChangesetCommentsModel()
 
        cs_model = ChangesetStatusModel()
 
@@ -478,7 +477,7 @@ class PullrequestsController(BaseRepoCon
 
            #don't allow deleting comments on closed pull request
 
            raise HTTPForbidden()
 

	
 
        owner = lambda: co.author.user_id == c.rhodecode_user.user_id
 
        owner = co.author.user_id == c.rhodecode_user.user_id
 
        if h.HasPermissionAny('hg.admin', 'repository.admin')() or owner:
 
            ChangesetCommentsModel().delete(comment=co)
 
            Session().commit()
rhodecode/i18n/ja/LC_MESSAGES/rhodecode.mo
Show inline comments
 
binary diff not shown
rhodecode/i18n/ja/LC_MESSAGES/rhodecode.po
Show inline comments
 
@@ -13,7 +13,7 @@ msgstr ""
 
"Project-Id-Version: RhodeCode 1.2.0\n"
 
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
 
"POT-Creation-Date: 2012-12-14 04:19+0100\n"
 
"PO-Revision-Date: 2012-10-27 15:06+0900\n"
 
"PO-Revision-Date: 2013-01-02 01:39+0900\n"
 
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 
"Language-Team: ja <LL@li.org>\n"
 
"Plural-Forms: nplurals=1; plural=0\n"
 
@@ -41,9 +41,9 @@ msgstr ""
 

	
 
#: rhodecode/controllers/changeset.py:314
 
#: rhodecode/controllers/pullrequests.py:417
 
#, fuzzy, python-format
 
#, python-format
 
msgid "Status change -> %s"
 
msgstr ""
 
msgstr "ステータス変更 -> %s"
 

	
 
#: rhodecode/controllers/changeset.py:345
 
msgid ""
 
@@ -71,7 +71,7 @@ msgstr "リソースにアクセスする権限がありません"
 

	
 
#: rhodecode/controllers/error.py:103
 
msgid "You don't have permission to view this page"
 
msgstr "このページを見る権限がありません"
 
msgstr "このページを閲覧する権限がありません"
 

	
 
#: rhodecode/controllers/error.py:105
 
msgid "The resource could not be found"
 
@@ -285,19 +285,17 @@ msgid "An error occurred during deletion
 
msgstr "リポジトリ %s の削除中にエラーが発生しました"
 

	
 
#: rhodecode/controllers/settings.py:185
 
#, fuzzy
 
msgid "unlocked"
 
msgstr "変更可能にする"
 
msgstr "アンロック"
 

	
 
#: rhodecode/controllers/settings.py:188
 
#, fuzzy
 
msgid "locked"
 
msgstr "変更可能にする"
 
msgstr "ロック"
 

	
 
#: rhodecode/controllers/settings.py:190
 
#, fuzzy, python-format
 
#, python-format
 
msgid "Repository has been %s"
 
msgstr ""
 
msgstr "リポジトリは %s されています"
 

	
 
#: rhodecode/controllers/settings.py:194
 
#: rhodecode/controllers/admin/repos.py:423
 
@@ -314,14 +312,12 @@ msgid "Statistics are disabled for this 
 
msgstr "このリポジトリの統計は無効化されています"
 

	
 
#: rhodecode/controllers/admin/defaults.py:96
 
#, fuzzy
 
msgid "Default settings updated successfully"
 
msgstr "LDAP設定を更新しました"
 
msgstr "デフォルト設定を更新しました"
 

	
 
#: rhodecode/controllers/admin/defaults.py:110
 
#, fuzzy
 
msgid "error occurred during update of defaults"
 
msgstr "ユーザー %s の更新中にエラーが発生しました"
 
msgstr "デフォルト設定の更新中にエラーが発生しました"
 

	
 
#: rhodecode/controllers/admin/ldap_settings.py:50
 
msgid "BASE"
 
@@ -473,7 +469,7 @@ msgstr "リポジトリ %s を作成中にエラーが発生しました"
 
#: rhodecode/controllers/admin/repos.py:320
 
#, python-format
 
msgid "Cannot delete %s it still contains attached forks"
 
msgstr ""
 
msgstr "フォークしたリポジトリが存在するため、 %s は削除できません"
 

	
 
#: rhodecode/controllers/admin/repos.py:349
 
msgid "An error occurred during deletion of repository user"
 
@@ -493,11 +489,11 @@ msgstr "キャッシュの無効化時にエラーが発生しました"
 

	
 
#: rhodecode/controllers/admin/repos.py:443
 
msgid "Updated repository visibility in public journal"
 
msgstr ""
 
msgstr "公開ジャーナルでのリポジトリの可視性を更新しました"
 

	
 
#: rhodecode/controllers/admin/repos.py:447
 
msgid "An error occurred during setting this repository in public journal"
 
msgstr ""
 
msgstr "このリポジトリの公開ジャーナルの設定中にエラーが発生しました"
 

	
 
#: rhodecode/controllers/admin/repos.py:452 rhodecode/model/validators.py:300
 
msgid "Token mismatch"
 
@@ -750,7 +746,7 @@ msgstr "バイナリファイル"
 

	
 
#: rhodecode/lib/diffs.py:90
 
msgid "Changeset was too big and was cut off, use diff menu to display this diff"
 
msgstr ""
 
msgstr "チェンジセットが大きすぎるため省略しました。差分を表示する場合は差分メニューを使用してください"
 

	
 
#: rhodecode/lib/diffs.py:100
 
msgid "No changes detected"
 
@@ -770,14 +766,14 @@ msgid "False"
 
msgstr "False"
 

	
 
#: rhodecode/lib/helpers.py:530
 
#, fuzzy, python-format
 
#, python-format
 
msgid "Deleted branch: %s"
 
msgstr "リポジトリ %s を削除しました"
 
msgstr "削除されたブランチ: %s"
 

	
 
#: rhodecode/lib/helpers.py:533
 
#, fuzzy, python-format
 
#, python-format
 
msgid "Created tag: %s"
 
msgstr "ユーザー %s を作成しました"
 
msgstr "作成したタグ: %s"
 

	
 
#: rhodecode/lib/helpers.py:546
 
msgid "Changeset not found"
 
@@ -794,21 +790,21 @@ msgstr "比較の表示"
 

	
 
#: rhodecode/lib/helpers.py:615
 
msgid "and"
 
msgstr ""
 
msgstr ""
 

	
 
#: rhodecode/lib/helpers.py:616
 
#, python-format
 
msgid "%s more"
 
msgstr ""
 
msgstr "%s 以上"
 

	
 
#: rhodecode/lib/helpers.py:617 rhodecode/templates/changelog/changelog.html:51
 
msgid "revisions"
 
msgstr "リビジョン"
 

	
 
#: rhodecode/lib/helpers.py:641
 
#, fuzzy, python-format
 
#, python-format
 
msgid "fork name %s"
 
msgstr ""
 
msgstr "フォーク名 %s"
 

	
 
#: rhodecode/lib/helpers.py:658
 
#: rhodecode/templates/pullrequests/pullrequest_show.html:4
 
@@ -959,9 +955,9 @@ msgid "%s ago"
 
msgstr "%s 前"
 

	
 
#: rhodecode/lib/utils2.py:428
 
#, fuzzy, python-format
 
#, python-format
 
msgid "in %s and %s"
 
msgstr "%s と %s 前"
 
msgstr ""
 

	
 
#: rhodecode/lib/utils2.py:431
 
#, python-format
 
@@ -1084,39 +1080,39 @@ msgid "Enter %(min)i characters or more"
 
msgstr "%(min)i 文字以上必要です"
 

	
 
#: rhodecode/model/notification.py:220
 
#, fuzzy, python-format
 
#, python-format
 
msgid "commented on commit at %(when)s"
 
msgstr ""
 
msgstr "コミットにコメント %(when)s"
 

	
 
#: rhodecode/model/notification.py:221
 
#, python-format
 
msgid "sent message at %(when)s"
 
msgstr ""
 
msgstr "メッセージを送信 %(when)s"
 

	
 
#: rhodecode/model/notification.py:222
 
#, python-format
 
msgid "mentioned you at %(when)s"
 
msgstr ""
 
msgstr "Mention %(when)s"
 

	
 
#: rhodecode/model/notification.py:223
 
#, python-format
 
msgid "registered in RhodeCode at %(when)s"
 
msgstr ""
 
msgstr "RhodeCodeに登録 %(when)s"
 

	
 
#: rhodecode/model/notification.py:224
 
#, fuzzy, python-format
 
#, python-format
 
msgid "opened new pull request at %(when)s"
 
msgstr ""
 
msgstr "新しいプルリクエストを作成 %(when)s"
 

	
 
#: rhodecode/model/notification.py:225
 
#, fuzzy, python-format
 
#, python-format
 
msgid "commented on pull request at %(when)s"
 
msgstr ""
 
msgstr "プルリクエストにコメント %(when)s"
 

	
 
#: rhodecode/model/pull_request.py:90
 
#, python-format
 
msgid "%(user)s wants you to review pull request #%(pr_id)s"
 
msgstr ""
 
msgstr "%(user)s がプリリクエスト #%(pr_id)s のレビューを求めています"
 

	
 
#: rhodecode/model/scm.py:542
 
msgid "latest tip"
 
@@ -1129,11 +1125,11 @@ msgstr "新規ユーザー登録"
 
#: rhodecode/model/user.py:257 rhodecode/model/user.py:281
 
#: rhodecode/model/user.py:303
 
msgid "You can't Edit this user since it's crucial for entire application"
 
msgstr ""
 
msgstr "アプリケーション全体にとって重要なユーザなため、編集出来ません"
 

	
 
#: rhodecode/model/user.py:327
 
msgid "You can't remove this user since it's crucial for entire application"
 
msgstr ""
 
msgstr "アプリケーション全体にとって重要なユーザなため、削除できません"
 

	
 
#: rhodecode/model/user.py:333
 
#, python-format
 
@@ -1144,7 +1140,7 @@ msgstr ""
 

	
 
#: rhodecode/model/validators.py:36 rhodecode/model/validators.py:37
 
msgid "Value cannot be an empty list"
 
msgstr ""
 
msgstr "空のリストには出来ません"
 

	
 
#: rhodecode/model/validators.py:83
 
#, python-format
 
@@ -1244,16 +1240,15 @@ msgstr "無効なクローンURIです"
 

	
 
#: rhodecode/model/validators.py:433
 
msgid "Invalid clone url, provide a valid clone http(s)/svn+http(s) url"
 
msgstr ""
 
msgstr "無効なクローンURIです。有効な http(s)/svn+http(s) のURIを指定してください"
 

	
 
#: rhodecode/model/validators.py:458
 
msgid "Fork have to be the same type as parent"
 
msgstr "フォークは親と同じタイプの必要があります"
 

	
 
#: rhodecode/model/validators.py:473
 
#, fuzzy
 
msgid "You don't have permissions to create repository in this group"
 
msgstr "このページを見る権限がありません"
 
msgstr "このグループでリポジトリを作成する権限がありません"
 

	
 
#: rhodecode/model/validators.py:498
 
msgid "This username or users group name is not valid"
 
@@ -1617,22 +1612,20 @@ msgid "Admin journal"
 
msgstr "管理者ジャーナル"
 

	
 
#: rhodecode/templates/admin/admin.html:10
 
#, fuzzy
 
msgid "journal filter..."
 
msgstr "クイックフィルタ..."
 
msgstr "ジャーナルフィルタ..."
 

	
 
#: rhodecode/templates/admin/admin.html:12
 
#: rhodecode/templates/journal/journal.html:11
 
#, fuzzy
 
msgid "filter"
 
msgstr "ファイル"
 
msgstr "フィルタ"
 

	
 
#: rhodecode/templates/admin/admin.html:13
 
#: rhodecode/templates/journal/journal.html:12
 
#, python-format
 
msgid "%s entry"
 
msgid_plural "%s entries"
 
msgstr[0] ""
 
msgstr[0] "%s エントリ"
 

	
 
#: rhodecode/templates/admin/admin_log.html:6
 
#: rhodecode/templates/admin/repos/repos.html:74
 
@@ -1668,14 +1661,12 @@ msgstr "まだアクションがありません"
 

	
 
#: rhodecode/templates/admin/defaults/defaults.html:5
 
#: rhodecode/templates/admin/defaults/defaults.html:25
 
#, fuzzy
 
msgid "Repositories defaults"
 
msgstr "リポジトリグループ"
 
msgstr "リポジトリのデフォルト設定"
 

	
 
#: rhodecode/templates/admin/defaults/defaults.html:11
 
#, fuzzy
 
msgid "Defaults"
 
msgstr "default"
 
msgstr "デフォルト設定"
 

	
 
#: rhodecode/templates/admin/defaults/defaults.html:35
 
#: rhodecode/templates/admin/repos/repo_add_base.html:38
 
@@ -1722,7 +1713,7 @@ msgstr "ロックを有効にする"
 
#: rhodecode/templates/admin/defaults/defaults.html:79
 
#: rhodecode/templates/admin/repos/repo_edit.html:116
 
msgid "Enable lock-by-pulling on repository."
 
msgstr ""
 
msgstr "リポジトリのpullのロックを有効にします"
 

	
 
#: rhodecode/templates/admin/defaults/defaults.html:84
 
#: rhodecode/templates/admin/ldap/ldap.html:89
 
@@ -2076,20 +2067,19 @@ msgstr "リポジトリのキャッシュを無効化してもよろしいですか?"
 
msgid ""
 
"Manually invalidate cache for this repository. On first access repository"
 
" will be cached again"
 
msgstr ""
 
msgstr "このリポジトリのキャッシュを手動で無効化します。リポジトリへの初回アクセス時に再びキャッシュされます。"
 

	
 
#: rhodecode/templates/admin/repos/repo_edit.html:198
 
msgid "List of cached values"
 
msgstr ""
 
msgstr "キャッシュしている値の一覧"
 

	
 
#: rhodecode/templates/admin/repos/repo_edit.html:201
 
msgid "Prefix"
 
msgstr ""
 
msgstr "プレフィックス"
 

	
 
#: rhodecode/templates/admin/repos/repo_edit.html:202
 
#, fuzzy
 
msgid "Key"
 
msgstr "APIキー"
 
msgstr "キー"
 

	
 
#: rhodecode/templates/admin/repos/repo_edit.html:203
 
#: rhodecode/templates/admin/users/user_add.html:86
 
@@ -2146,7 +2136,7 @@ msgstr "リポジトリはロックされていません"
 

	
 
#: rhodecode/templates/admin/repos/repo_edit.html:252
 
msgid "Force locking on repository. Works only when anonymous access is disabled"
 
msgstr ""
 
msgstr "リポジトリを強制ロックします。匿名アクセスが無効になっている場合のみ動作します。"
 

	
 
#: rhodecode/templates/admin/repos/repo_edit.html:259
 
msgid "Set as fork of"
 
@@ -2173,14 +2163,13 @@ msgstr "このリポジトリを削除しますか?"
 

	
 
#: rhodecode/templates/admin/repos/repo_edit.html:282
 
#: rhodecode/templates/settings/repo_settings.html:119
 
#, fuzzy
 
msgid ""
 
"This repository will be renamed in a special way in order to be "
 
"unaccesible for RhodeCode and VCS systems. If you need fully delete it "
 
"from file system please do it manually"
 
msgstr ""
 
"このリポジトリはRhodeCodeとVCSシステムからアクセスされないような名前に、特別な方法で変更されます。\n"
 
"もし、ファイルシステムから完全に削除したい場合、手動で行ってください"
 
"このリポジトリはRhodeCodeとVCSシステムからアクセス出来ないようにするために特別な方法でリネームされます。\n"
 
"完全な削除が必要な場合はファイルシステムから手動で削除してください"
 

	
 
#: rhodecode/templates/admin/repos/repo_edit_perms.html:3
 
#: rhodecode/templates/admin/repos_groups/repos_group_edit_perms.html:3
 
@@ -2250,7 +2239,7 @@ msgstr "リポジトリ管理"
 

	
 
#: rhodecode/templates/admin/repos_groups/repos_group_edit_perms.html:73
 
msgid "apply to children"
 
msgstr ""
 
msgstr "子リポジトリにも適用"
 

	
 
#: rhodecode/templates/admin/repos_groups/repos_group_edit_perms.html:74
 
msgid ""
 
@@ -2356,10 +2345,10 @@ msgid "delete"
 
msgstr "削除"
 

	
 
#: rhodecode/templates/admin/repos_groups/repos_groups_show.html:55
 
#, fuzzy, python-format
 
#, python-format
 
msgid "Confirm to delete this group: %s with %s repository"
 
msgid_plural "Confirm to delete this group: %s with %s repositories"
 
msgstr[0] ""
 
msgstr[0] "このグループを削除してもよろしいですか?: %s %s リポジトリ"
 

	
 
#: rhodecode/templates/admin/repos_groups/repos_groups_show.html:63
 
msgid "There are no repositories groups yet"
 
@@ -2394,11 +2383,11 @@ msgstr "フックの削除に失敗しました"
 

	
 
#: rhodecode/templates/admin/settings/settings.html:24
 
msgid "Remap and rescan repositories"
 
msgstr ""
 
msgstr "リポジトリの再マッピングと再スキャン"
 

	
 
#: rhodecode/templates/admin/settings/settings.html:32
 
msgid "rescan option"
 
msgstr ""
 
msgstr "再スキャンオプション"
 

	
 
#: rhodecode/templates/admin/settings/settings.html:38
 
msgid ""
 
@@ -2464,13 +2453,12 @@ msgid "Visualisation settings"
 
msgstr "表示の設定"
 

	
 
#: rhodecode/templates/admin/settings/settings.html:127
 
#, fuzzy
 
msgid "General"
 
msgstr "有効にする"
 
msgstr "一般"
 

	
 
#: rhodecode/templates/admin/settings/settings.html:132
 
msgid "Use lightweight dashboard"
 
msgstr ""
 
msgstr "軽量ダッシュボードを使用"
 

	
 
#: rhodecode/templates/admin/settings/settings.html:139
 
msgid "Icons"
 
@@ -2508,7 +2496,7 @@ msgstr "VCSの操作にSSLを必須とする"
 
msgid ""
 
"RhodeCode will require SSL for pushing or pulling. If SSL is missing it "
 
"will return HTTP Error 406: Not Acceptable"
 
msgstr ""
 
msgstr "RhodeCodeはPushとPullにSSLを要求します。もしSSLでない場合、HTTP Error 406: Not Acceptalbeを返します"
 

	
 
#: rhodecode/templates/admin/settings/settings.html:209
 
msgid "Hooks"
 
@@ -2561,12 +2549,15 @@ msgid ""
 
"This a crucial application setting. If you are really sure you need to "
 
"change this, you must restart application in order to make this setting "
 
"take effect. Click this label to unlock."
 
msgstr "これはアプリケーションの重要な設定です。本当に変更が必要でしょうか。もし、変更した場合、変更を反映さ競るためにアプリケーションを再起動する必要があります。変更可能にするにはこのラベルをクリックして下さい"
 
msgstr ""
 
"これはアプリケーションの重要な設定です。本当に変更が必要でしょうか。"
 
"もし、変更した場合、変更を反映さ競るためにアプリケーションを再起動する必要があります。"
 
"アンロックにするにはこのラベルをクリックして下さい"
 

	
 
#: rhodecode/templates/admin/settings/settings.html:262
 
#: rhodecode/templates/base/base.html:221
 
msgid "unlock"
 
msgstr "変更可能にする"
 
msgstr "アンロック"
 

	
 
#: rhodecode/templates/admin/settings/settings.html:263
 
msgid ""
 
@@ -2869,19 +2860,16 @@ msgid "Group members"
 
msgstr "グループメンバー"
 

	
 
#: rhodecode/templates/admin/users_groups/users_group_edit.html:163
 
#, fuzzy
 
msgid "No members yet"
 
msgstr "メンバー"
 
msgstr "まだメンバーがいません"
 

	
 
#: rhodecode/templates/admin/users_groups/users_group_edit.html:171
 
#, fuzzy
 
msgid "Permissions defined for this group"
 
msgstr "権限管理"
 
msgstr "このリポジトリの権限設定"
 

	
 
#: rhodecode/templates/admin/users_groups/users_group_edit.html:178
 
#, fuzzy
 
msgid "No permissions set yet"
 
msgstr "権限のコピー"
 
msgstr "まだ権限設定がありません"
 

	
 
#: rhodecode/templates/admin/users_groups/users_groups.html:5
 
msgid "Users groups administration"
 
@@ -2992,9 +2980,8 @@ msgstr "オプション"
 

	
 
#: rhodecode/templates/base/base.html:204
 
#: rhodecode/templates/base/base.html:206
 
#, fuzzy
 
msgid "repository settings"
 
msgstr "リポジトリ作成"
 
msgstr "リポジトリ設定"
 

	
 
#: rhodecode/templates/base/base.html:210
 
#: rhodecode/templates/data_table/_dt_elements.html:80
 
@@ -3017,9 +3004,8 @@ msgid "search"
 
msgstr "検索"
 

	
 
#: rhodecode/templates/base/base.html:223
 
#, fuzzy
 
msgid "lock"
 
msgstr "変更可能にする"
 
msgstr "ロック"
 

	
 
#: rhodecode/templates/base/base.html:234
 
msgid "repositories groups"
 
@@ -3034,9 +3020,8 @@ msgid "permissions"
 
msgstr "権限"
 

	
 
#: rhodecode/templates/base/base.html:239
 
#, fuzzy
 
msgid "defaults"
 
msgstr "default"
 
msgstr "デフォルト設定"
 

	
 
#: rhodecode/templates/base/base.html:240
 
msgid "settings"
 
@@ -3087,13 +3072,12 @@ msgid "no matching files"
 
msgstr "マッチするファイルはありません"
 

	
 
#: rhodecode/templates/base/root.html:51
 
#, fuzzy
 
msgid "Open new pull request for selected changesets"
 
msgstr "新しいプルリクエストを作成"
 
msgstr "選択したチェンジセットから新しいプルリクエストを作成"
 

	
 
#: rhodecode/templates/base/root.html:52
 
msgid "Show selected changes __S -> __E"
 
msgstr ""
 
msgstr "選択した変更 __S -> __E を表示"
 

	
 
#: rhodecode/templates/base/root.html:53
 
msgid "Selection link"
 
@@ -3143,9 +3127,8 @@ msgid_plural "showing %d out of %d revis
 
msgstr[0] ""
 

	
 
#: rhodecode/templates/changelog/changelog.html:37
 
#, fuzzy
 
msgid "Clear selection"
 
msgstr "検索設定"
 
msgstr "選択を解除"
 

	
 
#: rhodecode/templates/changelog/changelog.html:40
 
#: rhodecode/templates/forks/forks_data.html:19
 
@@ -3154,9 +3137,8 @@ msgid "compare fork with %s"
 
msgstr "%s とフォークを比較"
 

	
 
#: rhodecode/templates/changelog/changelog.html:40
 
#, fuzzy
 
msgid "Compare fork with parent"
 
msgstr "%s とフォークを比較"
 
msgstr "フォークを比較"
 

	
 
#: rhodecode/templates/changelog/changelog.html:49
 
msgid "Show"
 
@@ -3259,7 +3241,7 @@ msgstr "チェンジセット"
 

	
 
#: rhodecode/templates/changeset/changeset.html:52
 
msgid "No children"
 
msgstr ""
 
msgstr "子リビジョンはありません"
 

	
 
#: rhodecode/templates/changeset/changeset.html:70
 
#: rhodecode/templates/changeset/diff_block.html:20
 
@@ -3267,9 +3249,8 @@ msgid "raw diff"
 
msgstr "差分を表示"
 

	
 
#: rhodecode/templates/changeset/changeset.html:71
 
#, fuzzy
 
msgid "patch diff"
 
msgstr "差分を表示"
 
msgstr "パッチとして差分を表示"
 

	
 
#: rhodecode/templates/changeset/changeset.html:72
 
#: rhodecode/templates/changeset/diff_block.html:21
 
@@ -3293,18 +3274,18 @@ msgstr[0] "(%d インライン)"
 
#: rhodecode/templates/changeset/changeset.html:122
 
#: rhodecode/templates/compare/compare_diff.html:44
 
#: rhodecode/templates/pullrequests/pullrequest_show.html:76
 
#, fuzzy, python-format
 
#, python-format
 
msgid "%s file changed"
 
msgid_plural "%s files changed"
 
msgstr[0] ""
 
msgstr[0] "%s ファイルに影響"
 

	
 
#: rhodecode/templates/changeset/changeset.html:124
 
#: rhodecode/templates/compare/compare_diff.html:46
 
#: rhodecode/templates/pullrequests/pullrequest_show.html:78
 
#, fuzzy, python-format
 
#, python-format
 
msgid "%s file changed with %s insertions and %s deletions"
 
msgid_plural "%s files changed with %s insertions and %s deletions"
 
msgstr[0] "%s ファイルに影響。 %s 個の追加と %s 個の削除:"
 
msgstr[0] "%s ファイルに影響。 %s 個の追加と %s 個の削除"
 

	
 
#: rhodecode/templates/changeset/changeset_file_comment.html:42
 
msgid "Submitting..."
 
@@ -3349,7 +3330,7 @@ msgstr "コメントを残す"
 

	
 
#: rhodecode/templates/changeset/changeset_file_comment.html:125
 
msgid "Check this to change current status of code-review for this changeset"
 
msgstr ""
 
msgstr "チェックするとチェンジセットの現在のコードレビューステータスを変更出来ます"
 

	
 
#: rhodecode/templates/changeset/changeset_file_comment.html:125
 
msgid "change status"
 
@@ -3370,9 +3351,8 @@ msgid "Compare View"
 
msgstr "比較ビュー"
 

	
 
#: rhodecode/templates/changeset/changeset_range.html:29
 
#, fuzzy
 
msgid "Show combined compare"
 
msgstr "インラインコメントを表示"
 
msgstr "結合した比較ビューを表示"
 

	
 
#: rhodecode/templates/changeset/changeset_range.html:54
 
msgid "Files affected"
 
@@ -3380,7 +3360,7 @@ msgstr "影響のあるファイル"
 

	
 
#: rhodecode/templates/changeset/diff_block.html:19
 
msgid "show full diff for this file"
 
msgstr ""
 
msgstr "このファイルの全差分を表示"
 

	
 
#: rhodecode/templates/changeset/diff_block.html:27
 
msgid "show inline comments"
 
@@ -3392,16 +3372,15 @@ msgstr "チェンジセットはありません"
 

	
 
#: rhodecode/templates/compare/compare_diff.html:37
 
#: rhodecode/templates/pullrequests/pullrequest_show.html:69
 
#, fuzzy, python-format
 
#, python-format
 
msgid "Showing %s commit"
 
msgid_plural "Showing %s commits"
 
msgstr[0] ""
 
msgstr[0] "%s コミットを表示"
 

	
 
#: rhodecode/templates/compare/compare_diff.html:52
 
#: rhodecode/templates/pullrequests/pullrequest_show.html:84
 
#, fuzzy
 
msgid "No files"
 
msgstr "ファイル"
 
msgstr "ファイルはありません"
 

	
 
#: rhodecode/templates/data_table/_dt_elements.html:39
 
#: rhodecode/templates/data_table/_dt_elements.html:41
 
@@ -3455,40 +3434,37 @@ msgid "Confirm to delete this user: %s"
 
msgstr "このユーザーを本当に削除してよろしいですか?: %s"
 

	
 
#: rhodecode/templates/email_templates/changeset_comment.html:10
 
#, fuzzy
 
msgid "New status$"
 
msgstr "ステータスを変更する"
 
msgstr "新しいステータス$"
 

	
 
#: rhodecode/templates/email_templates/main.html:8
 
#, fuzzy
 
msgid "This is a notification from RhodeCode."
 
msgstr "RhodeCodeからの通知があります"
 
msgstr "RhodeCodeからの通知です"
 

	
 
#: rhodecode/templates/email_templates/password_reset.html:4
 
msgid "Hello"
 
msgstr ""
 
msgstr "こんにちは"
 

	
 
#: rhodecode/templates/email_templates/password_reset.html:6
 
msgid "We received a request to create a new password for your account."
 
msgstr ""
 
msgstr "あなたのアカウントの新しいパスワードの生成リクエストを受け取りました。"
 

	
 
#: rhodecode/templates/email_templates/password_reset.html:8
 
msgid "You can generate it by clicking following URL"
 
msgstr ""
 
msgstr "下のURLをクリックすることで再生成が行えます。"
 

	
 
#: rhodecode/templates/email_templates/password_reset.html:12
 
msgid "If you didn't request new password please ignore this email."
 
msgstr ""
 
msgstr "新しいパスワードのリクエストをしていない場合は、このメールを無視して下さい。"
 

	
 
#: rhodecode/templates/email_templates/pull_request.html:4
 
#, python-format
 
msgid ""
 
"User %s opened pull request for repository %s and wants you to review "
 
"changes."
 
msgstr ""
 
msgstr "ユーザ %s がリポジトリ %s で新しいプルリクエストを作成しました。変更をレビューしてください。"
 

	
 
#: rhodecode/templates/email_templates/pull_request.html:5
 
#, fuzzy
 
msgid "title"
 
msgstr "タイトル"
 

	
 
@@ -3499,35 +3475,32 @@ msgstr "説明"
 

	
 
#: rhodecode/templates/email_templates/pull_request.html:11
 
msgid "revisions for reviewing"
 
msgstr ""
 
msgstr "レビュー対象のリビジョン"
 

	
 
#: rhodecode/templates/email_templates/pull_request.html:18
 
#, fuzzy
 
msgid "View this pull request here"
 
msgstr "このプルリクエストにレビュアーを追加"
 
msgstr "このプルリクエストを閲覧する"
 

	
 
#: rhodecode/templates/email_templates/pull_request_comment.html:4
 
#, fuzzy, python-format
 
#, python-format
 
msgid "User %s commented on pull request #%s for repository %s"
 
msgstr ""
 
msgstr "ユーザ %s がプルリクエスト #%s (リポジトリ %s) にコメントしました。"
 

	
 
#: rhodecode/templates/email_templates/pull_request_comment.html:10
 
#, fuzzy
 
msgid "New status"
 
msgstr "ステータスを変更する"
 
msgstr "新しいステータス"
 

	
 
#: rhodecode/templates/email_templates/pull_request_comment.html:14
 
msgid "View this comment here"
 
msgstr ""
 
msgstr "このコメントを閲覧する"
 

	
 
#: rhodecode/templates/email_templates/registration.html:4
 
#, fuzzy
 
msgid "A new user have registered in RhodeCode"
 
msgstr "rhodecodeへの登録を受け付けました"
 
msgstr "新しいユーザがRhodeCodeへ登録しました"
 

	
 
#: rhodecode/templates/email_templates/registration.html:9
 
msgid "View this user here"
 
msgstr ""
 
msgstr "このユーザを閲覧する"
 

	
 
#: rhodecode/templates/errors/error_document.html:46
 
#, python-format
 
@@ -3609,7 +3582,7 @@ msgstr "変更をコミット"
 

	
 
#: rhodecode/templates/files/files_browser.html:13
 
msgid "view"
 
msgstr "表示"
 
msgstr "閲覧"
 

	
 
#: rhodecode/templates/files/files_browser.html:14
 
msgid "previous revision"
 
@@ -3697,9 +3670,8 @@ msgid "show at revision"
 
msgstr "このリビジョンを見る"
 

	
 
#: rhodecode/templates/files/files_history_box.html:11
 
#, fuzzy
 
msgid "show full history"
 
msgstr "ファイル一覧を読み込み中..."
 
msgstr "すべての履歴を表示"
 

	
 
#: rhodecode/templates/files/files_history_box.html:16
 
#, python-format
 
@@ -3708,9 +3680,8 @@ msgid_plural "%s authors"
 
msgstr[0] "%s 作成者"
 

	
 
#: rhodecode/templates/files/files_source.html:6
 
#, fuzzy
 
msgid "Load file history"
 
msgstr "ファイル一覧を読み込み中..."
 
msgstr "ファイルの履歴を読み込む"
 

	
 
#: rhodecode/templates/files/files_source.html:21
 
msgid "show source"
 
@@ -3909,7 +3880,7 @@ msgstr "%s にクローズ"
 
#: rhodecode/templates/pullrequests/pullrequest_show.html:23
 
#, python-format
 
msgid "with status %s"
 
msgstr ""
 
msgstr "ステータス: %s"
 

	
 
#: rhodecode/templates/pullrequests/pullrequest_show.html:31
 
msgid "Status"
 
@@ -3930,9 +3901,8 @@ msgid_plural "%d reviewers"
 
msgstr[0] "%d レビュアー"
 

	
 
#: rhodecode/templates/pullrequests/pullrequest_show.html:50
 
#, fuzzy
 
msgid "pull request was reviewed by all reviewers"
 
msgstr "プルリクエストレビュアー"
 
msgstr "プルリクエストはすべてのレビュアーにレビューされました"
 

	
 
#: rhodecode/templates/pullrequests/pullrequest_show.html:58
 
msgid "Created on"
 
@@ -3943,9 +3913,8 @@ msgid "Compare view"
 
msgstr "比較ビュー"
 

	
 
#: rhodecode/templates/pullrequests/pullrequest_show.html:112
 
#, fuzzy
 
msgid "reviewer"
 
msgstr "%d レビュアー"
 
msgstr "レビュアー"
 

	
 
#: rhodecode/templates/pullrequests/pullrequest_show_all.html:4
 
msgid "all pull requests"
 
@@ -4012,12 +3981,10 @@ msgid "%s Settings"
 
msgstr "%s 設定"
 

	
 
#: rhodecode/templates/settings/repo_settings.html:102
 
#, fuzzy
 
msgid "Delete repository"
 
msgstr "リポジトリを[削除]"
 
msgstr "リポジトリを削除"
 

	
 
#: rhodecode/templates/settings/repo_settings.html:109
 
#, fuzzy
 
msgid "Remove repo"
 
msgstr "削除"
 

	
 
@@ -4080,19 +4047,18 @@ msgid "ATOM"
 
msgstr "ATOM"
 

	
 
#: rhodecode/templates/summary/summary.html:70
 
#, fuzzy, python-format
 
#, python-format
 
msgid "Repository locked by %s"
 
msgstr ""
 
msgstr "リポジトリは %s によってロックされました"
 

	
 
#: rhodecode/templates/summary/summary.html:72
 
#, fuzzy
 
msgid "Repository unlocked"
 
msgstr "リポジトリはロックされていません"
 

	
 
#: rhodecode/templates/summary/summary.html:91
 
#, python-format
 
msgid "Non changable ID %s"
 
msgstr ""
 
msgstr "変更不能ID %s"
 

	
 
#: rhodecode/templates/summary/summary.html:96
 
msgid "public"
 
@@ -4100,7 +4066,7 @@ msgstr "公開"
 

	
 
#: rhodecode/templates/summary/summary.html:104
 
msgid "remote clone"
 
msgstr ""
 
msgstr "リモートクローン"
 

	
 
#: rhodecode/templates/summary/summary.html:125
 
msgid "Contact"
 
@@ -4171,7 +4137,7 @@ msgstr "クイックスタート"
 
#: rhodecode/templates/summary/summary.html:243
 
#, python-format
 
msgid "Readme file at revision '%s'"
 
msgstr ""
 
msgstr "リビジョン '%s' のReadmeファイル"
 

	
 
#: rhodecode/templates/summary/summary.html:246
 
msgid "Permalink to this readme"
 
@@ -4220,9 +4186,8 @@ msgid "%s Tags"
 
msgstr "%s タグ"
 

	
 
#: rhodecode/templates/tags/tags.html:29
 
#, fuzzy
 
msgid "Compare tags"
 
msgstr "比較"
 
msgstr "タグの比較"
 

	
 
#~ msgid ""
 
#~ "%s repository is not mapped to db"
rhodecode/i18n/pl/LC_MESSAGES/rhodecode.po
Show inline comments
 
@@ -3,20 +3,21 @@
 
# This file is distributed under the same license as the rhodecode project.
 
# FIRST AUTHOR <EMAIL@ADDRESS>, 2010.
 
# Nemcio <bogdan114@g.pl>, 2012.
 
# Nemo <areczek01@gmail.com>, 2012.
 
# Nemo <areczek01@gmail.com>, 2012, 2013.
 
msgid ""
 
msgstr ""
 
"Project-Id-Version: rhodecode 0.1\n"
 
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
 
"POT-Creation-Date: 2012-12-14 04:19+0100\n"
 
"PO-Revision-Date: 2012-11-25 03:42+0200\n"
 
"Last-Translator: Nemo <areczek01@gmail.com>\n"
 
"PO-Revision-Date: 2013-01-18 18:12+0100\n"
 
"Last-Translator: Nemcio <bogdan114@g.pl>\n"
 
"Language-Team: Test\n"
 
"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && "
 
"(n%100<10 || n%100>=20) ? 1 : 2)\n"
 
"Language: pl\n"
 
"MIME-Version: 1.0\n"
 
"Content-Type: text/plain; charset=utf-8\n"
 
"Content-Transfer-Encoding: 8bit\n"
 
"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
 
"X-Generator: Virtaal 0.7.1\n"
 
"Generated-By: Babel 0.9.6\n"
 

	
 
#: rhodecode/controllers/changelog.py:95
 
@@ -27,7 +28,8 @@ msgstr "Wszystkie gałęzie"
 
msgid "show white space"
 
msgstr "pokazuj spacje"
 

	
 
#: rhodecode/controllers/changeset.py:90 rhodecode/controllers/changeset.py:97
 
#: rhodecode/controllers/changeset.py:90
 
#: rhodecode/controllers/changeset.py:97
 
msgid "ignore white space"
 
msgstr "ignoruj pokazywanie spacji"
 

	
 
@@ -43,12 +45,8 @@ msgid "Status change -> %s"
 
msgstr "Zmiana statusu -> %s"
 

	
 
#: rhodecode/controllers/changeset.py:345
 
msgid ""
 
"Changing status on a changeset associated witha closed pull request is "
 
"not allowed"
 
msgstr ""
 
"Zmiana statusu na grupy zmian powiązania łączy zamkniętego wniosku jest "
 
"niedozwolona"
 
msgid "Changing status on a changeset associated witha closed pull request is not allowed"
 
msgstr "Zmiana statusu na grupy zmian powiązania łączy zamkniętego wniosku jest niedozwolona"
 

	
 
#: rhodecode/controllers/compare.py:75
 
#: rhodecode/controllers/pullrequests.py:121
 
@@ -62,9 +60,7 @@ msgstr "Strona główna"
 

	
 
#: rhodecode/controllers/error.py:98
 
msgid "The request could not be understood by the server due to malformed syntax."
 
msgstr ""
 
"Wniosek nie może być rozumiany przez serwer z powodu zniekształconej "
 
"składni."
 
msgstr "Wniosek nie może być rozumiany przez serwer z powodu zniekształconej składni."
 

	
 
#: rhodecode/controllers/error.py:101
 
msgid "Unauthorized access to resource"
 
@@ -79,12 +75,8 @@ msgid "The resource could not be found"
 
msgstr "Zasób nie został znaleziony"
 

	
 
#: rhodecode/controllers/error.py:107
 
msgid ""
 
"The server encountered an unexpected condition which prevented it from "
 
"fulfilling the request."
 
msgstr ""
 
"Serwer napotkał niespodziewany warunek, który uniemożliwia jej spełnienie"
 
" żądania."
 
msgid "The server encountered an unexpected condition which prevented it from fulfilling the request."
 
msgstr "Serwer napotkał niespodziewany warunek, który uniemożliwia jej spełnienie żądania."
 

	
 
#: rhodecode/controllers/feed.py:52
 
#, python-format
 
@@ -119,7 +111,8 @@ msgstr "Kliknij tutaj, by dodać nowy plik"
 
msgid "There are no files yet %s"
 
msgstr "Brak plików %s"
 

	
 
#: rhodecode/controllers/files.py:265 rhodecode/controllers/files.py:325
 
#: rhodecode/controllers/files.py:265
 
#: rhodecode/controllers/files.py:325
 
#, python-format
 
msgid "This repository is has been locked by %s on %s"
 
msgstr "Repozytorium zostało zablokowane przez %s na %s"
 
@@ -133,12 +126,14 @@ msgstr "Edytowanie %s w RhodeCode"
 
msgid "No changes"
 
msgstr "Bez zmian"
 

	
 
#: rhodecode/controllers/files.py:308 rhodecode/controllers/files.py:372
 
#: rhodecode/controllers/files.py:308
 
#: rhodecode/controllers/files.py:372
 
#, python-format
 
msgid "Successfully committed to %s"
 
msgstr "Committ wykonany do %s"
 

	
 
#: rhodecode/controllers/files.py:313 rhodecode/controllers/files.py:378
 
#: rhodecode/controllers/files.py:313
 
#: rhodecode/controllers/files.py:378
 
msgid "Error occurred during commit"
 
msgstr "Wystąpił błąd w trakcie zatwierdzania"
 

	
 
@@ -178,13 +173,17 @@ msgstr "Nieznany typ archiwum"
 
msgid "Changesets"
 
msgstr "Różnice"
 

	
 
#: rhodecode/controllers/files.py:565 rhodecode/controllers/pullrequests.py:74
 
#: rhodecode/controllers/summary.py:236 rhodecode/model/scm.py:550
 
#: rhodecode/controllers/files.py:565
 
#: rhodecode/controllers/pullrequests.py:74
 
#: rhodecode/controllers/summary.py:236
 
#: rhodecode/model/scm.py:550
 
msgid "Branches"
 
msgstr "Gałęzie"
 

	
 
#: rhodecode/controllers/files.py:566 rhodecode/controllers/pullrequests.py:78
 
#: rhodecode/controllers/summary.py:237 rhodecode/model/scm.py:561
 
#: rhodecode/controllers/files.py:566
 
#: rhodecode/controllers/pullrequests.py:78
 
#: rhodecode/controllers/summary.py:237
 
#: rhodecode/model/scm.py:561
 
msgid "Tags"
 
msgstr "Etykiety"
 

	
 
@@ -198,11 +197,13 @@ msgstr "gałęzi %s w repozytorium %s"
 
msgid "An error occurred during repository forking %s"
 
msgstr "Wystąpił błąd podczas rozgałęzienia %s repozytorium"
 

	
 
#: rhodecode/controllers/journal.py:218 rhodecode/controllers/journal.py:261
 
#: rhodecode/controllers/journal.py:218
 
#: rhodecode/controllers/journal.py:261
 
msgid "public journal"
 
msgstr "Dziennik publiczny"
 

	
 
#: rhodecode/controllers/journal.py:222 rhodecode/controllers/journal.py:265
 
#: rhodecode/controllers/journal.py:222
 
#: rhodecode/controllers/journal.py:265
 
#: rhodecode/templates/base/base.html:232
 
#: rhodecode/templates/journal/journal.html:12
 
msgid "journal"
 
@@ -217,12 +218,11 @@ msgid "Your password reset link was sent
 
msgstr "Twój link zresetowania hasła został wysłany"
 

	
 
#: rhodecode/controllers/login.py:184
 
msgid ""
 
"Your password reset was successful, new password has been sent to your "
 
"email"
 
msgid "Your password reset was successful, new password has been sent to your email"
 
msgstr "Twoje hasło zostało zresetowane, nowe hasło zostanie wysłane na e-mail"
 

	
 
#: rhodecode/controllers/pullrequests.py:76 rhodecode/model/scm.py:556
 
#: rhodecode/controllers/pullrequests.py:76
 
#: rhodecode/model/scm.py:556
 
msgid "Bookmarks"
 
msgstr "Zakładki"
 

	
 
@@ -247,8 +247,9 @@ msgid "Successfully deleted pull request
 
msgstr "Prośba o skasowanie połączenia gałęzi została wykonana prawidłowo"
 

	
 
#: rhodecode/controllers/pullrequests.py:452
 
#, fuzzy
 
msgid "Closing pull request on other statuses than rejected or approved forbidden"
 
msgstr ""
 
msgstr "Zamknij wszystkie wnioski połączenia gałęzi innych stanów niż odrzucony, zatwierdzony lub zabroniony"
 

	
 
#: rhodecode/controllers/search.py:134
 
msgid "Invalid search query. Try quoting it."
 
@@ -315,14 +316,12 @@ msgid "Statistics are disabled for this 
 
msgstr "Statystyki są wyłączone dla tego repozytorium"
 

	
 
#: rhodecode/controllers/admin/defaults.py:96
 
#, fuzzy
 
msgid "Default settings updated successfully"
 
msgstr "Ustawienia LDAP zostały zaktualizowane"
 
msgstr "Domyślne ustawienia zostały pomyślnie zaktualizowane"
 

	
 
#: rhodecode/controllers/admin/defaults.py:110
 
#, fuzzy
 
msgid "error occurred during update of defaults"
 
msgstr "wystąpił błąd podczas aktualizacji użytkownika %s"
 
msgstr "wystąpił błąd podczas aktualizacji wartości domyślnych"
 

	
 
#: rhodecode/controllers/admin/ldap_settings.py:50
 
msgid "BASE"
 
@@ -500,7 +499,8 @@ msgstr "Zaktualizowano widoczność stron w publicznym dzienniku"
 
msgid "An error occurred during setting this repository in public journal"
 
msgstr "Wystąpił błąd podczas ustawiania tego repozytorium w dzienniku publicznym"
 

	
 
#: rhodecode/controllers/admin/repos.py:452 rhodecode/model/validators.py:300
 
#: rhodecode/controllers/admin/repos.py:452
 
#: rhodecode/model/validators.py:300
 
msgid "Token mismatch"
 
msgstr "Niezgodność tokenu"
 

	
 
@@ -576,9 +576,7 @@ msgstr "Wystąpił błąd podczas usuwania grup i grup użytkowników"
 
#: rhodecode/controllers/admin/settings.py:123
 
#, python-format
 
msgid "Repositories successfully rescanned added: %s,removed: %s"
 
msgstr ""
 
"Repozytoria z powodzeniem zostały ponownie zeskanowane dodano: %s, "
 
"usunięto: %s"
 
msgstr "Repozytoria z powodzeniem zostały ponownie zeskanowane dodano: %s, usunięto: %s"
 

	
 
#: rhodecode/controllers/admin/settings.py:131
 
msgid "Whoosh reindex task scheduled"
 
@@ -623,9 +621,7 @@ msgstr "E-mail został wysłany"
 

	
 
#: rhodecode/controllers/admin/settings.py:399
 
msgid "You can't edit this user since it's crucial for entire application"
 
msgstr ""
 
"Nie możesz edytować tego użytkownika ponieważ jest kluczowy dla całej "
 
"aplikacji"
 
msgstr "Nie możesz edytować tego użytkownika ponieważ jest kluczowy dla całej aplikacji"
 

	
 
#: rhodecode/controllers/admin/settings.py:430
 
msgid "Your account was updated successfully"
 
@@ -755,9 +751,7 @@ msgstr "plik binarny"
 

	
 
#: rhodecode/lib/diffs.py:90
 
msgid "Changeset was too big and was cut off, use diff menu to display this diff"
 
msgstr ""
 
"Lista zmian była zbyt duża i została obcięta, użyj menu porównań żeby "
 
"wyświetlić różnice"
 
msgstr "Lista zmian była zbyt duża i została obcięta, użyj menu porównań żeby wyświetlić różnice"
 

	
 
#: rhodecode/lib/diffs.py:100
 
msgid "No changes detected"
 
@@ -808,7 +802,8 @@ msgstr "i"
 
msgid "%s more"
 
msgstr "%s więcej"
 

	
 
#: rhodecode/lib/helpers.py:617 rhodecode/templates/changelog/changelog.html:51
 
#: rhodecode/lib/helpers.py:617
 
#: rhodecode/templates/changelog/changelog.html:51
 
msgid "revisions"
 
msgstr "rewizja"
 

	
 
@@ -828,7 +823,8 @@ msgstr "Połączonych gałęzi #%s"
 
msgid "[deleted] repository"
 
msgstr "[usunięte] repozytorium"
 

	
 
#: rhodecode/lib/helpers.py:666 rhodecode/lib/helpers.py:676
 
#: rhodecode/lib/helpers.py:666
 
#: rhodecode/lib/helpers.py:676
 
msgid "[created] repository"
 
msgstr "[utworzone] repozytorium"
 

	
 
@@ -836,11 +832,13 @@ msgstr "[utworzone] repozytorium"
 
msgid "[created] repository as fork"
 
msgstr "[utworzone] repozytorium jako rozgałęzienie"
 

	
 
#: rhodecode/lib/helpers.py:670 rhodecode/lib/helpers.py:678
 
#: rhodecode/lib/helpers.py:670
 
#: rhodecode/lib/helpers.py:678
 
msgid "[forked] repository"
 
msgstr "[rozgałęzione] repozytorium"
 

	
 
#: rhodecode/lib/helpers.py:672 rhodecode/lib/helpers.py:680
 
#: rhodecode/lib/helpers.py:672
 
#: rhodecode/lib/helpers.py:680
 
msgid "[updated] repository"
 
msgstr "[zaktualizowane] repozytorium"
 

	
 
@@ -911,14 +909,8 @@ msgstr "Brak Plików"
 

	
 
#: rhodecode/lib/helpers.py:1163
 
#, python-format
 
msgid ""
 
"%s repository is not mapped to db perhaps it was created or renamed from "
 
"the filesystem please run the application again in order to rescan "
 
"repositories"
 
msgstr ""
 
"%s repozytorium nie jest mapowane do db może zostało utworzone lub "
 
"zmienione z systemie plików proszę uruchomić aplikację ponownie, aby "
 
"ponownie przeskanować repozytoria"
 
msgid "%s repository is not mapped to db perhaps it was created or renamed from the filesystem please run the application again in order to rescan repositories"
 
msgstr "%s repozytorium nie jest mapowane do db może zostało utworzone lub zmienione z systemie plików proszę uruchomić aplikację ponownie, aby ponownie przeskanować repozytoria"
 

	
 
#: rhodecode/lib/utils2.py:403
 
#, python-format
 
@@ -996,83 +988,103 @@ msgstr "przed chwilą"
 
msgid "password reset link"
 
msgstr "łącze resetowania hasła"
 

	
 
#: rhodecode/lib/dbmigrate/schema/db_1_4_0.py:1163 rhodecode/model/db.py:1183
 
#: rhodecode/lib/dbmigrate/schema/db_1_4_0.py:1163
 
#: rhodecode/model/db.py:1183
 
msgid "Repository no access"
 
msgstr "Brak dostępu do repozytorium"
 

	
 
#: rhodecode/lib/dbmigrate/schema/db_1_4_0.py:1164 rhodecode/model/db.py:1184
 
#: rhodecode/lib/dbmigrate/schema/db_1_4_0.py:1164
 
#: rhodecode/model/db.py:1184
 
msgid "Repository read access"
 
msgstr "Repozytorium do odczytu"
 

	
 
#: rhodecode/lib/dbmigrate/schema/db_1_4_0.py:1165 rhodecode/model/db.py:1185
 
#: rhodecode/lib/dbmigrate/schema/db_1_4_0.py:1165
 
#: rhodecode/model/db.py:1185
 
msgid "Repository write access"
 
msgstr "Repozytorium do zapisu"
 

	
 
#: rhodecode/lib/dbmigrate/schema/db_1_4_0.py:1166 rhodecode/model/db.py:1186
 
#: rhodecode/lib/dbmigrate/schema/db_1_4_0.py:1166
 
#: rhodecode/model/db.py:1186
 
msgid "Repository admin access"
 
msgstr "Administracja dostępu do repozytorium"
 

	
 
#: rhodecode/lib/dbmigrate/schema/db_1_4_0.py:1168 rhodecode/model/db.py:1188
 
#: rhodecode/lib/dbmigrate/schema/db_1_4_0.py:1168
 
#: rhodecode/model/db.py:1188
 
msgid "Repositories Group no access"
 
msgstr "Grupy repozytoriów brak dostępu"
 

	
 
#: rhodecode/lib/dbmigrate/schema/db_1_4_0.py:1169 rhodecode/model/db.py:1189
 
#: rhodecode/lib/dbmigrate/schema/db_1_4_0.py:1169
 
#: rhodecode/model/db.py:1189
 
msgid "Repositories Group read access"
 
msgstr "Grupy repozytoriów dostęp do odczytu"
 

	
 
#: rhodecode/lib/dbmigrate/schema/db_1_4_0.py:1170 rhodecode/model/db.py:1190
 
#: rhodecode/lib/dbmigrate/schema/db_1_4_0.py:1170
 
#: rhodecode/model/db.py:1190
 
msgid "Repositories Group write access"
 
msgstr "Grupy repozytoriów dostęp do zapisu"
 

	
 
#: rhodecode/lib/dbmigrate/schema/db_1_4_0.py:1171 rhodecode/model/db.py:1191
 
#: rhodecode/lib/dbmigrate/schema/db_1_4_0.py:1171
 
#: rhodecode/model/db.py:1191
 
msgid "Repositories Group admin access"
 
msgstr "Repozytoria Grupy dostęp administratora"
 

	
 
#: rhodecode/lib/dbmigrate/schema/db_1_4_0.py:1173 rhodecode/model/db.py:1193
 
#: rhodecode/lib/dbmigrate/schema/db_1_4_0.py:1173
 
#: rhodecode/model/db.py:1193
 
msgid "RhodeCode Administrator"
 
msgstr "Administrator Repo"
 

	
 
#: rhodecode/lib/dbmigrate/schema/db_1_4_0.py:1174 rhodecode/model/db.py:1194
 
#: rhodecode/lib/dbmigrate/schema/db_1_4_0.py:1174
 
#: rhodecode/model/db.py:1194
 
msgid "Repository creation disabled"
 
msgstr "Repozytorium wyłączone"
 

	
 
#: rhodecode/lib/dbmigrate/schema/db_1_4_0.py:1175 rhodecode/model/db.py:1195
 
#: rhodecode/lib/dbmigrate/schema/db_1_4_0.py:1175
 
#: rhodecode/model/db.py:1195
 
msgid "Repository creation enabled"
 
msgstr "Repozytorium włączone"
 

	
 
#: rhodecode/lib/dbmigrate/schema/db_1_4_0.py:1176 rhodecode/model/db.py:1196
 
#: rhodecode/lib/dbmigrate/schema/db_1_4_0.py:1176
 
#: rhodecode/model/db.py:1196
 
msgid "Repository forking disabled"
 
msgstr "Rozwidlenie repozytorium wyłączone"
 

	
 
#: rhodecode/lib/dbmigrate/schema/db_1_4_0.py:1177 rhodecode/model/db.py:1197
 
#: rhodecode/lib/dbmigrate/schema/db_1_4_0.py:1177
 
#: rhodecode/model/db.py:1197
 
msgid "Repository forking enabled"
 
msgstr "Rozwidlenie repozytorium włączone"
 

	
 
#: rhodecode/lib/dbmigrate/schema/db_1_4_0.py:1178 rhodecode/model/db.py:1198
 
#: rhodecode/lib/dbmigrate/schema/db_1_4_0.py:1178
 
#: rhodecode/model/db.py:1198
 
msgid "Register disabled"
 
msgstr "Rejestracja wyłączona"
 

	
 
#: rhodecode/lib/dbmigrate/schema/db_1_4_0.py:1179 rhodecode/model/db.py:1199
 
#: rhodecode/lib/dbmigrate/schema/db_1_4_0.py:1179
 
#: rhodecode/model/db.py:1199
 
msgid "Register new user with RhodeCode with manual activation"
 
msgstr "Rejestracja nowego użytkownika na stronie z ręczną aktywacją"
 

	
 
#: rhodecode/lib/dbmigrate/schema/db_1_4_0.py:1182 rhodecode/model/db.py:1202
 
#: rhodecode/lib/dbmigrate/schema/db_1_4_0.py:1182
 
#: rhodecode/model/db.py:1202
 
msgid "Register new user with RhodeCode with auto activation"
 
msgstr "Rejestracja nowego użytkownika na stronie z automatyczną aktywacją"
 

	
 
#: rhodecode/lib/dbmigrate/schema/db_1_4_0.py:1623 rhodecode/model/db.py:1643
 
#: rhodecode/lib/dbmigrate/schema/db_1_4_0.py:1623
 
#: rhodecode/model/db.py:1643
 
msgid "Not Reviewed"
 
msgstr "Brak Korekty"
 

	
 
#: rhodecode/lib/dbmigrate/schema/db_1_4_0.py:1624 rhodecode/model/db.py:1644
 
#: rhodecode/lib/dbmigrate/schema/db_1_4_0.py:1624
 
#: rhodecode/model/db.py:1644
 
msgid "Approved"
 
msgstr "Zaakceptowano"
 

	
 
#: rhodecode/lib/dbmigrate/schema/db_1_4_0.py:1625 rhodecode/model/db.py:1645
 
#: rhodecode/lib/dbmigrate/schema/db_1_4_0.py:1625
 
#: rhodecode/model/db.py:1645
 
msgid "Rejected"
 
msgstr "Odrzucono"
 

	
 
#: rhodecode/lib/dbmigrate/schema/db_1_4_0.py:1626 rhodecode/model/db.py:1646
 
#: rhodecode/lib/dbmigrate/schema/db_1_4_0.py:1626
 
#: rhodecode/model/db.py:1646
 
msgid "Under Review"
 
msgstr "Objęty Przeglądem"
 

	
 
@@ -1146,29 +1158,23 @@ msgstr "ostatni tip"
 
msgid "new user registration"
 
msgstr "nowy użytkownik się zarejestrował"
 

	
 
#: rhodecode/model/user.py:257 rhodecode/model/user.py:281
 
#: rhodecode/model/user.py:257
 
#: rhodecode/model/user.py:281
 
#: rhodecode/model/user.py:303
 
msgid "You can't Edit this user since it's crucial for entire application"
 
msgstr ""
 
"Nie możesz edytować tego użytkownika ponieważ jest kluczowy dla całej "
 
"aplikacji"
 
msgstr "Nie możesz edytować tego użytkownika ponieważ jest kluczowy dla całej aplikacji"
 

	
 
#: rhodecode/model/user.py:327
 
msgid "You can't remove this user since it's crucial for entire application"
 
msgstr ""
 
"Nie możesz usunąć tego użytkownika ponieważ jest kluczowy dla całej "
 
"aplikacji"
 
msgstr "Nie możesz usunąć tego użytkownika ponieważ jest kluczowy dla całej aplikacji"
 

	
 
#: rhodecode/model/user.py:333
 
#, python-format
 
msgid ""
 
"user \"%s\" still owns %s repositories and cannot be removed. Switch "
 
"owners or remove those repositories. %s"
 
msgstr ""
 
"użytkownik \"%s\" wciąż posiada repozytoria następujące %s i nie może "
 
"zostać usunięty. Zmień właściciela lub usuń te repozytoria. %s"
 

	
 
#: rhodecode/model/validators.py:36 rhodecode/model/validators.py:37
 
msgid "user \"%s\" still owns %s repositories and cannot be removed. Switch owners or remove those repositories. %s"
 
msgstr "użytkownik \"%s\" wciąż posiada repozytoria następujące %s i nie może zostać usunięty. Zmień właściciela lub usuń te repozytoria. %s"
 

	
 
#: rhodecode/model/validators.py:36
 
#: rhodecode/model/validators.py:37
 
msgid "Value cannot be an empty list"
 
msgstr "Wartość listy nie może być pusta"
 

	
 
@@ -1183,12 +1189,8 @@ msgid "Username \"%(username)s\" is forb
 
msgstr "Nazwa użytkownika \"%(username)s\" jest zabroniona"
 

	
 
#: rhodecode/model/validators.py:87
 
msgid ""
 
"Username may only contain alphanumeric characters underscores, periods or"
 
" dashes and must begin with alphanumeric character"
 
msgstr ""
 
"Nazwa użytkownika może zawierać tylko znaki alfanumeryczne, podkreślenia,"
 
" kropki lub myślniki i musi zaczynać się znakiem alfanumerycznym"
 
msgid "Username may only contain alphanumeric characters underscores, periods or dashes and must begin with alphanumeric character"
 
msgstr "Nazwa użytkownika może zawierać tylko znaki alfanumeryczne, podkreślenia, kropki lub myślniki i musi zaczynać się znakiem alfanumerycznym"
 

	
 
#: rhodecode/model/validators.py:115
 
#, python-format
 
@@ -1205,12 +1207,8 @@ msgid "Users group \"%(usersgroup)s\" al
 
msgstr "Nazwa grupy \"%(usersgroup)s\" już istnieje"
 

	
 
#: rhodecode/model/validators.py:137
 
msgid ""
 
"users group name may only contain  alphanumeric characters underscores, "
 
"periods or dashes and must begin with alphanumeric character"
 
msgstr ""
 
"Nazwa grupy może zawierać tylko znaki alfanumeryczne, podkreślenia, "
 
"kropki lub myślniki i musi zaczynać się znakiem alfanumerycznym"
 
msgid "users group name may only contain  alphanumeric characters underscores, periods or dashes and must begin with alphanumeric character"
 
msgstr "Nazwa grupy może zawierać tylko znaki alfanumeryczne, podkreślenia, kropki lub myślniki i musi zaczynać się znakiem alfanumerycznym"
 

	
 
#: rhodecode/model/validators.py:175
 
msgid "Cannot assign this group as parent"
 
@@ -1300,12 +1298,8 @@ msgid "e-mail \"%(email)s\" does not exi
 
msgstr "e-mail \"%(email)s\" nie istnieje."
 

	
 
#: rhodecode/model/validators.py:663
 
msgid ""
 
"The LDAP Login attribute of the CN must be specified - this is the name "
 
"of the attribute that is equivalent to \"username\""
 
msgstr ""
 
"Atrybut logowania CN do LDAP należy określić, jest to nazwa atrybutu, "
 
"który jest odpowiednikiem  \"username\""
 
msgid "The LDAP Login attribute of the CN must be specified - this is the name of the attribute that is equivalent to \"username\""
 
msgstr "Atrybut logowania CN do LDAP należy określić, jest to nazwa atrybutu, który jest odpowiednikiem  \"username\""
 

	
 
#: rhodecode/model/validators.py:682
 
#, python-format
 
@@ -1498,7 +1492,8 @@ msgstr "Błąd danych."
 
msgid "Loading..."
 
msgstr "Wczytywanie..."
 

	
 
#: rhodecode/templates/login.html:5 rhodecode/templates/login.html:54
 
#: rhodecode/templates/login.html:5
 
#: rhodecode/templates/login.html:54
 
msgid "Sign In"
 
msgstr "Zaloguj się"
 

	
 
@@ -1506,7 +1501,8 @@ msgstr "Zaloguj się"
 
msgid "Sign In to"
 
msgstr "Zarejestruj się"
 

	
 
#: rhodecode/templates/login.html:31 rhodecode/templates/register.html:20
 
#: rhodecode/templates/login.html:31
 
#: rhodecode/templates/register.html:20
 
#: rhodecode/templates/admin/admin_log.html:5
 
#: rhodecode/templates/admin/users/user_add.html:32
 
#: rhodecode/templates/admin/users/user_edit.html:50
 
@@ -1516,7 +1512,8 @@ msgstr "Zarejestruj się"
 
msgid "Username"
 
msgstr "Nazwa użytkownika"
 

	
 
#: rhodecode/templates/login.html:40 rhodecode/templates/register.html:29
 
#: rhodecode/templates/login.html:40
 
#: rhodecode/templates/register.html:29
 
#: rhodecode/templates/admin/ldap/ldap.html:46
 
#: rhodecode/templates/admin/users/user_add.html:41
 
#: rhodecode/templates/base/base.html:92
 
@@ -1531,7 +1528,8 @@ msgstr "Zapamiętaj mnie"
 
msgid "Forgot your password ?"
 
msgstr "Zapomniałeś hasła?"
 

	
 
#: rhodecode/templates/login.html:63 rhodecode/templates/base/base.html:103
 
#: rhodecode/templates/login.html:63
 
#: rhodecode/templates/base/base.html:103
 
msgid "Don't have an account ?"
 
msgstr "Nie masz konta?"
 

	
 
@@ -1555,7 +1553,8 @@ msgstr "Zresetuj swoje hasło"
 
msgid "Password reset link will be send to matching email address"
 
msgstr "Link do zresetowania hasła zostanie wysłany na adres e-mail"
 

	
 
#: rhodecode/templates/register.html:5 rhodecode/templates/register.html:74
 
#: rhodecode/templates/register.html:5
 
#: rhodecode/templates/register.html:74
 
msgid "Sign Up"
 
msgstr "Zarejestruj się"
 

	
 
@@ -1646,24 +1645,22 @@ msgid "Admin journal"
 
msgstr "Dziennik administratora"
 

	
 
#: rhodecode/templates/admin/admin.html:10
 
#, fuzzy
 
msgid "journal filter..."
 
msgstr "szybki filtr..."
 
msgstr "szybkie wyszukiwanie..."
 

	
 
#: rhodecode/templates/admin/admin.html:12
 
#: rhodecode/templates/journal/journal.html:11
 
#, fuzzy
 
msgid "filter"
 
msgstr "pliki"
 
msgstr "filtr"
 

	
 
#: rhodecode/templates/admin/admin.html:13
 
#: rhodecode/templates/journal/journal.html:12
 
#, python-format
 
msgid "%s entry"
 
msgid_plural "%s entries"
 
msgstr[0] ""
 
msgstr[1] ""
 
msgstr[2] ""
 
msgstr[0] "%s wejście"
 
msgstr[1] "%s wejść"
 
msgstr[2] "%s wejść"
 

	
 
#: rhodecode/templates/admin/admin_log.html:6
 
#: rhodecode/templates/admin/repos/repos.html:74
 
@@ -1699,14 +1696,12 @@ msgstr "Brak akcji"
 

	
 
#: rhodecode/templates/admin/defaults/defaults.html:5
 
#: rhodecode/templates/admin/defaults/defaults.html:25
 
#, fuzzy
 
msgid "Repositories defaults"
 
msgstr "grupy w repozytorium"
 
msgstr "Repozytoria domyślne"
 

	
 
#: rhodecode/templates/admin/defaults/defaults.html:11
 
#, fuzzy
 
msgid "Defaults"
 
msgstr "domyślne"
 
msgstr "Domyślne"
 

	
 
#: rhodecode/templates/admin/defaults/defaults.html:35
 
#: rhodecode/templates/admin/repos/repo_add_base.html:38
 
@@ -1719,12 +1714,8 @@ msgstr "Typ"
 
#: rhodecode/templates/admin/repos/repo_edit.html:89
 
#: rhodecode/templates/forks/fork.html:72
 
#: rhodecode/templates/settings/repo_settings.html:80
 
msgid ""
 
"Private repositories are only visible to people explicitly added as "
 
"collaborators."
 
msgstr ""
 
"Prywatne repozytoria są widoczne tylko dla osób bezpośrednio dodanych "
 
"jako współpracownicy."
 
msgid "Private repositories are only visible to people explicitly added as collaborators."
 
msgstr "Prywatne repozytoria są widoczne tylko dla osób bezpośrednio dodanych jako współpracownicy."
 

	
 
#: rhodecode/templates/admin/defaults/defaults.html:55
 
#: rhodecode/templates/admin/repos/repo_edit.html:94
 
@@ -1900,14 +1891,8 @@ msgid "Anonymous access"
 
msgstr "Dostęp anonimowy"
 

	
 
#: rhodecode/templates/admin/permissions/permissions.html:49
 
msgid ""
 
"All default permissions on each repository will be reset to choosen "
 
"permission, note that all custom default permission on repositories will "
 
"be lost"
 
msgstr ""
 
"Wszystkie uprawnienia domyślne każdego repozytorium zostaną przywrócone. "
 
"Wybrane uprawnienie zostaną skasowane. Pamiętaj, że wszystkie "
 
"niestandardowe uprawnienia w repozytoriach zostaną utracone."
 
msgid "All default permissions on each repository will be reset to choosen permission, note that all custom default permission on repositories will be lost"
 
msgstr "Wszystkie uprawnienia domyślne każdego repozytorium zostaną przywrócone. Wybrane uprawnienie zostaną skasowane. Pamiętaj, że wszystkie niestandardowe uprawnienia w repozytoriach zostaną utracone."
 

	
 
#: rhodecode/templates/admin/permissions/permissions.html:50
 
#: rhodecode/templates/admin/permissions/permissions.html:63
 
@@ -1924,14 +1909,8 @@ msgid "Repository group"
 
msgstr "Repozytorium grupy"
 

	
 
#: rhodecode/templates/admin/permissions/permissions.html:62
 
msgid ""
 
"All default permissions on each repository group will be reset to choosen"
 
" permission, note that all custom default permission on repositories "
 
"group will be lost"
 
msgstr ""
 
"Wszystkie uprawnienia domyślne każdego repozytorium zostaną przywrócone. "
 
"Wybrane uprawnienie zostaną skasowane. Pamiętaj, że wszystkie "
 
"niestandardowe uprawnienia w repozytoriach zostaną utracone."
 
msgid "All default permissions on each repository group will be reset to choosen permission, note that all custom default permission on repositories group will be lost"
 
msgstr "Wszystkie uprawnienia domyślne każdego repozytorium zostaną przywrócone. Wybrane uprawnienie zostaną skasowane. Pamiętaj, że wszystkie niestandardowe uprawnienia w repozytoriach zostaną utracone."
 

	
 
#: rhodecode/templates/admin/permissions/permissions.html:69
 
msgid "Registration"
 
@@ -2112,12 +2091,8 @@ msgid "Confirm to invalidate repository 
 
msgstr "Potwierdź unieważnienie pamięci podręcznej repozytorium"
 

	
 
#: rhodecode/templates/admin/repos/repo_edit.html:193
 
msgid ""
 
"Manually invalidate cache for this repository. On first access repository"
 
" will be cached again"
 
msgstr ""
 
"Ręcznie unieważnienie cache dla tego repozytorium. Przy pierwszym "
 
"dostępie do repozytorium zostanie dodany do bufora ponownie"
 
msgid "Manually invalidate cache for this repository. On first access repository will be cached again"
 
msgstr "Ręcznie unieważnienie cache dla tego repozytorium. Przy pierwszym dostępie do repozytorium zostanie dodany do bufora ponownie"
 

	
 
#: rhodecode/templates/admin/repos/repo_edit.html:198
 
msgid "List of cached values"
 
@@ -2125,12 +2100,11 @@ msgstr "Lista buforowanych wartości"
 

	
 
#: rhodecode/templates/admin/repos/repo_edit.html:201
 
msgid "Prefix"
 
msgstr ""
 
msgstr "Prefiks"
 

	
 
#: rhodecode/templates/admin/repos/repo_edit.html:202
 
#, fuzzy
 
msgid "Key"
 
msgstr "Klucz API"
 
msgstr "Klucz"
 

	
 
#: rhodecode/templates/admin/repos/repo_edit.html:203
 
#: rhodecode/templates/admin/users/user_add.html:86
 
@@ -2156,12 +2130,8 @@ msgid "Add to public journal"
 
msgstr "Dodaj do dziennika publicznego"
 

	
 
#: rhodecode/templates/admin/repos/repo_edit.html:231
 
msgid ""
 
"All actions made on this repository will be accessible to everyone in "
 
"public journal"
 
msgstr ""
 
"Wszystkie działania wykonywane na tym repozytorium będą dostępne dla "
 
"wszystkich w dzienniku publicznym"
 
msgid "All actions made on this repository will be accessible to everyone in public journal"
 
msgstr "Wszystkie działania wykonywane na tym repozytorium będą dostępne dla wszystkich w dzienniku publicznym"
 

	
 
#: rhodecode/templates/admin/repos/repo_edit.html:238
 
msgid "Locking"
 
@@ -2189,9 +2159,7 @@ msgstr "Repozytorium nie jest zablokowan
 

	
 
#: rhodecode/templates/admin/repos/repo_edit.html:252
 
msgid "Force locking on repository. Works only when anonymous access is disabled"
 
msgstr ""
 
"Wymuś blokowanie na repozytorium. Działa tylko wtedy, gdy dostęp "
 
"anonimowy jest wyłączony"
 
msgstr "Wymuś blokowanie na repozytorium. Działa tylko wtedy, gdy dostęp anonimowy jest wyłączony"
 

	
 
#: rhodecode/templates/admin/repos/repo_edit.html:259
 
msgid "Set as fork of"
 
@@ -2218,15 +2186,8 @@ msgstr "Potwierdź, aby usunąć repozytorium"
 

	
 
#: rhodecode/templates/admin/repos/repo_edit.html:282
 
#: rhodecode/templates/settings/repo_settings.html:119
 
#, fuzzy
 
msgid ""
 
"This repository will be renamed in a special way in order to be "
 
"unaccesible for RhodeCode and VCS systems. If you need fully delete it "
 
"from file system please do it manually"
 
msgstr ""
 
"To repozytorium zostanie zmienione w sposób szczególny, żeby było "
 
"niedostępne dla strony i systemów VCS. Jeśli chcesz całkowicie usunąć go "
 
"z systemu plików prosimy zrobić to ręcznie"
 
msgid "This repository will be renamed in a special way in order to be unaccesible for RhodeCode and VCS systems. If you need fully delete it from file system please do it manually"
 
msgstr "To repozytorium zostanie zmienione w sposób szczególny, żeby było niedostępne dla strony i systemów VCS. Jeśli chcesz całkowicie usunąć go z systemu plików prosimy zrobić to ręcznie"
 

	
 
#: rhodecode/templates/admin/repos/repo_edit_perms.html:3
 
#: rhodecode/templates/admin/repos_groups/repos_group_edit_perms.html:3
 
@@ -2295,14 +2256,14 @@ msgid "Repositories administration"
 
msgstr "Administracja repozytoriami"
 

	
 
#: rhodecode/templates/admin/repos_groups/repos_group_edit_perms.html:73
 
#, fuzzy
 
msgid "apply to children"
 
msgstr ""
 
msgstr "dotyczy dzieci"
 

	
 
#: rhodecode/templates/admin/repos_groups/repos_group_edit_perms.html:74
 
msgid ""
 
"Set or revoke permission to all children of that group, including "
 
"repositories and other groups"
 
msgstr ""
 
#, fuzzy
 
msgid "Set or revoke permission to all children of that group, including repositories and other groups"
 
msgstr "Ustawia lub cofa uprawnienia do wszystkich dzieci z tej grupy, w tym repozytoria oraz innych grup"
 

	
 
#: rhodecode/templates/admin/repos_groups/repos_groups.html:9
 
#: rhodecode/templates/base/base.html:122
 
@@ -2320,7 +2281,8 @@ msgstr ""
 
#: rhodecode/templates/files/files_add.html:15
 
#: rhodecode/templates/files/files_edit.html:15
 
#: rhodecode/templates/followers/followers.html:9
 
#: rhodecode/templates/forks/fork.html:9 rhodecode/templates/forks/forks.html:9
 
#: rhodecode/templates/forks/fork.html:9
 
#: rhodecode/templates/forks/forks.html:9
 
#: rhodecode/templates/pullrequests/pullrequest.html:8
 
#: rhodecode/templates/pullrequests/pullrequest_show.html:8
 
#: rhodecode/templates/pullrequests/pullrequest_show_all.html:8
 
@@ -2370,12 +2332,8 @@ msgid "edit repos group"
 
msgstr "edytuj grupy repo"
 

	
 
#: rhodecode/templates/admin/repos_groups/repos_groups_edit.html:70
 
msgid ""
 
"Enable lock-by-pulling on group. This option will be applied to all other"
 
" groups and repositories inside"
 
msgstr ""
 
"Włącz blokowanie pulling przez grupy. Opcja ta będzie stosowana do "
 
"wszystkich innych grup i repozytoriów wewnątrz"
 
msgid "Enable lock-by-pulling on group. This option will be applied to all other groups and repositories inside"
 
msgstr "Włącz blokowanie pulling przez grupy. Opcja ta będzie stosowana do wszystkich innych grup i repozytoriów wewnątrz"
 

	
 
#: rhodecode/templates/admin/repos_groups/repos_groups_show.html:5
 
msgid "Repositories groups administration"
 
@@ -2404,12 +2362,12 @@ msgid "delete"
 
msgstr "usuń"
 

	
 
#: rhodecode/templates/admin/repos_groups/repos_groups_show.html:55
 
#, fuzzy, python-format
 
#, python-format
 
msgid "Confirm to delete this group: %s with %s repository"
 
msgid_plural "Confirm to delete this group: %s with %s repositories"
 
msgstr[0] "Potwierdz aby usunąć grupę %s wraz z %s repozytorium"
 
msgstr[1] "Potwierdz aby usunąć grupę %s wraz z %s repozytoriami"
 
msgstr[2] "Potwierdz aby usunąć grupę %s wraz z %s repozytoriami"
 
msgstr[0] "Potwierdź żeby usunąć grupę %s wraz z %s repozytorium"
 
msgstr[1] "Potwierdź żeby usunąć grupę %s wraz z %s repozytoriami"
 
msgstr[2] "Potwierdź żeby usunąć grupę %s wraz z %s repozytoriami"
 

	
 
#: rhodecode/templates/admin/repos_groups/repos_groups_show.html:63
 
msgid "There are no repositories groups yet"
 
@@ -2451,26 +2409,16 @@ msgid "rescan option"
 
msgstr "ponowne skanowanie opcji"
 

	
 
#: rhodecode/templates/admin/settings/settings.html:38
 
msgid ""
 
"In case a repository was deleted from filesystem and there are leftovers "
 
"in the database check this option to scan obsolete data in database and "
 
"remove it."
 
msgstr ""
 
"W przypadku repozytoriów zostaną usunięte systemy plików i jeśli są "
 
"pozostałości w bazie danych to ta opcja sprawdzi ją oraz przeskanuje, a "
 
"następnie usunie je z bazy danych."
 
msgid "In case a repository was deleted from filesystem and there are leftovers in the database check this option to scan obsolete data in database and remove it."
 
msgstr "W przypadku repozytoriów zostaną usunięte systemy plików i jeśli są pozostałości w bazie danych to ta opcja sprawdzi ją oraz przeskanuje, a następnie usunie je z bazy danych."
 

	
 
#: rhodecode/templates/admin/settings/settings.html:39
 
msgid "destroy old data"
 
msgstr "zniszcz stare dane"
 

	
 
#: rhodecode/templates/admin/settings/settings.html:41
 
msgid ""
 
"Rescan repositories location for new repositories. Also deletes obsolete "
 
"if `destroy` flag is checked "
 
msgstr ""
 
"Skanowanie ponowne lokalizacji dla nowych repozytoriów. Usuwa również "
 
"nieaktualne jeśli została zaznaczona flaga `zniszcz` do sprawdzana"
 
msgid "Rescan repositories location for new repositories. Also deletes obsolete if `destroy` flag is checked "
 
msgstr "Skanowanie ponowne lokalizacji dla nowych repozytoriów. Usuwa również nieaktualne jeśli została zaznaczona flaga `zniszcz` do sprawdzana"
 

	
 
#: rhodecode/templates/admin/settings/settings.html:46
 
msgid "Rescan repositories"
 
@@ -2519,9 +2467,8 @@ msgid "Visualisation settings"
 
msgstr "Ustawienia wizualizacji"
 

	
 
#: rhodecode/templates/admin/settings/settings.html:127
 
#, fuzzy
 
msgid "General"
 
msgstr "włącz"
 
msgstr "Główne"
 

	
 
#: rhodecode/templates/admin/settings/settings.html:132
 
msgid "Use lightweight dashboard"
 
@@ -2560,12 +2507,8 @@ msgid "require ssl for vcs operations"
 
msgstr "wymagaj ssl dla operacji vcs"
 

	
 
#: rhodecode/templates/admin/settings/settings.html:203
 
msgid ""
 
"RhodeCode will require SSL for pushing or pulling. If SSL is missing it "
 
"will return HTTP Error 406: Not Acceptable"
 
msgstr ""
 
"RhodeCode wymaga SSL do wysłania zmian lub pobierania. Jeśli brakuje SSL "
 
"zwróci błąd HTTP 406: Not Acceptable"
 
msgid "RhodeCode will require SSL for pushing or pulling. If SSL is missing it will return HTTP Error 406: Not Acceptable"
 
msgstr "RhodeCode wymaga SSL do wysłania zmian lub pobierania. Jeśli brakuje SSL zwróci błąd HTTP 406: Not Acceptable"
 

	
 
#: rhodecode/templates/admin/settings/settings.html:209
 
msgid "Hooks"
 
@@ -2604,26 +2547,16 @@ msgid "hgsubversion extensions"
 
msgstr "rozszerzenia hgsubversion"
 

	
 
#: rhodecode/templates/admin/settings/settings.html:246
 
msgid ""
 
"Requires hgsubversion library installed. Allows clonning from svn remote "
 
"locations"
 
msgstr ""
 
"Wymaga biblioteki hgsubversion zainstalowanej. Umożliwia klonowanie z "
 
"zdalnych lokalizacji svn"
 
msgid "Requires hgsubversion library installed. Allows clonning from svn remote locations"
 
msgstr "Wymaga biblioteki hgsubversion zainstalowanej. Umożliwia klonowanie z zdalnych lokalizacji svn"
 

	
 
#: rhodecode/templates/admin/settings/settings.html:256
 
msgid "Repositories location"
 
msgstr "Położenie repozytorium"
 

	
 
#: rhodecode/templates/admin/settings/settings.html:261
 
msgid ""
 
"This a crucial application setting. If you are really sure you need to "
 
"change this, you must restart application in order to make this setting "
 
"take effect. Click this label to unlock."
 
msgstr ""
 
"To kluczowe ustawienia aplikacji. Jeśli jesteś pewny, że chcesz to "
 
"zmienić, należy ponownie uruchomić aplikację w celu zaktualizowania "
 
"lokalizacji. Kliknij tą etykietę, żeby odblokować."
 
msgid "This a crucial application setting. If you are really sure you need to change this, you must restart application in order to make this setting take effect. Click this label to unlock."
 
msgstr "To kluczowe ustawienia aplikacji. Jeśli jesteś pewny, że chcesz to zmienić, należy ponownie uruchomić aplikację w celu zaktualizowania lokalizacji. Kliknij tą etykietę, żeby odblokować."
 

	
 
#: rhodecode/templates/admin/settings/settings.html:262
 
#: rhodecode/templates/base/base.html:221
 
@@ -2631,12 +2564,8 @@ msgid "unlock"
 
msgstr "odblokowany"
 

	
 
#: rhodecode/templates/admin/settings/settings.html:263
 
msgid ""
 
"Location where repositories are stored. After changing this value a "
 
"restart, and rescan is required"
 
msgstr ""
 
"Miejsce, w którym przechowywane są repozytoria. Po zmianie tej wartości "
 
"jest wymagany restart i ponowne skanowanie"
 
msgid "Location where repositories are stored. After changing this value a restart, and rescan is required"
 
msgstr "Miejsce, w którym przechowywane są repozytoria. Po zmianie tej wartości jest wymagany restart i ponowne skanowanie"
 

	
 
#: rhodecode/templates/admin/settings/settings.html:283
 
msgid "Test Email"
 
@@ -2716,12 +2645,8 @@ msgstr "Dziedziczą uprawnienia domyślne"
 
#: rhodecode/templates/admin/users/user_edit.html:156
 
#: rhodecode/templates/admin/users_groups/users_group_edit.html:113
 
#, python-format
 
msgid ""
 
"Select to inherit permissions from %s settings. With this selected below "
 
"options does not have any action"
 
msgstr ""
 
"Zaznacz, żeby dziedziczyć uprawnienia z %s ustawień. Po wybraniu tej "
 
"opcji, poniżej nie ma żadnych działań"
 
msgid "Select to inherit permissions from %s settings. With this selected below options does not have any action"
 
msgstr "Zaznacz, żeby dziedziczyć uprawnienia z %s ustawień. Po wybraniu tej opcji, poniżej nie ma żadnych działań"
 

	
 
#: rhodecode/templates/admin/users/user_edit.html:162
 
#: rhodecode/templates/admin/users_groups/users_group_edit.html:119
 
@@ -3010,7 +2935,8 @@ msgid "Products"
 
msgstr "Produkty"
 

	
 
#: rhodecode/templates/base/base.html:152
 
#: rhodecode/templates/base/base.html:182 rhodecode/templates/base/root.html:47
 
#: rhodecode/templates/base/base.html:182
 
#: rhodecode/templates/base/root.html:47
 
msgid "loading..."
 
msgstr "wczytywanie..."
 

	
 
@@ -3064,7 +2990,8 @@ msgstr "ustawienia repozytorium"
 
msgid "fork"
 
msgstr "gałąż"
 

	
 
#: rhodecode/templates/base/base.html:212 rhodecode/templates/base/root.html:50
 
#: rhodecode/templates/base/base.html:212
 
#: rhodecode/templates/base/root.html:50
 
#: rhodecode/templates/changelog/changelog.html:43
 
msgid "Open new pull request"
 
msgstr "Otwórz nową prośbę o połączenie gałęzi"
 
@@ -3095,7 +3022,6 @@ msgid "permissions"
 
msgstr "uprawnienia"
 

	
 
#: rhodecode/templates/base/base.html:239
 
#, fuzzy
 
msgid "defaults"
 
msgstr "domyślne"
 

	
 
@@ -3215,9 +3141,8 @@ msgid "compare fork with %s"
 
msgstr "porównaj gałęzie %s"
 

	
 
#: rhodecode/templates/changelog/changelog.html:40
 
#, fuzzy
 
msgid "Compare fork with parent"
 
msgstr "porównaj fork w rodzicem"
 
msgstr "porównaj gałąź w rodzicem"
 

	
 
#: rhodecode/templates/changelog/changelog.html:49
 
msgid "Show"
 
@@ -3319,8 +3244,9 @@ msgid "Changeset"
 
msgstr "Grupy zmian"
 

	
 
#: rhodecode/templates/changeset/changeset.html:52
 
#, fuzzy
 
msgid "No children"
 
msgstr ""
 
msgstr "Brak dzieci"
 

	
 
#: rhodecode/templates/changeset/changeset.html:70
 
#: rhodecode/templates/changeset/diff_block.html:20
 
@@ -3357,22 +3283,22 @@ msgstr[2] "(%d linii)"
 
#: rhodecode/templates/changeset/changeset.html:122
 
#: rhodecode/templates/compare/compare_diff.html:44
 
#: rhodecode/templates/pullrequests/pullrequest_show.html:76
 
#, fuzzy, python-format
 
#, python-format
 
msgid "%s file changed"
 
msgid_plural "%s files changed"
 
msgstr[0] "%s plik zmieniony"
 
msgstr[1] "%s plików zmienionych"
 
msgstr[2] "%s plików zmienionych"
 
msgstr[0] "%s plik został zmieniony"
 
msgstr[1] "%s pliki zostały zmienione"
 
msgstr[2] "%s plików zostało zmienionych"
 

	
 
#: rhodecode/templates/changeset/changeset.html:124
 
#: rhodecode/templates/compare/compare_diff.html:46
 
#: rhodecode/templates/pullrequests/pullrequest_show.html:78
 
#, fuzzy, python-format
 
#, python-format
 
msgid "%s file changed with %s insertions and %s deletions"
 
msgid_plural "%s files changed with %s insertions and %s deletions"
 
msgstr[0] "%s plik zmieniony z %s inserjcami i %s usunieciami"
 
msgstr[1] "%s plików zmienionych z %s inserjcami i %s usunieciami"
 
msgstr[2] "%s plików zmienionych z %s inserjcami i %s usunieciami"
 
msgstr[0] "%s plik został zmieniony z %s inercjami i %s usunięciami"
 
msgstr[1] "%s plików zostało zmienionych z %s inercjami i %s usunięciami"
 
msgstr[2] "%s plików zostało zmienionych z %s inercjami i %s usunięciami"
 

	
 
#: rhodecode/templates/changeset/changeset_file_comment.html:42
 
msgid "Submitting..."
 
@@ -3391,9 +3317,7 @@ msgstr "Komentarze analizowane za pomocą %s składni od %s wsparcia."
 
#: rhodecode/templates/changeset/changeset_file_comment.html:48
 
#: rhodecode/templates/changeset/changeset_file_comment.html:123
 
msgid "Use @username inside this text to send notification to this RhodeCode user"
 
msgstr ""
 
"Użyj @username wewnątrz tego tekstu, aby wysłać powiadomienie do "
 
"użytkownika strony"
 
msgstr "Użyj @username wewnątrz tego tekstu, aby wysłać powiadomienie do użytkownika strony"
 

	
 
#: rhodecode/templates/changeset/changeset_file_comment.html:59
 
#: rhodecode/templates/changeset/changeset_file_comment.html:143
 
@@ -3440,9 +3364,8 @@ msgid "Compare View"
 
msgstr "Wyświetl Porównanie"
 

	
 
#: rhodecode/templates/changeset/changeset_range.html:29
 
#, fuzzy
 
msgid "Show combined compare"
 
msgstr "pokaż online komentarz"
 
msgstr "Pokaż połączone porównaj"
 

	
 
#: rhodecode/templates/changeset/changeset_range.html:54
 
msgid "Files affected"
 
@@ -3462,18 +3385,17 @@ msgstr "Brak zestawienia zmian"
 

	
 
#: rhodecode/templates/compare/compare_diff.html:37
 
#: rhodecode/templates/pullrequests/pullrequest_show.html:69
 
#, fuzzy, python-format
 
#, python-format
 
msgid "Showing %s commit"
 
msgid_plural "Showing %s commits"
 
msgstr[0] "Wyswietlane %s commit"
 
msgstr[1] "Wyswietlane %s commits"
 
msgstr[2] "Wyswietlane %s commits"
 
msgstr[0] "Pokaż %s komentarz"
 
msgstr[1] "Pokaż %s komentarze"
 
msgstr[2] "Pokaż %s komentarze"
 

	
 
#: rhodecode/templates/compare/compare_diff.html:52
 
#: rhodecode/templates/pullrequests/pullrequest_show.html:84
 
#, fuzzy
 
msgid "No files"
 
msgstr "pliki"
 
msgstr "Brak plików"
 

	
 
#: rhodecode/templates/data_table/_dt_elements.html:39
 
#: rhodecode/templates/data_table/_dt_elements.html:41
 
@@ -3527,42 +3449,37 @@ msgid "Confirm to delete this user: %s"
 
msgstr "Potwierdź usunięcie tego użytkownika: %s"
 

	
 
#: rhodecode/templates/email_templates/changeset_comment.html:10
 
#, fuzzy
 
msgid "New status$"
 
msgstr "zmień status"
 
msgstr "Nowy status$"
 

	
 
#: rhodecode/templates/email_templates/main.html:8
 
#, fuzzy
 
msgid "This is a notification from RhodeCode."
 
msgstr "To jest powiadomienie z strony"
 

	
 
#: rhodecode/templates/email_templates/password_reset.html:4
 
msgid "Hello"
 
msgstr ""
 
msgstr "Witaj"
 

	
 
#: rhodecode/templates/email_templates/password_reset.html:6
 
msgid "We received a request to create a new password for your account."
 
msgstr ""
 
msgstr "Otrzymaliśmy prośbę o utworzenie nowego hasła do twojego konta."
 

	
 
#: rhodecode/templates/email_templates/password_reset.html:8
 
msgid "You can generate it by clicking following URL"
 
msgstr ""
 
msgstr "Możesz wygenerować nowe hasło klikając w link URL poniżej:"
 

	
 
#: rhodecode/templates/email_templates/password_reset.html:12
 
msgid "If you didn't request new password please ignore this email."
 
msgstr ""
 
msgstr "Jeśli nie chcesz wygenerować nowego hasła to zignoruj tą wiadomość."
 

	
 
#: rhodecode/templates/email_templates/pull_request.html:4
 
#, python-format
 
msgid ""
 
"User %s opened pull request for repository %s and wants you to review "
 
"changes."
 
msgstr ""
 
msgid "User %s opened pull request for repository %s and wants you to review changes."
 
msgstr "Użytkownik %s zgłosił wniosek połączenia w repozytorium %s i chce żeby sprawdzić zmiany."
 

	
 
#: rhodecode/templates/email_templates/pull_request.html:5
 
#, fuzzy
 
msgid "title"
 
msgstr "Tytuł"
 
msgstr "tytuł"
 

	
 
#: rhodecode/templates/email_templates/pull_request.html:6
 
#: rhodecode/templates/pullrequests/pullrequest.html:115
 
@@ -3571,37 +3488,32 @@ msgstr "opis"
 

	
 
#: rhodecode/templates/email_templates/pull_request.html:11
 
msgid "revisions for reviewing"
 
msgstr ""
 
msgstr "korekty dotyczące rewizji"
 

	
 
#: rhodecode/templates/email_templates/pull_request.html:18
 
#, fuzzy
 
msgid "View this pull request here"
 
msgstr "Pokarz wszystkie zmiany"
 
msgstr "Wyświetl prośby pobrania tutaj"
 

	
 
#: rhodecode/templates/email_templates/pull_request_comment.html:4
 
#, fuzzy, python-format
 
#, python-format
 
msgid "User %s commented on pull request #%s for repository %s"
 
msgstr ""
 
"Użytkownik %s skomentował wniosek o połączenie gałęzi #%s dla "
 
"repozytorium %s"
 
msgstr "Użytkownik %s skomentował wniosek o połączenie gałęzi #%s dla repozytorium %s"
 

	
 
#: rhodecode/templates/email_templates/pull_request_comment.html:10
 
#, fuzzy
 
msgid "New status"
 
msgstr "zmień status"
 
msgstr "Nowy status"
 

	
 
#: rhodecode/templates/email_templates/pull_request_comment.html:14
 
msgid "View this comment here"
 
msgstr ""
 
msgstr "Zobacz ten komentarz tutaj"
 

	
 
#: rhodecode/templates/email_templates/registration.html:4
 
#, fuzzy
 
msgid "A new user have registered in RhodeCode"
 
msgstr "Udało Ci się zarejestrować na stronie"
 
msgstr "Nowy użytkownik został zarejestrowany na stronie"
 

	
 
#: rhodecode/templates/email_templates/registration.html:9
 
msgid "View this user here"
 
msgstr ""
 
msgstr "Zobacz tego użytkownika tutaj"
 

	
 
#: rhodecode/templates/errors/error_document.html:46
 
#, python-format
 
@@ -3771,9 +3683,8 @@ msgid "show at revision"
 
msgstr "wskaż zmiany"
 

	
 
#: rhodecode/templates/files/files_history_box.html:11
 
#, fuzzy
 
msgid "show full history"
 
msgstr "Wczytywanie listy plików..."
 
msgstr "pokaż pełną historię"
 

	
 
#: rhodecode/templates/files/files_history_box.html:16
 
#, python-format
 
@@ -3784,9 +3695,8 @@ msgstr[1] "%s autorzy"
 
msgstr[2] "%s autorzy"
 

	
 
#: rhodecode/templates/files/files_source.html:6
 
#, fuzzy
 
msgid "Load file history"
 
msgstr "Wczytywanie listy plików..."
 
msgstr "Załaduj historię pliku"
 

	
 
#: rhodecode/templates/files/files_source.html:21
 
msgid "show source"
 
@@ -4020,9 +3930,8 @@ msgid "Compare view"
 
msgstr "Wyświetl porównanie"
 

	
 
#: rhodecode/templates/pullrequests/pullrequest_show.html:112
 
#, fuzzy
 
msgid "reviewer"
 
msgstr "%d recenzent"
 
msgstr "recenzent"
 

	
 
#: rhodecode/templates/pullrequests/pullrequest_show_all.html:4
 
msgid "all pull requests"
 
@@ -4089,14 +3998,12 @@ msgid "%s Settings"
 
msgstr "Ustawienia %s"
 

	
 
#: rhodecode/templates/settings/repo_settings.html:102
 
#, fuzzy
 
msgid "Delete repository"
 
msgstr "[skasowane] repozytorium"
 
msgstr "Usuń repozytorium"
 

	
 
#: rhodecode/templates/settings/repo_settings.html:109
 
#, fuzzy
 
msgid "Remove repo"
 
msgstr "usuń"
 
msgstr "Usuń repo"
 

	
 
#: rhodecode/templates/shortlog/shortlog.html:5
 
#, python-format
 
@@ -4296,9 +4203,8 @@ msgid "%s Tags"
 
msgstr "Etykiety pliku %s"
 

	
 
#: rhodecode/templates/tags/tags.html:29
 
#, fuzzy
 
msgid "Compare tags"
 
msgstr "porównanie"
 
msgstr "Porównaj tagi"
 

	
 
#~ msgid ""
 
#~ "%s repository is not mapped to db"
 
@@ -4325,4 +4231,3 @@ msgstr "porównanie"
 
#~ "zmienione w systemie plików proszę "
 
#~ "uruchomić aplikację ponownie, aby ponownie "
 
#~ "przeskanować repozytoria"
 

	
rhodecode/i18n/zh_CN/LC_MESSAGES/rhodecode.mo
Show inline comments
 
binary diff not shown
rhodecode/i18n/zh_CN/LC_MESSAGES/rhodecode.po
Show inline comments
 
@@ -8,15 +8,16 @@ msgid ""
 
msgstr ""
 
"Project-Id-Version: RhodeCode 1.4.4\n"
 
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
 
"POT-Creation-Date: 2012-12-14 04:19+0100\n"
 
"PO-Revision-Date: 2012-11-26 15:19+0800\n"
 
"POT-Creation-Date: 2012-12-14 12:53+0800\n"
 
"PO-Revision-Date: 2012-12-14 12:57+0800\n"
 
"Last-Translator: xpol <xpolife@gmail.com>\n"
 
"Language-Team: mikespook\n"
 
"Plural-Forms: nplurals=1; plural=0\n"
 
"Plural-Forms: nplurals=1; plural=0;\n"
 
"MIME-Version: 1.0\n"
 
"Content-Type: text/plain; charset=utf-8\n"
 
"Content-Transfer-Encoding: 8bit\n"
 
"Generated-By: Babel 0.9.6\n"
 
"X-Generator: Poedit 1.5.4\n"
 

	
 
#: rhodecode/controllers/changelog.py:95
 
msgid "All Branches"
 
@@ -43,8 +44,8 @@ msgstr "状态修改为%s"
 

	
 
#: rhodecode/controllers/changeset.py:345
 
msgid ""
 
"Changing status on a changeset associated witha closed pull request is "
 
"not allowed"
 
"Changing status on a changeset associated witha closed pull request is not "
 
"allowed"
 
msgstr "不允许修改已关闭拉取请求的修订集状态"
 

	
 
#: rhodecode/controllers/compare.py:75
 
@@ -58,7 +59,8 @@ msgid "Home page"
 
msgstr "主页"
 

	
 
#: rhodecode/controllers/error.py:98
 
msgid "The request could not be understood by the server due to malformed syntax."
 
msgid ""
 
"The request could not be understood by the server due to malformed syntax."
 
msgstr "由于错误的语法,服务器无法对请求进行响应。"
 

	
 
#: rhodecode/controllers/error.py:101
 
@@ -211,8 +213,7 @@ msgstr "密码重置链接已经发送"
 

	
 
#: rhodecode/controllers/login.py:184
 
msgid ""
 
"Your password reset was successful, new password has been sent to your "
 
"email"
 
"Your password reset was successful, new password has been sent to your email"
 
msgstr "密码已经成功重置,新密码已经发送到你的邮箱"
 

	
 
#: rhodecode/controllers/pullrequests.py:76 rhodecode/model/scm.py:556
 
@@ -240,8 +241,9 @@ msgid "Successfully deleted pull request
 
msgstr "成功删除拉取请求"
 

	
 
#: rhodecode/controllers/pullrequests.py:452
 
msgid "Closing pull request on other statuses than rejected or approved forbidden"
 
msgstr ""
 
msgid ""
 
"Closing pull request on other statuses than rejected or approved forbidden"
 
msgstr "只能以批准或者驳回的状态关闭拉取请求"
 

	
 
#: rhodecode/controllers/search.py:134
 
msgid "Invalid search query. Try quoting it."
 
@@ -308,14 +310,12 @@ msgid "Statistics are disabled for this 
 
msgstr "该版本库统计功能已经禁用"
 

	
 
#: rhodecode/controllers/admin/defaults.py:96
 
#, fuzzy
 
msgid "Default settings updated successfully"
 
msgstr "LDAP设置已经成功更新"
 
msgstr "默认设置已经成功更新"
 

	
 
#: rhodecode/controllers/admin/defaults.py:110
 
#, fuzzy
 
msgid "error occurred during update of defaults"
 
msgstr "更新用户%s时发生错误"
 
msgstr "更新默认设置时发生错误"
 

	
 
#: rhodecode/controllers/admin/ldap_settings.py:50
 
msgid "BASE"
 
@@ -654,11 +654,11 @@ msgstr "无法编辑该用户"
 

	
 
#: rhodecode/controllers/admin/users.py:272
 
msgid "Granted 'repository create' permission to user"
 
msgstr "已授予用户创建版本库的权限"
 
msgstr "已授予用户创建版本库的权限"
 

	
 
#: rhodecode/controllers/admin/users.py:277
 
msgid "Revoked 'repository create' permission to user"
 
msgstr "已撤销用户创建版本库的权限"
 
msgstr "已撤销用户创建版本库的权限"
 

	
 
#: rhodecode/controllers/admin/users.py:283
 
msgid "Granted 'repository fork' permission to user"
 
@@ -716,19 +716,19 @@ msgstr "删除用户组时发生错误"
 

	
 
#: rhodecode/controllers/admin/users_groups.py:257
 
msgid "Granted 'repository create' permission to users group"
 
msgstr "已授予用户组创建版本库的权限"
 
msgstr "已授予用户组创建版本库的权限"
 

	
 
#: rhodecode/controllers/admin/users_groups.py:262
 
msgid "Revoked 'repository create' permission to users group"
 
msgstr "已撤销用户组创建版本库的权限"
 
msgstr "已撤销用户组创建版本库的权限"
 

	
 
#: rhodecode/controllers/admin/users_groups.py:268
 
msgid "Granted 'repository fork' permission to users group"
 
msgstr "已授予用户组复刻版本库的权限"
 
msgstr "已授予用户组复刻版本库的权限"
 

	
 
#: rhodecode/controllers/admin/users_groups.py:273
 
msgid "Revoked 'repository fork' permission to users group"
 
msgstr "已撤销用户组复刻版本库的权限"
 
msgstr "已撤销用户组复刻版本库的权限"
 

	
 
#: rhodecode/lib/auth.py:499
 
msgid "You need to be a registered user to perform this action"
 
@@ -743,7 +743,8 @@ msgid "binary file"
 
msgstr "二进制文件"
 

	
 
#: rhodecode/lib/diffs.py:90
 
msgid "Changeset was too big and was cut off, use diff menu to display this diff"
 
msgid ""
 
"Changeset was too big and was cut off, use diff menu to display this diff"
 
msgstr "修订集因过大而被截断,可查看原始修订集作为替代"
 

	
 
#: rhodecode/lib/diffs.py:100
 
@@ -795,7 +796,8 @@ msgstr "还有"
 
msgid "%s more"
 
msgstr "%s个"
 

	
 
#: rhodecode/lib/helpers.py:617 rhodecode/templates/changelog/changelog.html:51
 
#: rhodecode/lib/helpers.py:617
 
#: rhodecode/templates/changelog/changelog.html:51
 
msgid "revisions"
 
msgstr "修订"
 

	
 
@@ -899,10 +901,11 @@ msgstr "没有文件"
 
#: rhodecode/lib/helpers.py:1163
 
#, python-format
 
msgid ""
 
"%s repository is not mapped to db perhaps it was created or renamed from "
 
"the filesystem please run the application again in order to rescan "
 
"repositories"
 
msgstr "版本库%s没有映射到数据库,可能是从文件系统创建或者重命名,请重启RhodeCode以重新扫描版本库"
 
"%s repository is not mapped to db perhaps it was created or renamed from the "
 
"filesystem please run the application again in order to rescan repositories"
 
msgstr ""
 
"版本库%s没有映射到数据库,可能是从文件系统创建或者重命名,请重启RhodeCode以重"
 
"新扫描版本库"
 

	
 
#: rhodecode/lib/utils2.py:403
 
#, python-format
 
@@ -1130,9 +1133,10 @@ msgstr "由于是系统帐号,无法删除该用户"
 
#: rhodecode/model/user.py:333
 
#, python-format
 
msgid ""
 
"user \"%s\" still owns %s repositories and cannot be removed. Switch "
 
"owners or remove those repositories. %s"
 
msgstr "由于用户 \"%s\" 拥有版本库%s因而无法删除,请修改版本库所有者或删除版本库。%s"
 
"user \"%s\" still owns %s repositories and cannot be removed. Switch owners "
 
"or remove those repositories. %s"
 
msgstr ""
 
"由于用户 \"%s\" 拥有版本库%s因而无法删除,请修改版本库所有者或删除版本库。%s"
 

	
 
#: rhodecode/model/validators.py:36 rhodecode/model/validators.py:37
 
msgid "Value cannot be an empty list"
 
@@ -1150,9 +1154,10 @@ msgstr "不允许用户名 \"%(username)s\""
 

	
 
#: rhodecode/model/validators.py:87
 
msgid ""
 
"Username may only contain alphanumeric characters underscores, periods or"
 
" dashes and must begin with alphanumeric character"
 
msgstr "只能使用字母、数字、下划线、小数点或减号作为用户名,且必须由数字或字母开头"
 
"Username may only contain alphanumeric characters underscores, periods or "
 
"dashes and must begin with alphanumeric character"
 
msgstr ""
 
"只能使用字母、数字、下划线、小数点或减号作为用户名,且必须由数字或字母开头"
 

	
 
#: rhodecode/model/validators.py:115
 
#, python-format
 
@@ -1172,7 +1177,8 @@ msgstr "用户组 \"%(usersgroup)s\" 已经存在"
 
msgid ""
 
"users group name may only contain  alphanumeric characters underscores, "
 
"periods or dashes and must begin with alphanumeric character"
 
msgstr "只能使用字母、数字、下划线、小数点或减号作为用户组名,且必须由数字或字母开头"
 
msgstr ""
 
"只能使用字母、数字、下划线、小数点或减号作为用户组名,且必须由数字或字母开头"
 

	
 
#: rhodecode/model/validators.py:175
 
msgid "Cannot assign this group as parent"
 
@@ -1263,8 +1269,8 @@ msgstr "邮件地址\"%(email)s\"不存在"
 

	
 
#: rhodecode/model/validators.py:663
 
msgid ""
 
"The LDAP Login attribute of the CN must be specified - this is the name "
 
"of the attribute that is equivalent to \"username\""
 
"The LDAP Login attribute of the CN must be specified - this is the name of "
 
"the attribute that is equivalent to \"username\""
 
msgstr "LDAP 登陆属性的 CN 必须指定 - 这个名字作为用户名"
 

	
 
#: rhodecode/model/validators.py:682
 
@@ -1606,22 +1612,20 @@ msgid "Admin journal"
 
msgstr "系统日志"
 

	
 
#: rhodecode/templates/admin/admin.html:10
 
#, fuzzy
 
msgid "journal filter..."
 
msgstr "快速过滤..."
 
msgstr "日志过滤..."
 

	
 
#: rhodecode/templates/admin/admin.html:12
 
#: rhodecode/templates/journal/journal.html:11
 
#, fuzzy
 
msgid "filter"
 
msgstr "文件"
 
msgstr "过滤"
 

	
 
#: rhodecode/templates/admin/admin.html:13
 
#: rhodecode/templates/journal/journal.html:12
 
#, python-format
 
msgid "%s entry"
 
msgid_plural "%s entries"
 
msgstr[0] ""
 
msgstr[0] "%s条"
 

	
 
#: rhodecode/templates/admin/admin_log.html:6
 
#: rhodecode/templates/admin/repos/repos.html:74
 
@@ -1657,14 +1661,12 @@ msgstr "无操作"
 

	
 
#: rhodecode/templates/admin/defaults/defaults.html:5
 
#: rhodecode/templates/admin/defaults/defaults.html:25
 
#, fuzzy
 
msgid "Repositories defaults"
 
msgstr "版本库组"
 
msgstr "版本库默认设置"
 

	
 
#: rhodecode/templates/admin/defaults/defaults.html:11
 
#, fuzzy
 
msgid "Defaults"
 
msgstr "默认"
 
msgstr "默认设置"
 

	
 
#: rhodecode/templates/admin/defaults/defaults.html:35
 
#: rhodecode/templates/admin/repos/repo_add_base.html:38
 
@@ -1858,9 +1860,10 @@ msgstr "匿名访问"
 
#: rhodecode/templates/admin/permissions/permissions.html:49
 
msgid ""
 
"All default permissions on each repository will be reset to choosen "
 
"permission, note that all custom default permission on repositories will "
 
"be lost"
 
msgstr "所有版本库的默认权限将被重置到选择的权限,所有版本库的自定义权限将被丢弃"
 
"permission, note that all custom default permission on repositories will be "
 
"lost"
 
msgstr ""
 
"所有版本库的默认权限将被重置到选择的权限,所有版本库的自定义权限将被丢弃"
 

	
 
#: rhodecode/templates/admin/permissions/permissions.html:50
 
#: rhodecode/templates/admin/permissions/permissions.html:63
 
@@ -1877,12 +1880,12 @@ msgid "Repository group"
 
msgstr "版本库组"
 

	
 
#: rhodecode/templates/admin/permissions/permissions.html:62
 
#, fuzzy
 
msgid ""
 
"All default permissions on each repository group will be reset to choosen"
 
" permission, note that all custom default permission on repositories "
 
"group will be lost"
 
msgstr "所有版本库的默认权限将被重置到选择的权限,所有版本库的自定义权限将被丢弃"
 
"All default permissions on each repository group will be reset to choosen "
 
"permission, note that all custom default permission on repositories group "
 
"will be lost"
 
msgstr ""
 
"所有版本库组的默认权限将被重置到选择的权限,所有版本库组的自定义权限将被丢弃"
 

	
 
#: rhodecode/templates/admin/permissions/permissions.html:69
 
msgid "Registration"
 
@@ -1955,7 +1958,8 @@ msgstr "文件浏览、下载、whoosh和README的默认修订版本"
 
#: rhodecode/templates/admin/repos/repo_edit.html:79
 
#: rhodecode/templates/forks/fork.html:63
 
#: rhodecode/templates/settings/repo_settings.html:70
 
msgid "Keep it short and to the point. Use a README file for longer descriptions."
 
msgid ""
 
"Keep it short and to the point. Use a README file for longer descriptions."
 
msgstr "保持简短。用README文件来写更长的描述。"
 

	
 
#: rhodecode/templates/admin/repos/repo_add_base.html:73
 
@@ -2064,8 +2068,8 @@ msgstr "确认清除版本库缓存"
 

	
 
#: rhodecode/templates/admin/repos/repo_edit.html:193
 
msgid ""
 
"Manually invalidate cache for this repository. On first access repository"
 
" will be cached again"
 
"Manually invalidate cache for this repository. On first access repository "
 
"will be cached again"
 
msgstr "手动清除版本库缓存。之后第一次访问的时候将重建缓存"
 

	
 
#: rhodecode/templates/admin/repos/repo_edit.html:198
 
@@ -2105,8 +2109,8 @@ msgstr "添加到公共日志"
 

	
 
#: rhodecode/templates/admin/repos/repo_edit.html:231
 
msgid ""
 
"All actions made on this repository will be accessible to everyone in "
 
"public journal"
 
"All actions made on this repository will be accessible to everyone in public "
 
"journal"
 
msgstr "任何人都可以在公共日志上看到这个版本库上的所有动作"
 

	
 
#: rhodecode/templates/admin/repos/repo_edit.html:238
 
@@ -2134,7 +2138,8 @@ msgid "Repository is not locked"
 
msgstr "版本库未锁定"
 

	
 
#: rhodecode/templates/admin/repos/repo_edit.html:252
 
msgid "Force locking on repository. Works only when anonymous access is disabled"
 
msgid ""
 
"Force locking on repository. Works only when anonymous access is disabled"
 
msgstr "强制锁定版本库。只有在禁止匿名访问时候才有效"
 

	
 
#: rhodecode/templates/admin/repos/repo_edit.html:259
 
@@ -2162,14 +2167,13 @@ msgstr "确认删除版本库"
 

	
 
#: rhodecode/templates/admin/repos/repo_edit.html:282
 
#: rhodecode/templates/settings/repo_settings.html:119
 
#, fuzzy
 
msgid ""
 
"This repository will be renamed in a special way in order to be "
 
"unaccesible for RhodeCode and VCS systems. If you need fully delete it "
 
"from file system please do it manually"
 
"This repository will be renamed in a special way in order to be unaccesible "
 
"for RhodeCode and VCS systems. If you need fully delete it from file system "
 
"please do it manually"
 
msgstr ""
 
"这个版本库将以特殊的方式重命名这样RhodeCode和版本控制系统将不能访问它。\n"
 
"                         如果需要从文件系统完全删除,你需要手动操作"
 
"这个版本库将以特殊的方式重命名这样RhodeCode和版本控制系统将不能访问它。如果需"
 
"要从文件系统完全删除,请要手动操作"
 

	
 
#: rhodecode/templates/admin/repos/repo_edit_perms.html:3
 
#: rhodecode/templates/admin/repos_groups/repos_group_edit_perms.html:3
 
@@ -2263,7 +2267,8 @@ msgstr "设置或者撤销该组所有成员的权限,包括版本库和其他组"
 
#: rhodecode/templates/files/files_add.html:15
 
#: rhodecode/templates/files/files_edit.html:15
 
#: rhodecode/templates/followers/followers.html:9
 
#: rhodecode/templates/forks/fork.html:9 rhodecode/templates/forks/forks.html:9
 
#: rhodecode/templates/forks/fork.html:9
 
#: rhodecode/templates/forks/forks.html:9
 
#: rhodecode/templates/pullrequests/pullrequest.html:8
 
#: rhodecode/templates/pullrequests/pullrequest_show.html:8
 
#: rhodecode/templates/pullrequests/pullrequest_show_all.html:8
 
@@ -2314,8 +2319,8 @@ msgstr "编辑版本库组"
 

	
 
#: rhodecode/templates/admin/repos_groups/repos_groups_edit.html:70
 
msgid ""
 
"Enable lock-by-pulling on group. This option will be applied to all other"
 
" groups and repositories inside"
 
"Enable lock-by-pulling on group. This option will be applied to all other "
 
"groups and repositories inside"
 
msgstr "启用组的拉取锁定。这个选项将应用到组内的其他组和版本库"
 

	
 
#: rhodecode/templates/admin/repos_groups/repos_groups_show.html:5
 
@@ -2391,10 +2396,11 @@ msgstr "重新扫描选项"
 

	
 
#: rhodecode/templates/admin/settings/settings.html:38
 
msgid ""
 
"In case a repository was deleted from filesystem and there are leftovers "
 
"in the database check this option to scan obsolete data in database and "
 
"remove it."
 
msgstr "如果版本库已经从文件系统删除,但数据库仍然有遗留信息,请勾选该项进行清理"
 
"In case a repository was deleted from filesystem and there are leftovers in "
 
"the database check this option to scan obsolete data in database and remove "
 
"it."
 
msgstr ""
 
"如果版本库已经从文件系统删除,但数据库仍然有遗留信息,请勾选该项进行清理"
 

	
 
#: rhodecode/templates/admin/settings/settings.html:39
 
msgid "destroy old data"
 
@@ -2402,9 +2408,10 @@ msgstr "清理旧数据"
 

	
 
#: rhodecode/templates/admin/settings/settings.html:41
 
msgid ""
 
"Rescan repositories location for new repositories. Also deletes obsolete "
 
"if `destroy` flag is checked "
 
msgstr "重新扫描版本库路径以发现新版本库。 同时删除过时的,如果设置有 `destroy` 标志"
 
"Rescan repositories location for new repositories. Also deletes obsolete if "
 
"`destroy` flag is checked "
 
msgstr ""
 
"重新扫描版本库路径以发现新版本库。 同时删除过时的,如果设置有 `destroy` 标志"
 

	
 
#: rhodecode/templates/admin/settings/settings.html:46
 
msgid "Rescan repositories"
 
@@ -2494,9 +2501,11 @@ msgstr "要求使用SSL进行版本控制系统操作"
 

	
 
#: rhodecode/templates/admin/settings/settings.html:203
 
msgid ""
 
"RhodeCode will require SSL for pushing or pulling. If SSL is missing it "
 
"will return HTTP Error 406: Not Acceptable"
 
msgstr "勾选后RhodeCode将要求使用SSL进行推送和拉取。如果没有使用SSL将返回HTTP 406错误:Not Acceptable"
 
"RhodeCode will require SSL for pushing or pulling. If SSL is missing it will "
 
"return HTTP Error 406: Not Acceptable"
 
msgstr ""
 
"勾选后RhodeCode将要求使用SSL进行推送和拉取。如果没有使用SSL将返回HTTP 406错"
 
"误:Not Acceptable"
 

	
 
#: rhodecode/templates/admin/settings/settings.html:209
 
msgid "Hooks"
 
@@ -2547,8 +2556,8 @@ msgstr "版本库路径"
 
#: rhodecode/templates/admin/settings/settings.html:261
 
msgid ""
 
"This a crucial application setting. If you are really sure you need to "
 
"change this, you must restart application in order to make this setting "
 
"take effect. Click this label to unlock."
 
"change this, you must restart application in order to make this setting take "
 
"effect. Click this label to unlock."
 
msgstr "这是一个关键设置。如果确认修改该项设置,请重启服务以便设置生效。"
 

	
 
#: rhodecode/templates/admin/settings/settings.html:262
 
@@ -2558,8 +2567,8 @@ msgstr "解锁"
 

	
 
#: rhodecode/templates/admin/settings/settings.html:263
 
msgid ""
 
"Location where repositories are stored. After changing this value a "
 
"restart, and rescan is required"
 
"Location where repositories are stored. After changing this value a restart, "
 
"and rescan is required"
 
msgstr "版本库存储路径。 修改后需要重启和重新扫描"
 

	
 
#: rhodecode/templates/admin/settings/settings.html:283
 
@@ -2932,7 +2941,8 @@ msgid "Products"
 
msgstr "产品"
 

	
 
#: rhodecode/templates/base/base.html:152
 
#: rhodecode/templates/base/base.html:182 rhodecode/templates/base/root.html:47
 
#: rhodecode/templates/base/base.html:182
 
#: rhodecode/templates/base/root.html:47
 
msgid "loading..."
 
msgstr "载入中..."
 

	
 
@@ -2986,7 +2996,8 @@ msgstr "版本库选项"
 
msgid "fork"
 
msgstr "复刻"
 

	
 
#: rhodecode/templates/base/base.html:212 rhodecode/templates/base/root.html:50
 
#: rhodecode/templates/base/base.html:212
 
#: rhodecode/templates/base/root.html:50
 
#: rhodecode/templates/changelog/changelog.html:43
 
msgid "Open new pull request"
 
msgstr "新建拉取请求"
 
@@ -3017,9 +3028,8 @@ msgid "permissions"
 
msgstr "权限"
 

	
 
#: rhodecode/templates/base/base.html:239
 
#, fuzzy
 
msgid "defaults"
 
msgstr "默认"
 
msgstr "默认设置"
 

	
 
#: rhodecode/templates/base/base.html:240
 
msgid "settings"
 
@@ -3238,9 +3248,8 @@ msgid "Changeset"
 
msgstr "修订集"
 

	
 
#: rhodecode/templates/changeset/changeset.html:52
 
#, fuzzy
 
msgid "No children"
 
msgstr "应用到成员"
 
msgstr "无子对象"
 

	
 
#: rhodecode/templates/changeset/changeset.html:70
 
#: rhodecode/templates/changeset/diff_block.html:20
 
@@ -3302,7 +3311,8 @@ msgstr "评论使用%s语法并支持%s"
 

	
 
#: rhodecode/templates/changeset/changeset_file_comment.html:48
 
#: rhodecode/templates/changeset/changeset_file_comment.html:123
 
msgid "Use @username inside this text to send notification to this RhodeCode user"
 
msgid ""
 
"Use @username inside this text to send notification to this RhodeCode user"
 
msgstr "在文本中使用 @用户名 以发送通知到该RhodeCode用户"
 

	
 
#: rhodecode/templates/changeset/changeset_file_comment.html:59
 
@@ -3433,9 +3443,8 @@ msgid "Confirm to delete this user: %s"
 
msgstr "确认删除用户:%s"
 

	
 
#: rhodecode/templates/email_templates/changeset_comment.html:10
 
#, fuzzy
 
msgid "New status$"
 
msgstr "改变状态"
 
msgstr "新状态$"
 

	
 
#: rhodecode/templates/email_templates/main.html:8
 
msgid "This is a notification from RhodeCode."
 
@@ -3443,29 +3452,28 @@ msgstr "这是一个RhodeCode通知。"
 

	
 
#: rhodecode/templates/email_templates/password_reset.html:4
 
msgid "Hello"
 
msgstr ""
 
msgstr "你好"
 

	
 
#: rhodecode/templates/email_templates/password_reset.html:6
 
msgid "We received a request to create a new password for your account."
 
msgstr ""
 
msgstr "我们收到重置你用户密码的请求。"
 

	
 
#: rhodecode/templates/email_templates/password_reset.html:8
 
msgid "You can generate it by clicking following URL"
 
msgstr ""
 
msgstr "点击下面的链接以重新生成密码:"
 

	
 
#: rhodecode/templates/email_templates/password_reset.html:12
 
msgid "If you didn't request new password please ignore this email."
 
msgstr ""
 
msgstr "如果你没有要求重置密码,请忽略这封邮件。"
 

	
 
#: rhodecode/templates/email_templates/pull_request.html:4
 
#, python-format
 
msgid ""
 
"User %s opened pull request for repository %s and wants you to review "
 
"changes."
 
msgstr ""
 
msgstr "用户%s在版本库%s中创建了一个拉取请求需要你检视"
 

	
 
#: rhodecode/templates/email_templates/pull_request.html:5
 
#, fuzzy
 
msgid "title"
 
msgstr "标题"
 

	
 
@@ -3476,35 +3484,32 @@ msgstr "描述"
 

	
 
#: rhodecode/templates/email_templates/pull_request.html:11
 
msgid "revisions for reviewing"
 
msgstr ""
 
msgstr "待检视修订"
 

	
 
#: rhodecode/templates/email_templates/pull_request.html:18
 
#, fuzzy
 
msgid "View this pull request here"
 
msgstr "为这个拉取请求增加检视人员"
 
msgstr "查看拉取请求"
 

	
 
#: rhodecode/templates/email_templates/pull_request_comment.html:4
 
#, fuzzy, python-format
 
#, python-format
 
msgid "User %s commented on pull request #%s for repository %s"
 
msgstr ""
 
msgstr "用户%s评论了版本库%s的拉取请求%s"
 

	
 
#: rhodecode/templates/email_templates/pull_request_comment.html:10
 
#, fuzzy
 
msgid "New status"
 
msgstr "改变状态"
 
msgstr "新状态"
 

	
 
#: rhodecode/templates/email_templates/pull_request_comment.html:14
 
msgid "View this comment here"
 
msgstr ""
 
msgstr "查看评论"
 

	
 
#: rhodecode/templates/email_templates/registration.html:4
 
#, fuzzy
 
msgid "A new user have registered in RhodeCode"
 
msgstr "成功注册到RhodeCode"
 
msgstr "新用户注册RhodeCode"
 

	
 
#: rhodecode/templates/email_templates/registration.html:9
 
msgid "View this user here"
 
msgstr ""
 
msgstr "查看用户"
 

	
 
#: rhodecode/templates/errors/error_document.html:46
 
#, python-format
 
@@ -3674,9 +3679,8 @@ msgid "show at revision"
 
msgstr "显示修订"
 

	
 
#: rhodecode/templates/files/files_history_box.html:11
 
#, fuzzy
 
msgid "show full history"
 
msgstr "加载文件历史记录..."
 
msgstr "显示全部历史记录"
 

	
 
#: rhodecode/templates/files/files_history_box.html:16
 
#, python-format
 
@@ -3918,9 +3922,8 @@ msgid "Compare view"
 
msgstr "比较显示"
 

	
 
#: rhodecode/templates/pullrequests/pullrequest_show.html:112
 
#, fuzzy
 
msgid "reviewer"
 
msgstr "%d个检视者"
 
msgstr "检视者"
 

	
 
#: rhodecode/templates/pullrequests/pullrequest_show_all.html:4
 
msgid "all pull requests"
 
@@ -3987,14 +3990,12 @@ msgid "%s Settings"
 
msgstr "%s设置"
 

	
 
#: rhodecode/templates/settings/repo_settings.html:102
 
#, fuzzy
 
msgid "Delete repository"
 
msgstr "[删除]版本库"
 
msgstr "删除版本库"
 

	
 
#: rhodecode/templates/settings/repo_settings.html:109
 
#, fuzzy
 
msgid "Remove repo"
 
msgstr "删除"
 
msgstr "删除版本库"
 

	
 
#: rhodecode/templates/shortlog/shortlog.html:5
 
#, python-format
 
@@ -4196,20 +4197,3 @@ msgstr "%s标签"
 
#: rhodecode/templates/tags/tags.html:29
 
msgid "Compare tags"
 
msgstr "比较标签"
 

	
 
#~ msgid ""
 
#~ "%s repository is not mapped to db"
 
#~ " perhaps it was created or renamed"
 
#~ " from the file system please run "
 
#~ "the application again in order to "
 
#~ "rescan repositories"
 
#~ msgstr " 版本库%s没有映射到数据库,可能是从文件系统创建或者重命名,请重启RhodeCode以重新扫描版本库"
 

	
 
#~ msgid ""
 
#~ "%s repository is not mapped to db"
 
#~ " perhaps it was moved or renamed  "
 
#~ "from the filesystem please run the "
 
#~ "application again in order to rescan "
 
#~ "repositories"
 
#~ msgstr "版本库%s没有映射到数据库,可能是从文件系统创建或者重命名,请重启RhodeCode以重新扫描版本库"
 

	
rhodecode/lib/auth.py
Show inline comments
 
@@ -45,7 +45,8 @@ from rhodecode.lib.auth_ldap import Auth
 

	
 
from rhodecode.model import meta
 
from rhodecode.model.user import UserModel
 
from rhodecode.model.db import Permission, RhodeCodeSetting, User
 
from rhodecode.model.db import Permission, RhodeCodeSetting, User, UserIpMap
 
from rhodecode.lib.caching_query import FromCache
 

	
 
log = logging.getLogger(__name__)
 

	
 
@@ -269,21 +270,34 @@ def login_container_auth(username):
 
    return user
 

	
 

	
 
def get_container_username(environ, config):
 
def get_container_username(environ, config, clean_username=False):
 
    """
 
    Get's the container_auth username (or email). It tries to get username
 
    from REMOTE_USER if container_auth_enabled is enabled, if that fails
 
    it tries to get username from HTTP_X_FORWARDED_USER if proxypass_auth_enabled
 
    is enabled. clean_username extracts the username from this data if it's
 
    having @ in it.
 

	
 
    :param environ:
 
    :param config:
 
    :param clean_username:
 
    """
 
    username = None
 

	
 
    if str2bool(config.get('container_auth_enabled', False)):
 
        from paste.httpheaders import REMOTE_USER
 
        username = REMOTE_USER(environ)
 
        log.debug('extracted REMOTE_USER:%s' % (username))
 

	
 
    if not username and str2bool(config.get('proxypass_auth_enabled', False)):
 
        username = environ.get('HTTP_X_FORWARDED_USER')
 
        log.debug('extracted HTTP_X_FORWARDED_USER:%s' % (username))
 

	
 
    if username:
 
    if username and clean_username:
 
        # Removing realm and domain from username
 
        username = username.partition('@')[0]
 
        username = username.rpartition('\\')[2]
 
        log.debug('Received username %s from container' % username)
 
    log.debug('Received username %s from container' % username)
 

	
 
    return username
 

	
 
@@ -313,11 +327,12 @@ class  AuthUser(object):
 
    in
 
    """
 

	
 
    def __init__(self, user_id=None, api_key=None, username=None):
 
    def __init__(self, user_id=None, api_key=None, username=None, ip_addr=None):
 

	
 
        self.user_id = user_id
 
        self.api_key = None
 
        self.username = username
 
        self.ip_addr = ip_addr
 

	
 
        self.name = ''
 
        self.lastname = ''
 
@@ -380,6 +395,24 @@ class  AuthUser(object):
 
    def is_admin(self):
 
        return self.admin
 

	
 
    @property
 
    def ip_allowed(self):
 
        """
 
        Checks if ip_addr used in constructor is allowed from defined list of
 
        allowed ip_addresses for user
 

	
 
        :returns: boolean, True if ip is in allowed ip range
 
        """
 
        #check IP
 
        allowed_ips = AuthUser.get_allowed_ips(self.user_id, cache=True)
 
        if check_ip_access(source_ip=self.ip_addr, allowed_ips=allowed_ips):
 
            log.debug('IP:%s is in range of %s' % (self.ip_addr, allowed_ips))
 
            return True
 
        else:
 
            log.info('Access for IP:%s forbidden, '
 
                     'not in %s' % (self.ip_addr, allowed_ips))
 
            return False
 

	
 
    def __repr__(self):
 
        return "<AuthUser('id:%s:%s|%s')>" % (self.user_id, self.username,
 
                                              self.is_authenticated)
 
@@ -406,6 +439,17 @@ class  AuthUser(object):
 
        api_key = cookie_store.get('api_key')
 
        return AuthUser(user_id, api_key, username)
 

	
 
    @classmethod
 
    def get_allowed_ips(cls, user_id, cache=False):
 
        _set = set()
 
        user_ips = UserIpMap.query().filter(UserIpMap.user_id == user_id)
 
        if cache:
 
            user_ips = user_ips.options(FromCache("sql_cache_short",
 
                                                  "get_user_ips_%s" % user_id))
 
        for ip in user_ips:
 
            _set.add(ip.ip_addr)
 
        return _set or set(['0.0.0.0/0'])
 

	
 

	
 
def set_available_permissions(config):
 
    """
 
@@ -450,6 +494,15 @@ class LoginRequired(object):
 
    def __wrapper(self, func, *fargs, **fkwargs):
 
        cls = fargs[0]
 
        user = cls.rhodecode_user
 
        loc = "%s:%s" % (cls.__class__.__name__, func.__name__)
 

	
 
        #check IP
 
        ip_access_ok = True
 
        if not user.ip_allowed:
 
            from rhodecode.lib import helpers as h
 
            h.flash(h.literal(_('IP %s not allowed' % (user.ip_addr))),
 
                    category='warning')
 
            ip_access_ok = False
 

	
 
        api_access_ok = False
 
        if self.api_access:
 
@@ -458,9 +511,9 @@ class LoginRequired(object):
 
                api_access_ok = True
 
            else:
 
                log.debug("API KEY token not valid")
 
        loc = "%s:%s" % (cls.__class__.__name__, func.__name__)
 

	
 
        log.debug('Checking if %s is authenticated @ %s' % (user.username, loc))
 
        if user.is_authenticated or api_access_ok:
 
        if (user.is_authenticated or api_access_ok) and ip_access_ok:
 
            reason = 'RegularAuth' if user.is_authenticated else 'APIAuth'
 
            log.info('user %s is authenticated and granted access to %s '
 
                     'using %s' % (user.username, loc, reason)
 
@@ -682,12 +735,12 @@ class PermsFunction(object):
 
            return False
 
        self.user_perms = user.permissions
 
        if self.check_permissions():
 
            log.debug('Permission granted for user: %s @ %s', user,
 
            log.debug('Permission to %s granted for user: %s @ %s', self.repo_name, user,
 
                      check_Location or 'unspecified location')
 
            return True
 

	
 
        else:
 
            log.debug('Permission denied for user: %s @ %s', user,
 
            log.debug('Permission to %s denied for user: %s @ %s', self.repo_name, user,
 
                        check_Location or 'unspecified location')
 
            return False
 

	
 
@@ -821,3 +874,122 @@ class HasPermissionAnyMiddleware(object)
 
                 )
 
        )
 
        return False
 

	
 

	
 
#==============================================================================
 
# SPECIAL VERSION TO HANDLE API AUTH
 
#==============================================================================
 
class _BaseApiPerm(object):
 
    def __init__(self, *perms):
 
        self.required_perms = set(perms)
 

	
 
    def __call__(self, check_location='unspecified', user=None, repo_name=None):
 
        cls_name = self.__class__.__name__
 
        check_scope = 'user:%s, repo:%s' % (user, repo_name)
 
        log.debug('checking cls:%s %s %s @ %s', cls_name,
 
                  self.required_perms, check_scope, check_location)
 
        if not user:
 
            log.debug('Empty User passed into arguments')
 
            return False
 

	
 
        ## process user
 
        if not isinstance(user, AuthUser):
 
            user = AuthUser(user.user_id)
 

	
 
        if self.check_permissions(user.permissions, repo_name):
 
            log.debug('Permission to %s granted for user: %s @ %s', repo_name,
 
                      user, check_location)
 
            return True
 

	
 
        else:
 
            log.debug('Permission to %s denied for user: %s @ %s', repo_name,
 
                      user, check_location)
 
            return False
 

	
 
    def check_permissions(self, perm_defs, repo_name):
 
        """
 
        implement in child class should return True if permissions are ok,
 
        False otherwise
 

	
 
        :param perm_defs: dict with permission definitions
 
        :param repo_name: repo name
 
        """
 
        raise NotImplementedError()
 

	
 

	
 
class HasPermissionAllApi(_BaseApiPerm):
 
    def __call__(self, user, check_location=''):
 
        return super(HasPermissionAllApi, self)\
 
            .__call__(check_location=check_location, user=user)
 

	
 
    def check_permissions(self, perm_defs, repo):
 
        if self.required_perms.issubset(perm_defs.get('global')):
 
            return True
 
        return False
 

	
 

	
 
class HasPermissionAnyApi(_BaseApiPerm):
 
    def __call__(self, user, check_location=''):
 
        return super(HasPermissionAnyApi, self)\
 
            .__call__(check_location=check_location, user=user)
 

	
 
    def check_permissions(self, perm_defs, repo):
 
        if self.required_perms.intersection(perm_defs.get('global')):
 
            return True
 
        return False
 

	
 

	
 
class HasRepoPermissionAllApi(_BaseApiPerm):
 
    def __call__(self, user, repo_name, check_location=''):
 
        return super(HasRepoPermissionAllApi, self)\
 
            .__call__(check_location=check_location, user=user,
 
                      repo_name=repo_name)
 

	
 
    def check_permissions(self, perm_defs, repo_name):
 

	
 
        try:
 
            self._user_perms = set(
 
                [perm_defs['repositories'][repo_name]]
 
            )
 
        except KeyError:
 
            log.warning(traceback.format_exc())
 
            return False
 
        if self.required_perms.issubset(self._user_perms):
 
            return True
 
        return False
 

	
 

	
 
class HasRepoPermissionAnyApi(_BaseApiPerm):
 
    def __call__(self, user, repo_name, check_location=''):
 
        return super(HasRepoPermissionAnyApi, self)\
 
            .__call__(check_location=check_location, user=user,
 
                      repo_name=repo_name)
 

	
 
    def check_permissions(self, perm_defs, repo_name):
 

	
 
        try:
 
            _user_perms = set(
 
                [perm_defs['repositories'][repo_name]]
 
            )
 
        except KeyError:
 
            log.warning(traceback.format_exc())
 
            return False
 
        if self.required_perms.intersection(_user_perms):
 
            return True
 
        return False
 

	
 

	
 
def check_ip_access(source_ip, allowed_ips=None):
 
    """
 
    Checks if source_ip is a subnet of any of allowed_ips.
 

	
 
    :param source_ip:
 
    :param allowed_ips: list of allowed ips together with mask
 
    """
 
    from rhodecode.lib import ipaddr
 
    log.debug('checking if ip:%s is subnet of %s' % (source_ip, allowed_ips))
 
    if isinstance(allowed_ips, (tuple, list, set)):
 
        for ip in allowed_ips:
 
            if ipaddr.IPAddress(source_ip) in ipaddr.IPNetwork(ip):
 
                return True
 
    return False
rhodecode/lib/base.py
Show inline comments
 
@@ -37,13 +37,18 @@ def _get_ip_addr(environ):
 
    proxy_key2 = 'HTTP_X_FORWARDED_FOR'
 
    def_key = 'REMOTE_ADDR'
 

	
 
    ip = environ.get(proxy_key2)
 
    ip = environ.get(proxy_key)
 
    if ip:
 
        return ip
 

	
 
    ip = environ.get(proxy_key)
 

	
 
    ip = environ.get(proxy_key2)
 
    if ip:
 
        # HTTP_X_FORWARDED_FOR can have mutliple ips inside
 
        # the left-most being the original client, and each successive proxy
 
        # that passed the request adding the IP address where it received the
 
        # request from.
 
        if ',' in ip:
 
            ip = ip.split(',')[0].strip()
 
        return ip
 

	
 
    ip = environ.get(def_key, '0.0.0.0')
 
@@ -101,7 +106,7 @@ class BaseVCSController(object):
 
        #authenticate this mercurial request using authfunc
 
        self.authenticate = BasicAuth('', authfunc,
 
                                      config.get('auth_ret_code'))
 
        self.ipaddr = '0.0.0.0'
 
        self.ip_addr = '0.0.0.0'
 

	
 
    def _handle_request(self, environ, start_response):
 
        raise NotImplementedError()
 
@@ -136,7 +141,7 @@ class BaseVCSController(object):
 
        """
 
        invalidate_cache('get_repo_cached_%s' % repo_name)
 

	
 
    def _check_permission(self, action, user, repo_name):
 
    def _check_permission(self, action, user, repo_name, ip_addr=None):
 
        """
 
        Checks permissions using action (push/pull) user and repository
 
        name
 
@@ -145,6 +150,12 @@ class BaseVCSController(object):
 
        :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,
 
@@ -235,6 +246,9 @@ class BaseVCSController(object):
 
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')
 
@@ -258,7 +272,6 @@ class BaseController(WSGIController):
 

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

	
 
    def __call__(self, environ, start_response):
 
        """Invoke the Controller"""
 
@@ -273,7 +286,7 @@ class BaseController(WSGIController):
 
            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)
 
            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 \
 
@@ -311,7 +324,7 @@ class BaseRepoController(BaseController)
 
            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_last_change(c.rhodecode_repo.last_change)
 
            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)
rhodecode/lib/celerylib/tasks.py
Show inline comments
 
@@ -347,6 +347,10 @@ def send_email(recipients, subject, body
 
    debug = str2bool(config.get('debug'))
 
    smtp_auth = email_config.get('smtp_auth')
 

	
 
    if not mail_server:
 
        log.error("SMTP mail server not configured - cannot send mail")
 
        return False
 

	
 
    try:
 
        m = SmtpMailer(mail_from, user, passwd, mail_server, smtp_auth,
 
                       mail_port, ssl, tls, debug=debug)
rhodecode/lib/db_manage.py
Show inline comments
 
@@ -164,8 +164,8 @@ class DbManage(object):
 

	
 
            def step_0(self):
 
                # step 0 is the schema upgrade, and than follow proper upgrades
 
                notify('attempting to do database upgrade to version %s' \
 
                                % __dbversion__)
 
                notify('attempting to do database upgrade from '
 
                       'version %s to version %s' %(curr_version, __dbversion__))
 
                api.upgrade(db_uri, repository_path, __dbversion__)
 
                notify('Schema upgrade completed')
 

	
 
@@ -286,6 +286,9 @@ class DbManage(object):
 
                           'Please validate and check default permissions '
 
                           'in admin panel')
 

	
 
            def step_10(self):
 
                pass
 

	
 
        upgrade_steps = [0] + range(curr_version + 1, __dbversion__ + 1)
 

	
 
        # CALL THE PROPER ORDER OF STEPS TO PERFORM FULL UPGRADE
rhodecode/lib/dbmigrate/schema/db_1_2_0.py
Show inline comments
 
@@ -619,7 +619,7 @@ class Repository(Base, BaseModel):
 
        hg_ui = ret
 
        for ui_ in hg_ui:
 
            if ui_.ui_active:
 
                log.debug('settings ui from db[%s]%s:%s', ui_.ui_section,
 
                log.debug('settings ui from db: [%s] %s=%s', ui_.ui_section,
 
                          ui_.ui_key, ui_.ui_value)
 
                baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
 

	
rhodecode/lib/dbmigrate/schema/db_1_3_0.py
Show inline comments
 
@@ -623,7 +623,7 @@ class Repository(Base, BaseModel):
 
        hg_ui = ret
 
        for ui_ in hg_ui:
 
            if ui_.ui_active:
 
                log.debug('settings ui from db[%s]%s:%s', ui_.ui_section,
 
                log.debug('settings ui from db: [%s] %s=%s', ui_.ui_section,
 
                          ui_.ui_key, ui_.ui_value)
 
                baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
 

	
rhodecode/lib/dbmigrate/schema/db_1_5_0.py
Show inline comments
 
# -*- coding: utf-8 -*-
 
"""
 
    rhodecode.model.db_1_4_0
 
    rhodecode.model.db_1_5_0
 
    ~~~~~~~~~~~~~~~~~~~~~~~~
 

	
 
    Database Models for RhodeCode <=1.5.X
 
    Database Models for RhodeCode <=1.5.2
 

	
 
    :created_on: Apr 08, 2010
 
    :author: marcink
 
@@ -23,6 +23,1812 @@
 
# You should have received a copy of the GNU General Public License
 
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
 

	
 
#TODO: replace that will db.py content after 1.6 Release
 
import os
 
import logging
 
import datetime
 
import traceback
 
import hashlib
 
import time
 
from collections import defaultdict
 

	
 
from sqlalchemy import *
 
from sqlalchemy.ext.hybrid import hybrid_property
 
from sqlalchemy.orm import relationship, joinedload, class_mapper, validates
 
from sqlalchemy.exc import DatabaseError
 
from beaker.cache import cache_region, region_invalidate
 
from webob.exc import HTTPNotFound
 

	
 
from pylons.i18n.translation import lazy_ugettext as _
 

	
 
from rhodecode.lib.vcs import get_backend
 
from rhodecode.lib.vcs.utils.helpers import get_scm
 
from rhodecode.lib.vcs.exceptions import VCSError
 
from rhodecode.lib.vcs.utils.lazy import LazyProperty
 

	
 
from rhodecode.lib.utils2 import str2bool, safe_str, get_changeset_safe, \
 
    safe_unicode, remove_suffix, remove_prefix
 
from rhodecode.lib.compat import json
 
from rhodecode.lib.caching_query import FromCache
 

	
 
from rhodecode.model.meta import Base, Session
 

	
 
URL_SEP = '/'
 
log = logging.getLogger(__name__)
 

	
 
#==============================================================================
 
# BASE CLASSES
 
#==============================================================================
 

	
 
_hash_key = lambda k: hashlib.md5(safe_str(k)).hexdigest()
 

	
 

	
 
class BaseModel(object):
 
    """
 
    Base Model for all classess
 
    """
 

	
 
    @classmethod
 
    def _get_keys(cls):
 
        """return column names for this model """
 
        return class_mapper(cls).c.keys()
 

	
 
    def get_dict(self):
 
        """
 
        return dict with keys and values corresponding
 
        to this model data """
 

	
 
        d = {}
 
        for k in self._get_keys():
 
            d[k] = getattr(self, k)
 

	
 
        # also use __json__() if present to get additional fields
 
        _json_attr = getattr(self, '__json__', None)
 
        if _json_attr:
 
            # update with attributes from __json__
 
            if callable(_json_attr):
 
                _json_attr = _json_attr()
 
            for k, val in _json_attr.iteritems():
 
                d[k] = val
 
        return d
 

	
 
    def get_appstruct(self):
 
        """return list with keys and values tupples corresponding
 
        to this model data """
 

	
 
        l = []
 
        for k in self._get_keys():
 
            l.append((k, getattr(self, k),))
 
        return l
 

	
 
    def populate_obj(self, populate_dict):
 
        """populate model with data from given populate_dict"""
 

	
 
        for k in self._get_keys():
 
            if k in populate_dict:
 
                setattr(self, k, populate_dict[k])
 

	
 
    @classmethod
 
    def query(cls):
 
        return Session().query(cls)
 

	
 
    @classmethod
 
    def get(cls, id_):
 
        if id_:
 
            return cls.query().get(id_)
 

	
 
    @classmethod
 
    def get_or_404(cls, id_):
 
        try:
 
            id_ = int(id_)
 
        except (TypeError, ValueError):
 
            raise HTTPNotFound
 

	
 
        res = cls.query().get(id_)
 
        if not res:
 
            raise HTTPNotFound
 
        return res
 

	
 
    @classmethod
 
    def getAll(cls):
 
        return cls.query().all()
 

	
 
    @classmethod
 
    def delete(cls, id_):
 
        obj = cls.query().get(id_)
 
        Session().delete(obj)
 

	
 
    def __repr__(self):
 
        if hasattr(self, '__unicode__'):
 
            # python repr needs to return str
 
            return safe_str(self.__unicode__())
 
        return '<DB:%s>' % (self.__class__.__name__)
 

	
 

	
 
class RhodeCodeSetting(Base, BaseModel):
 
    __tablename__ = 'rhodecode_settings'
 
    __table_args__ = (
 
        UniqueConstraint('app_settings_name'),
 
        {'extend_existing': True, 'mysql_engine': 'InnoDB',
 
         'mysql_charset': 'utf8'}
 
    )
 
    app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
 
    app_settings_name = Column("app_settings_name", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
 
    _app_settings_value = Column("app_settings_value", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
 

	
 
    def __init__(self, k='', v=''):
 
        self.app_settings_name = k
 
        self.app_settings_value = v
 

	
 
    @validates('_app_settings_value')
 
    def validate_settings_value(self, key, val):
 
        assert type(val) == unicode
 
        return val
 

	
 
    @hybrid_property
 
    def app_settings_value(self):
 
        v = self._app_settings_value
 
        if self.app_settings_name in ["ldap_active",
 
                                      "default_repo_enable_statistics",
 
                                      "default_repo_enable_locking",
 
                                      "default_repo_private",
 
                                      "default_repo_enable_downloads"]:
 
            v = str2bool(v)
 
        return v
 

	
 
    @app_settings_value.setter
 
    def app_settings_value(self, val):
 
        """
 
        Setter that will always make sure we use unicode in app_settings_value
 

	
 
        :param val:
 
        """
 
        self._app_settings_value = safe_unicode(val)
 

	
 
    def __unicode__(self):
 
        return u"<%s('%s:%s')>" % (
 
            self.__class__.__name__,
 
            self.app_settings_name, self.app_settings_value
 
        )
 

	
 
    @classmethod
 
    def get_by_name(cls, key):
 
        return cls.query()\
 
            .filter(cls.app_settings_name == key).scalar()
 

	
 
    @classmethod
 
    def get_by_name_or_create(cls, key):
 
        res = cls.get_by_name(key)
 
        if not res:
 
            res = cls(key)
 
        return res
 

	
 
    @classmethod
 
    def get_app_settings(cls, cache=False):
 

	
 
        ret = cls.query()
 

	
 
        if cache:
 
            ret = ret.options(FromCache("sql_cache_short", "get_hg_settings"))
 

	
 
        if not ret:
 
            raise Exception('Could not get application settings !')
 
        settings = {}
 
        for each in ret:
 
            settings['rhodecode_' + each.app_settings_name] = \
 
                each.app_settings_value
 

	
 
        return settings
 

	
 
    @classmethod
 
    def get_ldap_settings(cls, cache=False):
 
        ret = cls.query()\
 
                .filter(cls.app_settings_name.startswith('ldap_')).all()
 
        fd = {}
 
        for row in ret:
 
            fd.update({row.app_settings_name: row.app_settings_value})
 

	
 
        return fd
 

	
 
    @classmethod
 
    def get_default_repo_settings(cls, cache=False, strip_prefix=False):
 
        ret = cls.query()\
 
                .filter(cls.app_settings_name.startswith('default_')).all()
 
        fd = {}
 
        for row in ret:
 
            key = row.app_settings_name
 
            if strip_prefix:
 
                key = remove_prefix(key, prefix='default_')
 
            fd.update({key: row.app_settings_value})
 

	
 
        return fd
 

	
 

	
 
class RhodeCodeUi(Base, BaseModel):
 
    __tablename__ = 'rhodecode_ui'
 
    __table_args__ = (
 
        UniqueConstraint('ui_key'),
 
        {'extend_existing': True, 'mysql_engine': 'InnoDB',
 
         'mysql_charset': 'utf8'}
 
    )
 

	
 
    HOOK_UPDATE = 'changegroup.update'
 
    HOOK_REPO_SIZE = 'changegroup.repo_size'
 
    HOOK_PUSH = 'changegroup.push_logger'
 
    HOOK_PRE_PUSH = 'prechangegroup.pre_push'
 
    HOOK_PULL = 'outgoing.pull_logger'
 
    HOOK_PRE_PULL = 'preoutgoing.pre_pull'
 

	
 
    ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
 
    ui_section = Column("ui_section", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
 
    ui_key = Column("ui_key", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
 
    ui_value = Column("ui_value", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
 
    ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
 

	
 
    @classmethod
 
    def get_by_key(cls, key):
 
        return cls.query().filter(cls.ui_key == key).scalar()
 

	
 
    @classmethod
 
    def get_builtin_hooks(cls):
 
        q = cls.query()
 
        q = q.filter(cls.ui_key.in_([cls.HOOK_UPDATE, cls.HOOK_REPO_SIZE,
 
                                     cls.HOOK_PUSH, cls.HOOK_PRE_PUSH,
 
                                     cls.HOOK_PULL, cls.HOOK_PRE_PULL]))
 
        return q.all()
 

	
 
    @classmethod
 
    def get_custom_hooks(cls):
 
        q = cls.query()
 
        q = q.filter(~cls.ui_key.in_([cls.HOOK_UPDATE, cls.HOOK_REPO_SIZE,
 
                                      cls.HOOK_PUSH, cls.HOOK_PRE_PUSH,
 
                                      cls.HOOK_PULL, cls.HOOK_PRE_PULL]))
 
        q = q.filter(cls.ui_section == 'hooks')
 
        return q.all()
 

	
 
    @classmethod
 
    def get_repos_location(cls):
 
        return cls.get_by_key('/').ui_value
 

	
 
    @classmethod
 
    def create_or_update_hook(cls, key, val):
 
        new_ui = cls.get_by_key(key) or cls()
 
        new_ui.ui_section = 'hooks'
 
        new_ui.ui_active = True
 
        new_ui.ui_key = key
 
        new_ui.ui_value = val
 

	
 
        Session().add(new_ui)
 

	
 
    def __repr__(self):
 
        return '<DB:%s[%s:%s]>' % (self.__class__.__name__, self.ui_key,
 
                                   self.ui_value)
 

	
 

	
 
class User(Base, BaseModel):
 
    __tablename__ = 'users'
 
    __table_args__ = (
 
        UniqueConstraint('username'), UniqueConstraint('email'),
 
        Index('u_username_idx', 'username'),
 
        Index('u_email_idx', 'email'),
 
        {'extend_existing': True, 'mysql_engine': 'InnoDB',
 
         'mysql_charset': 'utf8'}
 
    )
 
    DEFAULT_USER = 'default'
 
    DEFAULT_PERMISSIONS = [
 
        'hg.register.manual_activate', 'hg.create.repository',
 
        'hg.fork.repository', 'repository.read', 'group.read'
 
    ]
 
    user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
 
    username = Column("username", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
 
    password = Column("password", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
 
    active = Column("active", Boolean(), nullable=True, unique=None, default=True)
 
    admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
 
    name = Column("firstname", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
 
    lastname = Column("lastname", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
 
    _email = Column("email", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
 
    last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
 
    ldap_dn = Column("ldap_dn", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
 
    api_key = Column("api_key", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
 
    inherit_default_permissions = Column("inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
 

	
 
    user_log = relationship('UserLog')
 
    user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
 

	
 
    repositories = relationship('Repository')
 
    user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
 
    followings = relationship('UserFollowing', primaryjoin='UserFollowing.user_id==User.user_id', cascade='all')
 

	
 
    repo_to_perm = relationship('UserRepoToPerm', primaryjoin='UserRepoToPerm.user_id==User.user_id', cascade='all')
 
    repo_group_to_perm = relationship('UserRepoGroupToPerm', primaryjoin='UserRepoGroupToPerm.user_id==User.user_id', cascade='all')
 

	
 
    group_member = relationship('UsersGroupMember', cascade='all')
 

	
 
    notifications = relationship('UserNotification', cascade='all')
 
    # notifications assigned to this user
 
    user_created_notifications = relationship('Notification', cascade='all')
 
    # comments created by this user
 
    user_comments = relationship('ChangesetComment', cascade='all')
 
    #extra emails for this user
 
    user_emails = relationship('UserEmailMap', cascade='all')
 

	
 
    @hybrid_property
 
    def email(self):
 
        return self._email
 

	
 
    @email.setter
 
    def email(self, val):
 
        self._email = val.lower() if val else None
 

	
 
    @property
 
    def firstname(self):
 
        # alias for future
 
        return self.name
 

	
 
    @property
 
    def emails(self):
 
        other = UserEmailMap.query().filter(UserEmailMap.user==self).all()
 
        return [self.email] + [x.email for x in other]
 

	
 
    @property
 
    def username_and_name(self):
 
        return '%s (%s %s)' % (self.username, self.firstname, self.lastname)
 

	
 
    @property
 
    def full_name(self):
 
        return '%s %s' % (self.firstname, self.lastname)
 

	
 
    @property
 
    def full_name_or_username(self):
 
        return ('%s %s' % (self.firstname, self.lastname)
 
                if (self.firstname and self.lastname) else self.username)
 

	
 
    @property
 
    def full_contact(self):
 
        return '%s %s <%s>' % (self.firstname, self.lastname, self.email)
 

	
 
    @property
 
    def short_contact(self):
 
        return '%s %s' % (self.firstname, self.lastname)
 

	
 
    @property
 
    def is_admin(self):
 
        return self.admin
 

	
 
    def __unicode__(self):
 
        return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
 
                                     self.user_id, self.username)
 

	
 
    @classmethod
 
    def get_by_username(cls, username, case_insensitive=False, cache=False):
 
        if case_insensitive:
 
            q = cls.query().filter(cls.username.ilike(username))
 
        else:
 
            q = cls.query().filter(cls.username == username)
 

	
 
        if cache:
 
            q = q.options(FromCache(
 
                            "sql_cache_short",
 
                            "get_user_%s" % _hash_key(username)
 
                          )
 
            )
 
        return q.scalar()
 

	
 
    @classmethod
 
    def get_by_api_key(cls, api_key, cache=False):
 
        q = cls.query().filter(cls.api_key == api_key)
 

	
 
        if cache:
 
            q = q.options(FromCache("sql_cache_short",
 
                                    "get_api_key_%s" % api_key))
 
        return q.scalar()
 

	
 
    @classmethod
 
    def get_by_email(cls, email, case_insensitive=False, cache=False):
 
        if case_insensitive:
 
            q = cls.query().filter(cls.email.ilike(email))
 
        else:
 
            q = cls.query().filter(cls.email == email)
 

	
 
        if cache:
 
            q = q.options(FromCache("sql_cache_short",
 
                                    "get_email_key_%s" % email))
 

	
 
        ret = q.scalar()
 
        if ret is None:
 
            q = UserEmailMap.query()
 
            # try fetching in alternate email map
 
            if case_insensitive:
 
                q = q.filter(UserEmailMap.email.ilike(email))
 
            else:
 
                q = q.filter(UserEmailMap.email == email)
 
            q = q.options(joinedload(UserEmailMap.user))
 
            if cache:
 
                q = q.options(FromCache("sql_cache_short",
 
                                        "get_email_map_key_%s" % email))
 
            ret = getattr(q.scalar(), 'user', None)
 

	
 
        return ret
 

	
 
    def update_lastlogin(self):
 
        """Update user lastlogin"""
 
        self.last_login = datetime.datetime.now()
 
        Session().add(self)
 
        log.debug('updated user %s lastlogin' % self.username)
 

	
 
    def get_api_data(self):
 
        """
 
        Common function for generating user related data for API
 
        """
 
        user = self
 
        data = dict(
 
            user_id=user.user_id,
 
            username=user.username,
 
            firstname=user.name,
 
            lastname=user.lastname,
 
            email=user.email,
 
            emails=user.emails,
 
            api_key=user.api_key,
 
            active=user.active,
 
            admin=user.admin,
 
            ldap_dn=user.ldap_dn,
 
            last_login=user.last_login,
 
        )
 
        return data
 

	
 
    def __json__(self):
 
        data = dict(
 
            full_name=self.full_name,
 
            full_name_or_username=self.full_name_or_username,
 
            short_contact=self.short_contact,
 
            full_contact=self.full_contact
 
        )
 
        data.update(self.get_api_data())
 
        return data
 

	
 

	
 
class UserEmailMap(Base, BaseModel):
 
    __tablename__ = 'user_email_map'
 
    __table_args__ = (
 
        Index('uem_email_idx', 'email'),
 
        UniqueConstraint('email'),
 
        {'extend_existing': True, 'mysql_engine': 'InnoDB',
 
         'mysql_charset': 'utf8'}
 
    )
 
    __mapper_args__ = {}
 

	
 
    email_id = Column("email_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
 
    user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
 
    _email = Column("email", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
 
    user = relationship('User', lazy='joined')
 

	
 
    @validates('_email')
 
    def validate_email(self, key, email):
 
        # check if this email is not main one
 
        main_email = Session().query(User).filter(User.email == email).scalar()
 
        if main_email is not None:
 
            raise AttributeError('email %s is present is user table' % email)
 
        return email
 

	
 
    @hybrid_property
 
    def email(self):
 
        return self._email
 

	
 
    @email.setter
 
    def email(self, val):
 
        self._email = val.lower() if val else None
 

	
 

	
 
class UserLog(Base, BaseModel):
 
    __tablename__ = 'user_logs'
 
    __table_args__ = (
 
        {'extend_existing': True, 'mysql_engine': 'InnoDB',
 
         'mysql_charset': 'utf8'},
 
    )
 
    user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
 
    user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
 
    username = Column("username", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
 
    repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True)
 
    repository_name = Column("repository_name", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
 
    user_ip = Column("user_ip", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
 
    action = Column("action", UnicodeText(1200000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
 
    action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
 

	
 
    @property
 
    def action_as_day(self):
 
        return datetime.date(*self.action_date.timetuple()[:3])
 

	
 
    user = relationship('User')
 
    repository = relationship('Repository', cascade='')
 

	
 

	
 
class UsersGroup(Base, BaseModel):
 
    __tablename__ = 'users_groups'
 
    __table_args__ = (
 
        {'extend_existing': True, 'mysql_engine': 'InnoDB',
 
         'mysql_charset': 'utf8'},
 
    )
 

	
 
    users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
 
    users_group_name = Column("users_group_name", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
 
    users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
 
    inherit_default_permissions = Column("users_group_inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
 

	
 
    members = relationship('UsersGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
 
    users_group_to_perm = relationship('UsersGroupToPerm', cascade='all')
 
    users_group_repo_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
 

	
 
    def __unicode__(self):
 
        return u'<userGroup(%s)>' % (self.users_group_name)
 

	
 
    @classmethod
 
    def get_by_group_name(cls, group_name, cache=False,
 
                          case_insensitive=False):
 
        if case_insensitive:
 
            q = cls.query().filter(cls.users_group_name.ilike(group_name))
 
        else:
 
            q = cls.query().filter(cls.users_group_name == group_name)
 
        if cache:
 
            q = q.options(FromCache(
 
                            "sql_cache_short",
 
                            "get_user_%s" % _hash_key(group_name)
 
                          )
 
            )
 
        return q.scalar()
 

	
 
    @classmethod
 
    def get(cls, users_group_id, cache=False):
 
        users_group = cls.query()
 
        if cache:
 
            users_group = users_group.options(FromCache("sql_cache_short",
 
                                    "get_users_group_%s" % users_group_id))
 
        return users_group.get(users_group_id)
 

	
 
    def get_api_data(self):
 
        users_group = self
 

	
 
        data = dict(
 
            users_group_id=users_group.users_group_id,
 
            group_name=users_group.users_group_name,
 
            active=users_group.users_group_active,
 
        )
 

	
 
        return data
 

	
 

	
 
class UsersGroupMember(Base, BaseModel):
 
    __tablename__ = 'users_groups_members'
 
    __table_args__ = (
 
        {'extend_existing': True, 'mysql_engine': 'InnoDB',
 
         'mysql_charset': 'utf8'},
 
    )
 

	
 
    users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
 
    users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
 
    user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
 

	
 
    user = relationship('User', lazy='joined')
 
    users_group = relationship('UsersGroup')
 

	
 
    def __init__(self, gr_id='', u_id=''):
 
        self.users_group_id = gr_id
 
        self.user_id = u_id
 

	
 

	
 
class Repository(Base, BaseModel):
 
    __tablename__ = 'repositories'
 
    __table_args__ = (
 
        UniqueConstraint('repo_name'),
 
        Index('r_repo_name_idx', 'repo_name'),
 
        {'extend_existing': True, 'mysql_engine': 'InnoDB',
 
         'mysql_charset': 'utf8'},
 
    )
 

	
 
    repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
 
    repo_name = Column("repo_name", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
 
    clone_uri = Column("clone_uri", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
 
    repo_type = Column("repo_type", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default=None)
 
    user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
 
    private = Column("private", Boolean(), nullable=True, unique=None, default=None)
 
    enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
 
    enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
 
    description = Column("description", String(10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
 
    created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
 
    updated_on = Column('updated_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
 
    landing_rev = Column("landing_revision", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default=None)
 
    enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
 
    _locked = Column("locked", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
 

	
 
    fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
 
    group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
 

	
 
    user = relationship('User')
 
    fork = relationship('Repository', remote_side=repo_id)
 
    group = relationship('RepoGroup')
 
    repo_to_perm = relationship('UserRepoToPerm', cascade='all', order_by='UserRepoToPerm.repo_to_perm_id')
 
    users_group_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
 
    stats = relationship('Statistics', cascade='all', uselist=False)
 

	
 
    followers = relationship('UserFollowing',
 
                             primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id',
 
                             cascade='all')
 

	
 
    logs = relationship('UserLog')
 
    comments = relationship('ChangesetComment', cascade="all, delete, delete-orphan")
 

	
 
    pull_requests_org = relationship('PullRequest',
 
                    primaryjoin='PullRequest.org_repo_id==Repository.repo_id',
 
                    cascade="all, delete, delete-orphan")
 

	
 
    pull_requests_other = relationship('PullRequest',
 
                    primaryjoin='PullRequest.other_repo_id==Repository.repo_id',
 
                    cascade="all, delete, delete-orphan")
 

	
 
    def __unicode__(self):
 
        return u"<%s('%s:%s')>" % (self.__class__.__name__, self.repo_id,
 
                                   self.repo_name)
 

	
 
    @hybrid_property
 
    def locked(self):
 
        # always should return [user_id, timelocked]
 
        if self._locked:
 
            _lock_info = self._locked.split(':')
 
            return int(_lock_info[0]), _lock_info[1]
 
        return [None, None]
 

	
 
    @locked.setter
 
    def locked(self, val):
 
        if val and isinstance(val, (list, tuple)):
 
            self._locked = ':'.join(map(str, val))
 
        else:
 
            self._locked = None
 

	
 
    @classmethod
 
    def url_sep(cls):
 
        return URL_SEP
 

	
 
    @classmethod
 
    def get_by_repo_name(cls, repo_name):
 
        q = Session().query(cls).filter(cls.repo_name == repo_name)
 
        q = q.options(joinedload(Repository.fork))\
 
                .options(joinedload(Repository.user))\
 
                .options(joinedload(Repository.group))
 
        return q.scalar()
 

	
 
    @classmethod
 
    def get_by_full_path(cls, repo_full_path):
 
        repo_name = repo_full_path.split(cls.base_path(), 1)[-1]
 
        return cls.get_by_repo_name(repo_name.strip(URL_SEP))
 

	
 
    @classmethod
 
    def get_repo_forks(cls, repo_id):
 
        return cls.query().filter(Repository.fork_id == repo_id)
 

	
 
    @classmethod
 
    def base_path(cls):
 
        """
 
        Returns base path when all repos are stored
 

	
 
        :param cls:
 
        """
 
        q = Session().query(RhodeCodeUi)\
 
            .filter(RhodeCodeUi.ui_key == cls.url_sep())
 
        q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
 
        return q.one().ui_value
 

	
 
    @property
 
    def forks(self):
 
        """
 
        Return forks of this repo
 
        """
 
        return Repository.get_repo_forks(self.repo_id)
 

	
 
    @property
 
    def parent(self):
 
        """
 
        Returns fork parent
 
        """
 
        return self.fork
 

	
 
    @property
 
    def just_name(self):
 
        return self.repo_name.split(Repository.url_sep())[-1]
 

	
 
    @property
 
    def groups_with_parents(self):
 
        groups = []
 
        if self.group is None:
 
            return groups
 

	
 
        cur_gr = self.group
 
        groups.insert(0, cur_gr)
 
        while 1:
 
            gr = getattr(cur_gr, 'parent_group', None)
 
            cur_gr = cur_gr.parent_group
 
            if gr is None:
 
                break
 
            groups.insert(0, gr)
 

	
 
        return groups
 

	
 
    @property
 
    def groups_and_repo(self):
 
        return self.groups_with_parents, self.just_name
 

	
 
    @LazyProperty
 
    def repo_path(self):
 
        """
 
        Returns base full path for that repository means where it actually
 
        exists on a filesystem
 
        """
 
        q = Session().query(RhodeCodeUi).filter(RhodeCodeUi.ui_key ==
 
                                              Repository.url_sep())
 
        q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
 
        return q.one().ui_value
 

	
 
    @property
 
    def repo_full_path(self):
 
        p = [self.repo_path]
 
        # we need to split the name by / since this is how we store the
 
        # names in the database, but that eventually needs to be converted
 
        # into a valid system path
 
        p += self.repo_name.split(Repository.url_sep())
 
        return os.path.join(*p)
 

	
 
    @property
 
    def cache_keys(self):
 
        """
 
        Returns associated cache keys for that repo
 
        """
 
        return CacheInvalidation.query()\
 
            .filter(CacheInvalidation.cache_args == self.repo_name)\
 
            .order_by(CacheInvalidation.cache_key)\
 
            .all()
 

	
 
    def get_new_name(self, repo_name):
 
        """
 
        returns new full repository name based on assigned group and new new
 

	
 
        :param group_name:
 
        """
 
        path_prefix = self.group.full_path_splitted if self.group else []
 
        return Repository.url_sep().join(path_prefix + [repo_name])
 

	
 
    @property
 
    def _ui(self):
 
        """
 
        Creates an db based ui object for this repository
 
        """
 
        from rhodecode.lib.utils import make_ui
 
        return make_ui('db', clear_session=False)
 

	
 
    @classmethod
 
    def inject_ui(cls, repo, extras={}):
 
        from rhodecode.lib.vcs.backends.hg import MercurialRepository
 
        from rhodecode.lib.vcs.backends.git import GitRepository
 
        required = (MercurialRepository, GitRepository)
 
        if not isinstance(repo, required):
 
            raise Exception('repo must be instance of %s' % required)
 

	
 
        # inject ui extra param to log this action via push logger
 
        for k, v in extras.items():
 
            repo._repo.ui.setconfig('rhodecode_extras', k, v)
 

	
 
    @classmethod
 
    def is_valid(cls, repo_name):
 
        """
 
        returns True if given repo name is a valid filesystem repository
 

	
 
        :param cls:
 
        :param repo_name:
 
        """
 
        from rhodecode.lib.utils import is_valid_repo
 

	
 
        return is_valid_repo(repo_name, cls.base_path())
 

	
 
    def get_api_data(self):
 
        """
 
        Common function for generating repo api data
 

	
 
        """
 
        repo = self
 
        data = dict(
 
            repo_id=repo.repo_id,
 
            repo_name=repo.repo_name,
 
            repo_type=repo.repo_type,
 
            clone_uri=repo.clone_uri,
 
            private=repo.private,
 
            created_on=repo.created_on,
 
            description=repo.description,
 
            landing_rev=repo.landing_rev,
 
            owner=repo.user.username,
 
            fork_of=repo.fork.repo_name if repo.fork else None
 
        )
 

	
 
        return data
 

	
 
    @classmethod
 
    def lock(cls, repo, user_id):
 
        repo.locked = [user_id, time.time()]
 
        Session().add(repo)
 
        Session().commit()
 

	
 
    @classmethod
 
    def unlock(cls, repo):
 
        repo.locked = None
 
        Session().add(repo)
 
        Session().commit()
 

	
 
    @property
 
    def last_db_change(self):
 
        return self.updated_on
 

	
 
    #==========================================================================
 
    # SCM PROPERTIES
 
    #==========================================================================
 

	
 
    def get_changeset(self, rev=None):
 
        return get_changeset_safe(self.scm_instance, rev)
 

	
 
    def get_landing_changeset(self):
 
        """
 
        Returns landing changeset, or if that doesn't exist returns the tip
 
        """
 
        cs = self.get_changeset(self.landing_rev) or self.get_changeset()
 
        return cs
 

	
 
    def update_last_change(self, last_change=None):
 
        if last_change is None:
 
            last_change = datetime.datetime.now()
 
        if self.updated_on is None or self.updated_on != last_change:
 
            log.debug('updated repo %s with new date %s' % (self, last_change))
 
            self.updated_on = last_change
 
            Session().add(self)
 
            Session().commit()
 

	
 
    @property
 
    def tip(self):
 
        return self.get_changeset('tip')
 

	
 
    @property
 
    def author(self):
 
        return self.tip.author
 

	
 
    @property
 
    def last_change(self):
 
        return self.scm_instance.last_change
 

	
 
    def get_comments(self, revisions=None):
 
        """
 
        Returns comments for this repository grouped by revisions
 

	
 
        :param revisions: filter query by revisions only
 
        """
 
        cmts = ChangesetComment.query()\
 
            .filter(ChangesetComment.repo == self)
 
        if revisions:
 
            cmts = cmts.filter(ChangesetComment.revision.in_(revisions))
 
        grouped = defaultdict(list)
 
        for cmt in cmts.all():
 
            grouped[cmt.revision].append(cmt)
 
        return grouped
 

	
 
    def statuses(self, revisions=None):
 
        """
 
        Returns statuses for this repository
 

	
 
        :param revisions: list of revisions to get statuses for
 
        :type revisions: list
 
        """
 

	
 
        statuses = ChangesetStatus.query()\
 
            .filter(ChangesetStatus.repo == self)\
 
            .filter(ChangesetStatus.version == 0)
 
        if revisions:
 
            statuses = statuses.filter(ChangesetStatus.revision.in_(revisions))
 
        grouped = {}
 

	
 
from rhodecode.model.db import *
 
        #maybe we have open new pullrequest without a status ?
 
        stat = ChangesetStatus.STATUS_UNDER_REVIEW
 
        status_lbl = ChangesetStatus.get_status_lbl(stat)
 
        for pr in PullRequest.query().filter(PullRequest.org_repo == self).all():
 
            for rev in pr.revisions:
 
                pr_id = pr.pull_request_id
 
                pr_repo = pr.other_repo.repo_name
 
                grouped[rev] = [stat, status_lbl, pr_id, pr_repo]
 

	
 
        for stat in statuses.all():
 
            pr_id = pr_repo = None
 
            if stat.pull_request:
 
                pr_id = stat.pull_request.pull_request_id
 
                pr_repo = stat.pull_request.other_repo.repo_name
 
            grouped[stat.revision] = [str(stat.status), stat.status_lbl,
 
                                      pr_id, pr_repo]
 
        return grouped
 

	
 
    #==========================================================================
 
    # SCM CACHE INSTANCE
 
    #==========================================================================
 

	
 
    @property
 
    def invalidate(self):
 
        return CacheInvalidation.invalidate(self.repo_name)
 

	
 
    def set_invalidate(self):
 
        """
 
        set a cache for invalidation for this instance
 
        """
 
        CacheInvalidation.set_invalidate(repo_name=self.repo_name)
 

	
 
    @LazyProperty
 
    def scm_instance(self):
 
        import rhodecode
 
        full_cache = str2bool(rhodecode.CONFIG.get('vcs_full_cache'))
 
        if full_cache:
 
            return self.scm_instance_cached()
 
        return self.__get_instance()
 

	
 
    def scm_instance_cached(self, cache_map=None):
 
        @cache_region('long_term')
 
        def _c(repo_name):
 
            return self.__get_instance()
 
        rn = self.repo_name
 
        log.debug('Getting cached instance of repo')
 

	
 
        if cache_map:
 
            # get using prefilled cache_map
 
            invalidate_repo = cache_map[self.repo_name]
 
            if invalidate_repo:
 
                invalidate_repo = (None if invalidate_repo.cache_active
 
                                   else invalidate_repo)
 
        else:
 
            # get from invalidate
 
            invalidate_repo = self.invalidate
 

	
 
        if invalidate_repo is not None:
 
            region_invalidate(_c, None, rn)
 
            # update our cache
 
            CacheInvalidation.set_valid(invalidate_repo.cache_key)
 
        return _c(rn)
 

	
 
    def __get_instance(self):
 
        repo_full_path = self.repo_full_path
 
        try:
 
            alias = get_scm(repo_full_path)[0]
 
            log.debug('Creating instance of %s repository' % alias)
 
            backend = get_backend(alias)
 
        except VCSError:
 
            log.error(traceback.format_exc())
 
            log.error('Perhaps this repository is in db and not in '
 
                      'filesystem run rescan repositories with '
 
                      '"destroy old data " option from admin panel')
 
            return
 

	
 
        if alias == 'hg':
 

	
 
            repo = backend(safe_str(repo_full_path), create=False,
 
                           baseui=self._ui)
 
            # skip hidden web repository
 
            if repo._get_hidden():
 
                return
 
        else:
 
            repo = backend(repo_full_path, create=False)
 

	
 
        return repo
 

	
 

	
 
class RepoGroup(Base, BaseModel):
 
    __tablename__ = 'groups'
 
    __table_args__ = (
 
        UniqueConstraint('group_name', 'group_parent_id'),
 
        CheckConstraint('group_id != group_parent_id'),
 
        {'extend_existing': True, 'mysql_engine': 'InnoDB',
 
         'mysql_charset': 'utf8'},
 
    )
 
    __mapper_args__ = {'order_by': 'group_name'}
 

	
 
    group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
 
    group_name = Column("group_name", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
 
    group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
 
    group_description = Column("group_description", String(10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
 
    enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
 

	
 
    repo_group_to_perm = relationship('UserRepoGroupToPerm', cascade='all', order_by='UserRepoGroupToPerm.group_to_perm_id')
 
    users_group_to_perm = relationship('UsersGroupRepoGroupToPerm', cascade='all')
 

	
 
    parent_group = relationship('RepoGroup', remote_side=group_id)
 

	
 
    def __init__(self, group_name='', parent_group=None):
 
        self.group_name = group_name
 
        self.parent_group = parent_group
 

	
 
    def __unicode__(self):
 
        return u"<%s('%s:%s')>" % (self.__class__.__name__, self.group_id,
 
                                  self.group_name)
 

	
 
    @classmethod
 
    def groups_choices(cls, check_perms=False):
 
        from webhelpers.html import literal as _literal
 
        from rhodecode.model.scm import ScmModel
 
        groups = cls.query().all()
 
        if check_perms:
 
            #filter group user have access to, it's done
 
            #magically inside ScmModel based on current user
 
            groups = ScmModel().get_repos_groups(groups)
 
        repo_groups = [('', '')]
 
        sep = ' &raquo; '
 
        _name = lambda k: _literal(sep.join(k))
 

	
 
        repo_groups.extend([(x.group_id, _name(x.full_path_splitted))
 
                              for x in groups])
 

	
 
        repo_groups = sorted(repo_groups, key=lambda t: t[1].split(sep)[0])
 
        return repo_groups
 

	
 
    @classmethod
 
    def url_sep(cls):
 
        return URL_SEP
 

	
 
    @classmethod
 
    def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
 
        if case_insensitive:
 
            gr = cls.query()\
 
                .filter(cls.group_name.ilike(group_name))
 
        else:
 
            gr = cls.query()\
 
                .filter(cls.group_name == group_name)
 
        if cache:
 
            gr = gr.options(FromCache(
 
                            "sql_cache_short",
 
                            "get_group_%s" % _hash_key(group_name)
 
                            )
 
            )
 
        return gr.scalar()
 

	
 
    @property
 
    def parents(self):
 
        parents_recursion_limit = 5
 
        groups = []
 
        if self.parent_group is None:
 
            return groups
 
        cur_gr = self.parent_group
 
        groups.insert(0, cur_gr)
 
        cnt = 0
 
        while 1:
 
            cnt += 1
 
            gr = getattr(cur_gr, 'parent_group', None)
 
            cur_gr = cur_gr.parent_group
 
            if gr is None:
 
                break
 
            if cnt == parents_recursion_limit:
 
                # this will prevent accidental infinit loops
 
                log.error('group nested more than %s' %
 
                          parents_recursion_limit)
 
                break
 

	
 
            groups.insert(0, gr)
 
        return groups
 

	
 
    @property
 
    def children(self):
 
        return RepoGroup.query().filter(RepoGroup.parent_group == self)
 

	
 
    @property
 
    def name(self):
 
        return self.group_name.split(RepoGroup.url_sep())[-1]
 

	
 
    @property
 
    def full_path(self):
 
        return self.group_name
 

	
 
    @property
 
    def full_path_splitted(self):
 
        return self.group_name.split(RepoGroup.url_sep())
 

	
 
    @property
 
    def repositories(self):
 
        return Repository.query()\
 
                .filter(Repository.group == self)\
 
                .order_by(Repository.repo_name)
 

	
 
    @property
 
    def repositories_recursive_count(self):
 
        cnt = self.repositories.count()
 

	
 
        def children_count(group):
 
            cnt = 0
 
            for child in group.children:
 
                cnt += child.repositories.count()
 
                cnt += children_count(child)
 
            return cnt
 

	
 
        return cnt + children_count(self)
 

	
 
    def recursive_groups_and_repos(self):
 
        """
 
        Recursive return all groups, with repositories in those groups
 
        """
 
        all_ = []
 

	
 
        def _get_members(root_gr):
 
            for r in root_gr.repositories:
 
                all_.append(r)
 
            childs = root_gr.children.all()
 
            if childs:
 
                for gr in childs:
 
                    all_.append(gr)
 
                    _get_members(gr)
 

	
 
        _get_members(self)
 
        return [self] + all_
 

	
 
    def get_new_name(self, group_name):
 
        """
 
        returns new full group name based on parent and new name
 

	
 
        :param group_name:
 
        """
 
        path_prefix = (self.parent_group.full_path_splitted if
 
                       self.parent_group else [])
 
        return RepoGroup.url_sep().join(path_prefix + [group_name])
 

	
 

	
 
class Permission(Base, BaseModel):
 
    __tablename__ = 'permissions'
 
    __table_args__ = (
 
        Index('p_perm_name_idx', 'permission_name'),
 
        {'extend_existing': True, 'mysql_engine': 'InnoDB',
 
         'mysql_charset': 'utf8'},
 
    )
 
    PERMS = [
 
        ('repository.none', _('Repository no access')),
 
        ('repository.read', _('Repository read access')),
 
        ('repository.write', _('Repository write access')),
 
        ('repository.admin', _('Repository admin access')),
 

	
 
        ('group.none', _('Repositories Group no access')),
 
        ('group.read', _('Repositories Group read access')),
 
        ('group.write', _('Repositories Group write access')),
 
        ('group.admin', _('Repositories Group admin access')),
 

	
 
        ('hg.admin', _('RhodeCode Administrator')),
 
        ('hg.create.none', _('Repository creation disabled')),
 
        ('hg.create.repository', _('Repository creation enabled')),
 
        ('hg.fork.none', _('Repository forking disabled')),
 
        ('hg.fork.repository', _('Repository forking enabled')),
 
        ('hg.register.none', _('Register disabled')),
 
        ('hg.register.manual_activate', _('Register new user with RhodeCode '
 
                                          'with manual activation')),
 

	
 
        ('hg.register.auto_activate', _('Register new user with RhodeCode '
 
                                        'with auto activation')),
 
    ]
 

	
 
    # defines which permissions are more important higher the more important
 
    PERM_WEIGHTS = {
 
        'repository.none': 0,
 
        'repository.read': 1,
 
        'repository.write': 3,
 
        'repository.admin': 4,
 

	
 
        'group.none': 0,
 
        'group.read': 1,
 
        'group.write': 3,
 
        'group.admin': 4,
 

	
 
        'hg.fork.none': 0,
 
        'hg.fork.repository': 1,
 
        'hg.create.none': 0,
 
        'hg.create.repository':1
 
    }
 

	
 
    permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
 
    permission_name = Column("permission_name", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
 
    permission_longname = Column("permission_longname", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
 

	
 
    def __unicode__(self):
 
        return u"<%s('%s:%s')>" % (
 
            self.__class__.__name__, self.permission_id, self.permission_name
 
        )
 

	
 
    @classmethod
 
    def get_by_key(cls, key):
 
        return cls.query().filter(cls.permission_name == key).scalar()
 

	
 
    @classmethod
 
    def get_default_perms(cls, default_user_id):
 
        q = Session().query(UserRepoToPerm, Repository, cls)\
 
         .join((Repository, UserRepoToPerm.repository_id == Repository.repo_id))\
 
         .join((cls, UserRepoToPerm.permission_id == cls.permission_id))\
 
         .filter(UserRepoToPerm.user_id == default_user_id)
 

	
 
        return q.all()
 

	
 
    @classmethod
 
    def get_default_group_perms(cls, default_user_id):
 
        q = Session().query(UserRepoGroupToPerm, RepoGroup, cls)\
 
         .join((RepoGroup, UserRepoGroupToPerm.group_id == RepoGroup.group_id))\
 
         .join((cls, UserRepoGroupToPerm.permission_id == cls.permission_id))\
 
         .filter(UserRepoGroupToPerm.user_id == default_user_id)
 

	
 
        return q.all()
 

	
 

	
 
class UserRepoToPerm(Base, BaseModel):
 
    __tablename__ = 'repo_to_perm'
 
    __table_args__ = (
 
        UniqueConstraint('user_id', 'repository_id', 'permission_id'),
 
        {'extend_existing': True, 'mysql_engine': 'InnoDB',
 
         'mysql_charset': 'utf8'}
 
    )
 
    repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
 
    user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
 
    permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
 
    repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
 

	
 
    user = relationship('User')
 
    repository = relationship('Repository')
 
    permission = relationship('Permission')
 

	
 
    @classmethod
 
    def create(cls, user, repository, permission):
 
        n = cls()
 
        n.user = user
 
        n.repository = repository
 
        n.permission = permission
 
        Session().add(n)
 
        return n
 

	
 
    def __unicode__(self):
 
        return u'<user:%s => %s >' % (self.user, self.repository)
 

	
 

	
 
class UserToPerm(Base, BaseModel):
 
    __tablename__ = 'user_to_perm'
 
    __table_args__ = (
 
        UniqueConstraint('user_id', 'permission_id'),
 
        {'extend_existing': True, 'mysql_engine': 'InnoDB',
 
         'mysql_charset': 'utf8'}
 
    )
 
    user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
 
    user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
 
    permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
 

	
 
    user = relationship('User')
 
    permission = relationship('Permission', lazy='joined')
 

	
 

	
 
class UsersGroupRepoToPerm(Base, BaseModel):
 
    __tablename__ = 'users_group_repo_to_perm'
 
    __table_args__ = (
 
        UniqueConstraint('repository_id', 'users_group_id', 'permission_id'),
 
        {'extend_existing': True, 'mysql_engine': 'InnoDB',
 
         'mysql_charset': 'utf8'}
 
    )
 
    users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
 
    users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
 
    permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
 
    repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
 

	
 
    users_group = relationship('UsersGroup')
 
    permission = relationship('Permission')
 
    repository = relationship('Repository')
 

	
 
    @classmethod
 
    def create(cls, users_group, repository, permission):
 
        n = cls()
 
        n.users_group = users_group
 
        n.repository = repository
 
        n.permission = permission
 
        Session().add(n)
 
        return n
 

	
 
    def __unicode__(self):
 
        return u'<userGroup:%s => %s >' % (self.users_group, self.repository)
 

	
 

	
 
class UsersGroupToPerm(Base, BaseModel):
 
    __tablename__ = 'users_group_to_perm'
 
    __table_args__ = (
 
        UniqueConstraint('users_group_id', 'permission_id',),
 
        {'extend_existing': True, 'mysql_engine': 'InnoDB',
 
         'mysql_charset': 'utf8'}
 
    )
 
    users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
 
    users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
 
    permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
 

	
 
    users_group = relationship('UsersGroup')
 
    permission = relationship('Permission')
 

	
 

	
 
class UserRepoGroupToPerm(Base, BaseModel):
 
    __tablename__ = 'user_repo_group_to_perm'
 
    __table_args__ = (
 
        UniqueConstraint('user_id', 'group_id', 'permission_id'),
 
        {'extend_existing': True, 'mysql_engine': 'InnoDB',
 
         'mysql_charset': 'utf8'}
 
    )
 

	
 
    group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
 
    user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
 
    group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
 
    permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
 

	
 
    user = relationship('User')
 
    group = relationship('RepoGroup')
 
    permission = relationship('Permission')
 

	
 

	
 
class UsersGroupRepoGroupToPerm(Base, BaseModel):
 
    __tablename__ = 'users_group_repo_group_to_perm'
 
    __table_args__ = (
 
        UniqueConstraint('users_group_id', 'group_id'),
 
        {'extend_existing': True, 'mysql_engine': 'InnoDB',
 
         'mysql_charset': 'utf8'}
 
    )
 

	
 
    users_group_repo_group_to_perm_id = Column("users_group_repo_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
 
    users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
 
    group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
 
    permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
 

	
 
    users_group = relationship('UsersGroup')
 
    permission = relationship('Permission')
 
    group = relationship('RepoGroup')
 

	
 

	
 
class Statistics(Base, BaseModel):
 
    __tablename__ = 'statistics'
 
    __table_args__ = (
 
         UniqueConstraint('repository_id'),
 
         {'extend_existing': True, 'mysql_engine': 'InnoDB',
 
          'mysql_charset': 'utf8'}
 
    )
 
    stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
 
    repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
 
    stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
 
    commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
 
    commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
 
    languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
 

	
 
    repository = relationship('Repository', single_parent=True)
 

	
 

	
 
class UserFollowing(Base, BaseModel):
 
    __tablename__ = 'user_followings'
 
    __table_args__ = (
 
        UniqueConstraint('user_id', 'follows_repository_id'),
 
        UniqueConstraint('user_id', 'follows_user_id'),
 
        {'extend_existing': True, 'mysql_engine': 'InnoDB',
 
         'mysql_charset': 'utf8'}
 
    )
 

	
 
    user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
 
    user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
 
    follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
 
    follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
 
    follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
 

	
 
    user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
 

	
 
    follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
 
    follows_repository = relationship('Repository', order_by='Repository.repo_name')
 

	
 
    @classmethod
 
    def get_repo_followers(cls, repo_id):
 
        return cls.query().filter(cls.follows_repo_id == repo_id)
 

	
 

	
 
class CacheInvalidation(Base, BaseModel):
 
    __tablename__ = 'cache_invalidation'
 
    __table_args__ = (
 
        UniqueConstraint('cache_key'),
 
        Index('key_idx', 'cache_key'),
 
        {'extend_existing': True, 'mysql_engine': 'InnoDB',
 
         'mysql_charset': 'utf8'},
 
    )
 
    cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
 
    cache_key = Column("cache_key", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
 
    cache_args = Column("cache_args", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
 
    cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
 

	
 
    def __init__(self, cache_key, cache_args=''):
 
        self.cache_key = cache_key
 
        self.cache_args = cache_args
 
        self.cache_active = False
 

	
 
    def __unicode__(self):
 
        return u"<%s('%s:%s')>" % (self.__class__.__name__,
 
                                  self.cache_id, self.cache_key)
 

	
 
    @property
 
    def prefix(self):
 
        _split = self.cache_key.split(self.cache_args, 1)
 
        if _split and len(_split) == 2:
 
            return _split[0]
 
        return ''
 

	
 
    @classmethod
 
    def clear_cache(cls):
 
        cls.query().delete()
 

	
 
    @classmethod
 
    def _get_key(cls, key):
 
        """
 
        Wrapper for generating a key, together with a prefix
 

	
 
        :param key:
 
        """
 
        import rhodecode
 
        prefix = ''
 
        org_key = key
 
        iid = rhodecode.CONFIG.get('instance_id')
 
        if iid:
 
            prefix = iid
 

	
 
        return "%s%s" % (prefix, key), prefix, org_key
 

	
 
    @classmethod
 
    def get_by_key(cls, key):
 
        return cls.query().filter(cls.cache_key == key).scalar()
 

	
 
    @classmethod
 
    def get_by_repo_name(cls, repo_name):
 
        return cls.query().filter(cls.cache_args == repo_name).all()
 

	
 
    @classmethod
 
    def _get_or_create_key(cls, key, repo_name, commit=True):
 
        inv_obj = Session().query(cls).filter(cls.cache_key == key).scalar()
 
        if not inv_obj:
 
            try:
 
                inv_obj = CacheInvalidation(key, repo_name)
 
                Session().add(inv_obj)
 
                if commit:
 
                    Session().commit()
 
            except Exception:
 
                log.error(traceback.format_exc())
 
                Session().rollback()
 
        return inv_obj
 

	
 
    @classmethod
 
    def invalidate(cls, key):
 
        """
 
        Returns Invalidation object if this given key should be invalidated
 
        None otherwise. `cache_active = False` means that this cache
 
        state is not valid and needs to be invalidated
 

	
 
        :param key:
 
        """
 
        repo_name = key
 
        repo_name = remove_suffix(repo_name, '_README')
 
        repo_name = remove_suffix(repo_name, '_RSS')
 
        repo_name = remove_suffix(repo_name, '_ATOM')
 

	
 
        # adds instance prefix
 
        key, _prefix, _org_key = cls._get_key(key)
 
        inv = cls._get_or_create_key(key, repo_name)
 

	
 
        if inv and inv.cache_active is False:
 
            return inv
 

	
 
    @classmethod
 
    def set_invalidate(cls, key=None, repo_name=None):
 
        """
 
        Mark this Cache key for invalidation, either by key or whole
 
        cache sets based on repo_name
 

	
 
        :param key:
 
        """
 
        if key:
 
            key, _prefix, _org_key = cls._get_key(key)
 
            inv_objs = Session().query(cls).filter(cls.cache_key == key).all()
 
        elif repo_name:
 
            inv_objs = Session().query(cls).filter(cls.cache_args == repo_name).all()
 

	
 
        log.debug('marking %s key[s] for invalidation based on key=%s,repo_name=%s'
 
                  % (len(inv_objs), key, repo_name))
 
        try:
 
            for inv_obj in inv_objs:
 
                inv_obj.cache_active = False
 
                Session().add(inv_obj)
 
            Session().commit()
 
        except Exception:
 
            log.error(traceback.format_exc())
 
            Session().rollback()
 

	
 
    @classmethod
 
    def set_valid(cls, key):
 
        """
 
        Mark this cache key as active and currently cached
 

	
 
        :param key:
 
        """
 
        inv_obj = cls.get_by_key(key)
 
        inv_obj.cache_active = True
 
        Session().add(inv_obj)
 
        Session().commit()
 

	
 
    @classmethod
 
    def get_cache_map(cls):
 

	
 
        class cachemapdict(dict):
 

	
 
            def __init__(self, *args, **kwargs):
 
                fixkey = kwargs.get('fixkey')
 
                if fixkey:
 
                    del kwargs['fixkey']
 
                self.fixkey = fixkey
 
                super(cachemapdict, self).__init__(*args, **kwargs)
 

	
 
            def __getattr__(self, name):
 
                key = name
 
                if self.fixkey:
 
                    key, _prefix, _org_key = cls._get_key(key)
 
                if key in self.__dict__:
 
                    return self.__dict__[key]
 
                else:
 
                    return self[key]
 

	
 
            def __getitem__(self, key):
 
                if self.fixkey:
 
                    key, _prefix, _org_key = cls._get_key(key)
 
                try:
 
                    return super(cachemapdict, self).__getitem__(key)
 
                except KeyError:
 
                    return
 

	
 
        cache_map = cachemapdict(fixkey=True)
 
        for obj in cls.query().all():
 
            cache_map[obj.cache_key] = cachemapdict(obj.get_dict())
 
        return cache_map
 

	
 

	
 
class ChangesetComment(Base, BaseModel):
 
    __tablename__ = 'changeset_comments'
 
    __table_args__ = (
 
        Index('cc_revision_idx', 'revision'),
 
        {'extend_existing': True, 'mysql_engine': 'InnoDB',
 
         'mysql_charset': 'utf8'},
 
    )
 
    comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True)
 
    repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
 
    revision = Column('revision', String(40), nullable=True)
 
    pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
 
    line_no = Column('line_no', Unicode(10), nullable=True)
 
    hl_lines = Column('hl_lines', Unicode(512), nullable=True)
 
    f_path = Column('f_path', Unicode(1000), nullable=True)
 
    user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=False)
 
    text = Column('text', UnicodeText(25000), nullable=False)
 
    created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
 
    modified_at = Column('modified_at', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
 

	
 
    author = relationship('User', lazy='joined')
 
    repo = relationship('Repository')
 
    status_change = relationship('ChangesetStatus', cascade="all, delete, delete-orphan")
 
    pull_request = relationship('PullRequest', lazy='joined')
 

	
 
    @classmethod
 
    def get_users(cls, revision=None, pull_request_id=None):
 
        """
 
        Returns user associated with this ChangesetComment. ie those
 
        who actually commented
 

	
 
        :param cls:
 
        :param revision:
 
        """
 
        q = Session().query(User)\
 
                .join(ChangesetComment.author)
 
        if revision:
 
            q = q.filter(cls.revision == revision)
 
        elif pull_request_id:
 
            q = q.filter(cls.pull_request_id == pull_request_id)
 
        return q.all()
 

	
 

	
 
class ChangesetStatus(Base, BaseModel):
 
    __tablename__ = 'changeset_statuses'
 
    __table_args__ = (
 
        Index('cs_revision_idx', 'revision'),
 
        Index('cs_version_idx', 'version'),
 
        UniqueConstraint('repo_id', 'revision', 'version'),
 
        {'extend_existing': True, 'mysql_engine': 'InnoDB',
 
         'mysql_charset': 'utf8'}
 
    )
 
    STATUS_NOT_REVIEWED = DEFAULT = 'not_reviewed'
 
    STATUS_APPROVED = 'approved'
 
    STATUS_REJECTED = 'rejected'
 
    STATUS_UNDER_REVIEW = 'under_review'
 

	
 
    STATUSES = [
 
        (STATUS_NOT_REVIEWED, _("Not Reviewed")),  # (no icon) and default
 
        (STATUS_APPROVED, _("Approved")),
 
        (STATUS_REJECTED, _("Rejected")),
 
        (STATUS_UNDER_REVIEW, _("Under Review")),
 
    ]
 

	
 
    changeset_status_id = Column('changeset_status_id', Integer(), nullable=False, primary_key=True)
 
    repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
 
    user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
 
    revision = Column('revision', String(40), nullable=False)
 
    status = Column('status', String(128), nullable=False, default=DEFAULT)
 
    changeset_comment_id = Column('changeset_comment_id', Integer(), ForeignKey('changeset_comments.comment_id'))
 
    modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
 
    version = Column('version', Integer(), nullable=False, default=0)
 
    pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
 

	
 
    author = relationship('User', lazy='joined')
 
    repo = relationship('Repository')
 
    comment = relationship('ChangesetComment', lazy='joined')
 
    pull_request = relationship('PullRequest', lazy='joined')
 

	
 
    def __unicode__(self):
 
        return u"<%s('%s:%s')>" % (
 
            self.__class__.__name__,
 
            self.status, self.author
 
        )
 

	
 
    @classmethod
 
    def get_status_lbl(cls, value):
 
        return dict(cls.STATUSES).get(value)
 

	
 
    @property
 
    def status_lbl(self):
 
        return ChangesetStatus.get_status_lbl(self.status)
 

	
 

	
 
class PullRequest(Base, BaseModel):
 
    __tablename__ = 'pull_requests'
 
    __table_args__ = (
 
        {'extend_existing': True, 'mysql_engine': 'InnoDB',
 
         'mysql_charset': 'utf8'},
 
    )
 

	
 
    STATUS_NEW = u'new'
 
    STATUS_OPEN = u'open'
 
    STATUS_CLOSED = u'closed'
 

	
 
    pull_request_id = Column('pull_request_id', Integer(), nullable=False, primary_key=True)
 
    title = Column('title', Unicode(256), nullable=True)
 
    description = Column('description', UnicodeText(10240), nullable=True)
 
    status = Column('status', Unicode(256), nullable=False, default=STATUS_NEW)
 
    created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
 
    updated_on = Column('updated_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
 
    user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
 
    _revisions = Column('revisions', UnicodeText(20500))  # 500 revisions max
 
    org_repo_id = Column('org_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
 
    org_ref = Column('org_ref', Unicode(256), nullable=False)
 
    other_repo_id = Column('other_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
 
    other_ref = Column('other_ref', Unicode(256), nullable=False)
 

	
 
    @hybrid_property
 
    def revisions(self):
 
        return self._revisions.split(':')
 

	
 
    @revisions.setter
 
    def revisions(self, val):
 
        self._revisions = ':'.join(val)
 

	
 
    author = relationship('User', lazy='joined')
 
    reviewers = relationship('PullRequestReviewers',
 
                             cascade="all, delete, delete-orphan")
 
    org_repo = relationship('Repository', primaryjoin='PullRequest.org_repo_id==Repository.repo_id')
 
    other_repo = relationship('Repository', primaryjoin='PullRequest.other_repo_id==Repository.repo_id')
 
    statuses = relationship('ChangesetStatus')
 
    comments = relationship('ChangesetComment',
 
                             cascade="all, delete, delete-orphan")
 

	
 
    def is_closed(self):
 
        return self.status == self.STATUS_CLOSED
 

	
 
    def __json__(self):
 
        return dict(
 
          revisions=self.revisions
 
        )
 

	
 

	
 
class PullRequestReviewers(Base, BaseModel):
 
    __tablename__ = 'pull_request_reviewers'
 
    __table_args__ = (
 
        {'extend_existing': True, 'mysql_engine': 'InnoDB',
 
         'mysql_charset': 'utf8'},
 
    )
 

	
 
    def __init__(self, user=None, pull_request=None):
 
        self.user = user
 
        self.pull_request = pull_request
 

	
 
    pull_requests_reviewers_id = Column('pull_requests_reviewers_id', Integer(), nullable=False, primary_key=True)
 
    pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=False)
 
    user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True)
 

	
 
    user = relationship('User')
 
    pull_request = relationship('PullRequest')
 

	
 

	
 
class Notification(Base, BaseModel):
 
    __tablename__ = 'notifications'
 
    __table_args__ = (
 
        Index('notification_type_idx', 'type'),
 
        {'extend_existing': True, 'mysql_engine': 'InnoDB',
 
         'mysql_charset': 'utf8'},
 
    )
 

	
 
    TYPE_CHANGESET_COMMENT = u'cs_comment'
 
    TYPE_MESSAGE = u'message'
 
    TYPE_MENTION = u'mention'
 
    TYPE_REGISTRATION = u'registration'
 
    TYPE_PULL_REQUEST = u'pull_request'
 
    TYPE_PULL_REQUEST_COMMENT = u'pull_request_comment'
 

	
 
    notification_id = Column('notification_id', Integer(), nullable=False, primary_key=True)
 
    subject = Column('subject', Unicode(512), nullable=True)
 
    body = Column('body', UnicodeText(50000), nullable=True)
 
    created_by = Column("created_by", Integer(), ForeignKey('users.user_id'), nullable=True)
 
    created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
 
    type_ = Column('type', Unicode(256))
 

	
 
    created_by_user = relationship('User')
 
    notifications_to_users = relationship('UserNotification', lazy='joined',
 
                                          cascade="all, delete, delete-orphan")
 

	
 
    @property
 
    def recipients(self):
 
        return [x.user for x in UserNotification.query()\
 
                .filter(UserNotification.notification == self)\
 
                .order_by(UserNotification.user_id.asc()).all()]
 

	
 
    @classmethod
 
    def create(cls, created_by, subject, body, recipients, type_=None):
 
        if type_ is None:
 
            type_ = Notification.TYPE_MESSAGE
 

	
 
        notification = cls()
 
        notification.created_by_user = created_by
 
        notification.subject = subject
 
        notification.body = body
 
        notification.type_ = type_
 
        notification.created_on = datetime.datetime.now()
 

	
 
        for u in recipients:
 
            assoc = UserNotification()
 
            assoc.notification = notification
 
            u.notifications.append(assoc)
 
        Session().add(notification)
 
        return notification
 

	
 
    @property
 
    def description(self):
 
        from rhodecode.model.notification import NotificationModel
 
        return NotificationModel().make_description(self)
 

	
 

	
 
class UserNotification(Base, BaseModel):
 
    __tablename__ = 'user_to_notification'
 
    __table_args__ = (
 
        UniqueConstraint('user_id', 'notification_id'),
 
        {'extend_existing': True, 'mysql_engine': 'InnoDB',
 
         'mysql_charset': 'utf8'}
 
    )
 
    user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), primary_key=True)
 
    notification_id = Column("notification_id", Integer(), ForeignKey('notifications.notification_id'), primary_key=True)
 
    read = Column('read', Boolean, default=False)
 
    sent_on = Column('sent_on', DateTime(timezone=False), nullable=True, unique=None)
 

	
 
    user = relationship('User', lazy="joined")
 
    notification = relationship('Notification', lazy="joined",
 
                                order_by=lambda: Notification.created_on.desc(),)
 

	
 
    def mark_as_read(self):
 
        self.read = True
 
        Session().add(self)
 

	
 

	
 
class DbMigrateVersion(Base, BaseModel):
 
    __tablename__ = 'db_migrate_version'
 
    __table_args__ = (
 
        {'extend_existing': True, 'mysql_engine': 'InnoDB',
 
         'mysql_charset': 'utf8'},
 
    )
 
    repository_id = Column('repository_id', String(250), primary_key=True)
 
    repository_path = Column('repository_path', Text)
 
    version = Column('version', Integer)
rhodecode/lib/dbmigrate/schema/db_1_5_2.py
Show inline comments
 
new file 100644
 
# -*- coding: utf-8 -*-
 
"""
 
    rhodecode.model.db_1_4_0
 
    ~~~~~~~~~~~~~~~~~~~~~~~~
 

	
 
    Database Models for RhodeCode <=1.5.X
 

	
 
    :created_on: Apr 08, 2010
 
    :author: marcink
 
    :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
 
    :license: GPLv3, see COPYING for more details.
 
"""
 
# This program is free software: you can redistribute it and/or modify
 
# it under the terms of the GNU General Public License as published by
 
# the Free Software Foundation, either version 3 of the License, or
 
# (at your option) any later version.
 
#
 
# 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 General Public License
 
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
 

	
 
#TODO: replace that will db.py content after 1.6 Release
 

	
 
from rhodecode.model.db import *
rhodecode/lib/dbmigrate/versions/006_version_1_4_0.py
Show inline comments
 
@@ -12,6 +12,7 @@ from rhodecode.lib.dbmigrate.migrate.cha
 

	
 
from rhodecode.model.meta import Base
 
from rhodecode.model import meta
 
from rhodecode.lib.dbmigrate.versions import _reset_base
 

	
 
log = logging.getLogger(__name__)
 

	
 
@@ -49,12 +50,7 @@ def upgrade(migrate_engine):
 
    tbl = ChangesetStatus.__table__
 
    tbl.create()
 

	
 
    ## RESET COMPLETLY THE metadata for sqlalchemy to use the 1_3_0 Base
 
    Base = declarative_base()
 
    Base.metadata.clear()
 
    Base.metadata = MetaData()
 
    Base.metadata.bind = migrate_engine
 
    meta.Base = Base
 
    _reset_base(migrate_engine)
 

	
 
    #==========================================================================
 
    # USERS TABLE
 
@@ -173,12 +169,7 @@ def upgrade(migrate_engine):
 
                             ForeignKey('pull_requests.pull_request_id'),
 
                             nullable=True)
 
    pull_request_id.create(table=tbl)
 
    ## RESET COMPLETLY THE metadata for sqlalchemy back after using 1_3_0
 
    Base = declarative_base()
 
    Base.metadata.clear()
 
    Base.metadata = MetaData()
 
    Base.metadata.bind = migrate_engine
 
    meta.Base = Base
 
    _reset_base(migrate_engine)
 

	
 

	
 
def downgrade(migrate_engine):
rhodecode/lib/dbmigrate/versions/008_version_1_5_0.py
Show inline comments
 
@@ -12,6 +12,7 @@ from rhodecode.lib.dbmigrate.migrate.cha
 

	
 
from rhodecode.model.meta import Base
 
from rhodecode.model import meta
 
from rhodecode.lib.dbmigrate.versions import _reset_base
 

	
 
log = logging.getLogger(__name__)
 

	
 
@@ -24,6 +25,7 @@ def upgrade(migrate_engine):
 
    #==========================================================================
 
    # USER LOGS
 
    #==========================================================================
 
    _reset_base(migrate_engine)
 
    from rhodecode.lib.dbmigrate.schema.db_1_5_0 import UserLog
 
    tbl = UserLog.__table__
 
    username = Column("username", String(255, convert_unicode=False,
rhodecode/lib/dbmigrate/versions/010_version_1_5_2.py
Show inline comments
 
new file 100644
 
import logging
 
import datetime
 

	
 
from sqlalchemy import *
 
from sqlalchemy.exc import DatabaseError
 
from sqlalchemy.orm import relation, backref, class_mapper, joinedload
 
from sqlalchemy.orm.session import Session
 
from sqlalchemy.ext.declarative import declarative_base
 

	
 
from rhodecode.lib.dbmigrate.migrate import *
 
from rhodecode.lib.dbmigrate.migrate.changeset import *
 

	
 
from rhodecode.model.meta import Base
 
from rhodecode.model import meta
 
from rhodecode.lib.dbmigrate.versions import _reset_base
 

	
 
log = logging.getLogger(__name__)
 

	
 

	
 
def upgrade(migrate_engine):
 
    """
 
    Upgrade operations go here.
 
    Don't create your own engine; bind migrate_engine to your metadata
 
    """
 
    _reset_base(migrate_engine)
 
    #==========================================================================
 
    # USER LOGS
 
    #==========================================================================
 
    from rhodecode.lib.dbmigrate.schema.db_1_5_2 import UserIpMap
 
    tbl = UserIpMap.__table__
 
    tbl.create()
 

	
 
    #==========================================================================
 
    # REPOSITORIES
 
    #==========================================================================
 
    from rhodecode.lib.dbmigrate.schema.db_1_5_2 import Repository
 
    tbl = Repository.__table__
 
    changeset_cache = Column("changeset_cache", LargeBinary(), nullable=True)
 
    # create username column
 
    changeset_cache.create(table=tbl)
 

	
 
    #fix cache data
 
    repositories = Repository.getAll()
 
    for entry in repositories:
 
        entry.update_changeset_cache()
 

	
 

	
 
def downgrade(migrate_engine):
 
    meta = MetaData()
 
    meta.bind = migrate_engine
rhodecode/lib/dbmigrate/versions/__init__.py
Show inline comments
 
@@ -22,3 +22,23 @@
 
#
 
# You should have received a copy of the GNU General Public License
 
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
from sqlalchemy import *
 
from sqlalchemy.exc import DatabaseError
 
from sqlalchemy.orm import relation, backref, class_mapper, joinedload
 
from sqlalchemy.orm.session import Session
 
from sqlalchemy.ext.declarative import declarative_base
 

	
 
from rhodecode.lib.dbmigrate.migrate import *
 
from rhodecode.lib.dbmigrate.migrate.changeset import *
 

	
 
from rhodecode.model.meta import Base
 
from rhodecode.model import meta
 

	
 

	
 
def _reset_base(migrate_engine):
 
    ## RESET COMPLETLY THE metadata for sqlalchemy to use previous declared Base
 
    Base = declarative_base()
 
    Base.metadata.clear()
 
    Base.metadata = MetaData()
 
    Base.metadata.bind = migrate_engine
 
    meta.Base = Base
rhodecode/lib/diffs.py
Show inline comments
 
@@ -583,7 +583,7 @@ class DiffProcessor(object):
 
        #return u''.join(imap(self._line_counter, self._diff.splitlines(1)))
 

	
 
    def as_html(self, table_class='code-difftable', line_class='line',
 
                new_lineno_class='lineno old', old_lineno_class='lineno new',
 
                old_lineno_class='lineno old', new_lineno_class='lineno new',
 
                code_class='code', enable_comments=False, parsed_lines=None):
 
        """
 
        Return given diff as html table with customized css classes
rhodecode/lib/helpers.py
Show inline comments
 
@@ -464,7 +464,7 @@ def desc_stylize(value):
 
                   '<div class="metatag" tag="see">see =&gt; \\1 </div>', value)
 
    value = re.sub(r'\[license\ \=\>\ *([a-zA-Z0-9\/\=\?\&\ \:\/\.\-]*)\]',
 
                   '<div class="metatag" tag="license"><a href="http:\/\/www.opensource.org/licenses/\\1">\\1</a></div>', value)
 
    value = re.sub(r'\[(requires|recommends|conflicts|base)\ \=\>\ *([a-zA-Z\-\/]*)\]',
 
    value = re.sub(r'\[(requires|recommends|conflicts|base)\ \=\>\ *([a-zA-Z0-9\-\/]*)\]',
 
                   '<div class="metatag" tag="\\1">\\1 =&gt; <a href="/\\2">\\2</a></div>', value)
 
    value = re.sub(r'\[(lang|language)\ \=\>\ *([a-zA-Z\-\/\#\+]*)\]',
 
                   '<div class="metatag" tag="lang">\\2</div>', value)
 
@@ -1164,3 +1164,9 @@ def not_mapped_error(repo_name):
 
            ' it was created or renamed from the filesystem'
 
            ' please run the application again'
 
            ' in order to rescan repositories') % repo_name, category='error')
 

	
 

	
 
def ip_range(ip_addr):
 
    from rhodecode.model.db import UserIpMap
 
    s, e = UserIpMap._get_ip_range(ip_addr)
 
    return '%s - %s' % (s, e)
rhodecode/lib/ipaddr.py
Show inline comments
 
new file 100644
 
# Copyright 2007 Google Inc.
 
#  Licensed to PSF under a Contributor Agreement.
 
#
 
# Licensed under the Apache License, Version 2.0 (the "License");
 
# you may not use this file except in compliance with the License.
 
# You may obtain a copy of the License at
 
#
 
#      http://www.apache.org/licenses/LICENSE-2.0
 
#
 
# Unless required by applicable law or agreed to in writing, software
 
# distributed under the License is distributed on an "AS IS" BASIS,
 
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
 
# implied. See the License for the specific language governing
 
# permissions and limitations under the License.
 

	
 
"""A fast, lightweight IPv4/IPv6 manipulation library in Python.
 

	
 
This library is used to create/poke/manipulate IPv4 and IPv6 addresses
 
and networks.
 

	
 
"""
 

	
 
__version__ = 'trunk'
 

	
 
import struct
 

	
 
IPV4LENGTH = 32
 
IPV6LENGTH = 128
 

	
 

	
 
class AddressValueError(ValueError):
 
    """A Value Error related to the address."""
 

	
 

	
 
class NetmaskValueError(ValueError):
 
    """A Value Error related to the netmask."""
 

	
 

	
 
def IPAddress(address, version=None):
 
    """Take an IP string/int and return an object of the correct type.
 

	
 
    Args:
 
        address: A string or integer, the IP address.  Either IPv4 or
 
          IPv6 addresses may be supplied; integers less than 2**32 will
 
          be considered to be IPv4 by default.
 
        version: An Integer, 4 or 6. If set, don't try to automatically
 
          determine what the IP address type is. important for things
 
          like IPAddress(1), which could be IPv4, '0.0.0.1',  or IPv6,
 
          '::1'.
 

	
 
    Returns:
 
        An IPv4Address or IPv6Address object.
 

	
 
    Raises:
 
        ValueError: if the string passed isn't either a v4 or a v6
 
          address.
 

	
 
    """
 
    if version:
 
        if version == 4:
 
            return IPv4Address(address)
 
        elif version == 6:
 
            return IPv6Address(address)
 

	
 
    try:
 
        return IPv4Address(address)
 
    except (AddressValueError, NetmaskValueError):
 
        pass
 

	
 
    try:
 
        return IPv6Address(address)
 
    except (AddressValueError, NetmaskValueError):
 
        pass
 

	
 
    raise ValueError('%r does not appear to be an IPv4 or IPv6 address' %
 
                     address)
 

	
 

	
 
def IPNetwork(address, version=None, strict=False):
 
    """Take an IP string/int and return an object of the correct type.
 

	
 
    Args:
 
        address: A string or integer, the IP address.  Either IPv4 or
 
          IPv6 addresses may be supplied; integers less than 2**32 will
 
          be considered to be IPv4 by default.
 
        version: An Integer, if set, don't try to automatically
 
          determine what the IP address type is. important for things
 
          like IPNetwork(1), which could be IPv4, '0.0.0.1/32', or IPv6,
 
          '::1/128'.
 

	
 
    Returns:
 
        An IPv4Network or IPv6Network object.
 

	
 
    Raises:
 
        ValueError: if the string passed isn't either a v4 or a v6
 
          address. Or if a strict network was requested and a strict
 
          network wasn't given.
 

	
 
    """
 
    if version:
 
        if version == 4:
 
            return IPv4Network(address, strict)
 
        elif version == 6:
 
            return IPv6Network(address, strict)
 

	
 
    try:
 
        return IPv4Network(address, strict)
 
    except (AddressValueError, NetmaskValueError):
 
        pass
 

	
 
    try:
 
        return IPv6Network(address, strict)
 
    except (AddressValueError, NetmaskValueError):
 
        pass
 

	
 
    raise ValueError('%r does not appear to be an IPv4 or IPv6 network' %
 
                     address)
 

	
 

	
 
def v4_int_to_packed(address):
 
    """The binary representation of this address.
 

	
 
    Args:
 
        address: An integer representation of an IPv4 IP address.
 

	
 
    Returns:
 
        The binary representation of this address.
 

	
 
    Raises:
 
        ValueError: If the integer is too large to be an IPv4 IP
 
          address.
 
    """
 
    if address > _BaseV4._ALL_ONES:
 
        raise ValueError('Address too large for IPv4')
 
    return Bytes(struct.pack('!I', address))
 

	
 

	
 
def v6_int_to_packed(address):
 
    """The binary representation of this address.
 

	
 
    Args:
 
        address: An integer representation of an IPv6 IP address.
 

	
 
    Returns:
 
        The binary representation of this address.
 
    """
 
    return Bytes(struct.pack('!QQ', address >> 64, address & (2 ** 64 - 1)))
 

	
 

	
 
def _find_address_range(addresses):
 
    """Find a sequence of addresses.
 

	
 
    Args:
 
        addresses: a list of IPv4 or IPv6 addresses.
 

	
 
    Returns:
 
        A tuple containing the first and last IP addresses in the sequence.
 

	
 
    """
 
    first = last = addresses[0]
 
    for ip in addresses[1:]:
 
        if ip._ip == last._ip + 1:
 
            last = ip
 
        else:
 
            break
 
    return (first, last)
 

	
 

	
 
def _get_prefix_length(number1, number2, bits):
 
    """Get the number of leading bits that are same for two numbers.
 

	
 
    Args:
 
        number1: an integer.
 
        number2: another integer.
 
        bits: the maximum number of bits to compare.
 

	
 
    Returns:
 
        The number of leading bits that are the same for two numbers.
 

	
 
    """
 
    for i in range(bits):
 
        if number1 >> i == number2 >> i:
 
            return bits - i
 
    return 0
 

	
 

	
 
def _count_righthand_zero_bits(number, bits):
 
    """Count the number of zero bits on the right hand side.
 

	
 
    Args:
 
        number: an integer.
 
        bits: maximum number of bits to count.
 

	
 
    Returns:
 
        The number of zero bits on the right hand side of the number.
 

	
 
    """
 
    if number == 0:
 
        return bits
 
    for i in range(bits):
 
        if (number >> i) % 2:
 
            return i
 

	
 

	
 
def summarize_address_range(first, last):
 
    """Summarize a network range given the first and last IP addresses.
 

	
 
    Example:
 
        >>> summarize_address_range(IPv4Address('1.1.1.0'),
 
            IPv4Address('1.1.1.130'))
 
        [IPv4Network('1.1.1.0/25'), IPv4Network('1.1.1.128/31'),
 
        IPv4Network('1.1.1.130/32')]
 

	
 
    Args:
 
        first: the first IPv4Address or IPv6Address in the range.
 
        last: the last IPv4Address or IPv6Address in the range.
 

	
 
    Returns:
 
        The address range collapsed to a list of IPv4Network's or
 
        IPv6Network's.
 

	
 
    Raise:
 
        TypeError:
 
            If the first and last objects are not IP addresses.
 
            If the first and last objects are not the same version.
 
        ValueError:
 
            If the last object is not greater than the first.
 
            If the version is not 4 or 6.
 

	
 
    """
 
    if not (isinstance(first, _BaseIP) and isinstance(last, _BaseIP)):
 
        raise TypeError('first and last must be IP addresses, not networks')
 
    if first.version != last.version:
 
        raise TypeError("%s and %s are not of the same version" % (
 
                str(first), str(last)))
 
    if first > last:
 
        raise ValueError('last IP address must be greater than first')
 

	
 
    networks = []
 

	
 
    if first.version == 4:
 
        ip = IPv4Network
 
    elif first.version == 6:
 
        ip = IPv6Network
 
    else:
 
        raise ValueError('unknown IP version')
 

	
 
    ip_bits = first._max_prefixlen
 
    first_int = first._ip
 
    last_int = last._ip
 
    while first_int <= last_int:
 
        nbits = _count_righthand_zero_bits(first_int, ip_bits)
 
        current = None
 
        while nbits >= 0:
 
            addend = 2 ** nbits - 1
 
            current = first_int + addend
 
            nbits -= 1
 
            if current <= last_int:
 
                break
 
        prefix = _get_prefix_length(first_int, current, ip_bits)
 
        net = ip('%s/%d' % (str(first), prefix))
 
        networks.append(net)
 
        if current == ip._ALL_ONES:
 
            break
 
        first_int = current + 1
 
        first = IPAddress(first_int, version=first._version)
 
    return networks
 

	
 

	
 
def _collapse_address_list_recursive(addresses):
 
    """Loops through the addresses, collapsing concurrent netblocks.
 

	
 
    Example:
 

	
 
        ip1 = IPv4Network('1.1.0.0/24')
 
        ip2 = IPv4Network('1.1.1.0/24')
 
        ip3 = IPv4Network('1.1.2.0/24')
 
        ip4 = IPv4Network('1.1.3.0/24')
 
        ip5 = IPv4Network('1.1.4.0/24')
 
        ip6 = IPv4Network('1.1.0.1/22')
 

	
 
        _collapse_address_list_recursive([ip1, ip2, ip3, ip4, ip5, ip6]) ->
 
          [IPv4Network('1.1.0.0/22'), IPv4Network('1.1.4.0/24')]
 

	
 
        This shouldn't be called directly; it is called via
 
          collapse_address_list([]).
 

	
 
    Args:
 
        addresses: A list of IPv4Network's or IPv6Network's
 

	
 
    Returns:
 
        A list of IPv4Network's or IPv6Network's depending on what we were
 
        passed.
 

	
 
    """
 
    ret_array = []
 
    optimized = False
 

	
 
    for cur_addr in addresses:
 
        if not ret_array:
 
            ret_array.append(cur_addr)
 
            continue
 
        if cur_addr in ret_array[-1]:
 
            optimized = True
 
        elif cur_addr == ret_array[-1].supernet().subnet()[1]:
 
            ret_array.append(ret_array.pop().supernet())
 
            optimized = True
 
        else:
 
            ret_array.append(cur_addr)
 

	
 
    if optimized:
 
        return _collapse_address_list_recursive(ret_array)
 

	
 
    return ret_array
 

	
 

	
 
def collapse_address_list(addresses):
 
    """Collapse a list of IP objects.
 

	
 
    Example:
 
        collapse_address_list([IPv4('1.1.0.0/24'), IPv4('1.1.1.0/24')]) ->
 
          [IPv4('1.1.0.0/23')]
 

	
 
    Args:
 
        addresses: A list of IPv4Network or IPv6Network objects.
 

	
 
    Returns:
 
        A list of IPv4Network or IPv6Network objects depending on what we
 
        were passed.
 

	
 
    Raises:
 
        TypeError: If passed a list of mixed version objects.
 

	
 
    """
 
    i = 0
 
    addrs = []
 
    ips = []
 
    nets = []
 

	
 
    # split IP addresses and networks
 
    for ip in addresses:
 
        if isinstance(ip, _BaseIP):
 
            if ips and ips[-1]._version != ip._version:
 
                raise TypeError("%s and %s are not of the same version" % (
 
                        str(ip), str(ips[-1])))
 
            ips.append(ip)
 
        elif ip._prefixlen == ip._max_prefixlen:
 
            if ips and ips[-1]._version != ip._version:
 
                raise TypeError("%s and %s are not of the same version" % (
 
                        str(ip), str(ips[-1])))
 
            ips.append(ip.ip)
 
        else:
 
            if nets and nets[-1]._version != ip._version:
 
                raise TypeError("%s and %s are not of the same version" % (
 
                        str(ip), str(nets[-1])))
 
            nets.append(ip)
 

	
 
    # sort and dedup
 
    ips = sorted(set(ips))
 
    nets = sorted(set(nets))
 

	
 
    while i < len(ips):
 
        (first, last) = _find_address_range(ips[i:])
 
        i = ips.index(last) + 1
 
        addrs.extend(summarize_address_range(first, last))
 

	
 
    return _collapse_address_list_recursive(sorted(
 
        addrs + nets, key=_BaseNet._get_networks_key))
 

	
 
# backwards compatibility
 
CollapseAddrList = collapse_address_list
 

	
 
# We need to distinguish between the string and packed-bytes representations
 
# of an IP address.  For example, b'0::1' is the IPv4 address 48.58.58.49,
 
# while '0::1' is an IPv6 address.
 
#
 
# In Python 3, the native 'bytes' type already provides this functionality,
 
# so we use it directly.  For earlier implementations where bytes is not a
 
# distinct type, we create a subclass of str to serve as a tag.
 
#
 
# Usage example (Python 2):
 
#   ip = ipaddr.IPAddress(ipaddr.Bytes('xxxx'))
 
#
 
# Usage example (Python 3):
 
#   ip = ipaddr.IPAddress(b'xxxx')
 
try:
 
    if bytes is str:
 
        raise TypeError("bytes is not a distinct type")
 
    Bytes = bytes
 
except (NameError, TypeError):
 
    class Bytes(str):
 
        def __repr__(self):
 
            return 'Bytes(%s)' % str.__repr__(self)
 

	
 

	
 
def get_mixed_type_key(obj):
 
    """Return a key suitable for sorting between networks and addresses.
 

	
 
    Address and Network objects are not sortable by default; they're
 
    fundamentally different so the expression
 

	
 
        IPv4Address('1.1.1.1') <= IPv4Network('1.1.1.1/24')
 

	
 
    doesn't make any sense.  There are some times however, where you may wish
 
    to have ipaddr sort these for you anyway. If you need to do this, you
 
    can use this function as the key= argument to sorted().
 

	
 
    Args:
 
      obj: either a Network or Address object.
 
    Returns:
 
      appropriate key.
 

	
 
    """
 
    if isinstance(obj, _BaseNet):
 
        return obj._get_networks_key()
 
    elif isinstance(obj, _BaseIP):
 
        return obj._get_address_key()
 
    return NotImplemented
 

	
 

	
 
class _IPAddrBase(object):
 

	
 
    """The mother class."""
 

	
 
    def __index__(self):
 
        return self._ip
 

	
 
    def __int__(self):
 
        return self._ip
 

	
 
    def __hex__(self):
 
        return hex(self._ip)
 

	
 
    @property
 
    def exploded(self):
 
        """Return the longhand version of the IP address as a string."""
 
        return self._explode_shorthand_ip_string()
 

	
 
    @property
 
    def compressed(self):
 
        """Return the shorthand version of the IP address as a string."""
 
        return str(self)
 

	
 

	
 
class _BaseIP(_IPAddrBase):
 

	
 
    """A generic IP object.
 

	
 
    This IP class contains the version independent methods which are
 
    used by single IP addresses.
 

	
 
    """
 

	
 
    def __eq__(self, other):
 
        try:
 
            return (self._ip == other._ip
 
                    and self._version == other._version)
 
        except AttributeError:
 
            return NotImplemented
 

	
 
    def __ne__(self, other):
 
        eq = self.__eq__(other)
 
        if eq is NotImplemented:
 
            return NotImplemented
 
        return not eq
 

	
 
    def __le__(self, other):
 
        gt = self.__gt__(other)
 
        if gt is NotImplemented:
 
            return NotImplemented
 
        return not gt
 

	
 
    def __ge__(self, other):
 
        lt = self.__lt__(other)
 
        if lt is NotImplemented:
 
            return NotImplemented
 
        return not lt
 

	
 
    def __lt__(self, other):
 
        if self._version != other._version:
 
            raise TypeError('%s and %s are not of the same version' % (
 
                    str(self), str(other)))
 
        if not isinstance(other, _BaseIP):
 
            raise TypeError('%s and %s are not of the same type' % (
 
                    str(self), str(other)))
 
        if self._ip != other._ip:
 
            return self._ip < other._ip
 
        return False
 

	
 
    def __gt__(self, other):
 
        if self._version != other._version:
 
            raise TypeError('%s and %s are not of the same version' % (
 
                    str(self), str(other)))
 
        if not isinstance(other, _BaseIP):
 
            raise TypeError('%s and %s are not of the same type' % (
 
                    str(self), str(other)))
 
        if self._ip != other._ip:
 
            return self._ip > other._ip
 
        return False
 

	
 
    # Shorthand for Integer addition and subtraction. This is not
 
    # meant to ever support addition/subtraction of addresses.
 
    def __add__(self, other):
 
        if not isinstance(other, int):
 
            return NotImplemented
 
        return IPAddress(int(self) + other, version=self._version)
 

	
 
    def __sub__(self, other):
 
        if not isinstance(other, int):
 
            return NotImplemented
 
        return IPAddress(int(self) - other, version=self._version)
 

	
 
    def __repr__(self):
 
        return '%s(%r)' % (self.__class__.__name__, str(self))
 

	
 
    def __str__(self):
 
        return  '%s' % self._string_from_ip_int(self._ip)
 

	
 
    def __hash__(self):
 
        return hash(hex(long(self._ip)))
 

	
 
    def _get_address_key(self):
 
        return (self._version, self)
 

	
 
    @property
 
    def version(self):
 
        raise NotImplementedError('BaseIP has no version')
 

	
 

	
 
class _BaseNet(_IPAddrBase):
 

	
 
    """A generic IP object.
 

	
 
    This IP class contains the version independent methods which are
 
    used by networks.
 

	
 
    """
 

	
 
    def __init__(self, address):
 
        self._cache = {}
 

	
 
    def __repr__(self):
 
        return '%s(%r)' % (self.__class__.__name__, str(self))
 

	
 
    def iterhosts(self):
 
        """Generate Iterator over usable hosts in a network.
 

	
 
           This is like __iter__ except it doesn't return the network
 
           or broadcast addresses.
 

	
 
        """
 
        cur = int(self.network) + 1
 
        bcast = int(self.broadcast) - 1
 
        while cur <= bcast:
 
            cur += 1
 
            yield IPAddress(cur - 1, version=self._version)
 

	
 
    def __iter__(self):
 
        cur = int(self.network)
 
        bcast = int(self.broadcast)
 
        while cur <= bcast:
 
            cur += 1
 
            yield IPAddress(cur - 1, version=self._version)
 

	
 
    def __getitem__(self, n):
 
        network = int(self.network)
 
        broadcast = int(self.broadcast)
 
        if n >= 0:
 
            if network + n > broadcast:
 
                raise IndexError
 
            return IPAddress(network + n, version=self._version)
 
        else:
 
            n += 1
 
            if broadcast + n < network:
 
                raise IndexError
 
            return IPAddress(broadcast + n, version=self._version)
 

	
 
    def __lt__(self, other):
 
        if self._version != other._version:
 
            raise TypeError('%s and %s are not of the same version' % (
 
                    str(self), str(other)))
 
        if not isinstance(other, _BaseNet):
 
            raise TypeError('%s and %s are not of the same type' % (
 
                    str(self), str(other)))
 
        if self.network != other.network:
 
            return self.network < other.network
 
        if self.netmask != other.netmask:
 
            return self.netmask < other.netmask
 
        return False
 

	
 
    def __gt__(self, other):
 
        if self._version != other._version:
 
            raise TypeError('%s and %s are not of the same version' % (
 
                    str(self), str(other)))
 
        if not isinstance(other, _BaseNet):
 
            raise TypeError('%s and %s are not of the same type' % (
 
                    str(self), str(other)))
 
        if self.network != other.network:
 
            return self.network > other.network
 
        if self.netmask != other.netmask:
 
            return self.netmask > other.netmask
 
        return False
 

	
 
    def __le__(self, other):
 
        gt = self.__gt__(other)
 
        if gt is NotImplemented:
 
            return NotImplemented
 
        return not gt
 

	
 
    def __ge__(self, other):
 
        lt = self.__lt__(other)
 
        if lt is NotImplemented:
 
            return NotImplemented
 
        return not lt
 

	
 
    def __eq__(self, other):
 
        try:
 
            return (self._version == other._version
 
                    and self.network == other.network
 
                    and int(self.netmask) == int(other.netmask))
 
        except AttributeError:
 
            if isinstance(other, _BaseIP):
 
                return (self._version == other._version
 
                        and self._ip == other._ip)
 

	
 
    def __ne__(self, other):
 
        eq = self.__eq__(other)
 
        if eq is NotImplemented:
 
            return NotImplemented
 
        return not eq
 

	
 
    def __str__(self):
 
        return  '%s/%s' % (str(self.ip),
 
                           str(self._prefixlen))
 

	
 
    def __hash__(self):
 
        return hash(int(self.network) ^ int(self.netmask))
 

	
 
    def __contains__(self, other):
 
        # always false if one is v4 and the other is v6.
 
        if self._version != other._version:
 
            return False
 
        # dealing with another network.
 
        if isinstance(other, _BaseNet):
 
            return (self.network <= other.network and
 
                    self.broadcast >= other.broadcast)
 
        # dealing with another address
 
        else:
 
            return (int(self.network) <= int(other._ip) <=
 
                    int(self.broadcast))
 

	
 
    def overlaps(self, other):
 
        """Tell if self is partly contained in other."""
 
        return self.network in other or self.broadcast in other or (
 
            other.network in self or other.broadcast in self)
 

	
 
    @property
 
    def network(self):
 
        x = self._cache.get('network')
 
        if x is None:
 
            x = IPAddress(self._ip & int(self.netmask), version=self._version)
 
            self._cache['network'] = x
 
        return x
 

	
 
    @property
 
    def broadcast(self):
 
        x = self._cache.get('broadcast')
 
        if x is None:
 
            x = IPAddress(self._ip | int(self.hostmask), version=self._version)
 
            self._cache['broadcast'] = x
 
        return x
 

	
 
    @property
 
    def hostmask(self):
 
        x = self._cache.get('hostmask')
 
        if x is None:
 
            x = IPAddress(int(self.netmask) ^ self._ALL_ONES,
 
                          version=self._version)
 
            self._cache['hostmask'] = x
 
        return x
 

	
 
    @property
 
    def with_prefixlen(self):
 
        return '%s/%d' % (str(self.ip), self._prefixlen)
 

	
 
    @property
 
    def with_netmask(self):
 
        return '%s/%s' % (str(self.ip), str(self.netmask))
 

	
 
    @property
 
    def with_hostmask(self):
 
        return '%s/%s' % (str(self.ip), str(self.hostmask))
 

	
 
    @property
 
    def numhosts(self):
 
        """Number of hosts in the current subnet."""
 
        return int(self.broadcast) - int(self.network) + 1
 

	
 
    @property
 
    def version(self):
 
        raise NotImplementedError('BaseNet has no version')
 

	
 
    @property
 
    def prefixlen(self):
 
        return self._prefixlen
 

	
 
    def address_exclude(self, other):
 
        """Remove an address from a larger block.
 

	
 
        For example:
 

	
 
            addr1 = IPNetwork('10.1.1.0/24')
 
            addr2 = IPNetwork('10.1.1.0/26')
 
            addr1.address_exclude(addr2) =
 
                [IPNetwork('10.1.1.64/26'), IPNetwork('10.1.1.128/25')]
 

	
 
        or IPv6:
 

	
 
            addr1 = IPNetwork('::1/32')
 
            addr2 = IPNetwork('::1/128')
 
            addr1.address_exclude(addr2) = [IPNetwork('::0/128'),
 
                IPNetwork('::2/127'),
 
                IPNetwork('::4/126'),
 
                IPNetwork('::8/125'),
 
                ...
 
                IPNetwork('0:0:8000::/33')]
 

	
 
        Args:
 
            other: An IPvXNetwork object of the same type.
 

	
 
        Returns:
 
            A sorted list of IPvXNetwork objects addresses which is self
 
            minus other.
 

	
 
        Raises:
 
            TypeError: If self and other are of difffering address
 
              versions, or if other is not a network object.
 
            ValueError: If other is not completely contained by self.
 

	
 
        """
 
        if not self._version == other._version:
 
            raise TypeError("%s and %s are not of the same version" % (
 
                str(self), str(other)))
 

	
 
        if not isinstance(other, _BaseNet):
 
            raise TypeError("%s is not a network object" % str(other))
 

	
 
        if other not in self:
 
            raise ValueError('%s not contained in %s' % (str(other),
 
                                                         str(self)))
 
        if other == self:
 
            return []
 

	
 
        ret_addrs = []
 

	
 
        # Make sure we're comparing the network of other.
 
        other = IPNetwork('%s/%s' % (str(other.network), str(other.prefixlen)),
 
                   version=other._version)
 

	
 
        s1, s2 = self.subnet()
 
        while s1 != other and s2 != other:
 
            if other in s1:
 
                ret_addrs.append(s2)
 
                s1, s2 = s1.subnet()
 
            elif other in s2:
 
                ret_addrs.append(s1)
 
                s1, s2 = s2.subnet()
 
            else:
 
                # If we got here, there's a bug somewhere.
 
                assert True == False, ('Error performing exclusion: '
 
                                       's1: %s s2: %s other: %s' %
 
                                       (str(s1), str(s2), str(other)))
 
        if s1 == other:
 
            ret_addrs.append(s2)
 
        elif s2 == other:
 
            ret_addrs.append(s1)
 
        else:
 
            # If we got here, there's a bug somewhere.
 
            assert True == False, ('Error performing exclusion: '
 
                                   's1: %s s2: %s other: %s' %
 
                                   (str(s1), str(s2), str(other)))
 

	
 
        return sorted(ret_addrs, key=_BaseNet._get_networks_key)
 

	
 
    def compare_networks(self, other):
 
        """Compare two IP objects.
 

	
 
        This is only concerned about the comparison of the integer
 
        representation of the network addresses.  This means that the
 
        host bits aren't considered at all in this method.  If you want
 
        to compare host bits, you can easily enough do a
 
        'HostA._ip < HostB._ip'
 

	
 
        Args:
 
            other: An IP object.
 

	
 
        Returns:
 
            If the IP versions of self and other are the same, returns:
 

	
 
            -1 if self < other:
 
              eg: IPv4('1.1.1.0/24') < IPv4('1.1.2.0/24')
 
              IPv6('1080::200C:417A') < IPv6('1080::200B:417B')
 
            0 if self == other
 
              eg: IPv4('1.1.1.1/24') == IPv4('1.1.1.2/24')
 
              IPv6('1080::200C:417A/96') == IPv6('1080::200C:417B/96')
 
            1 if self > other
 
              eg: IPv4('1.1.1.0/24') > IPv4('1.1.0.0/24')
 
              IPv6('1080::1:200C:417A/112') >
 
              IPv6('1080::0:200C:417A/112')
 

	
 
            If the IP versions of self and other are different, returns:
 

	
 
            -1 if self._version < other._version
 
              eg: IPv4('10.0.0.1/24') < IPv6('::1/128')
 
            1 if self._version > other._version
 
              eg: IPv6('::1/128') > IPv4('255.255.255.0/24')
 

	
 
        """
 
        if self._version < other._version:
 
            return -1
 
        if self._version > other._version:
 
            return 1
 
        # self._version == other._version below here:
 
        if self.network < other.network:
 
            return -1
 
        if self.network > other.network:
 
            return 1
 
        # self.network == other.network below here:
 
        if self.netmask < other.netmask:
 
            return -1
 
        if self.netmask > other.netmask:
 
            return 1
 
        # self.network == other.network and self.netmask == other.netmask
 
        return 0
 

	
 
    def _get_networks_key(self):
 
        """Network-only key function.
 

	
 
        Returns an object that identifies this address' network and
 
        netmask. This function is a suitable "key" argument for sorted()
 
        and list.sort().
 

	
 
        """
 
        return (self._version, self.network, self.netmask)
 

	
 
    def _ip_int_from_prefix(self, prefixlen=None):
 
        """Turn the prefix length netmask into a int for comparison.
 

	
 
        Args:
 
            prefixlen: An integer, the prefix length.
 

	
 
        Returns:
 
            An integer.
 

	
 
        """
 
        if not prefixlen and prefixlen != 0:
 
            prefixlen = self._prefixlen
 
        return self._ALL_ONES ^ (self._ALL_ONES >> prefixlen)
 

	
 
    def _prefix_from_ip_int(self, ip_int, mask=32):
 
        """Return prefix length from the decimal netmask.
 

	
 
        Args:
 
            ip_int: An integer, the IP address.
 
            mask: The netmask.  Defaults to 32.
 

	
 
        Returns:
 
            An integer, the prefix length.
 

	
 
        """
 
        while mask:
 
            if ip_int & 1 == 1:
 
                break
 
            ip_int >>= 1
 
            mask -= 1
 

	
 
        return mask
 

	
 
    def _ip_string_from_prefix(self, prefixlen=None):
 
        """Turn a prefix length into a dotted decimal string.
 

	
 
        Args:
 
            prefixlen: An integer, the netmask prefix length.
 

	
 
        Returns:
 
            A string, the dotted decimal netmask string.
 

	
 
        """
 
        if not prefixlen:
 
            prefixlen = self._prefixlen
 
        return self._string_from_ip_int(self._ip_int_from_prefix(prefixlen))
 

	
 
    def iter_subnets(self, prefixlen_diff=1, new_prefix=None):
 
        """The subnets which join to make the current subnet.
 

	
 
        In the case that self contains only one IP
 
        (self._prefixlen == 32 for IPv4 or self._prefixlen == 128
 
        for IPv6), return a list with just ourself.
 

	
 
        Args:
 
            prefixlen_diff: An integer, the amount the prefix length
 
              should be increased by. This should not be set if
 
              new_prefix is also set.
 
            new_prefix: The desired new prefix length. This must be a
 
              larger number (smaller prefix) than the existing prefix.
 
              This should not be set if prefixlen_diff is also set.
 

	
 
        Returns:
 
            An iterator of IPv(4|6) objects.
 

	
 
        Raises:
 
            ValueError: The prefixlen_diff is too small or too large.
 
                OR
 
            prefixlen_diff and new_prefix are both set or new_prefix
 
              is a smaller number than the current prefix (smaller
 
              number means a larger network)
 

	
 
        """
 
        if self._prefixlen == self._max_prefixlen:
 
            yield self
 
            return
 

	
 
        if new_prefix is not None:
 
            if new_prefix < self._prefixlen:
 
                raise ValueError('new prefix must be longer')
 
            if prefixlen_diff != 1:
 
                raise ValueError('cannot set prefixlen_diff and new_prefix')
 
            prefixlen_diff = new_prefix - self._prefixlen
 

	
 
        if prefixlen_diff < 0:
 
            raise ValueError('prefix length diff must be > 0')
 
        new_prefixlen = self._prefixlen + prefixlen_diff
 

	
 
        if not self._is_valid_netmask(str(new_prefixlen)):
 
            raise ValueError(
 
                'prefix length diff %d is invalid for netblock %s' % (
 
                    new_prefixlen, str(self)))
 

	
 
        first = IPNetwork('%s/%s' % (str(self.network),
 
                                     str(self._prefixlen + prefixlen_diff)),
 
                         version=self._version)
 

	
 
        yield first
 
        current = first
 
        while True:
 
            broadcast = current.broadcast
 
            if broadcast == self.broadcast:
 
                return
 
            new_addr = IPAddress(int(broadcast) + 1, version=self._version)
 
            current = IPNetwork('%s/%s' % (str(new_addr), str(new_prefixlen)),
 
                                version=self._version)
 

	
 
            yield current
 

	
 
    def masked(self):
 
        """Return the network object with the host bits masked out."""
 
        return IPNetwork('%s/%d' % (self.network, self._prefixlen),
 
                         version=self._version)
 

	
 
    def subnet(self, prefixlen_diff=1, new_prefix=None):
 
        """Return a list of subnets, rather than an iterator."""
 
        return list(self.iter_subnets(prefixlen_diff, new_prefix))
 

	
 
    def supernet(self, prefixlen_diff=1, new_prefix=None):
 
        """The supernet containing the current network.
 

	
 
        Args:
 
            prefixlen_diff: An integer, the amount the prefix length of
 
              the network should be decreased by.  For example, given a
 
              /24 network and a prefixlen_diff of 3, a supernet with a
 
              /21 netmask is returned.
 

	
 
        Returns:
 
            An IPv4 network object.
 

	
 
        Raises:
 
            ValueError: If self.prefixlen - prefixlen_diff < 0. I.e., you have a
 
              negative prefix length.
 
                OR
 
            If prefixlen_diff and new_prefix are both set or new_prefix is a
 
              larger number than the current prefix (larger number means a
 
              smaller network)
 

	
 
        """
 
        if self._prefixlen == 0:
 
            return self
 

	
 
        if new_prefix is not None:
 
            if new_prefix > self._prefixlen:
 
                raise ValueError('new prefix must be shorter')
 
            if prefixlen_diff != 1:
 
                raise ValueError('cannot set prefixlen_diff and new_prefix')
 
            prefixlen_diff = self._prefixlen - new_prefix
 

	
 
        if self.prefixlen - prefixlen_diff < 0:
 
            raise ValueError(
 
                'current prefixlen is %d, cannot have a prefixlen_diff of %d' %
 
                (self.prefixlen, prefixlen_diff))
 
        return IPNetwork('%s/%s' % (str(self.network),
 
                                    str(self.prefixlen - prefixlen_diff)),
 
                         version=self._version)
 

	
 
    # backwards compatibility
 
    Subnet = subnet
 
    Supernet = supernet
 
    AddressExclude = address_exclude
 
    CompareNetworks = compare_networks
 
    Contains = __contains__
 

	
 

	
 
class _BaseV4(object):
 

	
 
    """Base IPv4 object.
 

	
 
    The following methods are used by IPv4 objects in both single IP
 
    addresses and networks.
 

	
 
    """
 

	
 
    # Equivalent to 255.255.255.255 or 32 bits of 1's.
 
    _ALL_ONES = (2 ** IPV4LENGTH) - 1
 
    _DECIMAL_DIGITS = frozenset('0123456789')
 

	
 
    def __init__(self, address):
 
        self._version = 4
 
        self._max_prefixlen = IPV4LENGTH
 

	
 
    def _explode_shorthand_ip_string(self):
 
        return str(self)
 

	
 
    def _ip_int_from_string(self, ip_str):
 
        """Turn the given IP string into an integer for comparison.
 

	
 
        Args:
 
            ip_str: A string, the IP ip_str.
 

	
 
        Returns:
 
            The IP ip_str as an integer.
 

	
 
        Raises:
 
            AddressValueError: if ip_str isn't a valid IPv4 Address.
 

	
 
        """
 
        octets = ip_str.split('.')
 
        if len(octets) != 4:
 
            raise AddressValueError(ip_str)
 

	
 
        packed_ip = 0
 
        for oc in octets:
 
            try:
 
                packed_ip = (packed_ip << 8) | self._parse_octet(oc)
 
            except ValueError:
 
                raise AddressValueError(ip_str)
 
        return packed_ip
 

	
 
    def _parse_octet(self, octet_str):
 
        """Convert a decimal octet into an integer.
 

	
 
        Args:
 
            octet_str: A string, the number to parse.
 

	
 
        Returns:
 
            The octet as an integer.
 

	
 
        Raises:
 
            ValueError: if the octet isn't strictly a decimal from [0..255].
 

	
 
        """
 
        # Whitelist the characters, since int() allows a lot of bizarre stuff.
 
        if not self._DECIMAL_DIGITS.issuperset(octet_str):
 
            raise ValueError
 
        octet_int = int(octet_str, 10)
 
        # Disallow leading zeroes, because no clear standard exists on
 
        # whether these should be interpreted as decimal or octal.
 
        if octet_int > 255 or (octet_str[0] == '0' and len(octet_str) > 1):
 
            raise ValueError
 
        return octet_int
 

	
 
    def _string_from_ip_int(self, ip_int):
 
        """Turns a 32-bit integer into dotted decimal notation.
 

	
 
        Args:
 
            ip_int: An integer, the IP address.
 

	
 
        Returns:
 
            The IP address as a string in dotted decimal notation.
 

	
 
        """
 
        octets = []
 
        for _ in xrange(4):
 
            octets.insert(0, str(ip_int & 0xFF))
 
            ip_int >>= 8
 
        return '.'.join(octets)
 

	
 
    @property
 
    def max_prefixlen(self):
 
        return self._max_prefixlen
 

	
 
    @property
 
    def packed(self):
 
        """The binary representation of this address."""
 
        return v4_int_to_packed(self._ip)
 

	
 
    @property
 
    def version(self):
 
        return self._version
 

	
 
    @property
 
    def is_reserved(self):
 
        """Test if the address is otherwise IETF reserved.
 

	
 
         Returns:
 
             A boolean, True if the address is within the
 
             reserved IPv4 Network range.
 

	
 
        """
 
        return self in IPv4Network('240.0.0.0/4')
 

	
 
    @property
 
    def is_private(self):
 
        """Test if this address is allocated for private networks.
 

	
 
        Returns:
 
            A boolean, True if the address is reserved per RFC 1918.
 

	
 
        """
 
        return (self in IPv4Network('10.0.0.0/8') or
 
                self in IPv4Network('172.16.0.0/12') or
 
                self in IPv4Network('192.168.0.0/16'))
 

	
 
    @property
 
    def is_multicast(self):
 
        """Test if the address is reserved for multicast use.
 

	
 
        Returns:
 
            A boolean, True if the address is multicast.
 
            See RFC 3171 for details.
 

	
 
        """
 
        return self in IPv4Network('224.0.0.0/4')
 

	
 
    @property
 
    def is_unspecified(self):
 
        """Test if the address is unspecified.
 

	
 
        Returns:
 
            A boolean, True if this is the unspecified address as defined in
 
            RFC 5735 3.
 

	
 
        """
 
        return self in IPv4Network('0.0.0.0')
 

	
 
    @property
 
    def is_loopback(self):
 
        """Test if the address is a loopback address.
 

	
 
        Returns:
 
            A boolean, True if the address is a loopback per RFC 3330.
 

	
 
        """
 
        return self in IPv4Network('127.0.0.0/8')
 

	
 
    @property
 
    def is_link_local(self):
 
        """Test if the address is reserved for link-local.
 

	
 
        Returns:
 
            A boolean, True if the address is link-local per RFC 3927.
 

	
 
        """
 
        return self in IPv4Network('169.254.0.0/16')
 

	
 

	
 
class IPv4Address(_BaseV4, _BaseIP):
 

	
 
    """Represent and manipulate single IPv4 Addresses."""
 

	
 
    def __init__(self, address):
 

	
 
        """
 
        Args:
 
            address: A string or integer representing the IP
 
              '192.168.1.1'
 

	
 
              Additionally, an integer can be passed, so
 
              IPv4Address('192.168.1.1') == IPv4Address(3232235777).
 
              or, more generally
 
              IPv4Address(int(IPv4Address('192.168.1.1'))) ==
 
                IPv4Address('192.168.1.1')
 

	
 
        Raises:
 
            AddressValueError: If ipaddr isn't a valid IPv4 address.
 

	
 
        """
 
        _BaseV4.__init__(self, address)
 

	
 
        # Efficient constructor from integer.
 
        if isinstance(address, (int, long)):
 
            self._ip = address
 
            if address < 0 or address > self._ALL_ONES:
 
                raise AddressValueError(address)
 
            return
 

	
 
        # Constructing from a packed address
 
        if isinstance(address, Bytes):
 
            try:
 
                self._ip, = struct.unpack('!I', address)
 
            except struct.error:
 
                raise AddressValueError(address)  # Wrong length.
 
            return
 

	
 
        # Assume input argument to be string or any object representation
 
        # which converts into a formatted IP string.
 
        addr_str = str(address)
 
        self._ip = self._ip_int_from_string(addr_str)
 

	
 

	
 
class IPv4Network(_BaseV4, _BaseNet):
 

	
 
    """This class represents and manipulates 32-bit IPv4 networks.
 

	
 
    Attributes: [examples for IPv4Network('1.2.3.4/27')]
 
        ._ip: 16909060
 
        .ip: IPv4Address('1.2.3.4')
 
        .network: IPv4Address('1.2.3.0')
 
        .hostmask: IPv4Address('0.0.0.31')
 
        .broadcast: IPv4Address('1.2.3.31')
 
        .netmask: IPv4Address('255.255.255.224')
 
        .prefixlen: 27
 

	
 
    """
 

	
 
    # the valid octets for host and netmasks. only useful for IPv4.
 
    _valid_mask_octets = set((255, 254, 252, 248, 240, 224, 192, 128, 0))
 

	
 
    def __init__(self, address, strict=False):
 
        """Instantiate a new IPv4 network object.
 

	
 
        Args:
 
            address: A string or integer representing the IP [& network].
 
              '192.168.1.1/24'
 
              '192.168.1.1/255.255.255.0'
 
              '192.168.1.1/0.0.0.255'
 
              are all functionally the same in IPv4. Similarly,
 
              '192.168.1.1'
 
              '192.168.1.1/255.255.255.255'
 
              '192.168.1.1/32'
 
              are also functionaly equivalent. That is to say, failing to
 
              provide a subnetmask will create an object with a mask of /32.
 

	
 
              If the mask (portion after the / in the argument) is given in
 
              dotted quad form, it is treated as a netmask if it starts with a
 
              non-zero field (e.g. /255.0.0.0 == /8) and as a hostmask if it
 
              starts with a zero field (e.g. 0.255.255.255 == /8), with the
 
              single exception of an all-zero mask which is treated as a
 
              netmask == /0. If no mask is given, a default of /32 is used.
 

	
 
              Additionally, an integer can be passed, so
 
              IPv4Network('192.168.1.1') == IPv4Network(3232235777).
 
              or, more generally
 
              IPv4Network(int(IPv4Network('192.168.1.1'))) ==
 
                IPv4Network('192.168.1.1')
 

	
 
            strict: A boolean. If true, ensure that we have been passed
 
              A true network address, eg, 192.168.1.0/24 and not an
 
              IP address on a network, eg, 192.168.1.1/24.
 

	
 
        Raises:
 
            AddressValueError: If ipaddr isn't a valid IPv4 address.
 
            NetmaskValueError: If the netmask isn't valid for
 
              an IPv4 address.
 
            ValueError: If strict was True and a network address was not
 
              supplied.
 

	
 
        """
 
        _BaseNet.__init__(self, address)
 
        _BaseV4.__init__(self, address)
 

	
 
        # Constructing from an integer or packed bytes.
 
        if isinstance(address, (int, long, Bytes)):
 
            self.ip = IPv4Address(address)
 
            self._ip = self.ip._ip
 
            self._prefixlen = self._max_prefixlen
 
            self.netmask = IPv4Address(self._ALL_ONES)
 
            return
 

	
 
        # Assume input argument to be string or any object representation
 
        # which converts into a formatted IP prefix string.
 
        addr = str(address).split('/')
 

	
 
        if len(addr) > 2:
 
            raise AddressValueError(address)
 

	
 
        self._ip = self._ip_int_from_string(addr[0])
 
        self.ip = IPv4Address(self._ip)
 

	
 
        if len(addr) == 2:
 
            mask = addr[1].split('.')
 
            if len(mask) == 4:
 
                # We have dotted decimal netmask.
 
                if self._is_valid_netmask(addr[1]):
 
                    self.netmask = IPv4Address(self._ip_int_from_string(
 
                            addr[1]))
 
                elif self._is_hostmask(addr[1]):
 
                    self.netmask = IPv4Address(
 
                        self._ip_int_from_string(addr[1]) ^ self._ALL_ONES)
 
                else:
 
                    raise NetmaskValueError('%s is not a valid netmask'
 
                                                     % addr[1])
 

	
 
                self._prefixlen = self._prefix_from_ip_int(int(self.netmask))
 
            else:
 
                # We have a netmask in prefix length form.
 
                if not self._is_valid_netmask(addr[1]):
 
                    raise NetmaskValueError(addr[1])
 
                self._prefixlen = int(addr[1])
 
                self.netmask = IPv4Address(self._ip_int_from_prefix(
 
                    self._prefixlen))
 
        else:
 
            self._prefixlen = self._max_prefixlen
 
            self.netmask = IPv4Address(self._ip_int_from_prefix(
 
                self._prefixlen))
 
        if strict:
 
            if self.ip != self.network:
 
                raise ValueError('%s has host bits set' %
 
                                 self.ip)
 
        if self._prefixlen == (self._max_prefixlen - 1):
 
            self.iterhosts = self.__iter__
 

	
 
    def _is_hostmask(self, ip_str):
 
        """Test if the IP string is a hostmask (rather than a netmask).
 

	
 
        Args:
 
            ip_str: A string, the potential hostmask.
 

	
 
        Returns:
 
            A boolean, True if the IP string is a hostmask.
 

	
 
        """
 
        bits = ip_str.split('.')
 
        try:
 
            parts = [int(x) for x in bits if int(x) in self._valid_mask_octets]
 
        except ValueError:
 
            return False
 
        if len(parts) != len(bits):
 
            return False
 
        if parts[0] < parts[-1]:
 
            return True
 
        return False
 

	
 
    def _is_valid_netmask(self, netmask):
 
        """Verify that the netmask is valid.
 

	
 
        Args:
 
            netmask: A string, either a prefix or dotted decimal
 
              netmask.
 

	
 
        Returns:
 
            A boolean, True if the prefix represents a valid IPv4
 
            netmask.
 

	
 
        """
 
        mask = netmask.split('.')
 
        if len(mask) == 4:
 
            if [x for x in mask if int(x) not in self._valid_mask_octets]:
 
                return False
 
            if [y for idx, y in enumerate(mask) if idx > 0 and
 
                y > mask[idx - 1]]:
 
                return False
 
            return True
 
        try:
 
            netmask = int(netmask)
 
        except ValueError:
 
            return False
 
        return 0 <= netmask <= self._max_prefixlen
 

	
 
    # backwards compatibility
 
    IsRFC1918 = lambda self: self.is_private
 
    IsMulticast = lambda self: self.is_multicast
 
    IsLoopback = lambda self: self.is_loopback
 
    IsLinkLocal = lambda self: self.is_link_local
 

	
 

	
 
class _BaseV6(object):
 

	
 
    """Base IPv6 object.
 

	
 
    The following methods are used by IPv6 objects in both single IP
 
    addresses and networks.
 

	
 
    """
 

	
 
    _ALL_ONES = (2 ** IPV6LENGTH) - 1
 
    _HEXTET_COUNT = 8
 
    _HEX_DIGITS = frozenset('0123456789ABCDEFabcdef')
 

	
 
    def __init__(self, address):
 
        self._version = 6
 
        self._max_prefixlen = IPV6LENGTH
 

	
 
    def _ip_int_from_string(self, ip_str):
 
        """Turn an IPv6 ip_str into an integer.
 

	
 
        Args:
 
            ip_str: A string, the IPv6 ip_str.
 

	
 
        Returns:
 
            A long, the IPv6 ip_str.
 

	
 
        Raises:
 
            AddressValueError: if ip_str isn't a valid IPv6 Address.
 

	
 
        """
 
        parts = ip_str.split(':')
 

	
 
        # An IPv6 address needs at least 2 colons (3 parts).
 
        if len(parts) < 3:
 
            raise AddressValueError(ip_str)
 

	
 
        # If the address has an IPv4-style suffix, convert it to hexadecimal.
 
        if '.' in parts[-1]:
 
            ipv4_int = IPv4Address(parts.pop())._ip
 
            parts.append('%x' % ((ipv4_int >> 16) & 0xFFFF))
 
            parts.append('%x' % (ipv4_int & 0xFFFF))
 

	
 
        # An IPv6 address can't have more than 8 colons (9 parts).
 
        if len(parts) > self._HEXTET_COUNT + 1:
 
            raise AddressValueError(ip_str)
 

	
 
        # Disregarding the endpoints, find '::' with nothing in between.
 
        # This indicates that a run of zeroes has been skipped.
 
        try:
 
            skip_index, = (
 
                [i for i in xrange(1, len(parts) - 1) if not parts[i]] or
 
                [None])
 
        except ValueError:
 
            # Can't have more than one '::'
 
            raise AddressValueError(ip_str)
 

	
 
        # parts_hi is the number of parts to copy from above/before the '::'
 
        # parts_lo is the number of parts to copy from below/after the '::'
 
        if skip_index is not None:
 
            # If we found a '::', then check if it also covers the endpoints.
 
            parts_hi = skip_index
 
            parts_lo = len(parts) - skip_index - 1
 
            if not parts[0]:
 
                parts_hi -= 1
 
                if parts_hi:
 
                    raise AddressValueError(ip_str)  # ^: requires ^::
 
            if not parts[-1]:
 
                parts_lo -= 1
 
                if parts_lo:
 
                    raise AddressValueError(ip_str)  # :$ requires ::$
 
            parts_skipped = self._HEXTET_COUNT - (parts_hi + parts_lo)
 
            if parts_skipped < 1:
 
                raise AddressValueError(ip_str)
 
        else:
 
            # Otherwise, allocate the entire address to parts_hi.  The endpoints
 
            # could still be empty, but _parse_hextet() will check for that.
 
            if len(parts) != self._HEXTET_COUNT:
 
                raise AddressValueError(ip_str)
 
            parts_hi = len(parts)
 
            parts_lo = 0
 
            parts_skipped = 0
 

	
 
        try:
 
            # Now, parse the hextets into a 128-bit integer.
 
            ip_int = 0L
 
            for i in xrange(parts_hi):
 
                ip_int <<= 16
 
                ip_int |= self._parse_hextet(parts[i])
 
            ip_int <<= 16 * parts_skipped
 
            for i in xrange(-parts_lo, 0):
 
                ip_int <<= 16
 
                ip_int |= self._parse_hextet(parts[i])
 
            return ip_int
 
        except ValueError:
 
            raise AddressValueError(ip_str)
 

	
 
    def _parse_hextet(self, hextet_str):
 
        """Convert an IPv6 hextet string into an integer.
 

	
 
        Args:
 
            hextet_str: A string, the number to parse.
 

	
 
        Returns:
 
            The hextet as an integer.
 

	
 
        Raises:
 
            ValueError: if the input isn't strictly a hex number from [0..FFFF].
 

	
 
        """
 
        # Whitelist the characters, since int() allows a lot of bizarre stuff.
 
        if not self._HEX_DIGITS.issuperset(hextet_str):
 
            raise ValueError
 
        if len(hextet_str) > 4:
 
            raise ValueError
 
        hextet_int = int(hextet_str, 16)
 
        if hextet_int > 0xFFFF:
 
            raise ValueError
 
        return hextet_int
 

	
 
    def _compress_hextets(self, hextets):
 
        """Compresses a list of hextets.
 

	
 
        Compresses a list of strings, replacing the longest continuous
 
        sequence of "0" in the list with "" and adding empty strings at
 
        the beginning or at the end of the string such that subsequently
 
        calling ":".join(hextets) will produce the compressed version of
 
        the IPv6 address.
 

	
 
        Args:
 
            hextets: A list of strings, the hextets to compress.
 

	
 
        Returns:
 
            A list of strings.
 

	
 
        """
 
        best_doublecolon_start = -1
 
        best_doublecolon_len = 0
 
        doublecolon_start = -1
 
        doublecolon_len = 0
 
        for index in range(len(hextets)):
 
            if hextets[index] == '0':
 
                doublecolon_len += 1
 
                if doublecolon_start == -1:
 
                    # Start of a sequence of zeros.
 
                    doublecolon_start = index
 
                if doublecolon_len > best_doublecolon_len:
 
                    # This is the longest sequence of zeros so far.
 
                    best_doublecolon_len = doublecolon_len
 
                    best_doublecolon_start = doublecolon_start
 
            else:
 
                doublecolon_len = 0
 
                doublecolon_start = -1
 

	
 
        if best_doublecolon_len > 1:
 
            best_doublecolon_end = (best_doublecolon_start +
 
                                    best_doublecolon_len)
 
            # For zeros at the end of the address.
 
            if best_doublecolon_end == len(hextets):
 
                hextets += ['']
 
            hextets[best_doublecolon_start:best_doublecolon_end] = ['']
 
            # For zeros at the beginning of the address.
 
            if best_doublecolon_start == 0:
 
                hextets = [''] + hextets
 

	
 
        return hextets
 

	
 
    def _string_from_ip_int(self, ip_int=None):
 
        """Turns a 128-bit integer into hexadecimal notation.
 

	
 
        Args:
 
            ip_int: An integer, the IP address.
 

	
 
        Returns:
 
            A string, the hexadecimal representation of the address.
 

	
 
        Raises:
 
            ValueError: The address is bigger than 128 bits of all ones.
 

	
 
        """
 
        if not ip_int and ip_int != 0:
 
            ip_int = int(self._ip)
 

	
 
        if ip_int > self._ALL_ONES:
 
            raise ValueError('IPv6 address is too large')
 

	
 
        hex_str = '%032x' % ip_int
 
        hextets = []
 
        for x in range(0, 32, 4):
 
            hextets.append('%x' % int(hex_str[x:x + 4], 16))
 

	
 
        hextets = self._compress_hextets(hextets)
 
        return ':'.join(hextets)
 

	
 
    def _explode_shorthand_ip_string(self):
 
        """Expand a shortened IPv6 address.
 

	
 
        Args:
 
            ip_str: A string, the IPv6 address.
 

	
 
        Returns:
 
            A string, the expanded IPv6 address.
 

	
 
        """
 
        if isinstance(self, _BaseNet):
 
            ip_str = str(self.ip)
 
        else:
 
            ip_str = str(self)
 

	
 
        ip_int = self._ip_int_from_string(ip_str)
 
        parts = []
 
        for i in xrange(self._HEXTET_COUNT):
 
            parts.append('%04x' % (ip_int & 0xFFFF))
 
            ip_int >>= 16
 
        parts.reverse()
 
        if isinstance(self, _BaseNet):
 
            return '%s/%d' % (':'.join(parts), self.prefixlen)
 
        return ':'.join(parts)
 

	
 
    @property
 
    def max_prefixlen(self):
 
        return self._max_prefixlen
 

	
 
    @property
 
    def packed(self):
 
        """The binary representation of this address."""
 
        return v6_int_to_packed(self._ip)
 

	
 
    @property
 
    def version(self):
 
        return self._version
 

	
 
    @property
 
    def is_multicast(self):
 
        """Test if the address is reserved for multicast use.
 

	
 
        Returns:
 
            A boolean, True if the address is a multicast address.
 
            See RFC 2373 2.7 for details.
 

	
 
        """
 
        return self in IPv6Network('ff00::/8')
 

	
 
    @property
 
    def is_reserved(self):
 
        """Test if the address is otherwise IETF reserved.
 

	
 
        Returns:
 
            A boolean, True if the address is within one of the
 
            reserved IPv6 Network ranges.
 

	
 
        """
 
        return (self in IPv6Network('::/8') or
 
                self in IPv6Network('100::/8') or
 
                self in IPv6Network('200::/7') or
 
                self in IPv6Network('400::/6') or
 
                self in IPv6Network('800::/5') or
 
                self in IPv6Network('1000::/4') or
 
                self in IPv6Network('4000::/3') or
 
                self in IPv6Network('6000::/3') or
 
                self in IPv6Network('8000::/3') or
 
                self in IPv6Network('A000::/3') or
 
                self in IPv6Network('C000::/3') or
 
                self in IPv6Network('E000::/4') or
 
                self in IPv6Network('F000::/5') or
 
                self in IPv6Network('F800::/6') or
 
                self in IPv6Network('FE00::/9'))
 

	
 
    @property
 
    def is_unspecified(self):
 
        """Test if the address is unspecified.
 

	
 
        Returns:
 
            A boolean, True if this is the unspecified address as defined in
 
            RFC 2373 2.5.2.
 

	
 
        """
 
        return self._ip == 0 and getattr(self, '_prefixlen', 128) == 128
 

	
 
    @property
 
    def is_loopback(self):
 
        """Test if the address is a loopback address.
 

	
 
        Returns:
 
            A boolean, True if the address is a loopback address as defined in
 
            RFC 2373 2.5.3.
 

	
 
        """
 
        return self._ip == 1 and getattr(self, '_prefixlen', 128) == 128
 

	
 
    @property
 
    def is_link_local(self):
 
        """Test if the address is reserved for link-local.
 

	
 
        Returns:
 
            A boolean, True if the address is reserved per RFC 4291.
 

	
 
        """
 
        return self in IPv6Network('fe80::/10')
 

	
 
    @property
 
    def is_site_local(self):
 
        """Test if the address is reserved for site-local.
 

	
 
        Note that the site-local address space has been deprecated by RFC 3879.
 
        Use is_private to test if this address is in the space of unique local
 
        addresses as defined by RFC 4193.
 

	
 
        Returns:
 
            A boolean, True if the address is reserved per RFC 3513 2.5.6.
 

	
 
        """
 
        return self in IPv6Network('fec0::/10')
 

	
 
    @property
 
    def is_private(self):
 
        """Test if this address is allocated for private networks.
 

	
 
        Returns:
 
            A boolean, True if the address is reserved per RFC 4193.
 

	
 
        """
 
        return self in IPv6Network('fc00::/7')
 

	
 
    @property
 
    def ipv4_mapped(self):
 
        """Return the IPv4 mapped address.
 

	
 
        Returns:
 
            If the IPv6 address is a v4 mapped address, return the
 
            IPv4 mapped address. Return None otherwise.
 

	
 
        """
 
        if (self._ip >> 32) != 0xFFFF:
 
            return None
 
        return IPv4Address(self._ip & 0xFFFFFFFF)
 

	
 
    @property
 
    def teredo(self):
 
        """Tuple of embedded teredo IPs.
 

	
 
        Returns:
 
            Tuple of the (server, client) IPs or None if the address
 
            doesn't appear to be a teredo address (doesn't start with
 
            2001::/32)
 

	
 
        """
 
        if (self._ip >> 96) != 0x20010000:
 
            return None
 
        return (IPv4Address((self._ip >> 64) & 0xFFFFFFFF),
 
                IPv4Address(~self._ip & 0xFFFFFFFF))
 

	
 
    @property
 
    def sixtofour(self):
 
        """Return the IPv4 6to4 embedded address.
 

	
 
        Returns:
 
            The IPv4 6to4-embedded address if present or None if the
 
            address doesn't appear to contain a 6to4 embedded address.
 

	
 
        """
 
        if (self._ip >> 112) != 0x2002:
 
            return None
 
        return IPv4Address((self._ip >> 80) & 0xFFFFFFFF)
 

	
 

	
 
class IPv6Address(_BaseV6, _BaseIP):
 

	
 
    """Represent and manipulate single IPv6 Addresses.
 
    """
 

	
 
    def __init__(self, address):
 
        """Instantiate a new IPv6 address object.
 

	
 
        Args:
 
            address: A string or integer representing the IP
 

	
 
              Additionally, an integer can be passed, so
 
              IPv6Address('2001:4860::') ==
 
                IPv6Address(42541956101370907050197289607612071936L).
 
              or, more generally
 
              IPv6Address(IPv6Address('2001:4860::')._ip) ==
 
                IPv6Address('2001:4860::')
 

	
 
        Raises:
 
            AddressValueError: If address isn't a valid IPv6 address.
 

	
 
        """
 
        _BaseV6.__init__(self, address)
 

	
 
        # Efficient constructor from integer.
 
        if isinstance(address, (int, long)):
 
            self._ip = address
 
            if address < 0 or address > self._ALL_ONES:
 
                raise AddressValueError(address)
 
            return
 

	
 
        # Constructing from a packed address
 
        if isinstance(address, Bytes):
 
            try:
 
                hi, lo = struct.unpack('!QQ', address)
 
            except struct.error:
 
                raise AddressValueError(address)  # Wrong length.
 
            self._ip = (hi << 64) | lo
 
            return
 

	
 
        # Assume input argument to be string or any object representation
 
        # which converts into a formatted IP string.
 
        addr_str = str(address)
 
        if not addr_str:
 
            raise AddressValueError('')
 

	
 
        self._ip = self._ip_int_from_string(addr_str)
 

	
 

	
 
class IPv6Network(_BaseV6, _BaseNet):
 

	
 
    """This class represents and manipulates 128-bit IPv6 networks.
 

	
 
    Attributes: [examples for IPv6('2001:658:22A:CAFE:200::1/64')]
 
        .ip: IPv6Address('2001:658:22a:cafe:200::1')
 
        .network: IPv6Address('2001:658:22a:cafe::')
 
        .hostmask: IPv6Address('::ffff:ffff:ffff:ffff')
 
        .broadcast: IPv6Address('2001:658:22a:cafe:ffff:ffff:ffff:ffff')
 
        .netmask: IPv6Address('ffff:ffff:ffff:ffff::')
 
        .prefixlen: 64
 

	
 
    """
 

	
 
    def __init__(self, address, strict=False):
 
        """Instantiate a new IPv6 Network object.
 

	
 
        Args:
 
            address: A string or integer representing the IPv6 network or the IP
 
              and prefix/netmask.
 
              '2001:4860::/128'
 
              '2001:4860:0000:0000:0000:0000:0000:0000/128'
 
              '2001:4860::'
 
              are all functionally the same in IPv6.  That is to say,
 
              failing to provide a subnetmask will create an object with
 
              a mask of /128.
 

	
 
              Additionally, an integer can be passed, so
 
              IPv6Network('2001:4860::') ==
 
                IPv6Network(42541956101370907050197289607612071936L).
 
              or, more generally
 
              IPv6Network(IPv6Network('2001:4860::')._ip) ==
 
                IPv6Network('2001:4860::')
 

	
 
            strict: A boolean. If true, ensure that we have been passed
 
              A true network address, eg, 192.168.1.0/24 and not an
 
              IP address on a network, eg, 192.168.1.1/24.
 

	
 
        Raises:
 
            AddressValueError: If address isn't a valid IPv6 address.
 
            NetmaskValueError: If the netmask isn't valid for
 
              an IPv6 address.
 
            ValueError: If strict was True and a network address was not
 
              supplied.
 

	
 
        """
 
        _BaseNet.__init__(self, address)
 
        _BaseV6.__init__(self, address)
 

	
 
        # Constructing from an integer or packed bytes.
 
        if isinstance(address, (int, long, Bytes)):
 
            self.ip = IPv6Address(address)
 
            self._ip = self.ip._ip
 
            self._prefixlen = self._max_prefixlen
 
            self.netmask = IPv6Address(self._ALL_ONES)
 
            return
 

	
 
        # Assume input argument to be string or any object representation
 
        # which converts into a formatted IP prefix string.
 
        addr = str(address).split('/')
 

	
 
        if len(addr) > 2:
 
            raise AddressValueError(address)
 

	
 
        self._ip = self._ip_int_from_string(addr[0])
 
        self.ip = IPv6Address(self._ip)
 

	
 
        if len(addr) == 2:
 
            if self._is_valid_netmask(addr[1]):
 
                self._prefixlen = int(addr[1])
 
            else:
 
                raise NetmaskValueError(addr[1])
 
        else:
 
            self._prefixlen = self._max_prefixlen
 

	
 
        self.netmask = IPv6Address(self._ip_int_from_prefix(self._prefixlen))
 

	
 
        if strict:
 
            if self.ip != self.network:
 
                raise ValueError('%s has host bits set' %
 
                                 self.ip)
 
        if self._prefixlen == (self._max_prefixlen - 1):
 
            self.iterhosts = self.__iter__
 

	
 
    def _is_valid_netmask(self, prefixlen):
 
        """Verify that the netmask/prefixlen is valid.
 

	
 
        Args:
 
            prefixlen: A string, the netmask in prefix length format.
 

	
 
        Returns:
 
            A boolean, True if the prefix represents a valid IPv6
 
            netmask.
 

	
 
        """
 
        try:
 
            prefixlen = int(prefixlen)
 
        except ValueError:
 
            return False
 
        return 0 <= prefixlen <= self._max_prefixlen
 

	
 
    @property
 
    def with_netmask(self):
 
        return self.with_prefixlen
rhodecode/lib/markup_renderer.py
Show inline comments
 
@@ -98,7 +98,7 @@ class MarkupRenderer(object):
 
        source = safe_unicode(source)
 
        try:
 
            import markdown as __markdown
 
            return __markdown.markdown(source, ['codehilite', 'tables'])
 
            return __markdown.markdown(source, ['codehilite', 'extra'])
 
        except ImportError:
 
            log.warning('Install markdown to use this function')
 
            return cls.plain(source)
rhodecode/lib/middleware/simplegit.py
Show inline comments
 
@@ -109,7 +109,7 @@ class SimpleGit(BaseVCSController):
 
        if not self._check_ssl(environ, start_response):
 
            return HTTPNotAcceptable('SSL REQUIRED !')(environ, start_response)
 

	
 
        ipaddr = self._get_ip_addr(environ)
 
        ip_addr = self._get_ip_addr(environ)
 
        username = None
 
        self._git_first_op = False
 
        # skip passing error to error controller
 
@@ -140,7 +140,7 @@ class SimpleGit(BaseVCSController):
 
            anonymous_user = self.__get_user('default')
 
            username = anonymous_user.username
 
            anonymous_perm = self._check_permission(action, anonymous_user,
 
                                                    repo_name)
 
                                                    repo_name, ip_addr)
 

	
 
            if anonymous_perm is not True or anonymous_user.active is False:
 
                if anonymous_perm is not True:
 
@@ -182,7 +182,7 @@ class SimpleGit(BaseVCSController):
 
                    return HTTPInternalServerError()(environ, start_response)
 

	
 
                #check permissions for this repository
 
                perm = self._check_permission(action, user, repo_name)
 
                perm = self._check_permission(action, user, repo_name, ip_addr)
 
                if perm is not True:
 
                    return HTTPForbidden()(environ, start_response)
 

	
 
@@ -191,7 +191,7 @@ class SimpleGit(BaseVCSController):
 
        from rhodecode import CONFIG
 
        server_url = get_server_url(environ)
 
        extras = {
 
            'ip': ipaddr,
 
            'ip': ip_addr,
 
            'username': username,
 
            'action': action,
 
            'repository': repo_name,
 
@@ -233,11 +233,12 @@ class SimpleGit(BaseVCSController):
 
                self._invalidate_cache(repo_name)
 
            self._handle_githooks(repo_name, action, baseui, environ)
 

	
 
            log.info('%s action on GIT repo "%s"' % (action, repo_name))
 
            log.info('%s action on GIT repo "%s" by "%s" from %s' %
 
                     (action, repo_name, username, ip_addr))
 
            app = self.__make_app(repo_name, repo_path, extras)
 
            return app(environ, start_response)
 
        except HTTPLockedRC, e:
 
            log.debug('Repositry LOCKED ret code 423!')
 
            log.debug('Repository LOCKED ret code 423!')
 
            return e(environ, start_response)
 
        except Exception:
 
            log.error(traceback.format_exc())
rhodecode/lib/middleware/simplehg.py
Show inline comments
 
@@ -73,7 +73,7 @@ class SimpleHg(BaseVCSController):
 
        if not self._check_ssl(environ, start_response):
 
            return HTTPNotAcceptable('SSL REQUIRED !')(environ, start_response)
 

	
 
        ipaddr = self._get_ip_addr(environ)
 
        ip_addr = self._get_ip_addr(environ)
 
        username = None
 
        # skip passing error to error controller
 
        environ['pylons.status_code_redirect'] = True
 
@@ -103,7 +103,7 @@ class SimpleHg(BaseVCSController):
 
            anonymous_user = self.__get_user('default')
 
            username = anonymous_user.username
 
            anonymous_perm = self._check_permission(action, anonymous_user,
 
                                                    repo_name)
 
                                                    repo_name, ip_addr)
 

	
 
            if anonymous_perm is not True or anonymous_user.active is False:
 
                if anonymous_perm is not True:
 
@@ -145,7 +145,7 @@ class SimpleHg(BaseVCSController):
 
                    return HTTPInternalServerError()(environ, start_response)
 

	
 
                #check permissions for this repository
 
                perm = self._check_permission(action, user, repo_name)
 
                perm = self._check_permission(action, user, repo_name, ip_addr)
 
                if perm is not True:
 
                    return HTTPForbidden()(environ, start_response)
 

	
 
@@ -154,7 +154,7 @@ class SimpleHg(BaseVCSController):
 
        from rhodecode import CONFIG
 
        server_url = get_server_url(environ)
 
        extras = {
 
            'ip': ipaddr,
 
            'ip': ip_addr,
 
            'username': username,
 
            'action': action,
 
            'repository': repo_name,
 
@@ -194,14 +194,15 @@ class SimpleHg(BaseVCSController):
 
            # invalidate cache on push
 
            if action == 'push':
 
                self._invalidate_cache(repo_name)
 
            log.info('%s action on HG repo "%s"' % (action, repo_name))
 
            log.info('%s action on HG repo "%s" by "%s" from %s' %
 
                     (action, repo_name, username, ip_addr))
 
            app = self.__make_app(repo_path, baseui, extras)
 
            return app(environ, start_response)
 
        except RepoError, e:
 
            if str(e).find('not found') != -1:
 
                return HTTPNotFound()(environ, start_response)
 
        except HTTPLockedRC, e:
 
            log.debug('Repositry LOCKED ret code 423!')
 
            log.debug('Repository LOCKED ret code 423!')
 
            return e(environ, start_response)
 
        except Exception:
 
            log.error(traceback.format_exc())
rhodecode/lib/update_repoinfo.py
Show inline comments
 
@@ -34,6 +34,7 @@ from os.path import dirname as dn, join 
 
from rhodecode.model import init_model
 
from rhodecode.lib.utils2 import engine_from_config, safe_str
 
from rhodecode.model.db import RhodeCodeUi, Repository
 
from rhodecode.lib.vcs.backends.base import EmptyChangeset
 

	
 

	
 
#to get the rhodecode import
 
@@ -73,8 +74,9 @@ class UpdateCommand(BasePasterCommand):
 
        else:
 
            repo_list = Repository.getAll()
 
        for repo in repo_list:
 
            last_change = repo.scm_instance.last_change
 
            repo.update_last_change(last_change)
 
            last_cs = (repo.scm_instance.get_changeset() if repo.scm_instance
 
                           else EmptyChangeset())
 
            repo.update_changeset_cache(last_cs)
 

	
 
    def update_parser(self):
 
        self.parser.add_option('--update-only',
rhodecode/lib/utils.py
Show inline comments
 
@@ -162,10 +162,8 @@ def action_logger(user, action, repo, ip
 
        user_log.user_ip = ipaddr
 
        sa.add(user_log)
 

	
 
        log.info(
 
            'Adding user %s, action %s on %s' % (user_obj, action,
 
                                                 safe_unicode(repo))
 
        )
 
        log.info('Logging action %s on %s by %s' %
 
                 (action, safe_unicode(repo), user_obj))
 
        if commit:
 
            sa.commit()
 
    except:
 
@@ -309,7 +307,7 @@ def make_ui(read_from='file', path=None,
 
        cfg.read(path)
 
        for section in ui_sections:
 
            for k, v in cfg.items(section):
 
                log.debug('settings ui from file[%s]%s:%s' % (section, k, v))
 
                log.debug('settings ui from file: [%s] %s=%s' % (section, k, v))
 
                baseui.setconfig(safe_str(section), safe_str(k), safe_str(v))
 

	
 
    elif read_from == 'db':
 
@@ -321,7 +319,7 @@ def make_ui(read_from='file', path=None,
 
        hg_ui = ret
 
        for ui_ in hg_ui:
 
            if ui_.ui_active:
 
                log.debug('settings ui from db[%s]%s:%s', ui_.ui_section,
 
                log.debug('settings ui from db: [%s] %s=%s', ui_.ui_section,
 
                          ui_.ui_key, ui_.ui_value)
 
                baseui.setconfig(safe_str(ui_.ui_section), safe_str(ui_.ui_key),
 
                                 safe_str(ui_.ui_value))
 
@@ -423,6 +421,13 @@ def repo2db_mapper(initial_repo_list, re
 
#    CacheInvalidation.clear_cache()
 
#    sa.commit()
 

	
 
    ##creation defaults
 
    defs = RhodeCodeSetting.get_default_repo_settings(strip_prefix=True)
 
    enable_statistics = defs.get('repo_enable_statistics')
 
    enable_locking = defs.get('repo_enable_locking')
 
    enable_downloads = defs.get('repo_enable_downloads')
 
    private = defs.get('repo_private')
 

	
 
    for name, repo in initial_repo_list.items():
 
        group = map_groups(name)
 
        db_repo = rm.get_by_repo_name(name)
 
@@ -433,18 +438,24 @@ def repo2db_mapper(initial_repo_list, re
 
            desc = (repo.description
 
                    if repo.description != 'unknown'
 
                    else '%s repository' % name)
 

	
 
            new_repo = rm.create_repo(
 
                repo_name=name,
 
                repo_type=repo.alias,
 
                description=desc,
 
                repos_group=getattr(group, 'group_id', None),
 
                owner=user,
 
                just_db=True
 
                just_db=True,
 
                enable_locking=enable_locking,
 
                enable_downloads=enable_downloads,
 
                enable_statistics=enable_statistics,
 
                private=private
 
            )
 
            # we added that repo just now, and make sure it has githook
 
            # installed
 
            if new_repo.repo_type == 'git':
 
                ScmModel().install_git_hook(new_repo.scm_instance)
 
            new_repo.update_changeset_cache()
 
        elif install_git_hook:
 
            if db_repo.repo_type == 'git':
 
                ScmModel().install_git_hook(db_repo.scm_instance)
 
@@ -452,8 +463,8 @@ def repo2db_mapper(initial_repo_list, re
 
        # system, this will register all repos and multiple instances
 
        key, _prefix, _org_key = CacheInvalidation._get_key(name)
 
        CacheInvalidation.invalidate(name)
 
        log.debug("Creating a cache key for %s instance_id=>`%s`"
 
                  % (name, _prefix or '-'))
 
        log.debug("Creating a cache key for %s, instance_id %s"
 
                  % (name, _prefix or 'unknown'))
 

	
 
    sa.commit()
 
    removed = []
 
@@ -740,4 +751,4 @@ def jsonify(func, *args, **kwargs):
 
        warnings.warn(msg, Warning, 2)
 
        log.warning(msg)
 
    log.debug("Returning JSON wrapped action output")
 
    return json.dumps(data, encoding='utf-8')
 
\ No newline at end of file
 
    return json.dumps(data, encoding='utf-8')
rhodecode/lib/vcs/backends/base.py
Show inline comments
 
@@ -376,6 +376,7 @@ class BaseChangeset(object):
 
        return dict(
 
            short_id=self.short_id,
 
            raw_id=self.raw_id,
 
            revision=self.revision,
 
            message=self.message,
 
            date=self.date,
 
            author=self.author,
rhodecode/lib/vcs/backends/git/repository.py
Show inline comments
 
@@ -606,10 +606,13 @@ class GitRepository(BaseRepository):
 
        Tries to pull changes from external location.
 
        """
 
        url = self._get_url(url)
 
        cmd = ['fetch']
 
        cmd.append(url)
 
        cmd = ' '.join(cmd)
 
        # If error occurs run_git_command raises RepositoryError already
 
        so, se = self.run_git_command('ls-remote -h %s' % url)
 
        refs = []
 
        for line in (x for x in so.splitlines()):
 
            sha, ref = line.split('\t')
 
            refs.append(ref)
 
        refs = ' '.join(('+%s:%s' % (r, r) for r in refs))
 
        cmd = '''fetch %s -- %s''' % (url, refs)
 
        self.run_git_command(cmd)
 

	
 
    @LazyProperty
rhodecode/lib/vcs/nodes.py
Show inline comments
 
@@ -362,10 +362,11 @@ class FileNode(Node):
 
        Returns pygment's lexer class. Would try to guess lexer taking file's
 
        content, name and mimetype.
 
        """
 

	
 
        try:
 
            lexer = lexers.guess_lexer_for_filename(self.name, self.content)
 
            lexer = lexers.guess_lexer_for_filename(self.name, self.content, stripnl=False)
 
        except lexers.ClassNotFound:
 
            lexer = lexers.TextLexer()
 
            lexer = lexers.TextLexer(stripnl=False)
 
        # returns first alias
 
        return lexer
 

	
rhodecode/model/changeset_status.py
Show inline comments
 
@@ -89,44 +89,39 @@ class ChangesetStatusModel(BaseModel):
 
                                   with_revisions)
 
        return q.all()
 

	
 
    def get_status(self, repo, revision=None, pull_request=None):
 
    def get_status(self, repo, revision=None, pull_request=None, as_str=True):
 
        """
 
        Returns latest status of changeset for given revision or for given
 
        pull request. Statuses are versioned inside a table itself and
 
        version == 0 is always the current one
 

	
 
        :param repo:
 
        :type repo:
 
        :param revision: 40char hash or None
 
        :type revision: str
 
        :param pull_request: pull_request reference
 
        :type:
 
        :param as_str: return status as string not object
 
        """
 
        q = self._get_status_query(repo, revision, pull_request)
 

	
 
        # need to use first here since there can be multiple statuses
 
        # returned from pull_request
 
        status = q.first()
 
        status = status.status if status else status
 
        st = status or ChangesetStatus.DEFAULT
 
        return str(st)
 
        if as_str:
 
            status = status.status if status else status
 
            st = status or ChangesetStatus.DEFAULT
 
            return str(st)
 
        return status
 

	
 
    def set_status(self, repo, status, user, comment, revision=None,
 
    def set_status(self, repo, status, user, comment=None, revision=None,
 
                   pull_request=None, dont_allow_on_closed_pull_request=False):
 
        """
 
        Creates new status for changeset or updates the old ones bumping their
 
        version, leaving the current status at
 

	
 
        :param repo:
 
        :type repo:
 
        :param revision:
 
        :type revision:
 
        :param status:
 
        :type status:
 
        :param user:
 
        :type user:
 
        :param comment:
 
        :type comment:
 
        :param dont_allow_on_closed_pull_request: don't allow a status change
 
            if last status was for pull request and it's closed. We shouldn't
 
            mess around this manually
 
@@ -134,14 +129,21 @@ class ChangesetStatusModel(BaseModel):
 
        repo = self._get_repo(repo)
 

	
 
        q = ChangesetStatus.query()
 

	
 
        if not comment:
 
            from rhodecode.model.comment import ChangesetCommentsModel
 
            comment = ChangesetCommentsModel().create(
 
                text='Auto status change',
 
                repo=repo,
 
                user=user,
 
                pull_request=pull_request,
 
            )
 
        if revision:
 
            q = q.filter(ChangesetStatus.repo == repo)
 
            q = q.filter(ChangesetStatus.revision == revision)
 
        elif pull_request:
 
            pull_request = self.__get_pull_request(pull_request)
 
            q = q.filter(ChangesetStatus.repo == pull_request.org_repo)
 
            q = q.filter(ChangesetStatus.pull_request == pull_request)
 
            q = q.filter(ChangesetStatus.revision.in_(pull_request.revisions))
 
        cur_statuses = q.all()
 

	
 
        #if statuses exists and last is associated with a closed pull request
 
@@ -153,6 +155,7 @@ class ChangesetStatusModel(BaseModel):
 
                'Changing status on closed pull request is not allowed'
 
            )
 

	
 
        #update all current statuses with older version
 
        if cur_statuses:
 
            for st in cur_statuses:
 
                st.version += 1
rhodecode/model/db.py
Show inline comments
 
@@ -370,6 +370,11 @@ class User(Base, BaseModel):
 
        return [self.email] + [x.email for x in other]
 

	
 
    @property
 
    def ip_addresses(self):
 
        ret = UserIpMap.query().filter(UserIpMap.user == self).all()
 
        return [x.ip_addr for x in ret]
 

	
 
    @property
 
    def username_and_name(self):
 
        return '%s (%s %s)' % (self.username, self.firstname, self.lastname)
 

	
 
@@ -472,6 +477,7 @@ class User(Base, BaseModel):
 
            admin=user.admin,
 
            ldap_dn=user.ldap_dn,
 
            last_login=user.last_login,
 
            ip_addresses=user.ip_addresses
 
        )
 
        return data
 

	
 
@@ -518,6 +524,34 @@ class UserEmailMap(Base, BaseModel):
 
        self._email = val.lower() if val else None
 

	
 

	
 
class UserIpMap(Base, BaseModel):
 
    __tablename__ = 'user_ip_map'
 
    __table_args__ = (
 
        UniqueConstraint('user_id', 'ip_addr'),
 
        {'extend_existing': True, 'mysql_engine': 'InnoDB',
 
         'mysql_charset': 'utf8'}
 
    )
 
    __mapper_args__ = {}
 

	
 
    ip_id = Column("ip_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
 
    user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
 
    ip_addr = Column("ip_addr", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
 
    active = Column("active", Boolean(), nullable=True, unique=None, default=True)
 
    user = relationship('User', lazy='joined')
 

	
 
    @classmethod
 
    def _get_ip_range(cls, ip_addr):
 
        from rhodecode.lib import ipaddr
 
        net = ipaddr.IPv4Network(ip_addr)
 
        return [str(net.network), str(net.broadcast)]
 

	
 
    def __json__(self):
 
        return dict(
 
          ip_addr=self.ip_addr,
 
          ip_range=self._get_ip_range(self.ip_addr)
 
        )
 

	
 

	
 
class UserLog(Base, BaseModel):
 
    __tablename__ = 'user_logs'
 
    __table_args__ = (
 
@@ -637,6 +671,7 @@ class Repository(Base, BaseModel):
 
    landing_rev = Column("landing_revision", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default=None)
 
    enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
 
    _locked = Column("locked", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
 
    _changeset_cache = Column("changeset_cache", LargeBinary(), nullable=True) #JSON data
 

	
 
    fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
 
    group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
 
@@ -682,11 +717,40 @@ class Repository(Base, BaseModel):
 
        else:
 
            self._locked = None
 

	
 
    @hybrid_property
 
    def changeset_cache(self):
 
        from rhodecode.lib.vcs.backends.base import EmptyChangeset
 
        dummy = EmptyChangeset().__json__()
 
        if not self._changeset_cache:
 
            return dummy
 
        try:
 
            return json.loads(self._changeset_cache)
 
        except TypeError:
 
            return dummy
 

	
 
    @changeset_cache.setter
 
    def changeset_cache(self, val):
 
        try:
 
            self._changeset_cache = json.dumps(val)
 
        except:
 
            log.error(traceback.format_exc())
 

	
 
    @classmethod
 
    def url_sep(cls):
 
        return URL_SEP
 

	
 
    @classmethod
 
    def normalize_repo_name(cls, repo_name):
 
        """
 
        Normalizes os specific repo_name to the format internally stored inside
 
        dabatabase using URL_SEP
 

	
 
        :param cls:
 
        :param repo_name:
 
        """
 
        return cls.url_sep().join(repo_name.split(os.sep))
 

	
 
    @classmethod
 
    def get_by_repo_name(cls, repo_name):
 
        q = Session().query(cls).filter(cls.repo_name == repo_name)
 
        q = q.options(joinedload(Repository.fork))\
 
@@ -697,6 +761,7 @@ class Repository(Base, BaseModel):
 
    @classmethod
 
    def get_by_full_path(cls, repo_full_path):
 
        repo_name = repo_full_path.split(cls.base_path(), 1)[-1]
 
        repo_name = cls.normalize_repo_name(repo_name)
 
        return cls.get_by_repo_name(repo_name.strip(URL_SEP))
 

	
 
    @classmethod
 
@@ -841,7 +906,11 @@ class Repository(Base, BaseModel):
 
            description=repo.description,
 
            landing_rev=repo.landing_rev,
 
            owner=repo.user.username,
 
            fork_of=repo.fork.repo_name if repo.fork else None
 
            fork_of=repo.fork.repo_name if repo.fork else None,
 
            enable_statistics=repo.enable_statistics,
 
            enable_locking=repo.enable_locking,
 
            enable_downloads=repo.enable_downloads,
 
            last_changeset=repo.changeset_cache
 
        )
 

	
 
        return data
 
@@ -862,6 +931,25 @@ class Repository(Base, BaseModel):
 
    def last_db_change(self):
 
        return self.updated_on
 

	
 
    def clone_url(self, **override):
 
        from pylons import url
 
        from urlparse import urlparse
 
        import urllib
 
        parsed_url = urlparse(url('home', qualified=True))
 
        default_clone_uri = '%(scheme)s://%(user)s%(pass)s%(netloc)s%(prefix)s%(path)s'
 
        decoded_path = safe_unicode(urllib.unquote(parsed_url.path))
 
        args = {
 
           'user': '',
 
           'pass': '',
 
           'scheme': parsed_url.scheme,
 
           'netloc': parsed_url.netloc,
 
           'prefix': decoded_path,
 
           'path': self.repo_name
 
        }
 

	
 
        args.update(override)
 
        return default_clone_uri % args
 

	
 
    #==========================================================================
 
    # SCM PROPERTIES
 
    #==========================================================================
 
@@ -876,12 +964,30 @@ class Repository(Base, BaseModel):
 
        cs = self.get_changeset(self.landing_rev) or self.get_changeset()
 
        return cs
 

	
 
    def update_last_change(self, last_change=None):
 
        if last_change is None:
 
            last_change = datetime.datetime.now()
 
        if self.updated_on is None or self.updated_on != last_change:
 
            log.debug('updated repo %s with new date %s' % (self, last_change))
 
    def update_changeset_cache(self, cs_cache=None):
 
        """
 
        Update cache of last changeset for repository, keys should be::
 

	
 
            short_id
 
            raw_id
 
            revision
 
            message
 
            date
 
            author
 

	
 
        :param cs_cache:
 
        """
 
        from rhodecode.lib.vcs.backends.base import BaseChangeset
 
        if cs_cache is None:
 
            cs_cache = self.get_changeset()
 
        if isinstance(cs_cache, BaseChangeset):
 
            cs_cache = cs_cache.__json__()
 

	
 
        if cs_cache != self.changeset_cache:
 
            last_change = cs_cache.get('date') or self.last_change
 
            log.debug('updated repo %s with new cs cache %s' % (self, cs_cache))
 
            self.updated_on = last_change
 
            self.changeset_cache = cs_cache
 
            Session().add(self)
 
            Session().commit()
 

	
 
@@ -1708,6 +1814,14 @@ class PullRequest(Base, BaseModel):
 
    def revisions(self, val):
 
        self._revisions = ':'.join(val)
 

	
 
    @property
 
    def org_ref_parts(self):
 
        return self.org_ref.split(':')
 

	
 
    @property
 
    def other_ref_parts(self):
 
        return self.other_ref.split(':')
 

	
 
    author = relationship('User', lazy='joined')
 
    reviewers = relationship('PullRequestReviewers',
 
                             cascade="all, delete, delete-orphan")
rhodecode/model/forms.py
Show inline comments
 
@@ -345,9 +345,14 @@ def LdapSettingsForm(tls_reqcert_choices
 

	
 
def UserExtraEmailForm():
 
    class _UserExtraEmailForm(formencode.Schema):
 
        email = All(v.UniqSystemEmail(), v.Email)
 
        email = All(v.UniqSystemEmail(), v.Email(not_empty=True))
 
    return _UserExtraEmailForm
 

	
 

	
 
    return _UserExtraEmailForm
 
def UserExtraIpForm():
 
    class _UserExtraIpForm(formencode.Schema):
 
        ip = v.ValidIp()(not_empty=True)
 
    return _UserExtraIpForm
 

	
 

	
 
def PullRequestForm(repo_id):
 
@@ -360,7 +365,8 @@ def PullRequestForm(repo_id):
 
        org_ref = v.UnicodeString(strip=True, required=True)
 
        other_repo = v.UnicodeString(strip=True, required=True)
 
        other_ref = v.UnicodeString(strip=True, required=True)
 
        revisions = All(v.NotReviewedRevisions(repo_id)(), v.UniqueList(not_empty=True))
 
        revisions = All(#v.NotReviewedRevisions(repo_id)(),
 
                        v.UniqueList(not_empty=True))
 
        review_members = v.UniqueList(not_empty=True)
 

	
 
        pullrequest_title = v.UnicodeString(strip=True, required=True, min=3)
rhodecode/model/notification.py
Show inline comments
 
@@ -270,8 +270,9 @@ class EmailNotificationModel(BaseModel):
 

	
 
        base = self.email_types.get(type_, self.email_types[self.TYPE_DEFAULT])
 
        email_template = self._tmpl_lookup.get_template(base)
 
        # translator inject
 
        _kwargs = {'_': _}
 
        # translator and helpers inject
 
        _kwargs = {'_': _,
 
                   'h': h}
 
        _kwargs.update(kwargs)
 
        log.debug('rendering tmpl %s with kwargs %s' % (base, _kwargs))
 
        return email_template.render(**_kwargs)
rhodecode/model/pull_request.py
Show inline comments
 
@@ -33,7 +33,8 @@ from pylons.i18n.translation import _
 
from rhodecode.model.meta import Session
 
from rhodecode.lib import helpers as h
 
from rhodecode.model import BaseModel
 
from rhodecode.model.db import PullRequest, PullRequestReviewers, Notification
 
from rhodecode.model.db import PullRequest, PullRequestReviewers, Notification,\
 
    ChangesetStatus
 
from rhodecode.model.notification import NotificationModel
 
from rhodecode.lib.utils2 import safe_unicode
 

	
 
@@ -54,8 +55,9 @@ class PullRequestModel(BaseModel):
 
        repo = self._get_repo(repo)
 
        return PullRequest.query().filter(PullRequest.other_repo == repo).all()
 

	
 
    def create(self, created_by, org_repo, org_ref, other_repo,
 
               other_ref, revisions, reviewers, title, description=None):
 
    def create(self, created_by, org_repo, org_ref, other_repo, other_ref,
 
               revisions, reviewers, title, description=None):
 
        from rhodecode.model.changeset_status import ChangesetStatusModel
 

	
 
        created_by_user = self._get_user(created_by)
 
        org_repo = self._get_repo(org_repo)
 
@@ -78,6 +80,14 @@ class PullRequestModel(BaseModel):
 
            reviewer = PullRequestReviewers(_usr, new)
 
            self.sa.add(reviewer)
 

	
 
        #reset state to under-review
 
        ChangesetStatusModel().set_status(
 
            repo=org_repo,
 
            status=ChangesetStatus.STATUS_UNDER_REVIEW,
 
            user=created_by_user,
 
            pull_request=new
 
        )
 

	
 
        #notification to reviewers
 
        notif = NotificationModel()
 

	
rhodecode/model/repo.py
Show inline comments
 
@@ -41,6 +41,7 @@ from rhodecode.model.db import Repositor
 
    Statistics, UsersGroup, UsersGroupRepoToPerm, RhodeCodeUi, RepoGroup,\
 
    RhodeCodeSetting
 
from rhodecode.lib import helpers as h
 
from rhodecode.lib.auth import HasRepoPermissionAny
 

	
 

	
 
log = logging.getLogger(__name__)
 
@@ -89,6 +90,22 @@ class RepoModel(BaseModel):
 
                                          "get_repo_%s" % repo_name))
 
        return repo.scalar()
 

	
 
    def get_all_user_repos(self, user):
 
        """
 
        Get's all repositories that user have at least read access
 

	
 
        :param user:
 
        :type user:
 
        """
 
        from rhodecode.lib.auth import AuthUser
 
        user = self._get_user(user)
 
        repos = AuthUser(user_id=user.user_id).permissions['repositories']
 
        access_check = lambda r: r[1] in ['repository.read',
 
                                          'repository.write',
 
                                          'repository.admin']
 
        repos = [x[0] for x in filter(access_check, repos.items())]
 
        return Repository.query().filter(Repository.repo_name.in_(repos))
 

	
 
    def get_users_js(self):
 
        users = self.sa.query(User).filter(User.active == True).all()
 
        return json.dumps([
 
@@ -113,6 +130,95 @@ class RepoModel(BaseModel):
 
            } for gr in users_groups]
 
        )
 

	
 
    @classmethod
 
    def _render_datatable(cls, tmpl, *args, **kwargs):
 
        import rhodecode
 
        from pylons import tmpl_context as c
 
        from pylons.i18n.translation import _
 

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

	
 
        tmpl = template.get_def(tmpl)
 
        kwargs.update(dict(_=_, h=h, c=c))
 
        return tmpl.render(*args, **kwargs)
 

	
 
    def get_repos_as_dict(self, repos_list=None, admin=False, perm_check=True):
 
        _render = self._render_datatable
 

	
 
        def quick_menu(repo_name):
 
            return _render('quick_menu', repo_name)
 

	
 
        def repo_lnk(name, rtype, private, fork_of):
 
            return _render('repo_name', name, rtype, private, fork_of,
 
                           short_name=not admin, admin=False)
 

	
 
        def last_change(last_change):
 
            return _render("last_change", last_change)
 

	
 
        def rss_lnk(repo_name):
 
            return _render("rss", repo_name)
 

	
 
        def atom_lnk(repo_name):
 
            return _render("atom", repo_name)
 

	
 
        def last_rev(repo_name, cs_cache):
 
            return _render('revision', repo_name, cs_cache.get('revision'),
 
                           cs_cache.get('raw_id'), cs_cache.get('author'),
 
                           cs_cache.get('message'))
 

	
 
        def desc(desc):
 
            from pylons import tmpl_context as c
 
            if c.visual.stylify_metatags:
 
                return h.urlify_text(h.desc_stylize(h.truncate(desc, 60)))
 
            else:
 
                return h.urlify_text(h.truncate(desc, 60))
 

	
 
        def repo_actions(repo_name):
 
            return _render('repo_actions', repo_name)
 

	
 
        def owner_actions(user_id, username):
 
            return _render('user_name', user_id, username)
 

	
 
        repos_data = []
 
        for repo in repos_list:
 
            if perm_check:
 
                # check permission at this level
 
                if not HasRepoPermissionAny(
 
                    'repository.read', 'repository.write', 'repository.admin'
 
                )(repo.repo_name, 'get_repos_as_dict check'):
 
                    continue
 
            cs_cache = repo.changeset_cache
 
            row = {
 
                "menu": quick_menu(repo.repo_name),
 
                "raw_name": repo.repo_name.lower(),
 
                "name": repo_lnk(repo.repo_name, repo.repo_type,
 
                                 repo.private, repo.fork),
 
                "last_change": last_change(repo.last_db_change),
 
                "last_changeset": last_rev(repo.repo_name, cs_cache),
 
                "raw_tip": cs_cache.get('revision'),
 
                "desc": desc(repo.description),
 
                "owner": h.person(repo.user.username),
 
                "rss": rss_lnk(repo.repo_name),
 
                "atom": atom_lnk(repo.repo_name),
 

	
 
            }
 
            if admin:
 
                row.update({
 
                    "action": repo_actions(repo.repo_name),
 
                    "owner": owner_actions(repo.user.user_id,
 
                                           h.person(repo.user.username))
 
                })
 
            repos_data.append(row)
 

	
 
        return {
 
            "totalRecords": len(repos_list),
 
            "startIndex": 0,
 
            "sort": "name",
 
            "dir": "asc",
 
            "records": repos_data
 
        }
 

	
 
    def _get_defaults(self, repo_name):
 
        """
 
        Get's information about repository, and returns a dict for
 
@@ -339,9 +445,9 @@ class RepoModel(BaseModel):
 
        copy_fork_permissions = form_data.get('copy_permissions')
 
        fork_of = form_data.get('fork_parent_id')
 

	
 
        ##defaults
 
        ## repo creation defaults, private and repo_type are filled in form
 
        defs = RhodeCodeSetting.get_default_repo_settings(strip_prefix=True)
 
        enable_statistics = defs.get('repo_enable_statistic')
 
        enable_statistics = defs.get('repo_enable_statistics')
 
        enable_locking = defs.get('repo_enable_locking')
 
        enable_downloads = defs.get('repo_enable_downloads')
 

	
rhodecode/model/repos_group.py
Show inline comments
 
@@ -273,7 +273,7 @@ class ReposGroupModel(BaseModel):
 
            self.sa.delete(repos_group)
 
            self.__delete_group(repos_group, force_delete)
 
        except:
 
            log.exception('Error removing repos_group %s' % repos_group)
 
            log.error('Error removing repos_group %s' % repos_group)
 
            raise
 

	
 
    def delete_permission(self, repos_group, obj, obj_type, recursive):
rhodecode/model/scm.py
Show inline comments
 
@@ -230,7 +230,7 @@ class ScmModel(BaseModel):
 

	
 
            # name need to be decomposed and put back together using the /
 
            # since this is internal storage separator for rhodecode
 
            name = Repository.url_sep().join(name.split(os.sep))
 
            name = Repository.normalize_repo_name(name)
 

	
 
            try:
 
                if name in repos:
 
@@ -292,6 +292,9 @@ class ScmModel(BaseModel):
 
        :param repo_name: this repo that should invalidation take place
 
        """
 
        CacheInvalidation.set_invalidate(repo_name=repo_name)
 
        repo = Repository.get_by_repo_name(repo_name)
 
        if repo:
 
            repo.update_changeset_cache()
 

	
 
    def toggle_following_repo(self, follow_repo_id, user_id):
 

	
rhodecode/model/user.py
Show inline comments
 
@@ -27,7 +27,6 @@ import logging
 
import traceback
 
import itertools
 
import collections
 
import functools
 
from pylons import url
 
from pylons.i18n.translation import _
 

	
 
@@ -40,7 +39,7 @@ from rhodecode.model import BaseModel
 
from rhodecode.model.db import User, UserRepoToPerm, Repository, Permission, \
 
    UserToPerm, UsersGroupRepoToPerm, UsersGroupToPerm, UsersGroupMember, \
 
    Notification, RepoGroup, UserRepoGroupToPerm, UsersGroupRepoGroupToPerm, \
 
    UserEmailMap
 
    UserEmailMap, UserIpMap
 
from rhodecode.lib.exceptions import DefaultUserException, \
 
    UserOwnsReposException
 

	
 
@@ -294,30 +293,6 @@ class UserModel(BaseModel):
 
            log.error(traceback.format_exc())
 
            raise
 

	
 
    def update_my_account(self, user_id, form_data):
 
        from rhodecode.lib.auth import get_crypt_password
 
        try:
 
            user = self.get(user_id, cache=False)
 
            if user.username == 'default':
 
                raise DefaultUserException(
 
                    _("You can't Edit this user since it's"
 
                      " crucial for entire application")
 
                )
 
            for k, v in form_data.items():
 
                if k == 'new_password' and v:
 
                    user.password = get_crypt_password(v)
 
                    user.api_key = generate_api_key(user.username)
 
                else:
 
                    if k == 'firstname':
 
                        k = 'name'
 
                    if k not in ['admin', 'active']:
 
                        setattr(user, k, v)
 

	
 
            self.sa.add(user)
 
        except:
 
            log.error(traceback.format_exc())
 
            raise
 

	
 
    def delete(self, user):
 
        user = self._get_user(user)
 

	
 
@@ -705,3 +680,33 @@ class UserModel(BaseModel):
 
        obj = UserEmailMap.query().get(email_id)
 
        if obj:
 
            self.sa.delete(obj)
 

	
 
    def add_extra_ip(self, user, ip):
 
        """
 
        Adds ip address to UserIpMap
 

	
 
        :param user:
 
        :param ip:
 
        """
 
        from rhodecode.model import forms
 
        form = forms.UserExtraIpForm()()
 
        data = form.to_python(dict(ip=ip))
 
        user = self._get_user(user)
 

	
 
        obj = UserIpMap()
 
        obj.user = user
 
        obj.ip_addr = data['ip']
 
        self.sa.add(obj)
 
        return obj
 

	
 
    def delete_extra_ip(self, user, ip_id):
 
        """
 
        Removes ip address from UserIpMap
 

	
 
        :param user:
 
        :param ip_id:
 
        """
 
        user = self._get_user(user)
 
        obj = UserIpMap.query().get(ip_id)
 
        if obj:
 
            self.sa.delete(obj)
rhodecode/model/validators.py
Show inline comments
 
@@ -11,7 +11,7 @@ from webhelpers.pylonslib.secure_form im
 

	
 
from formencode.validators import (
 
    UnicodeString, OneOf, Int, Number, Regex, Email, Bool, StringBoolean, Set,
 
    NotEmpty
 
    NotEmpty, IPAddress, CIDR
 
)
 
from rhodecode.lib.compat import OrderedSet
 
from rhodecode.lib.utils import repo_name_slug
 
@@ -23,7 +23,7 @@ from rhodecode.lib.auth import HasReposG
 

	
 
# silence warnings and pylint
 
UnicodeString, OneOf, Int, Number, Regex, Email, Bool, StringBoolean, Set, \
 
    NotEmpty
 
    NotEmpty, IPAddress, CIDR
 

	
 
log = logging.getLogger(__name__)
 

	
 
@@ -566,7 +566,7 @@ def ValidPerms(type_='repo'):
 
def ValidSettings():
 
    class _validator(formencode.validators.FancyValidator):
 
        def _to_python(self, value, state):
 
            # settings  form for users that are not admin 
 
            # settings  form for users that are not admin
 
            # can't edit certain parameters, it's extra backup if they mangle
 
            # with forms
 

	
 
@@ -706,3 +706,40 @@ def NotReviewedRevisions(repo_id):
 
                )
 

	
 
    return _validator
 

	
 

	
 
def ValidIp():
 
    class _validator(CIDR):
 
        messages = dict(
 
            badFormat=_('Please enter a valid IP address (a.b.c.d)'),
 
            illegalOctets=_('The octets must be within the range of 0-255'
 
                ' (not %(octet)r)'),
 
            illegalBits=_('The network size (bits) must be within the range'
 
                ' of 0-32 (not %(bits)r)'))
 

	
 
        def validate_python(self, value, state):
 
            try:
 
                # Split into octets and bits
 
                if '/' in value:  # a.b.c.d/e
 
                    addr, bits = value.split('/')
 
                else:  # a.b.c.d
 
                    addr, bits = value, 32
 
                # Use IPAddress validator to validate the IP part
 
                IPAddress.validate_python(self, addr, state)
 
                # Bits (netmask) correct?
 
                if not 0 <= int(bits) <= 32:
 
                    raise formencode.Invalid(
 
                        self.message('illegalBits', state, bits=bits),
 
                        value, state)
 
            # Splitting faild: wrong syntax
 
            except ValueError:
 
                raise formencode.Invalid(self.message('badFormat', state),
 
                                         value, state)
 

	
 
        def to_python(self, value, state):
 
            v = super(_validator, self).to_python(value, state)
 
            #if IP doesn't end with a mask, add /32
 
            if '/' not in value:
 
                v += '/32'
 
            return v
 
    return _validator
rhodecode/public/css/style.css
Show inline comments
 
@@ -2002,7 +2002,6 @@ a.metatag[tag="license"]:hover {
 
}
 
 
#login div.title {
 
	width: 420px;
 
	clear: both;
 
	overflow: hidden;
 
	position: relative;
 
@@ -2021,7 +2020,6 @@ a.metatag[tag="license"]:hover {
 
}
 
 
#login div.inner {
 
	width: 380px;
 
	background: #FFF url("../images/login.png") no-repeat top left;
 
	border-top: none;
 
	border-bottom: none;
 
@@ -2038,7 +2036,6 @@ a.metatag[tag="license"]:hover {
 
}
 
 
#login div.form div.fields div.field div.input input {
 
	width: 176px;
 
	background: #FFF;
 
	border-top: 1px solid #b3b3b3;
 
	border-left: 1px solid #b3b3b3;
 
@@ -2781,7 +2778,9 @@ h3.files_location {
 
    margin: 0px 2px;
 
}
 
 
.right .logtags .branchtag,.logtags .branchtag {
 
.right .logtags .branchtag,
 
.logtags .branchtag,
 
.spantag {
 
  padding: 1px 3px 1px 3px;
 
  background-color: #bfbfbf;
 
  font-size: 10px;
 
@@ -3238,7 +3237,7 @@ table.code-browser .submodule-dir {
 
}
 
 
.edit_icon {
 
	background: url("../images/icons/folder_edit.png") no-repeat scroll 3px;
 
	background: url("../images/icons/application_form_edit.png") no-repeat scroll 3px;
 
	padding-left: 20px;
 
	padding-top: 0px;
 
	text-align: left;
 
@@ -4040,6 +4039,22 @@ div#legend_container table td,div#legend
 
	float: left
 
}
 
 
.ips_wrap{
 
    padding: 0px 20px;
 
}
 
 
.ips_wrap .ip_entry{
 
    height: 30px;
 
    padding:0px 0px 0px 10px;
 
}
 
.ips_wrap .ip_entry .ip{
 
    float: left
 
}
 
.ips_wrap .ip_entry .ip_action{
 
    float: left
 
}
 
 
 
/*README STYLE*/
 
 
div.readme {
rhodecode/public/js/rhodecode.js
Show inline comments
 
@@ -334,7 +334,7 @@ var show_changeset_tooltip = function(){
 
			YUD.setAttribute(target, 'title',_TM['loading...']);
 
			YAHOO.yuitip.main.set_listeners(target);
 
			YAHOO.yuitip.main.show_yuitip(e, target);			
 
			ajaxGET('/changeset_info/{0}/{1}'.format(repo_name,rid), success)
 
			ajaxGET(LAZY_CS_URL.replace('__NAME__',repo_name).replace('__REV__', rid), success)
 
		}
 
	});
 
};
 
@@ -416,7 +416,6 @@ YAHOO.yuitip.main = {
 
	},
 

	
 
	init: function(){
 
		yt._tooltip = '';
 
		yt.tipBox = yt.$('tip-box');
 
		if(!yt.tipBox){
 
			yt.tipBox = document.createElement('div');
 
@@ -457,7 +456,7 @@ YAHOO.yuitip.main = {
 

	
 
		if(yt.tipText !== ''){
 
			// save org title
 
			yt._tooltip = yt.tipText;
 
			YUD.setAttribute(el, 'tt_title', yt.tipText);
 
			// reset title to not show org tooltips
 
			YUD.setAttribute(el, 'title', '');
 

	
 
@@ -495,7 +494,7 @@ YAHOO.yuitip.main = {
 
		} else {
 
			YUD.setStyle(yt.tipBox, 'display', 'none');
 
		}
 
		YUD.setAttribute(el,'title', yt._tooltip);
 
		YUD.setAttribute(el,'title', YUD.getAttribute(el, 'tt_title'));
 
	}
 
}
 

	
rhodecode/templates/admin/admin.html
Show inline comments
 
@@ -53,4 +53,3 @@ YUE.on('filter_form','submit',function(e
 
fix_j_filter_width(YUD.get('j_filter').value.length);
 
</script>
 
</%def>
 

	
rhodecode/templates/admin/admin_log.html
Show inline comments
 
@@ -16,7 +16,7 @@
 
          ${h.link_to(l.user.username,h.url('edit_user', id=l.user.user_id))}
 
        %else:
 
          ${l.username}
 
        %endif    
 
        %endif
 
        </td>
 
		<td>${h.action_parser(l)[0]()}
 
		  <div class="journal_action_params">
rhodecode/templates/admin/permissions/permissions.html
Show inline comments
 
@@ -16,7 +16,7 @@
 
</%def>
 

	
 
<%def name="main()">
 
<div class="box">
 
<div class="box box-left">
 
    <!-- box / title -->
 
    <div class="title">
 
        ${self.breadcrumbs()}
 
@@ -89,10 +89,127 @@
 
                </div>
 
             </div>
 
	        <div class="buttons">
 
	        ${h.submit('set',_('set'),class_="ui-btn large")}
 
              ${h.submit('save',_('Save'),class_="ui-btn large")}
 
              ${h.reset('reset',_('Reset'),class_="ui-btn large")}
 
	        </div>
 
        </div>
 
    </div>
 
    ${h.end_form()}
 
</div>
 

	
 
<div style="min-height:780px" class="box box-right">
 
    <!-- box / title -->
 
    <div class="title">
 
        <h5>${_('Default User Permissions')}</h5>
 
    </div>
 

	
 
    ## permissions overview
 
    <div id="perms" class="table">
 
           %for section in sorted(c.perm_user.permissions.keys()):
 
              <div class="perms_section_head">${section.replace("_"," ").capitalize()}</div>
 
              %if not c.perm_user.permissions[section]:
 
                  <span class="empty_data">${_('Nothing here yet')}</span>
 
              %else:
 
              <div id='tbl_list_wrap_${section}' class="yui-skin-sam">
 
               <table id="tbl_list_${section}">
 
                <thead>
 
                    <tr>
 
                    <th class="left">${_('Name')}</th>
 
                    <th class="left">${_('Permission')}</th>
 
                    <th class="left">${_('Edit Permission')}</th>
 
                </thead>
 
                <tbody>
 
                %for k in c.perm_user.permissions[section]:
 
                     <%
 
                     if section != 'global':
 
                         section_perm = c.perm_user.permissions[section].get(k)
 
                         _perm = section_perm.split('.')[-1]
 
                     else:
 
                         _perm = section_perm = None
 
                     %>
 
                    <tr>
 
                        <td>
 
                            %if section == 'repositories':
 
                                <a href="${h.url('summary_home',repo_name=k)}">${k}</a>
 
                            %elif section == 'repositories_groups':
 
                                <a href="${h.url('repos_group_home',group_name=k)}">${k}</a>
 
                            %else:
 
                                ${h.get_permission_name(k)}
 
                            %endif
 
                        </td>
 
                        <td>
 
                            %if section == 'global':
 
                             ${h.bool2icon(k.split('.')[-1] != 'none')}
 
                            %else:
 
                             <span class="perm_tag ${_perm}">${section_perm}</span>
 
                            %endif
 
                        </td>
 
                        <td>
 
                            %if section == 'repositories':
 
                                <a href="${h.url('edit_repo',repo_name=k,anchor='permissions_manage')}">${_('edit')}</a>
 
                            %elif section == 'repositories_groups':
 
                                <a href="${h.url('edit_repos_group',id=k,anchor='permissions_manage')}">${_('edit')}</a>
 
                            %else:
 
                                --
 
                            %endif
 
                        </td>
 
                    </tr>
 
                %endfor
 
                </tbody>
 
               </table>
 
              </div>
 
              %endif
 
           %endfor
 
    </div>
 
</div>
 
<div class="box box-left" style="clear:left">
 
    <!-- box / title -->
 
    <div class="title">
 
        <h5>${_('Allowed IP addresses')}</h5>
 
    </div>
 

	
 
    <div class="ips_wrap">
 
      <table class="noborder">
 
      %if c.user_ip_map:
 
        %for ip in c.user_ip_map:
 
          <tr>
 
              <td><div class="ip">${ip.ip_addr}</div></td>
 
              <td><div class="ip">${h.ip_range(ip.ip_addr)}</div></td>
 
              <td>
 
                ${h.form(url('user_ips_delete', id=c.user.user_id),method='delete')}
 
                    ${h.hidden('del_ip',ip.ip_id)}
 
                    ${h.hidden('default_user', 'True')}
 
                    ${h.submit('remove_',_('delete'),id="remove_ip_%s" % ip.ip_id,
 
                    class_="delete_icon action_button", onclick="return  confirm('"+_('Confirm to delete this ip: %s') % ip.ip_addr+"');")}
 
                ${h.end_form()}
 
              </td>
 
          </tr>
 
        %endfor
 
       %else:
 
        <tr><td><div class="ip">${_('All IP addresses are allowed')}</div></td></tr>
 
       %endif
 
      </table>
 
    </div>
 

	
 
    ${h.form(url('user_ips', id=c.user.user_id),method='put')}
 
    <div class="form">
 
        <!-- fields -->
 
        <div class="fields">
 
             <div class="field">
 
                <div class="label">
 
                    <label for="new_ip">${_('New ip address')}:</label>
 
                </div>
 
                <div class="input">
 
                    ${h.hidden('default_user', 'True')}
 
                    ${h.text('new_ip', class_='medium')}
 
                </div>
 
             </div>
 
            <div class="buttons">
 
              ${h.submit('save',_('Add'),class_="ui-btn large")}
 
              ${h.reset('reset',_('Reset'),class_="ui-btn large")}
 
            </div>
 
        </div>
 
    </div>
 
    ${h.end_form()}
 
</div>
 
</%def>
rhodecode/templates/admin/repos/repos.html
Show inline comments
 
@@ -40,6 +40,7 @@
 
         {key:"raw_name"},
 
         {key:"name"},
 
         {key:"desc"},
 
         {key:"last_changeset"},
 
         {key:"owner"},
 
         {key:"action"},
 
      ]
 
@@ -70,6 +71,8 @@
 
      {key:"name",label:"${_('Name')}",sortable:true,
 
    	  sortOptions: { sortFunction: nameSort }},
 
      {key:"desc",label:"${_('Description')}",sortable:true},
 
      {key:"last_changeset",label:"${_('Tip')}",sortable:true,
 
          sortOptions: { sortFunction: revisionSort }},
 
      {key:"owner",label:"${_('Owner')}",sortable:true},
 
      {key:"action",label:"${_('Action')}",sortable:false},
 
  ];
 
@@ -77,7 +80,7 @@
 
  var myDataTable = new YAHOO.widget.DataTable("repos_list_wrap", myColumnDefs, myDataSource,{
 
    sortedBy:{key:"name",dir:"asc"},
 
    paginator: new YAHOO.widget.Paginator({
 
        rowsPerPage: 15,
 
        rowsPerPage: 25,
 
        alwaysVisible: false,
 
        template : "{PreviousPageLink} {FirstPageLink} {PageLinks} {LastPageLink} {NextPageLink}",
 
        pageLinks: 5,
 
@@ -111,7 +114,7 @@
 

	
 
      // Reset sort
 
      var state = myDataTable.getState();
 
          state.sortedBy = {key:'name', dir:YAHOO.widget.DataTable.CLASS_ASC};
 
      state.sortedBy = {key:'name', dir:YAHOO.widget.DataTable.CLASS_ASC};
 

	
 
      // Get filtered data
 
      myDataSource.sendRequest(YUD.get('q_filter').value,{
 
@@ -123,7 +126,11 @@
 

	
 
  };
 
  YUE.on('q_filter','click',function(){
 
      YUD.get('q_filter').value = '';
 
	  if(!YUD.hasClass('q_filter', 'loaded')){
 
		  YUD.get('q_filter').value = '';
 
		  //TODO: load here full list later to do search within groups
 
		  YUD.addClass('q_filter', 'loaded');
 
	  }
 
   });
 

	
 
  YUE.on('q_filter','keyup',function (e) {
rhodecode/templates/admin/users/user_edit.html
Show inline comments
 
@@ -43,7 +43,11 @@
 
                <label>${_('API key')}</label> ${c.user.api_key}
 
            </div>
 
        </div>
 

	
 
        <div class="field">
 
            <div class="label">
 
                <label>${_('Your IP')}</label> ${c.perm_user.ip_addr or "?"}
 
            </div>
 
        </div>
 
        <div class="fields">
 
             <div class="field">
 
                <div class="label">
 
@@ -271,7 +275,7 @@
 
        <div class="fields">
 
             <div class="field">
 
                <div class="label">
 
                    <label for="email">${_('New email address')}:</label>
 
                    <label for="new_email">${_('New email address')}:</label>
 
                </div>
 
                <div class="input">
 
                    ${h.text('new_email', class_='medium')}
 
@@ -285,4 +289,52 @@
 
    </div>
 
    ${h.end_form()}
 
</div>
 
<div class="box box-left" style="clear:left">
 
    <!-- box / title -->
 
    <div class="title">
 
        <h5>${_('Allowed IP addresses')}</h5>
 
    </div>
 

	
 
    <div class="ips_wrap">
 
      <table class="noborder">
 
      %if c.user_ip_map:
 
        %for ip in c.user_ip_map:
 
          <tr>
 
              <td><div class="ip">${ip.ip_addr}</div></td>
 
              <td><div class="ip">${h.ip_range(ip.ip_addr)}</div></td>
 
              <td>
 
                ${h.form(url('user_ips_delete', id=c.user.user_id),method='delete')}
 
                    ${h.hidden('del_ip',ip.ip_id)}
 
                    ${h.submit('remove_',_('delete'),id="remove_ip_%s" % ip.ip_id,
 
                    class_="delete_icon action_button", onclick="return  confirm('"+_('Confirm to delete this ip: %s') % ip.ip_addr+"');")}
 
                ${h.end_form()}
 
              </td>
 
          </tr>
 
        %endfor
 
       %else:
 
        <tr><td><div class="ip">${_('All IP addresses are allowed')}</div></td></tr>
 
       %endif
 
      </table>
 
    </div>
 

	
 
    ${h.form(url('user_ips', id=c.user.user_id),method='put')}
 
    <div class="form">
 
        <!-- fields -->
 
        <div class="fields">
 
             <div class="field">
 
                <div class="label">
 
                    <label for="new_ip">${_('New ip address')}:</label>
 
                </div>
 
                <div class="input">
 
                    ${h.text('new_ip', class_='medium')}
 
                </div>
 
             </div>
 
            <div class="buttons">
 
              ${h.submit('save',_('Add'),class_="ui-btn large")}
 
              ${h.reset('reset',_('Reset'),class_="ui-btn large")}
 
            </div>
 
        </div>
 
    </div>
 
    ${h.end_form()}
 
</div>
 
</%def>
rhodecode/templates/admin/users/user_edit_my_account.html
Show inline comments
 
@@ -48,7 +48,7 @@
 
         </ul>
 
    </div>
 
    <!-- end box / title -->
 
    <div id="perms" class="table">
 
    <div id="perms_container" class="table">
 
           %for section in sorted(c.rhodecode_user.permissions.keys()):
 
            <div class="perms_section_head">${section.replace("_"," ").capitalize()}</div>
 

	
 
@@ -94,30 +94,26 @@
 
            </div>
 
           %endfor
 
    </div>
 
    <div id="my" class="table" style="display:none">
 
    <div id="my_container" style="display:none">
 
        <div class="table yui-skin-sam" id="repos_list_wrap"></div>
 
        <div id="user-paginator" style="padding: 0px 0px 0px 20px"></div>
 
    </div>
 
    <div id="pullrequests" class="table" style="display:none"></div>
 
    <div id="pullrequests_container" class="table" style="display:none">
 
        ## loaded via AJAX
 
        ${_('Loading...')}
 
    </div>
 
</div>
 

	
 

	
 

	
 
<script type="text/javascript">
 
var filter_activate = function(){
 
    var nodes = YUQ('#my tr td a.repo_name');
 
    var func = function(node){
 
        return node.parentNode.parentNode.parentNode.parentNode;
 
    }
 
    q_filter('q_filter',YUQ('#my tr td a.repo_name'),func);
 
}
 

	
 
var show_perms = function(e){
 
    YUD.addClass('show_perms', 'current');
 
    YUD.removeClass('show_my','current');
 
    YUD.removeClass('show_pullrequests','current');
 

	
 
    YUD.setStyle('my','display','none');
 
    YUD.setStyle('pullrequests','display','none');
 
    YUD.setStyle('perms','display','');
 
    YUD.setStyle('my_container','display','none');
 
    YUD.setStyle('pullrequests_container','display','none');
 
    YUD.setStyle('perms_container','display','');
 
    YUD.setStyle('q_filter','display','none');
 
}
 
YUE.on('show_perms','click',function(e){
 
@@ -129,17 +125,14 @@ var show_my = function(e){
 
    YUD.removeClass('show_perms','current');
 
    YUD.removeClass('show_pullrequests','current');
 

	
 
    YUD.setStyle('perms','display','none');
 
    YUD.setStyle('pullrequests','display','none');
 
    YUD.setStyle('my','display','');
 
    YUD.setStyle('perms_container','display','none');
 
    YUD.setStyle('pullrequests_container','display','none');
 
    YUD.setStyle('my_container','display','');
 
    YUD.setStyle('q_filter','display','');
 

	
 

	
 
    var url = "${h.url('journal_my_repos')}";
 
    ypjax(url, 'my', function(){
 
        table_sort();
 
        filter_activate();
 
    });
 
    if(!YUD.hasClass('show_my', 'loaded')){
 
    	table_renderer(${c.data |n});
 
        YUD.addClass('show_my', 'loaded');
 
    }
 
}
 
YUE.on('show_my','click',function(e){
 
	show_my(e);
 
@@ -150,13 +143,13 @@ var show_pullrequests = function(e){
 
    YUD.removeClass('show_my','current');
 
    YUD.removeClass('show_perms','current');
 

	
 
    YUD.setStyle('my','display','none');
 
    YUD.setStyle('perms','display','none');
 
    YUD.setStyle('pullrequests','display','');
 
    YUD.setStyle('my_container','display','none');
 
    YUD.setStyle('perms_container','display','none');
 
    YUD.setStyle('pullrequests_container','display','');
 
    YUD.setStyle('q_filter','display','none');
 

	
 
    var url = "${h.url('admin_settings_my_pullrequests')}";
 
    ypjax(url, 'pullrequests');
 
    ypjax(url, 'pullrequests_container');
 
}
 
YUE.on('show_pullrequests','click',function(e){
 
	show_pullrequests(e)
 
@@ -171,75 +164,115 @@ var url = location.href.split('#');
 
if (url[1]) {
 
    //We have a hash
 
    var tabHash = url[1];
 
    tabs[tabHash]();
 
    var func = tabs[tabHash]
 
    if (func){
 
        func();
 
    }
 
}
 

	
 
// main table sorting
 
var myColumnDefs = [
 
    {key:"menu",label:"",sortable:false,className:"quick_repo_menu hidden"},
 
    {key:"name",label:"${_('Name')}",sortable:true,
 
        sortOptions: { sortFunction: nameSort }},
 
    {key:"tip",label:"${_('Tip')}",sortable:true,
 
        sortOptions: { sortFunction: revisionSort }},
 
    {key:"action1",label:"",sortable:false},
 
    {key:"action2",label:"",sortable:false},
 
];
 
function table_renderer(data){
 
	  var myDataSource = new YAHOO.util.DataSource(data);
 
	  myDataSource.responseType = YAHOO.util.DataSource.TYPE_JSON;
 

	
 
	  myDataSource.responseSchema = {
 
	      resultsList: "records",
 
	      fields: [
 
	         {key:"menu"},
 
	         {key:"raw_name"},
 
	         {key:"name"},
 
	         {key:"last_changeset"},
 
	         {key:"action"},
 
	      ]
 
	   };
 
      myDataSource.doBeforeCallback = function(req,raw,res,cb) {
 
          // This is the filter function
 
          var data     = res.results || [],
 
              filtered = [],
 
              i,l;
 

	
 
          if (req) {
 
              req = req.toLowerCase();
 
              for (i = 0; i<data.length; i++) {
 
                  var pos = data[i].raw_name.toLowerCase().indexOf(req)
 
                  if (pos != -1) {
 
                      filtered.push(data[i]);
 
                  }
 
              }
 
              res.results = filtered;
 
          }
 
          return res;
 
      }
 

	
 
	  // main table sorting
 
	  var myColumnDefs = [
 
	      {key:"menu",label:"",sortable:false,className:"quick_repo_menu hidden"},
 
	      {key:"name",label:"${_('Name')}",sortable:true,
 
	          sortOptions: { sortFunction: nameSort }},
 
	      {key:"last_changeset",label:"${_('Tip')}",sortable:true,
 
	          sortOptions: { sortFunction: revisionSort }},
 
	      {key:"action",label:"${_('Action')}",sortable:false},
 
	  ];
 

	
 
function table_sort(){
 
var myDataSource = new YAHOO.util.DataSource(YUD.get("repos_list"));
 
myDataSource.responseType = YAHOO.util.DataSource.TYPE_HTMLTABLE;
 
myDataSource.responseSchema = {
 
    fields: [
 
        {key:"menu"},
 
        {key:"name"},
 
        {key:"tip"},
 
        {key:"action1"},
 
        {key:"action2"},
 
    ]
 
};
 
var trans_defs =  {
 
    sortedBy:{key:"name",dir:"asc"},
 
    MSG_SORTASC:"${_('Click to sort ascending')}",
 
    MSG_SORTDESC:"${_('Click to sort descending')}",
 
    MSG_EMPTY:"${_('No records found.')}",
 
    MSG_ERROR:"${_('Data error.')}",
 
    MSG_LOADING:"${_('Loading...')}",
 
}
 
var myDataTable = new YAHOO.widget.DataTable("repos_list_wrap", myColumnDefs, myDataSource,trans_defs);
 
myDataTable.subscribe('postRenderEvent',function(oArgs) {
 
    tooltip_activate();
 
    quick_repo_menu();
 
    filter_activate();
 
});
 
	  var myDataTable = new YAHOO.widget.DataTable("repos_list_wrap", myColumnDefs, myDataSource,{
 
	    sortedBy:{key:"name",dir:"asc"},
 
	    paginator: new YAHOO.widget.Paginator({
 
	        rowsPerPage: 50,
 
	        alwaysVisible: false,
 
	        template : "{PreviousPageLink} {FirstPageLink} {PageLinks} {LastPageLink} {NextPageLink}",
 
	        pageLinks: 5,
 
	        containerClass: 'pagination-wh',
 
	        currentPageClass: 'pager_curpage',
 
	        pageLinkClass: 'pager_link',
 
	        nextPageLinkLabel: '&gt;',
 
	        previousPageLinkLabel: '&lt;',
 
	        firstPageLinkLabel: '&lt;&lt;',
 
	        lastPageLinkLabel: '&gt;&gt;',
 
	        containers:['user-paginator']
 
	    }),
 

	
 
	    MSG_SORTASC:"${_('Click to sort ascending')}",
 
	    MSG_SORTDESC:"${_('Click to sort descending')}",
 
	    MSG_EMPTY:"${_('No records found.')}",
 
	    MSG_ERROR:"${_('Data error.')}",
 
	    MSG_LOADING:"${_('Loading...')}",
 
	  }
 
	  );
 
	  myDataTable.subscribe('postRenderEvent',function(oArgs) {
 
	      tooltip_activate();
 
	      quick_repo_menu();
 
	  });
 

	
 
	  var filterTimeout = null;
 

	
 
var permsColumnDefs = [
 
    {key:"name",label:"${_('Name')}",sortable:true, sortOptions: { sortFunction: permNameSort }},
 
    {key:"perm",label:"${_('Permission')}",sortable:false,},
 
];
 
	  updateFilter = function() {
 
	      // Reset timeout
 
	      filterTimeout = null;
 

	
 
// perms repos table
 
var myDataSource2 = new YAHOO.util.DataSource(YUD.get("tbl_list_repositories"));
 
myDataSource2.responseType = YAHOO.util.DataSource.TYPE_HTMLTABLE;
 
myDataSource2.responseSchema = {
 
    fields: [
 
        {key:"name"},
 
        {key:"perm"},
 
    ]
 
};
 
	      // Reset sort
 
	      var state = myDataTable.getState();
 
	      state.sortedBy = {key:'name', dir:YAHOO.widget.DataTable.CLASS_ASC};
 

	
 
new YAHOO.widget.DataTable("tbl_list_wrap_repositories", permsColumnDefs, myDataSource2, trans_defs);
 
	      // Get filtered data
 
	      myDataSource.sendRequest(YUD.get('q_filter').value,{
 
	          success : myDataTable.onDataReturnInitializeTable,
 
	          failure : myDataTable.onDataReturnInitializeTable,
 
	          scope   : myDataTable,
 
	          argument: state
 
	      });
 

	
 
//perms groups table
 
var myDataSource3 = new YAHOO.util.DataSource(YUD.get("tbl_list_repositories_groups"));
 
myDataSource3.responseType = YAHOO.util.DataSource.TYPE_HTMLTABLE;
 
myDataSource3.responseSchema = {
 
    fields: [
 
        {key:"name"},
 
        {key:"perm"},
 
    ]
 
};
 
	  };
 
	  YUE.on('q_filter','click',function(){
 
	      if(!YUD.hasClass('q_filter', 'loaded')){
 
	          YUD.get('q_filter').value = '';
 
	          //TODO: load here full list later to do search within groups
 
	          YUD.addClass('q_filter', 'loaded');
 
	      }
 
	   });
 

	
 
new YAHOO.widget.DataTable("tbl_list_wrap_repositories_groups", permsColumnDefs, myDataSource3, trans_defs);
 
}
 
	  YUE.on('q_filter','keyup',function (e) {
 
	      clearTimeout(filterTimeout);
 
	      filterTimeout = setTimeout(updateFilter,600);
 
	  });
 
	}
 
</script>
 
</%def>
rhodecode/templates/admin/users/user_edit_my_account_form.html
Show inline comments
 
@@ -26,7 +26,11 @@
 
                        <label for="username">${_('Username')}:</label>
 
                    </div>
 
                    <div class="input">
 
                        ${h.text('username',class_="medium")}
 
                      %if c.ldap_dn:
 
                          ${h.text('username',class_='medium disabled', readonly="readonly")}
 
                      %else:
 
                          ${h.text('username',class_='medium')}
 
                      %endif:
 
                    </div>
 
                 </div>
 

	
rhodecode/templates/admin/users/user_edit_my_account_repos.html
Show inline comments
 
<div id='repos_list_wrap' class="yui-skin-sam">
 
  <table id="repos_list">
 
  <thead>
 
      <tr>
 
      <th></th>
 
      <th class="left">${_('Name')}</th>
 
      <th class="left">${_('Revision')}</th>
 
      <th class="left">${_('Action')}</th>
 
      <th class="left">${_('Action')}</th>
 
  </thead>
 
   <tbody>
 
   <%namespace name="dt" file="/data_table/_dt_elements.html"/>
 
   %if c.user_repos:
 
       %for repo in c.user_repos:
 
          <tr>
 
              ##QUICK MENU
 
              <td class="quick_repo_menu">
 
                ${dt.quick_menu(repo['name'])}
 
              </td>
 
              ##REPO NAME AND ICONS
 
              <td class="reponame">
 
                ${dt.repo_name(repo['name'],repo['dbrepo']['repo_type'],repo['dbrepo']['private'],h.AttributeDict(repo['dbrepo_fork']))}
 
              </td>
 
              ##LAST REVISION
 
              <td>
 
                  ${dt.revision(repo['name'],repo['rev'],repo['tip'],repo['author'],repo['last_msg'])}
 
              </td>
 
              <td><a href="${h.url('repo_settings_home',repo_name=repo['name'])}" title="${_('edit')}"><img class="icon" alt="${_('private')}" src="${h.url('/images/icons/application_form_edit.png')}"/></a></td>
 
              <td>
 
                ${h.form(url('repo_settings_delete', repo_name=repo['name']),method='delete')}
 
                  ${h.submit('remove_%s' % repo['name'],'',class_="delete_icon action_button",onclick="return confirm('"+_('Confirm to delete this repository: %s') % repo['name']+"');")}
 
                ${h.end_form()}
 
              </td>
 
          </tr>
 
       %endfor
 
   %else:
 
      <div style="padding:5px 0px 10px 0px;">
 
      ${_('No repositories yet')}
 
      %if h.HasPermissionAny('hg.admin','hg.create.repository')():
 
          ${h.link_to(_('create one now'),h.url('admin_settings_create_repository'),class_="ui-btn")}
 
      %endif
 
      </div>
 
   %endif
 
   </tbody>
 
   </table>
 
</div>
rhodecode/templates/base/root.html
Show inline comments
 
@@ -54,6 +54,7 @@
 
            };
 
            var _TM = TRANSLATION_MAP;
 
            var TOGGLE_FOLLOW_URL  = "${h.url('toggle_following')}";
 
            var LAZY_CS_URL = "${h.url('changeset_info', repo_name='__NAME__', revision='__REV__')}"
 
            </script>
 
            <script type="text/javascript" src="${h.url('/js/yui.2.9.js', ver=c.rhodecode_version)}"></script>
 
            <!--[if lt IE 9]>
 
@@ -74,7 +75,7 @@
 
                    return false;
 
                }
 
            })(window);
 
            
 

	
 
            YUE.onDOMReady(function(){
 
              tooltip_activate();
 
              show_more_event();
 
@@ -83,7 +84,7 @@
 
              YUE.on('quick_login_link','click',function(e){
 
                 // make sure we don't redirect
 
                 YUE.preventDefault(e);
 
              
 

	
 
                 if(YUD.hasClass('quick_login_link','enabled')){
 
                     YUD.setStyle('quick_login','display','none');
 
                     YUD.removeClass('quick_login_link','enabled');
rhodecode/templates/changelog/changelog.html
Show inline comments
 
@@ -149,6 +149,7 @@ ${_('%s Changelog') % c.repo_name} - ${c
 
                    //ranges
 
                    var checkboxes = YUD.getElementsByClassName('changeset_range');
 
                    var url_tmpl = "${h.url('changeset_home',repo_name=c.repo_name,revision='__REVRANGE__')}";
 
                    var pr_tmpl = "${h.url('pullrequest_home',repo_name=c.repo_name)}";
 
                    YUE.on(checkboxes,'click',function(e){
 
                    	var clicked_cb = e.currentTarget;
 
                        var checked_checkboxes = [];
 
@@ -203,7 +204,7 @@ ${_('%s Changelog') % c.repo_name} - ${c
 
                            YUD.setStyle('rev_range_container','display','');
 
                            YUD.setStyle('rev_range_clear','display','');
 

	
 
                            YUD.get('open_new_pr').href += '?rev_start={0}&rev_end={1}'.format(rev_start,rev_end);
 
                            YUD.get('open_new_pr').href = pr_tmpl + '?rev_start={0}&rev_end={1}'.format(rev_start,rev_end);
 

	
 
                        }
 
                        else{
rhodecode/templates/changeset/changeset.html
Show inline comments
 
@@ -40,7 +40,7 @@
 
                 %endfor
 
                 %else:
 
                    <span>${_('No parents')}</span>
 
                 %endif                
 
                 %endif
 
            </div>
 
            <div class="children">
 
                %if c.changeset.children:
 
@@ -50,10 +50,10 @@
 
                 %endfor
 
                 %else:
 
                    <span>${_('No children')}</span>
 
                 %endif                
 
            </div>              
 
                 %endif
 
            </div>
 
			<div class="code-header banner">
 
      
 

	
 
                <div class="hash">
 
                 r${c.changeset.revision}:${h.short_id(c.changeset.raw_id)}
 
                </div>
 
@@ -74,7 +74,7 @@
 
                  ${c.context_url(request.GET)}
 
                </div>
 
                <div class="comments-number" style="float:right;padding-right:5px">${ungettext("%d comment", "%d comments", len(c.comments)) % len(c.comments)} ${ungettext("(%d inline)", "(%d inline)", c.inline_cnt) % c.inline_cnt}</div>
 
            </div>          
 
            </div>
 
		</div>
 
	    <div id="changeset_content">
 
			<div class="container">
rhodecode/templates/changeset/changeset_file_comment.html
Show inline comments
 
@@ -19,6 +19,11 @@
 
             <div style="float:left;padding:0px 2px 0px 2px"><span style="font-size: 18px;">&rsaquo;</span></div>
 
             <div title="${_('Changeset status')}" class="changeset-status-lbl"> ${co.status_change[0].status_lbl}</div>
 
             <div class="changeset-status-ico"><img src="${h.url(str('/images/icons/flag_status_%s.png' % co.status_change[0].status))}" /></div>
 
             <div style="float:left;padding:3px 0px 0px 5px"> <span class="">
 
             %if co.pull_request:
 
                <a href="${h.url('pullrequest_show',repo_name=co.pull_request.other_repo.repo_name,pull_request_id=co.pull_request.pull_request_id)}">${_('Status from pull request %s') % co.pull_request.pull_request_id}</a>   
 
             %endif
 
             </span> </div>
 
           </div>
 
        %endif
 
      %if h.HasPermissionAny('hg.admin', 'repository.admin')() or co.author.user_id == c.rhodecode_user.user_id:
 
@@ -130,7 +135,7 @@
 
            <div id="status_block_container" class="status-block" style="display:none">
 
                %for status,lbl in c.changeset_statuses:
 
                    <div class="">
 
                        <img src="${h.url('/images/icons/flag_status_%s.png' % status)}" /> <input ${'checked="checked"' if status == cur_status else ''}" type="radio" class="status_change_radio" name="changeset_status" id="${status}" value="${status}"> 
 
                        <img src="${h.url('/images/icons/flag_status_%s.png' % status)}" /> <input ${'checked="checked"' if status == cur_status else ''}" type="radio" class="status_change_radio" name="changeset_status" id="${status}" value="${status}">
 
                        <label for="${status}">${lbl}</label>
 
                    </div>
 
                %endfor
rhodecode/templates/compare/compare_diff.html
Show inline comments
 
@@ -2,7 +2,7 @@
 
<%inherit file="/base/base.html"/>
 

	
 
<%def name="title()">
 
    ${c.repo_name} ${_('Compare')} ${'%s@%s' % (c.org_repo.repo_name, c.org_ref)} -> ${'%s@%s' % (c.other_repo.repo_name, c.other_ref)}
 
    ${c.repo_name} ${_('Compare')} ${'%s@%s' % (c.org_repo.repo_name, c.org_ref)} -&gt; ${'%s@%s' % (c.other_repo.repo_name, c.other_ref)}
 
</%def>
 

	
 
<%def name="breadcrumbs_links()">
 
@@ -28,7 +28,7 @@
 
            <div class="code-header cv">
 
                <h3 class="code-header-title">${_('Compare View')}</h3>
 
                <div>
 
                ${'%s@%s' % (c.org_repo.repo_name, c.org_ref)} -> ${'%s@%s' % (c.other_repo.repo_name, c.other_ref)}  <a href="${c.swap_url}">[swap]</a>
 
                ${'%s@%s' % (c.org_repo.repo_name, c.org_ref)} -&gt; ${'%s@%s' % (c.other_repo.repo_name, c.other_ref)}  <a href="${c.swap_url}">[swap]</a>
 
                </div>
 
            </div>
 
        </div>
rhodecode/templates/data_table/_dt_elements.html
Show inline comments
 
@@ -2,12 +2,6 @@
 
## usage:
 
## <%namespace name="dt" file="/data_table/_dt_elements.html"/>
 

	
 
<%def name="repo_actions(repo_name)">
 
  ${h.form(h.url('repo', repo_name=repo_name),method='delete')}
 
    ${h.submit('remove_%s' % repo_name,_('delete'),class_="delete_icon action_button",onclick="return confirm('"+_('Confirm to delete this repository: %s') % repo_name+"');")}
 
  ${h.end_form()}
 
</%def>
 

	
 
<%def name="quick_menu(repo_name)">
 
  <ul class="menu_items hidden">
 
    <li style="border-top:1px solid #003367;margin-left:18px;padding-left:-99px"></li>
 
@@ -46,7 +40,7 @@
 
  </ul>
 
</%def>
 

	
 
<%def name="repo_name(name,rtype,private,fork_of,short_name=False, admin=False)">
 
<%def name="repo_name(name,rtype,private,fork_of,short_name=False,admin=False)">
 
    <%
 
    def get_name(name,short_name=short_name):
 
      if short_name:
 
@@ -116,6 +110,21 @@
 
    <div class="gravatar"><img alt="gravatar" src="${h.gravatar_url(email, size)}"/> </div>
 
</%def>
 

	
 
<%def name="repo_actions(repo_name)">
 
  <div>
 
    <div style="float:left">
 
    <a href="${h.url('repo_settings_home',repo_name=repo_name)}" title="${_('edit')}">
 
      ${h.submit('edit_%s' % repo_name,_('edit'),class_="edit_icon action_button")}
 
    </a>
 
    </div>
 
    <div style="float:left">
 
    ${h.form(h.url('repo', repo_name=repo_name),method='delete')}
 
      ${h.submit('remove_%s' % repo_name,_('delete'),class_="delete_icon action_button",onclick="return confirm('"+_('Confirm to delete this repository: %s') % repo_name+"');")}
 
    ${h.end_form()}
 
    </div>
 
  </div>
 
</%def>
 

	
 
<%def name="user_actions(user_id, username)">
 
  ${h.form(h.url('delete_user', id=user_id),method='delete')}
 
      ${h.submit('remove_',_('delete'),id="remove_user_%s" % user_id,
 
@@ -126,3 +135,9 @@
 
<%def name="user_name(user_id, username)">
 
    ${h.link_to(username,h.url('edit_user', id=user_id))}
 
</%def>
 

	
 
<%def name="toggle_follow(repo_id)">
 
  <span id="follow_toggle_${repo_id}" class="following" title="${_('Stop following this repository')}"
 
        onclick="javascript:toggleFollowingRepo(this, ${repo_id},'${str(h.get_token())}')">
 
  </span>
 
</%def>
rhodecode/templates/email_templates/pull_request.html
Show inline comments
 
## -*- coding: utf-8 -*-
 
<%inherit file="main.html"/>
 

	
 
${_('User %s opened pull request for repository %s and wants you to review changes.') % ('<b>%s</b>' % pr_user_created,pr_repo_url)}
 
${_('User %s opened pull request for repository %s and wants you to review changes.') % (('<b>%s</b>' % pr_user_created),pr_repo_url) |n}
 
<div>${_('title')}: ${pr_title}</div>
 
<div>${_('description')}:</div>
 
<div>${_('View this pull request here')}: ${pr_url}</div>
 
<p>
 
${body}
 
</p>
 
@@ -14,5 +15,3 @@ ${body}
 
    <li>${r}</li>
 
%endfor
 
</ul>
 

	
 
${_('View this pull request here')}: ${pr_url}
rhodecode/templates/files/files_source.html
Show inline comments
 
@@ -86,15 +86,6 @@ YUE.onDOMReady(function(){
 
      }
 
    highlight_lines(h_lines);
 

	
 
    //remember original location
 
    var old_hash  = location.href.substring(location.href.indexOf('#'));
 

	
 
    // this makes a jump to anchor moved by 3 posstions for padding
 
    window.location.hash = '#L'+Math.max(parseInt(h_lines[0])-3,1);
 

	
 
    //sets old anchor
 
    window.location.hash = old_hash;
 

	
 
    }
 

	
 
    // select code link event
rhodecode/templates/index_base.html
Show inline comments
 
@@ -51,9 +51,9 @@
 
                        ##<td><b>${gr.repositories_recursive_count}</b></td>
 
                    </tr>
 
                  % endfor
 

	
 
              </table>
 
            </div>
 
            <div id="group-user-paginator" style="padding: 0px 0px 0px 0px"></div>
 
            <div style="height: 20px"></div>
 
            % endif
 
            <div id="welcome" style="display:none;text-align:center">
 
@@ -127,9 +127,6 @@
 
    % if c.visual.lightweight_dashboard is False:
 
    <script>
 
      YUD.get('repo_count').innerHTML = ${cnt+1 if cnt else 0};
 
      var func = function(node){
 
          return node.parentNode.parentNode.parentNode.parentNode;
 
      }
 

	
 
      // groups table sorting
 
      var myColumnDefs = [
 
@@ -151,7 +148,7 @@
 
      var myDataTable = new YAHOO.widget.DataTable("groups_list_wrap", myColumnDefs, myDataSource,{
 
          sortedBy:{key:"name",dir:"asc"},
 
          paginator: new YAHOO.widget.Paginator({
 
              rowsPerPage: 5,
 
              rowsPerPage: 50,
 
              alwaysVisible: false,
 
              template : "{PreviousPageLink} {FirstPageLink} {PageLinks} {LastPageLink} {NextPageLink}",
 
              pageLinks: 5,
 
@@ -162,7 +159,7 @@
 
              previousPageLinkLabel: '&lt;',
 
              firstPageLinkLabel: '&lt;&lt;',
 
              lastPageLinkLabel: '&gt;&gt;',
 
              containers:['user-paginator']
 
              containers:['group-user-paginator']
 
          }),
 
          MSG_SORTASC:"${_('Click to sort ascending')}",
 
          MSG_SORTDESC:"${_('Click to sort descending')}"
 
@@ -214,13 +211,15 @@
 
      myDataTable.subscribe('postRenderEvent',function(oArgs) {
 
          tooltip_activate();
 
          quick_repo_menu();
 
          var func = function(node){
 
              return node.parentNode.parentNode.parentNode.parentNode;
 
          }
 
          q_filter('q_filter',YUQ('div.table tr td a.repo_name'),func);
 
      });
 

	
 
    </script>
 
    % else:
 
      <script>
 
        //var url = "${h.url('formatted_users', format='json')}";
 
        var data = ${c.data|n};
 
        var myDataSource = new YAHOO.util.DataSource(data);
 
        myDataSource.responseType = YAHOO.util.DataSource.TYPE_JSON;
 
@@ -233,6 +232,7 @@
 
               {key:"name"},
 
               {key:"desc"},
 
               {key:"last_change"},
 
               {key:"last_changeset"},
 
               {key:"owner"},
 
               {key:"rss"},
 
               {key:"atom"},
 
@@ -266,6 +266,8 @@
 
            {key:"desc",label:"${_('Description')}",sortable:true},
 
            {key:"last_change",label:"${_('Last Change')}",sortable:true,
 
                sortOptions: { sortFunction: ageSort }},
 
            {key:"last_changeset",label:"${_('Tip')}",sortable:true,
 
                sortOptions: { sortFunction: revisionSort }},
 
            {key:"owner",label:"${_('Owner')}",sortable:true},
 
            {key:"rss",label:"",sortable:false},
 
            {key:"atom",label:"",sortable:false},
 
@@ -308,7 +310,7 @@
 

	
 
            // Reset sort
 
            var state = myDataTable.getState();
 
                state.sortedBy = {key:'name', dir:YAHOO.widget.DataTable.CLASS_ASC};
 
            state.sortedBy = {key:'name', dir:YAHOO.widget.DataTable.CLASS_ASC};
 

	
 
            // Get filtered data
 
            myDataSource.sendRequest(YUD.get('q_filter').value,{
 
@@ -320,7 +322,11 @@
 

	
 
        };
 
        YUE.on('q_filter','click',function(){
 
            YUD.get('q_filter').value = '';
 
            if(!YUD.hasClass('q_filter', 'loaded')){
 
                YUD.get('q_filter').value = '';
 
                //TODO: load here full list later to do search within groups
 
                YUD.addClass('q_filter', 'loaded');
 
            }
 
         });
 

	
 
        YUE.on('q_filter','keyup',function (e) {
rhodecode/templates/journal/journal.html
Show inline comments
 
@@ -43,78 +43,41 @@
 
    </div>
 
    <div class="box box-right">
 
        <!-- box / title -->
 

	
 
        <div class="title">
 
            <h5>
 
            <input class="q_filter_box" id="q_filter" size="15" type="text" name="filter" value="${_('quick filter...')}"/>
 
            <a id="show_watched" class="link-white" href="#watched">${_('Watched')}</a> / <a id="show_my" class="link-white" href="#my">${_('My repos')}</a>
 
            <input class="q_filter_box" id="q_filter" size="15" type="text" name="filter" value="${_('quick filter...')}" style="display: none"/>
 
            <input class="q_filter_box" id="q_filter_watched" size="15" type="text" name="filter" value="${_('quick filter...')}" style="display: none"/>
 
            </h5>
 
             %if h.HasPermissionAny('hg.admin','hg.create.repository')():
 
             <ul class="links">
 
             <ul class="links" style="color:#DADADA">
 
               <li>
 
                 <span>${h.link_to(_('ADD'),h.url('admin_settings_create_repository'))}</span>
 
                 <span><a id="show_watched" class="link-white current" href="#watched">${_('Watched')}</a> </span>
 
               </li>
 
               <li>
 
                 <span><a id="show_my" class="link-white" href="#my">${_('My repos')}</a> </span>
 
               </li>
 
               %if h.HasPermissionAny('hg.admin','hg.create.repository')():
 
                 <li>
 
                   <span>${h.link_to(_('Add repo'),h.url('admin_settings_create_repository'))}</span>
 
                 </li>
 
               %endif
 
             </ul>
 
             %endif
 
        </div>
 
        <!-- end box / title -->
 
        <div id="my" class="table" style="display:none">
 
        ## loaded via AJAX
 
        ${_('Loading...')}
 
        </div>
 

	
 
        <div id="watched" class="table">
 
          %if c.following:
 
            <table>
 
            <thead>
 
                <tr>
 
                <th class="left">${_('Name')}</th>
 
            </thead>
 
             <tbody>
 
                %for entry in c.following:
 
                  <tr>
 
                    <td>
 
                      %if entry.follows_user_id:
 
                        <img title="${_('following user')}" alt="${_('user')}" src="${h.url('/images/icons/user.png')}"/>
 
                        ${entry.follows_user.full_contact}
 
                      %endif
 

	
 
                      %if entry.follows_repo_id:
 
                        <div style="float:right;padding-right:5px">
 
                        <span id="follow_toggle_${entry.follows_repository.repo_id}" class="following" title="${_('Stop following this repository')}"
 
                              onclick="javascript:toggleFollowingRepo(this,${entry.follows_repository.repo_id},'${str(h.get_token())}')">
 
                        </span>
 
                        </div>
 
        <!-- end box / title -->
 
        <div id="my_container" style="display:none">
 
            <div class="table yui-skin-sam" id="repos_list_wrap"></div>
 
            <div id="user-paginator" style="padding: 0px 0px 0px 20px"></div>
 
        </div>
 

	
 
                         %if h.is_hg(entry.follows_repository):
 
                           <img class="icon" title="${_('Mercurial repository')}" alt="${_('Mercurial repository')}" src="${h.url('/images/icons/hgicon.png')}"/>
 
                         %elif h.is_git(entry.follows_repository):
 
                           <img class="icon" title="${_('Git repository')}" alt="${_('Git repository')}" src="${h.url('/images/icons/giticon.png')}"/>
 
                         %endif
 

	
 
                        %if entry.follows_repository.private and c.visual.show_private_icon:
 
                          <img class="icon" title="${_('private repository')}" alt="${_('private repository')}" src="${h.url('/images/icons/lock.png')}"/>
 
                        %elif not entry.follows_repository.private and c.visual.show_public_icon:
 
                          <img class="icon" title="${_('public repository')}" alt="${_('public repository')}" src="${h.url('/images/icons/lock_open.png')}"/>
 
                        %endif
 
                        <span class="watched_repo">
 
                            ${h.link_to(entry.follows_repository.repo_name,h.url('summary_home',repo_name=entry.follows_repository.repo_name))}
 
                        </span>
 
                      %endif
 
                    </td>
 
                  </tr>
 
                %endfor
 
            </tbody>
 
            </table>
 
          %else:
 
              <div style="padding:5px 0px 10px 0px;">
 
              ${_('You are not following any users or repositories')}
 
              </div>
 
          %endif
 
        <div id="watched_container">
 
            <div class="table yui-skin-sam" id="watched_repos_list_wrap"></div>
 
            <div id="watched-user-paginator" style="padding: 0px 0px 0px 20px"></div>
 
        </div>
 
    </div>
 

	
 
    <script type="text/javascript">
 
    
 

	
 
    YUE.on('j_filter','click',function(){
 
        var jfilter = YUD.get('j_filter');
 
        if(YUD.hasClass(jfilter, 'initial')){
 
@@ -132,31 +95,49 @@
 
        var val = YUD.get('j_filter').value;
 
        window.location = "${url.current(filter='__FILTER__')}".replace('__FILTER__',val);
 
    });
 
    fix_j_filter_width(YUD.get('j_filter').value.length);    
 
    
 
    var show_my = function(e){
 
        YUD.setStyle('watched','display','none');
 
        YUD.setStyle('my','display','');
 
    fix_j_filter_width(YUD.get('j_filter').value.length);
 

	
 
        var url = "${h.url('admin_settings_my_repos')}";
 
        ypjax(url, 'my', function(){
 
    YUE.on('refresh','click',function(e){
 
        ypjax("${h.url.current(filter=c.search_term)}","journal",function(){
 
            show_more_event();
 
            tooltip_activate();
 
            quick_repo_menu();
 
            var nodes = YUQ('#my tr td a.repo_name');
 
            var func = function(node){
 
                return node.parentNode.parentNode.parentNode;
 
            }
 
            q_filter('q_filter',nodes,func);
 
        });
 
            show_changeset_tooltip();
 
            });
 
        YUE.preventDefault(e);
 
    });
 

	
 
    var show_my = function(e){
 
        YUD.setStyle('watched_container','display','none');
 
        YUD.setStyle('my_container','display','');
 
        YUD.setStyle('q_filter','display','');
 
        YUD.setStyle('q_filter_watched','display','none');
 

	
 
        YUD.addClass('show_my', 'current');
 
        YUD.removeClass('show_watched','current');
 

	
 
        if(!YUD.hasClass('show_my', 'loaded')){
 
            table_renderer(${c.data |n});
 
            YUD.addClass('show_my', 'loaded');
 
        }
 
    }
 
    YUE.on('show_my','click',function(e){
 
        show_my(e);
 
    })
 
    var show_watched = function(e){
 
    	YUD.setStyle('my','display','none');
 
        YUD.setStyle('watched','display','');
 
        var nodes = YUQ('#watched .watched_repo a');
 
    	YUD.setStyle('my_container','display','none');
 
        YUD.setStyle('watched_container','display','');
 
        YUD.setStyle('q_filter_watched','display','');
 
        YUD.setStyle('q_filter','display','none');
 

	
 
        YUD.addClass('show_watched', 'current');
 
        YUD.removeClass('show_my','current');
 
        if(!YUD.hasClass('show_watched', 'loaded')){
 
        	watched_renderer(${c.watched_data |n});
 
            YUD.addClass('show_watched', 'loaded');
 
        }
 

	
 
        return
 
        var nodes = YUQ('#watched_container .watched_repo a');
 
        var target = 'q_filter';
 
        var func = function(node){
 
            return node.parentNode.parentNode;
 
@@ -177,62 +158,218 @@
 
    if (url[1]) {
 
        //We have a hash
 
        var tabHash = url[1];
 
        tabs[tabHash]();
 
        var func = tabs[tabHash]
 
        if (func){
 
        	func();
 
        }
 
    }
 
    function watched_renderer(data){
 
        var myDataSource = new YAHOO.util.DataSource(data);
 
        myDataSource.responseType = YAHOO.util.DataSource.TYPE_JSON;
 

	
 
    YUE.on('refresh','click',function(e){
 
        ypjax("${h.url.current(filter=c.search_term)}","journal",function(){
 
        	show_more_event();
 
        	tooltip_activate();
 
        	show_changeset_tooltip();
 
        	});
 
        YUE.preventDefault(e);
 
    });
 
        myDataSource.responseSchema = {
 
            resultsList: "records",
 
            fields: [
 
               {key:"menu"},
 
               {key:"raw_name"},
 
               {key:"name"},
 
               {key:"last_changeset"},
 
               {key:"action"},
 
            ]
 
         };
 
        myDataSource.doBeforeCallback = function(req,raw,res,cb) {
 
            // This is the filter function
 
            var data     = res.results || [],
 
                filtered = [],
 
                i,l;
 

	
 
            if (req) {
 
                req = req.toLowerCase();
 
                for (i = 0; i<data.length; i++) {
 
                    var pos = data[i].raw_name.toLowerCase().indexOf(req)
 
                    if (pos != -1) {
 
                        filtered.push(data[i]);
 
                    }
 
                }
 
                res.results = filtered;
 
            }
 
            return res;
 
        }
 
        // main table sorting
 
        var myColumnDefs = [
 
            {key:"menu",label:"",sortable:false,className:"quick_repo_menu hidden"},
 
            {key:"name",label:"${_('Name')}",sortable:true,
 
                sortOptions: { sortFunction: nameSort }},
 
            {key:"last_changeset",label:"${_('Tip')}",sortable:true,
 
                sortOptions: { sortFunction: revisionSort }},
 
            {key:"action",label:"${_('Action')}",sortable:false},
 
        ];
 

	
 
    // main table sorting
 
    var myColumnDefs = [
 
        {key:"menu",label:"",sortable:false,className:"quick_repo_menu hidden"},
 
        {key:"name",label:"${_('Name')}",sortable:true,
 
            sortOptions: { sortFunction: nameSort }},
 
        {key:"tip",label:"${_('Tip')}",sortable:true,
 
            sortOptions: { sortFunction: revisionSort }},
 
        {key:"action1",label:"",sortable:false},
 
        {key:"action2",label:"",sortable:false},
 
    ];
 
        var myDataTable = new YAHOO.widget.DataTable("watched_repos_list_wrap", myColumnDefs, myDataSource,{
 
          sortedBy:{key:"name",dir:"asc"},
 
          paginator: new YAHOO.widget.Paginator({
 
              rowsPerPage: 25,
 
              alwaysVisible: false,
 
              template : "{PreviousPageLink} {FirstPageLink} {PageLinks} {LastPageLink} {NextPageLink}",
 
              pageLinks: 5,
 
              containerClass: 'pagination-wh',
 
              currentPageClass: 'pager_curpage',
 
              pageLinkClass: 'pager_link',
 
              nextPageLinkLabel: '&gt;',
 
              previousPageLinkLabel: '&lt;',
 
              firstPageLinkLabel: '&lt;&lt;',
 
              lastPageLinkLabel: '&gt;&gt;',
 
              containers:['watched-user-paginator']
 
          }),
 

	
 
    var myDataSource = new YAHOO.util.DataSource(YUD.get("repos_list"));
 
          MSG_SORTASC:"${_('Click to sort ascending')}",
 
          MSG_SORTDESC:"${_('Click to sort descending')}",
 
          MSG_EMPTY:"${_('No records found.')}",
 
          MSG_ERROR:"${_('Data error.')}",
 
          MSG_LOADING:"${_('Loading...')}",
 
        }
 
        );
 
        myDataTable.subscribe('postRenderEvent',function(oArgs) {
 
            tooltip_activate();
 
            quick_repo_menu();
 
        });
 

	
 
        var filterTimeout = null;
 

	
 
        updateFilter  = function () {
 
            // Reset timeout
 
            filterTimeout = null;
 

	
 
    myDataSource.responseType = YAHOO.util.DataSource.TYPE_HTMLTABLE;
 
            // Reset sort
 
            var state = myDataTable.getState();
 
            state.sortedBy = {key:'name', dir:YAHOO.widget.DataTable.CLASS_ASC};
 

	
 
            // Get filtered data
 
            myDataSource.sendRequest(YUD.get('q_filter_watched').value,{
 
                success : myDataTable.onDataReturnInitializeTable,
 
                failure : myDataTable.onDataReturnInitializeTable,
 
                scope   : myDataTable,
 
                argument: state
 
            });
 

	
 
        };
 
        YUE.on('q_filter_watched','click',function(){
 
            if(!YUD.hasClass('q_filter_watched', 'loaded')){
 
                YUD.get('q_filter_watched').value = '';
 
                //TODO: load here full list later to do search within groups
 
                YUD.addClass('q_filter_watched', 'loaded');
 
            }
 
         });
 

	
 
    myDataSource.responseSchema = {
 
        fields: [
 
            {key:"menu"},
 
            {key:"name"},
 
            {key:"tip"},
 
            {key:"action1"},
 
            {key:"action2"}
 
        ]
 
    };
 
        YUE.on('q_filter_watched','keyup',function (e) {
 
            clearTimeout(filterTimeout);
 
            filterTimeout = setTimeout(updateFilter,600);
 
        });
 
      }
 

	
 
    function table_renderer(data){
 
        var myDataSource = new YAHOO.util.DataSource(data);
 
        myDataSource.responseType = YAHOO.util.DataSource.TYPE_JSON;
 

	
 
        myDataSource.responseSchema = {
 
            resultsList: "records",
 
            fields: [
 
               {key:"menu"},
 
               {key:"raw_name"},
 
               {key:"name"},
 
               {key:"last_changeset"},
 
               {key:"action"},
 
            ]
 
         };
 
        myDataSource.doBeforeCallback = function(req,raw,res,cb) {
 
            // This is the filter function
 
            var data     = res.results || [],
 
                filtered = [],
 
                i,l;
 

	
 
            if (req) {
 
                req = req.toLowerCase();
 
                for (i = 0; i<data.length; i++) {
 
                    var pos = data[i].raw_name.toLowerCase().indexOf(req)
 
                    if (pos != -1) {
 
                        filtered.push(data[i]);
 
                    }
 
                }
 
                res.results = filtered;
 
            }
 
            return res;
 
        }
 
        // main table sorting
 
        var myColumnDefs = [
 
            {key:"menu",label:"",sortable:false,className:"quick_repo_menu hidden"},
 
            {key:"name",label:"${_('Name')}",sortable:true,
 
                sortOptions: { sortFunction: nameSort }},
 
            {key:"last_changeset",label:"${_('Tip')}",sortable:true,
 
                sortOptions: { sortFunction: revisionSort }},
 
            {key:"action",label:"${_('Action')}",sortable:false},
 
        ];
 

	
 
    var myDataTable = new YAHOO.widget.DataTable("repos_list_wrap", myColumnDefs, myDataSource,
 
            {
 
              sortedBy:{key:"name",dir:"asc"},
 
              MSG_SORTASC:"${_('Click to sort ascending')}",
 
              MSG_SORTDESC:"${_('Click to sort descending')}",
 
              MSG_EMPTY:"${_('No records found.')}",
 
              MSG_ERROR:"${_('Data error.')}",
 
              MSG_LOADING:"${_('Loading...')}",
 
        var myDataTable = new YAHOO.widget.DataTable("repos_list_wrap", myColumnDefs, myDataSource,{
 
          sortedBy:{key:"name",dir:"asc"},
 
          paginator: new YAHOO.widget.Paginator({
 
              rowsPerPage: 25,
 
              alwaysVisible: false,
 
              template : "{PreviousPageLink} {FirstPageLink} {PageLinks} {LastPageLink} {NextPageLink}",
 
              pageLinks: 5,
 
              containerClass: 'pagination-wh',
 
              currentPageClass: 'pager_curpage',
 
              pageLinkClass: 'pager_link',
 
              nextPageLinkLabel: '&gt;',
 
              previousPageLinkLabel: '&lt;',
 
              firstPageLinkLabel: '&lt;&lt;',
 
              lastPageLinkLabel: '&gt;&gt;',
 
              containers:['user-paginator']
 
          }),
 

	
 
          MSG_SORTASC:"${_('Click to sort ascending')}",
 
          MSG_SORTDESC:"${_('Click to sort descending')}",
 
          MSG_EMPTY:"${_('No records found.')}",
 
          MSG_ERROR:"${_('Data error.')}",
 
          MSG_LOADING:"${_('Loading...')}",
 
        }
 
        );
 
        myDataTable.subscribe('postRenderEvent',function(oArgs) {
 
            tooltip_activate();
 
            quick_repo_menu();
 
        });
 

	
 
        var filterTimeout = null;
 

	
 
        updateFilter  = function () {
 
            // Reset timeout
 
            filterTimeout = null;
 

	
 
            // Reset sort
 
            var state = myDataTable.getState();
 
            state.sortedBy = {key:'name', dir:YAHOO.widget.DataTable.CLASS_ASC};
 

	
 
            // Get filtered data
 
            myDataSource.sendRequest(YUD.get('q_filter').value,{
 
                success : myDataTable.onDataReturnInitializeTable,
 
                failure : myDataTable.onDataReturnInitializeTable,
 
                scope   : myDataTable,
 
                argument: state
 
            });
 

	
 
        };
 
        YUE.on('q_filter','click',function(){
 
            if(!YUD.hasClass('q_filter', 'loaded')){
 
                YUD.get('q_filter').value = '';
 
                //TODO: load here full list later to do search within groups
 
                YUD.addClass('q_filter', 'loaded');
 
            }
 
    );
 
    myDataTable.subscribe('postRenderEvent',function(oArgs) {
 
        tooltip_activate();
 
        quick_repo_menu();
 
        var func = function(node){
 
            return node.parentNode.parentNode.parentNode.parentNode;
 
        }
 
        q_filter('q_filter',YUQ('#my tr td a.repo_name'),func);
 
    });
 
         });
 

	
 
        YUE.on('q_filter','keyup',function (e) {
 
            clearTimeout(filterTimeout);
 
            filterTimeout = setTimeout(updateFilter,600);
 
        });
 
      }
 

	
 
    </script>
 
</%def>
rhodecode/templates/journal/journal_page_repos.html
Show inline comments
 
deleted file
rhodecode/templates/pullrequests/pullrequest_show.html
Show inline comments
 
@@ -51,6 +51,24 @@
 
            %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]}</span>
 
              :
 
              <span class="spantag">${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>
 
    </div>
 
    <div style="white-space:pre-wrap;padding:3px 3px 5px 20px">${h.literal(c.pull_request.description)}</div>
 
@@ -198,7 +216,7 @@
 
          // inject comments into they proper positions
 
          var file_comments = YUQ('.inline-comment-placeholder');
 
          renderInlineComments(file_comments);
 
          
 

	
 
          YUE.on(YUD.get('update_pull_request'),'click',function(e){
 
        	  updateReviewers();
 
          })
rhodecode/templates/settings/repo_settings.html
Show inline comments
 
@@ -94,7 +94,7 @@
 
              ${h.submit('save',_('Save'),class_="ui-btn large")}
 
              ${h.reset('reset',_('Reset'),class_="ui-btn large")}
 
            </div>
 
        
 

	
 
    </div>
 
    ${h.end_form()}
 
</div>
rhodecode/tests/api/api_base.py
Show inline comments
 
@@ -59,13 +59,13 @@ def destroy_users_group(name=TEST_USERS_
 
    Session().commit()
 

	
 

	
 
def create_repo(repo_name, repo_type):
 
def create_repo(repo_name, repo_type, owner=None):
 
    # create new repo
 
    form_data = _get_repo_create_params(
 
                    repo_name_full=repo_name,
 
                    repo_description='description %s' % repo_name,
 
                )
 
    cur_user = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN)
 
    cur_user = UserModel().get_by_username(owner or TEST_USER_ADMIN_LOGIN)
 
    r = RepoModel().create(form_data, cur_user)
 
    Session().commit()
 
    return r
 
@@ -93,7 +93,7 @@ class BaseTestApi(object):
 
    def setUpClass(self):
 
        self.usr = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN)
 
        self.apikey = self.usr.api_key
 
        self.TEST_USER = UserModel().create_or_update(
 
        self.test_user = UserModel().create_or_update(
 
            username='test-api',
 
            password='test',
 
            email='test@api.rhodecode.org',
 
@@ -101,7 +101,8 @@ class BaseTestApi(object):
 
            lastname='last'
 
        )
 
        Session().commit()
 
        self.TEST_USER_LOGIN = self.TEST_USER.username
 
        self.TEST_USER_LOGIN = self.test_user.username
 
        self.apikey_regular = self.test_user.api_key
 

	
 
    @classmethod
 
    def teardownClass(self):
 
@@ -148,12 +149,40 @@ class BaseTestApi(object):
 
        self._compare_error(id_, expected, given=response.body)
 

	
 
    def test_api_missing_non_optional_param(self):
 
        id_, params = _build_data(self.apikey, 'get_user')
 
        id_, params = _build_data(self.apikey, 'get_repo')
 
        response = api_call(self, params)
 

	
 
        expected = 'Missing non optional `repoid` arg in JSON DATA'
 
        self._compare_error(id_, expected, given=response.body)
 

	
 
    def test_api_missing_non_optional_param_args_null(self):
 
        id_, params = _build_data(self.apikey, 'get_repo')
 
        params = params.replace('"args": {}', '"args": null')
 
        response = api_call(self, params)
 

	
 
        expected = 'Missing non optional `userid` arg in JSON DATA'
 
        expected = 'Missing non optional `repoid` arg in JSON DATA'
 
        self._compare_error(id_, expected, given=response.body)
 

	
 
    def test_api_missing_non_optional_param_args_bad(self):
 
        id_, params = _build_data(self.apikey, 'get_repo')
 
        params = params.replace('"args": {}', '"args": 1')
 
        response = api_call(self, params)
 

	
 
        expected = 'Missing non optional `repoid` arg in JSON DATA'
 
        self._compare_error(id_, expected, given=response.body)
 

	
 
    def test_api_args_is_null(self):
 
        id_, params = _build_data(self.apikey, 'get_users',)
 
        params = params.replace('"args": {}', '"args": null')
 
        response = api_call(self, params)
 
        self.assertEqual(response.status, '200 OK')
 

	
 
    def test_api_args_is_bad(self):
 
        id_, params = _build_data(self.apikey, 'get_users',)
 
        params = params.replace('"args": {}', '"args": 1')
 
        response = api_call(self, params)
 
        self.assertEqual(response.status, '200 OK')
 

	
 
    def test_api_get_users(self):
 
        id_, params = _build_data(self.apikey, 'get_users',)
 
        response = api_call(self, params)
 
@@ -184,6 +213,36 @@ class BaseTestApi(object):
 
        expected = "user `%s` does not exist" % 'trololo'
 
        self._compare_error(id_, expected, given=response.body)
 

	
 
    def test_api_get_user_without_giving_userid(self):
 
        id_, params = _build_data(self.apikey, 'get_user')
 
        response = api_call(self, params)
 

	
 
        usr = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN)
 
        ret = usr.get_api_data()
 
        ret['permissions'] = AuthUser(usr.user_id).permissions
 

	
 
        expected = ret
 
        self._compare_ok(id_, expected, given=response.body)
 

	
 
    def test_api_get_user_without_giving_userid_non_admin(self):
 
        id_, params = _build_data(self.apikey_regular, 'get_user')
 
        response = api_call(self, params)
 

	
 
        usr = UserModel().get_by_username(self.TEST_USER_LOGIN)
 
        ret = usr.get_api_data()
 
        ret['permissions'] = AuthUser(usr.user_id).permissions
 

	
 
        expected = ret
 
        self._compare_ok(id_, expected, given=response.body)
 

	
 
    def test_api_get_user_with_giving_userid_non_admin(self):
 
        id_, params = _build_data(self.apikey_regular, 'get_user',
 
                                  userid=self.TEST_USER_LOGIN)
 
        response = api_call(self, params)
 

	
 
        expected = 'userid is not the same as your user'
 
        self._compare_error(id_, expected, given=response.body)
 

	
 
    def test_api_pull(self):
 
        #TODO: issues with rhodecode_extras here.. not sure why !
 
        pass
 
@@ -237,6 +296,42 @@ class BaseTestApi(object):
 
                   % (TEST_USER_ADMIN_LOGIN, self.REPO, True))
 
        self._compare_ok(id_, expected, given=response.body)
 

	
 
    def test_api_lock_repo_lock_aquire_by_non_admin(self):
 
        repo_name = 'api_delete_me'
 
        create_repo(repo_name, self.REPO_TYPE, owner=self.TEST_USER_LOGIN)
 
        try:
 
            id_, params = _build_data(self.apikey_regular, 'lock',
 
                                      repoid=repo_name,
 
                                      locked=True)
 
            response = api_call(self, params)
 
            expected = ('User `%s` set lock state for repo `%s` to `%s`'
 
                       % (self.TEST_USER_LOGIN, repo_name, True))
 
            self._compare_ok(id_, expected, given=response.body)
 
        finally:
 
            destroy_repo(repo_name)
 

	
 
    def test_api_lock_repo_lock_aquire_non_admin_with_userid(self):
 
        repo_name = 'api_delete_me'
 
        create_repo(repo_name, self.REPO_TYPE, owner=self.TEST_USER_LOGIN)
 
        try:
 
            id_, params = _build_data(self.apikey_regular, 'lock',
 
                                      userid=TEST_USER_ADMIN_LOGIN,
 
                                      repoid=repo_name,
 
                                      locked=True)
 
            response = api_call(self, params)
 
            expected = 'userid is not the same as your user'
 
            self._compare_error(id_, expected, given=response.body)
 
        finally:
 
            destroy_repo(repo_name)
 

	
 
    def test_api_lock_repo_lock_aquire_non_admin_not_his_repo(self):
 
        id_, params = _build_data(self.apikey_regular, 'lock',
 
                                  repoid=self.REPO,
 
                                  locked=True)
 
        response = api_call(self, params)
 
        expected = 'repository `%s` does not exist' % (self.REPO)
 
        self._compare_error(id_, expected, given=response.body)
 

	
 
    def test_api_lock_repo_lock_release(self):
 
        id_, params = _build_data(self.apikey, 'lock',
 
                                  userid=TEST_USER_ADMIN_LOGIN,
 
@@ -247,6 +342,15 @@ class BaseTestApi(object):
 
                   % (TEST_USER_ADMIN_LOGIN, self.REPO, False))
 
        self._compare_ok(id_, expected, given=response.body)
 

	
 
    def test_api_lock_repo_lock_aquire_optional_userid(self):
 
        id_, params = _build_data(self.apikey, 'lock',
 
                                  repoid=self.REPO,
 
                                  locked=True)
 
        response = api_call(self, params)
 
        expected = ('User `%s` set lock state for repo `%s` to `%s`'
 
                   % (TEST_USER_ADMIN_LOGIN, self.REPO, True))
 
        self._compare_ok(id_, expected, given=response.body)
 

	
 
    @mock.patch.object(Repository, 'lock', crash)
 
    def test_api_lock_error(self):
 
        id_, params = _build_data(self.apikey, 'lock',
 
@@ -457,6 +561,48 @@ class BaseTestApi(object):
 
        self._compare_ok(id_, expected, given=response.body)
 
        destroy_users_group(new_group)
 

	
 
    def test_api_get_repo_by_non_admin(self):
 
        id_, params = _build_data(self.apikey, 'get_repo',
 
                                  repoid=self.REPO)
 
        response = api_call(self, params)
 

	
 
        repo = RepoModel().get_by_repo_name(self.REPO)
 
        ret = repo.get_api_data()
 

	
 
        members = []
 
        for user in repo.repo_to_perm:
 
            perm = user.permission.permission_name
 
            user = user.user
 
            user_data = user.get_api_data()
 
            user_data['type'] = "user"
 
            user_data['permission'] = perm
 
            members.append(user_data)
 

	
 
        for users_group in repo.users_group_to_perm:
 
            perm = users_group.permission.permission_name
 
            users_group = users_group.users_group
 
            users_group_data = users_group.get_api_data()
 
            users_group_data['type'] = "users_group"
 
            users_group_data['permission'] = perm
 
            members.append(users_group_data)
 

	
 
        ret['members'] = members
 

	
 
        expected = ret
 
        self._compare_ok(id_, expected, given=response.body)
 

	
 
    def test_api_get_repo_by_non_admin_no_permission_to_repo(self):
 
        RepoModel().grant_user_permission(repo=self.REPO,
 
                                          user=self.TEST_USER_LOGIN,
 
                                          perm='repository.none')
 

	
 
        id_, params = _build_data(self.apikey_regular, 'get_repo',
 
                                  repoid=self.REPO)
 
        response = api_call(self, params)
 

	
 
        expected = 'repository `%s` does not exist' % (self.REPO)
 
        self._compare_error(id_, expected, given=response.body)
 

	
 
    def test_api_get_repo_that_doesn_not_exist(self):
 
        id_, params = _build_data(self.apikey, 'get_repo',
 
                                  repoid='no-such-repo')
 
@@ -478,6 +624,18 @@ class BaseTestApi(object):
 
        expected = ret
 
        self._compare_ok(id_, expected, given=response.body)
 

	
 
    def test_api_get_repos_non_admin(self):
 
        id_, params = _build_data(self.apikey_regular, 'get_repos')
 
        response = api_call(self, params)
 

	
 
        result = []
 
        for repo in RepoModel().get_all_user_repos(self.TEST_USER_LOGIN):
 
            result.append(repo.get_api_data())
 
        ret = jsonify(result)
 

	
 
        expected = ret
 
        self._compare_ok(id_, expected, given=response.body)
 

	
 
    @parameterized.expand([('all', 'all'),
 
                           ('dirs', 'dirs'),
 
                           ('files', 'files'), ])
 
@@ -560,6 +718,56 @@ class BaseTestApi(object):
 
        expected = 'user `%s` does not exist' % owner
 
        self._compare_error(id_, expected, given=response.body)
 

	
 
    def test_api_create_repo_dont_specify_owner(self):
 
        repo_name = 'api-repo'
 
        owner = 'i-dont-exist'
 
        id_, params = _build_data(self.apikey, 'create_repo',
 
                                    repo_name=repo_name,
 
                                    repo_type='hg',
 
                                  )
 
        response = api_call(self, params)
 

	
 
        repo = RepoModel().get_by_repo_name(repo_name)
 
        ret = {
 
            'msg': 'Created new repository `%s`' % repo_name,
 
            'repo': jsonify(repo.get_api_data())
 
        }
 
        expected = ret
 
        self._compare_ok(id_, expected, given=response.body)
 
        destroy_repo(repo_name)
 

	
 
    def test_api_create_repo_by_non_admin(self):
 
        repo_name = 'api-repo'
 
        owner = 'i-dont-exist'
 
        id_, params = _build_data(self.apikey_regular, 'create_repo',
 
                                    repo_name=repo_name,
 
                                    repo_type='hg',
 
                                  )
 
        response = api_call(self, params)
 

	
 
        repo = RepoModel().get_by_repo_name(repo_name)
 
        ret = {
 
            'msg': 'Created new repository `%s`' % repo_name,
 
            'repo': jsonify(repo.get_api_data())
 
        }
 
        expected = ret
 
        self._compare_ok(id_, expected, given=response.body)
 
        destroy_repo(repo_name)
 

	
 
    def test_api_create_repo_by_non_admin_specify_owner(self):
 
        repo_name = 'api-repo'
 
        owner = 'i-dont-exist'
 
        id_, params = _build_data(self.apikey_regular, 'create_repo',
 
                                    repo_name=repo_name,
 
                                    repo_type='hg',
 
                                    owner=owner
 
                                  )
 
        response = api_call(self, params)
 

	
 
        expected = 'Only RhodeCode admin can specify `owner` param'
 
        self._compare_error(id_, expected, given=response.body)
 
        destroy_repo(repo_name)
 

	
 
    def test_api_create_repo_exists(self):
 
        repo_name = self.REPO
 
        id_, params = _build_data(self.apikey, 'create_repo',
 
@@ -598,6 +806,35 @@ class BaseTestApi(object):
 
        expected = ret
 
        self._compare_ok(id_, expected, given=response.body)
 

	
 
    def test_api_delete_repo_by_non_admin(self):
 
        repo_name = 'api_delete_me'
 
        create_repo(repo_name, self.REPO_TYPE, owner=self.TEST_USER_LOGIN)
 
        try:
 
            id_, params = _build_data(self.apikey_regular, 'delete_repo',
 
                                      repoid=repo_name,)
 
            response = api_call(self, params)
 

	
 
            ret = {
 
                'msg': 'Deleted repository `%s`' % repo_name,
 
                'success': True
 
            }
 
            expected = ret
 
            self._compare_ok(id_, expected, given=response.body)
 
        finally:
 
            destroy_repo(repo_name)
 

	
 
    def test_api_delete_repo_by_non_admin_no_permission(self):
 
        repo_name = 'api_delete_me'
 
        create_repo(repo_name, self.REPO_TYPE)
 
        try:
 
            id_, params = _build_data(self.apikey_regular, 'delete_repo',
 
                                      repoid=repo_name,)
 
            response = api_call(self, params)
 
            expected = 'repository `%s` does not exist' % (repo_name)
 
            self._compare_error(id_, expected, given=response.body)
 
        finally:
 
            destroy_repo(repo_name)
 

	
 
    def test_api_delete_repo_exception_occurred(self):
 
        repo_name = 'api_delete_me'
 
        create_repo(repo_name, self.REPO_TYPE)
 
@@ -630,6 +867,49 @@ class BaseTestApi(object):
 
        self._compare_ok(id_, expected, given=response.body)
 
        destroy_repo(fork_name)
 

	
 
    def test_api_fork_repo_non_admin(self):
 
        fork_name = 'api-repo-fork'
 
        id_, params = _build_data(self.apikey_regular, 'fork_repo',
 
                                    repoid=self.REPO,
 
                                    fork_name=fork_name,
 
                                  )
 
        response = api_call(self, params)
 

	
 
        ret = {
 
            'msg': 'Created fork of `%s` as `%s`' % (self.REPO,
 
                                                     fork_name),
 
            'success': True
 
        }
 
        expected = ret
 
        self._compare_ok(id_, expected, given=response.body)
 
        destroy_repo(fork_name)
 

	
 
    def test_api_fork_repo_non_admin_specify_owner(self):
 
        fork_name = 'api-repo-fork'
 
        id_, params = _build_data(self.apikey_regular, 'fork_repo',
 
                                    repoid=self.REPO,
 
                                    fork_name=fork_name,
 
                                    owner=TEST_USER_ADMIN_LOGIN,
 
                                  )
 
        response = api_call(self, params)
 
        expected = 'Only RhodeCode admin can specify `owner` param'
 
        self._compare_error(id_, expected, given=response.body)
 
        destroy_repo(fork_name)
 

	
 
    def test_api_fork_repo_non_admin_no_permission_to_fork(self):
 
        RepoModel().grant_user_permission(repo=self.REPO,
 
                                          user=self.TEST_USER_LOGIN,
 
                                          perm='repository.none')
 
        fork_name = 'api-repo-fork'
 
        id_, params = _build_data(self.apikey_regular, 'fork_repo',
 
                                    repoid=self.REPO,
 
                                    fork_name=fork_name,
 
                                  )
 
        response = api_call(self, params)
 
        expected = 'repository `%s` does not exist' % (self.REPO)
 
        self._compare_error(id_, expected, given=response.body)
 
        destroy_repo(fork_name)
 

	
 
    def test_api_fork_repo_unknown_owner(self):
 
        fork_name = 'api-repo-fork'
 
        owner = 'i-dont-exist'
rhodecode/tests/functional/test_admin_notifications.py
Show inline comments
 
@@ -82,6 +82,7 @@ class TestNotificationsController(TestCo
 
        response = self.app.delete(url('notification',
 
                                       notification_id=
 
                                       notification.notification_id))
 
        self.assertEqual(response.body, 'ok')
 

	
 
        cur_user = User.get(cur_usr_id)
 
        self.assertEqual(cur_user.notifications, [])
rhodecode/tests/functional/test_compare.py
Show inline comments
 
@@ -98,7 +98,7 @@ class TestCompareController(TestControll
 
                                    ))
 

	
 
        try:
 
            response.mustcontain('%s@%s -> %s@%s' % (repo2.repo_name, rev1, repo1.repo_name, rev2))
 
            response.mustcontain('%s@%s -&gt; %s@%s' % (repo2.repo_name, rev1, repo1.repo_name, rev2))
 
            response.mustcontain("""Showing 2 commits""")
 
            response.mustcontain("""1 file changed with 2 insertions and 0 deletions""")
 

	
 
@@ -156,7 +156,7 @@ class TestCompareController(TestControll
 
                                    ))
 

	
 
        try:
 
            response.mustcontain('%s@%s -> %s@%s' % (repo2.repo_name, rev1, repo1.repo_name, rev2))
 
            response.mustcontain('%s@%s -&gt; %s@%s' % (repo2.repo_name, rev1, repo1.repo_name, rev2))
 
            response.mustcontain("""Showing 2 commits""")
 
            response.mustcontain("""1 file changed with 2 insertions and 0 deletions""")
 

	
 
@@ -191,7 +191,7 @@ class TestCompareController(TestControll
 
#                                    ))
 
#
 
#        try:
 
#            response.mustcontain('%s@%s -> %s@%s' % (HG_REPO, rev1, HG_FORK, rev2))
 
#            response.mustcontain('%s@%s -&gt; %s@%s' % (HG_REPO, rev1, HG_FORK, rev2))
 
#            ## outgoing changesets between those revisions
 
#
 
#            response.mustcontain("""<a href="/%s/changeset/2dda4e345facb0ccff1a191052dd1606dba6781d">r4:2dda4e345fac</a>""" % (HG_REPO))
 
@@ -226,7 +226,7 @@ class TestCompareController(TestControll
 
#                                    ))
 
#
 
#        try:
 
#            response.mustcontain('%s@%s -> %s@%s' % (HG_REPO, rev1, HG_FORK, rev2))
 
#            response.mustcontain('%s@%s -&gt; %s@%s' % (HG_REPO, rev1, HG_FORK, rev2))
 
#            ## outgoing changesets between those revisions
 
#
 
#            response.mustcontain("""<a href="/%s/changeset/2dda4e345facb0ccff1a191052dd1606dba6781d">r4:2dda4e345fac</a>""" % (HG_REPO))
 
@@ -312,7 +312,7 @@ class TestCompareController(TestControll
 
#                                    ))
 
#
 
#        try:
 
#            #response.mustcontain('%s@%s -> %s@%s' % (r2_name, rev1, r1_name, rev2))
 
#            #response.mustcontain('%s@%s -&gt; %s@%s' % (r2_name, rev1, r1_name, rev2))
 
#
 
#            #add new commit into parent !
 
#            cs0 = ScmModel().create_node(
 
@@ -336,7 +336,7 @@ class TestCompareController(TestControll
 
#                                        bundle=False
 
#                                        ))
 
#
 
#            response.mustcontain('%s@%s -> %s@%s' % (r2_name, rev1, r1_name, rev2))
 
#            response.mustcontain('%s@%s -&gt; %s@%s' % (r2_name, rev1, r1_name, rev2))
 
#            response.mustcontain("""file1-line1-from-fork""")
 
#            response.mustcontain("""file2-line1-from-fork""")
 
#            response.mustcontain("""file3-line1-from-fork""")
rhodecode/tests/functional/test_compare_local.py
Show inline comments
 
@@ -19,7 +19,7 @@ class TestCompareController(TestControll
 
                                    other_ref_type="tag",
 
                                    other_ref=tag2,
 
                                    ))
 
        response.mustcontain('%s@%s -> %s@%s' % (HG_REPO, tag1, HG_REPO, tag2))
 
        response.mustcontain('%s@%s -&gt; %s@%s' % (HG_REPO, tag1, HG_REPO, tag2))
 
        ## outgoing changesets between tags
 
        response.mustcontain('''<a href="/%s/changeset/c5ddebc06eaaba3010c2d66ea6ec9d074eb0f678">r112:c5ddebc06eaa</a>''' % HG_REPO)
 
        response.mustcontain('''<a href="/%s/changeset/70d4cef8a37657ee4cf5aabb3bd9f68879769816">r115:70d4cef8a376</a>''' % HG_REPO)
 
@@ -56,7 +56,7 @@ class TestCompareController(TestControll
 
                                    other_ref=tag2,
 
                                    bundle=False
 
                                    ))
 
        response.mustcontain('%s@%s -> %s@%s' % (GIT_REPO, tag1, GIT_REPO, tag2))
 
        response.mustcontain('%s@%s -&gt; %s@%s' % (GIT_REPO, tag1, GIT_REPO, tag2))
 

	
 
        ## outgoing changesets between tags
 
        response.mustcontain('''<a href="/%s/changeset/794bbdd31545c199f74912709ea350dedcd189a2">r113:794bbdd31545</a>''' % GIT_REPO)
 
@@ -92,7 +92,7 @@ class TestCompareController(TestControll
 
                                    other_ref='default',
 
                                    ))
 

	
 
        response.mustcontain('%s@default -> %s@default' % (HG_REPO, HG_REPO))
 
        response.mustcontain('%s@default -&gt; %s@default' % (HG_REPO, HG_REPO))
 
        # branch are equal
 
        response.mustcontain('<span class="empty_data">No files</span>')
 
        response.mustcontain('<span class="empty_data">No changesets</span>')
 
@@ -107,7 +107,7 @@ class TestCompareController(TestControll
 
                                    other_ref='master',
 
                                    ))
 

	
 
        response.mustcontain('%s@master -> %s@master' % (GIT_REPO, GIT_REPO))
 
        response.mustcontain('%s@master -&gt; %s@master' % (GIT_REPO, GIT_REPO))
 
        # branch are equal
 
        response.mustcontain('<span class="empty_data">No files</span>')
 
        response.mustcontain('<span class="empty_data">No changesets</span>')
 
@@ -124,7 +124,7 @@ class TestCompareController(TestControll
 
                                    other_ref_type="rev",
 
                                    other_ref=rev2,
 
                                    ))
 
        response.mustcontain('%s@%s -> %s@%s' % (HG_REPO, rev1, HG_REPO, rev2))
 
        response.mustcontain('%s@%s -&gt; %s@%s' % (HG_REPO, rev1, HG_REPO, rev2))
 
        ## outgoing changesets between those revisions
 
        response.mustcontain("""<a href="/%s/changeset/3d8f361e72ab303da48d799ff1ac40d5ac37c67e">r1:%s</a>""" % (HG_REPO, rev2))
 

	
 
@@ -144,7 +144,7 @@ class TestCompareController(TestControll
 
                                    other_ref_type="rev",
 
                                    other_ref=rev2,
 
                                    ))
 
        response.mustcontain('%s@%s -> %s@%s' % (GIT_REPO, rev1, GIT_REPO, rev2))
 
        response.mustcontain('%s@%s -&gt; %s@%s' % (GIT_REPO, rev1, GIT_REPO, rev2))
 
        ## outgoing changesets between those revisions
 
        response.mustcontain("""<a href="/%s/changeset/38b5fe81f109cb111f549bfe9bb6b267e10bc557">r1:%s</a>""" % (GIT_REPO, rev2[:12]))
 
        response.mustcontain('1 file changed with 7 insertions and 0 deletions')
rhodecode/tests/functional/test_home.py
Show inline comments
 
@@ -3,6 +3,9 @@ from rhodecode.tests import *
 
from rhodecode.model.meta import Session
 
from rhodecode.model.db import User, RhodeCodeSetting, Repository
 
from rhodecode.lib.utils import set_rhodecode_config
 
from rhodecode.tests.models.common import _make_repo, _make_group
 
from rhodecode.model.repo import RepoModel
 
from rhodecode.model.repos_group import ReposGroupModel
 

	
 

	
 
class TestHomeController(TestController):
 
@@ -61,18 +64,45 @@ merge" class="tooltip" href="/vcs_test_h
 
            Session().add(anon)
 
            Session().commit()
 

	
 
    def _set_l_dash(self, set_to):
 
        self.app.post(url('admin_setting', setting_id='visual'),
 
                      params=dict(_method='put',
 
                                  rhodecode_lightweight_dashboard=set_to,))
 

	
 
    def test_index_with_lightweight_dashboard(self):
 
        self.log_user()
 

	
 
        def set_l_dash(set_to):
 
            self.app.post(url('admin_setting', setting_id='visual'),
 
                          params=dict(_method='put',
 
                                      rhodecode_lightweight_dashboard=set_to,))
 

	
 
        set_l_dash(True)
 
        self._set_l_dash(True)
 

	
 
        try:
 
            response = self.app.get(url(controller='home', action='index'))
 
            response.mustcontain("""var data = {"totalRecords": %s""" % len(Repository.getAll()))
 
        finally:
 
            set_l_dash(False)
 
            self._set_l_dash(False)
 

	
 
    def test_index_page_on_groups(self):
 
        self.log_user()
 
        _make_repo(name='gr1/repo_in_group', repos_group=_make_group('gr1'))
 
        Session().commit()
 
        response = self.app.get(url('repos_group_home', group_name='gr1'))
 

	
 
        try:
 
            response.mustcontain("""gr1/repo_in_group""")
 
        finally:
 
            RepoModel().delete('gr1/repo_in_group')
 
            ReposGroupModel().delete(repos_group='gr1', force_delete=True)
 
            Session().commit()
 

	
 
    def test_index_page_on_groups_with_lightweight_dashboard(self):
 
        self.log_user()
 
        self._set_l_dash(True)
 
        _make_repo(name='gr1/repo_in_group', repos_group=_make_group('gr1'))
 
        Session().commit()
 
        response = self.app.get(url('repos_group_home', group_name='gr1'))
 

	
 
        try:
 
            response.mustcontain("""gr1/repo_in_group""")
 
        finally:
 
            self._set_l_dash(False)
 
            RepoModel().delete('gr1/repo_in_group')
 
            ReposGroupModel().delete(repos_group='gr1', force_delete=True)
 
            Session().commit()
rhodecode/tests/functional/test_journal.py
Show inline comments
 
@@ -10,10 +10,7 @@ class TestJournalController(TestControll
 
        self.log_user()
 
        response = self.app.get(url(controller='journal', action='index'))
 

	
 
        # Test response...
 
        assert """ <span id="follow_toggle_1" class="following" title="Stop following this repository""" in response.body, 'no info about stop follwoing repo id 1'
 

	
 
        assert """<div class="journal_day">%s</div>""" % datetime.date.today() in response.body, 'no info about action journal day'
 
        response.mustcontain("""<div class="journal_day">%s</div>""" % datetime.date.today())
 

	
 
    def test_stop_following_repository(self):
 
        session = self.log_user()
rhodecode/tests/models/test_user_permissions_on_repos.py
Show inline comments
 
#TODO; write tests when we activate algo for permissions.
 
\ No newline at end of file
 
#TODO; write tests when we activate algo for permissions.
rhodecode/tests/scripts/test_vcs_operations.py
Show inline comments
 
@@ -39,6 +39,7 @@ from rhodecode.tests import *
 
from rhodecode.model.db import User, Repository, UserLog
 
from rhodecode.model.meta import Session
 
from rhodecode.model.repo import RepoModel
 
from rhodecode.model.user import UserModel
 

	
 
DEBUG = True
 
HOST = '127.0.0.1:5000'  # test host
 
@@ -420,3 +421,41 @@ class TestVCSOperations(unittest.TestCas
 
#        Session.remove()
 
#        r = Repository.get_by_repo_name(GIT_REPO)
 
#        assert r.locked == [None, None]
 

	
 
    def test_ip_restriction_hg(self):
 
        user_model = UserModel()
 
        new_ip = user_model.add_extra_ip(TEST_USER_ADMIN_LOGIN, '10.10.10.10/32')
 
        Session().commit()
 
        clone_url = _construct_url(HG_REPO)
 
        stdout, stderr = Command('/tmp').execute('hg clone', clone_url)
 
        assert 'abort: HTTP Error 403: Forbidden' in stderr
 

	
 
        #release IP restrictions
 
        clone_url = _construct_url(HG_REPO)
 
        user_model.delete_extra_ip(TEST_USER_ADMIN_LOGIN, new_ip.ip_id)
 
        Session().commit()
 
        stdout, stderr = Command('/tmp').execute('hg clone', clone_url)
 

	
 
        assert 'requesting all changes' in stdout
 
        assert 'adding changesets' in stdout
 
        assert 'adding manifests' in stdout
 
        assert 'adding file changes' in stdout
 

	
 
        assert stderr == ''
 

	
 
    def test_ip_restriction_git(self):
 
        user_model = UserModel()
 
        new_ip = user_model.add_extra_ip(TEST_USER_ADMIN_LOGIN, '10.10.10.10/32')
 
        Session().commit()
 
        clone_url = _construct_url(GIT_REPO)
 
        stdout, stderr = Command('/tmp').execute('git clone', clone_url)
 
        assert 'error: The requested URL returned error: 403 Forbidden' in stderr
 

	
 
        #release IP restrictions
 
        clone_url = _construct_url(GIT_REPO)
 
        user_model.delete_extra_ip(TEST_USER_ADMIN_LOGIN, new_ip.ip_id)
 
        Session().commit()
 
        stdout, stderr = Command('/tmp').execute('git clone', clone_url)
 

	
 
        assert 'Cloning into' in stdout
 
        assert stderr == ''
rhodecode/tests/test_libs.py
Show inline comments
 
@@ -123,15 +123,16 @@ class TestLibs(unittest.TestCase):
 
        from rhodecode.lib.utils2 import age
 
        n = datetime.datetime.now()
 
        delt = lambda *args, **kwargs: datetime.timedelta(*args, **kwargs)
 
        prev_month = n.month - 1 if n.month != 1 else n.month - 2
 
        self.assertEqual(age(n), u'just now')
 
        self.assertEqual(age(n - delt(seconds=1)), u'1 second ago')
 
        self.assertEqual(age(n - delt(seconds=60 * 2)), u'2 minutes ago')
 
        self.assertEqual(age(n - delt(hours=1)), u'1 hour ago')
 
        self.assertEqual(age(n - delt(hours=24)), u'1 day ago')
 
        self.assertEqual(age(n - delt(hours=24 * 5)), u'5 days ago')
 
        self.assertEqual(age(n - delt(hours=24 * (calendar.mdays[n.month - 1]))),
 
        self.assertEqual(age(n - delt(hours=24 * (calendar.mdays[prev_month]))),
 
                         u'1 month ago')
 
        self.assertEqual(age(n - delt(hours=24 * (calendar.mdays[n.month - 1] + 2))),
 
        self.assertEqual(age(n - delt(hours=24 * (calendar.mdays[prev_month] + 2))),
 
                         u'1 month and 2 days ago')
 
        self.assertEqual(age(n - delt(hours=24 * 400)), u'1 year and 1 month ago')
 

	
setup.py
Show inline comments
 
@@ -60,10 +60,10 @@ if sys.version_info < (2, 7):
 
    requirements.append("unittest2")
 

	
 
if is_windows:
 
    requirements.append("mercurial==2.4.1")
 
    requirements.append("mercurial==2.4.2")
 
else:
 
    requirements.append("py-bcrypt")
 
    requirements.append("mercurial==2.4.1")
 
    requirements.append("mercurial==2.4.2")
 

	
 

	
 
dependency_links = [
0 comments (0 inline, 0 general)