Changeset - b2c3d4568a5f
[Not reviewed]
Merge default
0 36 0
Mads Kiilerich (mads) - 3 years ago 2023-03-31 21:29:14
mads@kiilerich.com
Merge stable
36 files changed with 1431 insertions and 1530 deletions:
0 comments (0 inline, 0 general)
CONTRIBUTORS
Show inline comments
 
List of contributors to Kallithea project:
 

	
 
    Mads Kiilerich <mads@kiilerich.com> 2016-2023
 
    Manuel Jacob <me@manueljacob.de> 2019-2020 2022-2023
 
    Mathias De Mare <mathias.de_mare@nokia.com> 2023
 
    Asterios Dimitriou <steve@pci.gr> 2016-2017 2020 2022
 
    Jaime Marquínez Ferrándiz <weblate@jregistros.fastmail.net> 2022
 
    Louis Bertrand <louis.bertrand@durhamcollege.ca> 2022
 
    toras9000 <toras9000@gmail.com> 2022
 
    yzqzss <yzqzss@othing.xyz> 2022
 
    МАН69К <weblate@mah69k.net> 2022
 
    Thomas De Schampheleire <thomas.de_schampheleire@nokia.com> 2014-2021
 
    Mads Kiilerich <mads@kiilerich.com> 2016-2021
 
    ssantos <ssantos@web.de> 2018-2021
 
    Private <adamantine.sword@gmail.com> 2019-2021
 
    Étienne Gilli <etienne@gilli.io> 2020-2021
 
    fresh <fresh190@protonmail.com> 2020-2021
 
    robertus <robertuss12@gmail.com> 2020-2021
 
    Eugenia Russell <eugenia.russell2019@gmail.com> 2021
 
    Michalis <michalisntovas@yahoo.gr> 2021
 
    vs <vsuhachev@yandex.ru> 2021
 
    Александр <akonn7@mail.ru> 2021
 
    Asterios Dimitriou <steve@pci.gr> 2016-2017 2020
 
    Allan Nordhøy <epost@anotheragency.no> 2017-2020
 
    Anton Schur <tonich.sh@gmail.com> 2017 2020
 
    Manuel Jacob <me@manueljacob.de> 2019-2020
 
    Artem <kovalevartem.ru@gmail.com> 2020
 
    David Ignjić <ignjic@gmail.com> 2020
 
    Dennis Fink <dennis.fink@c3l.lu> 2020
 
    J. Lavoie <j.lavoie@net-c.ca> 2020
 
    Ross Thomas <ross@lns-nevasoft.com> 2020
 
    Tim Ooms <tatankat@users.noreply.github.com> 2020
docs/api/api.rst
Show inline comments
 
@@ -20,13 +20,13 @@ additional API keys can be generated.
 
API access
 
----------
 

	
 
Clients must send JSON encoded JSON-RPC requests::
 

	
 
    {
 
        "id: "<id>",
 
        "id": "<id>",
 
        "api_key": "<api_key>",
 
        "method": "<method_name>",
 
        "args": {"<arg_key>": "<arg_val>"}
 
    }
 

	
 
For example, to pull to a local "CPython" mirror using curl::
 
@@ -112,13 +112,13 @@ INPUT::
 
              }
 

	
 
OUTPUT::
 

	
 
    id : <id_given_in_input>
 
    result : "Pulled from `<reponame>`"
 
    error :  null
 
    error : null
 

	
 
rescan_repos
 
^^^^^^^^^^^^
 

	
 
Rescan repositories. If ``remove_obsolete`` is set,
 
Kallithea will delete repos that are in the database but not in the filesystem.
 
@@ -135,13 +135,13 @@ INPUT::
 

	
 
OUTPUT::
 

	
 
    id : <id_given_in_input>
 
    result : "{'added': [<list of names of added repos>],
 
               'removed': [<list of names of removed repos>]}"
 
    error :  null
 
    error : null
 

	
 
invalidate_cache
 
^^^^^^^^^^^^^^^^
 

	
 
Invalidate the cache for a repository.
 
This command can only be executed using the api_key of a user with admin rights,
 
@@ -157,13 +157,13 @@ INPUT::
 
              }
 

	
 
OUTPUT::
 

	
 
    id : <id_given_in_input>
 
    result : "Caches of repository `<reponame>`"
 
    error :  null
 
    error : null
 

	
 
get_ip
 
^^^^^^
 

	
 
Return IP address as seen from Kallithea server, together with all
 
defined IP addresses for given user.
 
@@ -172,30 +172,29 @@ This command can only be executed using 
 
INPUT::
 

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

	
 
OUTPUT::
 

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

	
 
    error :  null
 
    error : null
 

	
 
get_user
 
^^^^^^^^
 

	
 
Get a user by username or userid. The result is empty if user can't be found.
 
If userid param is skipped, it is set to id of user who is calling this method.
 
@@ -211,35 +210,35 @@ INPUT::
 
                "userid" : "<username or user_id Optional(=apiuser)>"
 
              }
 

	
 
OUTPUT::
 

	
 
    id : <id_given_in_input>
 
    result: None if user does not exist or
 
            {
 
    result : None if user does not exist or
 
             {
 
                "user_id" :     "<user_id>",
 
                "api_key" :     "<api_key>",
 
                "username" :    "<username>",
 
                "firstname":    "<firstname>",
 
                "firstname" :   "<firstname>",
 
                "lastname" :    "<lastname>",
 
                "email" :       "<email>",
 
                "emails":       "<list_of_all_additional_emails>",
 
                "emails" :      "<list_of_all_additional_emails>",
 
                "ip_addresses": "<list_of_ip_addresses_for_user>",
 
                "active" :      "<bool>",
 
                "admin" :       "<bool>",
 
                "admin" :       "<bool>",
 
                "ldap_dn" :     "<ldap_dn>",
 
                "last_login":   "<last_login>",
 
                "last_login" :  "<last_login>",
 
                "permissions": {
 
                    "global": ["hg.create.repository",
 
                               "repository.read",
 
                               "hg.register.manual_activate"],
 
                    "repositories": {"repo1": "repository.none"},
 
                    "repositories_groups": {"Group1": "group.read"}
 
                 },
 
            }
 
    error:  null
 
                    "repositories" : {"repo1" : "repository.none"},
 
                    "repositories_groups" : {"Group1" : "group.read"}
 
                 }
 
             }
 
    error : null
 

	
 
get_users
 
^^^^^^^^^
 

	
 
List all existing users.
 
This command can only be executed using the api_key of a user with admin rights.
 
@@ -251,30 +250,30 @@ INPUT::
 
    method :  "get_users"
 
    args :    { }
 

	
 
OUTPUT::
 

	
 
    id : <id_given_in_input>
 
    result: [
 
    result : [
 
              {
 
                "user_id" :     "<user_id>",
 
                "api_key" :     "<api_key>",
 
                "username" :    "<username>",
 
                "firstname":    "<firstname>",
 
                "firstname" :   "<firstname>",
 
                "lastname" :    "<lastname>",
 
                "email" :       "<email>",
 
                "emails":       "<list_of_all_additional_emails>",
 
                "emails" :      "<list_of_all_additional_emails>",
 
                "ip_addresses": "<list_of_ip_addresses_for_user>",
 
                "active" :      "<bool>",
 
                "admin" :       "<bool>",
 
                "admin" :       "<bool>",
 
                "ldap_dn" :     "<ldap_dn>",
 
                "last_login":   "<last_login>",
 
                "last_login" :  "<last_login>"
 
              },
 
 
            ]
 
    error:  null
 
             ]
 
    error : null
 

	
 
.. _create-user:
 

	
 
create_user
 
^^^^^^^^^^^
 

	
 
@@ -297,28 +296,28 @@ INPUT::
 
                "ldap_dn" :   "<ldap_dn> = Optional(None)"
 
              }
 

	
 
OUTPUT::
 

	
 
    id : <id_given_in_input>
 
    result: {
 
    result : {
 
              "msg" : "created new user `<username>`",
 
              "user": {
 
              "user" : {
 
                "user_id" :  "<user_id>",
 
                "username" : "<username>",
 
                "firstname": "<firstname>",
 
                "lastname" : "<lastname>",
 
                "email" :    "<email>",
 
                "emails":    "<list_of_all_additional_emails>",
 
                "emails" :   "<list_of_all_additional_emails>",
 
                "active" :   "<bool>",
 
                "admin" :    "<bool>",
 
                "admin" :    "<bool>",
 
                "ldap_dn" :  "<ldap_dn>",
 
                "last_login": "<last_login>",
 
              },
 
            }
 
    error:  null
 
                "last_login": "<last_login>"
 
              }
 
             }
 
    error : null
 

	
 
Example::
 

	
 
    kallithea-api create_user username:bent email:bent@example.com firstname:Bent lastname:Bentsen extern_type:ldap extern_name:uid=bent,dc=example,dc=com
 

	
 
update_user
 
@@ -344,29 +343,29 @@ INPUT::
 
                "ldap_dn" :   "<ldap_dn> = Optional(None)"
 
              }
 

	
 
OUTPUT::
 

	
 
    id : <id_given_in_input>
 
    result: {
 
    result : {
 
              "msg" : "updated user ID:<userid> <username>",
 
              "user": {
 
              "user" : {
 
                "user_id" :  "<user_id>",
 
                "api_key" :  "<api_key>",
 
                "username" : "<username>",
 
                "firstname": "<firstname>",
 
                "lastname" : "<lastname>",
 
                "email" :    "<email>",
 
                "emails":    "<list_of_all_additional_emails>",
 
                "emails" :   "<list_of_all_additional_emails>",
 
                "active" :   "<bool>",
 
                "admin" :    "<bool>",
 
                "admin" :    "<bool>",
 
                "ldap_dn" :  "<ldap_dn>",
 
                "last_login": "<last_login>",
 
              },
 
            }
 
    error:  null
 
                "last_login": "<last_login>"
 
              }
 
             }
 
    error : null
 

	
 
delete_user
 
^^^^^^^^^^^
 

	
 
Delete the given user if such a user exists.
 
This command can only be executed using the api_key of a user with admin rights.
 
@@ -374,23 +373,23 @@ This command can only be executed using 
 
INPUT::
 

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

	
 
OUTPUT::
 

	
 
    id : <id_given_in_input>
 
    result: {
 
    result : {
 
              "msg" : "deleted user ID:<userid> <username>",
 
              "user": null
 
            }
 
    error:  null
 
              "user" : null
 
             }
 
    error : null
 

	
 
get_user_group
 
^^^^^^^^^^^^^^
 

	
 
Get an existing user group.
 
This command can only be executed using the api_key of a user with admin rights.
 
@@ -408,26 +407,26 @@ OUTPUT::
 

	
 
    id : <id_given_in_input>
 
    result : None if group not exist
 
             {
 
               "users_group_id" : "<id>",
 
               "group_name" :     "<groupname>",
 
               "active":          "<bool>",
 
               "active" :         "<bool>",
 
               "members" :  [
 
                              {
 
                                "user_id" :  "<user_id>",
 
                                "api_key" :  "<api_key>",
 
                                "username" : "<username>",
 
                                "firstname": "<firstname>",
 
                                "lastname" : "<lastname>",
 
                                "email" :    "<email>",
 
                                "emails":    "<list_of_all_additional_emails>",
 
                                "emails" :   "<list_of_all_additional_emails>",
 
                                "active" :   "<bool>",
 
                                "admin" :    "<bool>",
 
                                "admin" :    "<bool>",
 
                                "ldap_dn" :  "<ldap_dn>",
 
                                "last_login": "<last_login>",
 
                                "last_login": "<last_login>"
 
                              },
 
 
                            ]
 
             }
 
    error : null
 

	
 
@@ -448,13 +447,13 @@ OUTPUT::
 

	
 
    id : <id_given_in_input>
 
    result : [
 
               {
 
               "users_group_id" : "<id>",
 
               "group_name" :     "<groupname>",
 
               "active":          "<bool>",
 
               "active" :         "<bool>"
 
               },
 
 
              ]
 
    error : null
 

	
 
create_user_group
 
@@ -465,57 +464,57 @@ This command can only be executed using 
 

	
 
INPUT::
 

	
 
    id : <id_for_response>
 
    api_key : "<api_key>"
 
    method :  "create_user_group"
 
    args:     {
 
    args :    {
 
                "group_name": "<groupname>",
 
                "owner" :     "<owner_name_or_id = Optional(=apiuser)>",
 
                "active":     "<bool> = Optional(True)"
 
                "active" :    "<bool> = Optional(True)"
 
              }
 

	
 
OUTPUT::
 

	
 
    id : <id_given_in_input>
 
    result: {
 
              "msg": "created new user group `<groupname>`",
 
              "users_group": {
 
    result : {
 
              "msg" : "created new user group `<groupname>`",
 
              "users_group" : {
 
                     "users_group_id" : "<id>",
 
                     "group_name" :     "<groupname>",
 
                     "active":          "<bool>",
 
               },
 
            }
 
    error:  null
 
                     "active" :         "<bool>"
 
               }
 
             }
 
    error : null
 

	
 
add_user_to_user_group
 
^^^^^^^^^^^^^^^^^^^^^^
 

	
 
Adds a user to a user group. If the user already is in that group, success will be
 
Add a user to a user group. If the user already is in that group, success will be
 
``false``.
 
This command can only be executed using the api_key of a user with admin rights.
 

	
 
INPUT::
 

	
 
    id : <id_for_response>
 
    api_key : "<api_key>"
 
    method :  "add_user_user_group"
 
    args:     {
 
    args :    {
 
                "usersgroupid" : "<user group id or name>",
 
                "userid" : "<user_id or username>",
 
                "userid" : "<user_id or username>"
 
              }
 

	
 
OUTPUT::
 

	
 
    id : <id_given_in_input>
 
    result: {
 
              "success": True|False # depends on if member is in group
 
              "msg": "added member `<username>` to a user group `<groupname>` |
 
                      User is already in that group"
 
            }
 
    error:  null
 
    result : {
 
              "success" : True|False,  # depends on if member is in group
 
              "msg" : "added member `<username>` to a user group `<groupname>` |
 
                       User is already in that group"
 
             }
 
    error : null
 

	
 
remove_user_from_user_group
 
^^^^^^^^^^^^^^^^^^^^^^^^^^^
 

	
 
Remove a user from a user group. If the user isn't in the given group, success will
 
be ``false``.
 
@@ -523,26 +522,187 @@ This command can only be executed using 
 

	
 
INPUT::
 

	
 
    id : <id_for_response>
 
    api_key : "<api_key>"
 
    method :  "remove_user_from_user_group"
 
    args:     {
 
    args :    {
 
                "usersgroupid" : "<user group id or name>",
 
                "userid" : "<user_id or username>",
 
                "userid" : "<user_id or username>"
 
              }
 

	
 
OUTPUT::
 

	
 
    id : <id_given_in_input>
 
    result : {
 
              "success" : True|False,  # depends on if member is in group
 
              "msg" : "removed member <username> from user group <groupname> |
 
                       User wasn't in group"
 
             }
 
    error : null
 

	
 
get_repo_group
 
^^^^^^^^^^^^^^
 

	
 
Get an existing repository group.
 
This command can only be executed using the api_key of a user with admin rights.
 

	
 
INPUT::
 

	
 
    id : <id_for_response>
 
    api_key : "<api_key>"
 
    method :  "get_repo_group"
 
    args :    {
 
                "repogroupid" : "<repo group id or name>"
 
              }
 

	
 
OUTPUT::
 

	
 
    id : <id_given_in_input>
 
    result: {
 
              "success":  True|False,  # depends on if member is in group
 
              "msg": "removed member <username> from user group <groupname> |
 
                      User wasn't in group"
 
            }
 
    error:  null
 
    result :
 
               {
 
               "group_id" :          "<id>",
 
               "group_name" :        "<groupname>",
 
               "group_description" : "<groupdescription>",
 
               "parent_group" :      "<groupid>"|null,
 
               "repositories" :      "<list_of_all_repo_names_in_group>",
 
               "owner" :             "<owner>",
 
               "members" :           [
 
                                       {
 
                                         "name" : "<name>",
 
                                         "type" : "user",
 
                                         "permission" : "group.(none|read|write|admin)"
 
                                       },
 
                                       {
 
                                         "name" : "<name>",
 
                                         "type" : "user_group",
 
                                         "permission" : "group.(none|read|write|admin)"
 
                                       },
 
 
                                     ]
 

	
 
               },
 
    error : null
 

	
 
get_repo_groups
 
^^^^^^^^^^^^^^^
 

	
 
List all existing repository groups.
 
This command can only be executed using the api_key of a user with admin rights.
 

	
 
INPUT::
 

	
 
    id : <id_for_response>
 
    api_key : "<api_key>"
 
    method :  "get_repo_groups"
 
    args :    { }
 

	
 
OUTPUT::
 

	
 
    id : <id_given_in_input>
 
    result : [
 
               {
 
               "group_id" :          "<id>",
 
               "group_name" :        "<groupname>",
 
               "group_description" : "<groupdescription>",
 
               "parent_group" :      "<groupid>"|null,
 
               "repositories" :      "<list_of_all_repo_names_in_group>",
 
               "owner" :             "<owner>"
 
               },
 
 
              ]
 
    error : null
 

	
 
create_repo_group
 
^^^^^^^^^^^^^^^^^
 

	
 
Create a new repository group.
 
This command can only be executed using the api_key of a user with admin rights.
 

	
 
INPUT::
 

	
 
    id : <id_for_response>
 
    api_key : "<api_key>"
 
    method :  "create_repo_group"
 
    args :    {
 
                "group_name" :       "<group_name>",
 
                "description" :      "<description> = Optional("")",
 
                "owner" :            "<username or user_id> = Optional(None)",
 
                "parent" :           "<reponame or id> = Optional(None)",
 
                "copy_permissions" : "<bool> = Optional(False)"
 
              }
 

	
 
OUTPUT::
 

	
 
    id : <id_given_in_input>
 
    result : {
 
                "msg" : "created new repo group `<group_name>`",
 
                "repo_group" : {
 
                                 "group_id" :          <id>,
 
                                 "group_name" :        "<parent_group>/<group_name>",
 
                                 "group_description" : "<description>",
 
                                 "parent_group" :      <id>|null,
 
                                 "repositories" :      <list of repositories>,
 
                                 "owner" :             "<user_name>"
 
                                }
 

	
 
update_repo_group
 
^^^^^^^^^^^^^^^^^
 

	
 
Update a repository group.
 
This command can only be executed using the api_key of a user with admin rights.
 

	
 
INPUT::
 

	
 
    id : <id_for_response>
 
    api_key : "<api_key>"
 
    method :  "update_repo_group"
 
    args :    {
 
                "repogroupid" :         "<id>",
 
                "group_name" :       "<group_name> = Optional(None)",
 
                "description" :      "<description> = Optional(None)",
 
                "owner" :            "<username or user_id> = Optional(None)",
 
                "parent" :           "<reponame or id> = Optional(None)"
 
              }
 

	
 
OUTPUT::
 

	
 
    id : <id_given_in_input>
 
    result : {
 
                "msg" : "updated repository group ID:<id> <group_name>",
 
                "repo_group" : {
 
                                 "group_id" :          <id>,
 
                                 "group_name" :        "<parent_group>/<group_name>",
 
                                 "group_description" : "<description>",
 
                                 "parent_group" :      <id>|null,
 
                                 "repositories" :      <list of repositories>,
 
                                 "owner" :             "<user_name>"
 
                                }
 

	
 
delete_repo_group
 
^^^^^^^^^^^^^^^^^
 

	
 
Delete a repository group.
 
This command can only be executed using the api_key of a user with admin rights.
 

	
 
INPUT::
 

	
 
    id : <id_for_response>
 
    api_key : "<api_key>"
 
    method :  "delete_repo_group"
 
    args :    {
 
                "repogroupid" : "<id>"
 
              }
 

	
 
OUTPUT::
 

	
 
    id : <id_given_in_input>
 
    result : {
 
                "msg" : "deleted repo group ID:<id> <group_name>",
 
                "repo_group" : null
 
              }
 

	
 
get_repo
 
^^^^^^^^
 

	
 
Get an existing repository by its name or repository_id. Members will contain
 
either users_group or users associated to that repository.
 
@@ -551,150 +711,150 @@ or that of a regular user with at least 
 

	
 
INPUT::
 

	
 
    id : <id_for_response>
 
    api_key : "<api_key>"
 
    method :  "get_repo"
 
    args:     {
 
    args :    {
 
                "repoid" : "<reponame or repo_id>",
 
                "with_revision_names": "<bool> = Optional(False)",
 
                "with_pullrequests": "<bool> = Optional(False)",
 
                "with_revision_names" : "<bool> = Optional(False)",
 
                "with_pullrequests" : "<bool> = Optional(False)"
 
              }
 

	
 
OUTPUT::
 

	
 
    id : <id_given_in_input>
 
    result: None if repository does not exist or
 
            {
 
    result : None if repository does not exist or
 
             {
 
                "repo_id" :          "<repo_id>",
 
                "repo_name" :        "<reponame>"
 
                "repo_name" :        "<reponame>",
 
                "repo_type" :        "<repo_type>",
 
                "clone_uri" :        "<clone_uri>",
 
                "enable_downloads":  "<bool>",
 
                "enable_downloads" : "<bool>",
 
                "enable_statistics": "<bool>",
 
                "private":           "<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>"
 
                "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" :     [
 
                "owner" :            "<repo_owner>",
 
                "fork_of" :          "<name_of_fork_parent>",
 
                "members" :   [
 
                                  {
 
                                    "type":        "user",
 
                                    "type" :       "user",
 
                                    "user_id" :    "<user_id>",
 
                                    "api_key" :    "<api_key>",
 
                                    "username" :   "<username>",
 
                                    "firstname":   "<firstname>",
 
                                    "firstname" :  "<firstname>",
 
                                    "lastname" :   "<lastname>",
 
                                    "email" :      "<email>",
 
                                    "emails":      "<list_of_all_additional_emails>",
 
                                    "emails" :     "<list_of_all_additional_emails>",
 
                                    "active" :     "<bool>",
 
                                    "admin" :      "<bool>",
 
                                    "admin" :      "<bool>",
 
                                    "ldap_dn" :    "<ldap_dn>",
 
                                    "last_login":  "<last_login>",
 
                                    "last_login" : "<last_login>",
 
                                    "permission" : "repository.(read|write|admin)"
 
                                  },
 
 
                                  {
 
                                    "type":      "users_group",
 
                                    "type" :     "users_group",
 
                                    "id" :       "<usersgroupid>",
 
                                    "name" :     "<usersgroupname>",
 
                                    "active":    "<bool>",
 
                                    "active" :   "<bool>",
 
                                    "permission" : "repository.(read|write|admin)"
 
                                  },
 
 
                                ],
 
                 "followers":   [
 
                              ],
 
                "followers" : [
 
                                  {
 
                                    "user_id" :     "<user_id>",
 
                                    "username" :    "<username>",
 
                                    "api_key" :     "<api_key>",
 
                                    "firstname":    "<firstname>",
 
                                    "firstname" :   "<firstname>",
 
                                    "lastname" :    "<lastname>",
 
                                    "email" :       "<email>",
 
                                    "emails":       "<list_of_all_additional_emails>",
 
                                    "emails" :      "<list_of_all_additional_emails>",
 
                                    "ip_addresses": "<list_of_ip_addresses_for_user>",
 
                                    "active" :      "<bool>",
 
                                    "admin" :       "<bool>",
 
                                    "admin" :       "<bool>",
 
                                    "ldap_dn" :     "<ldap_dn>",
 
                                    "last_login":   "<last_login>",
 
                                    "last_login" :  "<last_login>"
 
                                  },
 
 
                                ],
 
                 <if with_revision_names == True>
 
                 "tags": {
 
                            "<tagname>": "<raw_id>",
 
                              ],
 
                <if with_revision_names == True>
 
                "tags" : {
 
                            "<tagname>" : "<raw_id>",
 
                            ...
 
                         },
 
                 "branches": {
 
                            "<branchname>": "<raw_id>",
 
                        },
 
                "branches" : {
 
                            "<branchname>" : "<raw_id>",
 
                            ...
 
                         },
 
                 "bookmarks": {
 
                            "<bookmarkname>": "<raw_id>",
 
                        },
 
                "bookmarks" : {
 
                            "<bookmarkname>" : "<raw_id>",
 
                            ...
 
                         },
 
                        },
 
                <if with_pullrequests == True>
 
                "pull_requests": [
 
                "pull_requests" : [
 
                  {
 
                    "status": "<pull_request_status>",
 
                    "pull_request_id": <pull_request_id>,
 
                    "description": "<pull_request_description>",
 
                    "title": "<pull_request_title>",
 
                    "url": "<pull_request_url>",
 
                    "reviewers": [
 
                    "status" : "<pull_request_status>",
 
                    "pull_request_id" : <pull_request_id>,
 
                    "description" : "<pull_request_description>",
 
                    "title" : "<pull_request_title>",
 
                    "url" : "<pull_request_url>",
 
                    "reviewers" : [
 
                      {
 
                        "username": "<user_id>",
 
                        "username" : "<user_id>"
 
                      },
 
                      ...
 
                    ],
 
                    "org_repo_url": "<repo_url>",
 
                    "org_ref_parts": [
 
                    "org_repo_url" : "<repo_url>",
 
                    "org_ref_parts" : [
 
                      "<ref_type>",
 
                      "<ref_name>",
 
                      "<raw_id>"
 
                    ],
 
                    "other_ref_parts": [
 
                    "other_ref_parts" : [
 
                      "<ref_type>",
 
                      "<ref_name>",
 
                      "<raw_id>"
 
                    ],
 
                    "comments": [
 
                    "comments" : [
 
                      {
 
                        "username": "<user_id>",
 
                        "text": "<comment text>",
 
                        "comment_id": "<comment_id>",
 
                        "username" : "<user_id>",
 
                        "text" : "<comment text>",
 
                        "comment_id" : "<comment_id>"
 
                      },
 
                      ...
 
                    ],
 
                    "owner": "<username>",
 
                    "statuses": [
 
                    "owner" : "<username>",
 
                    "statuses" : [
 
                      {
 
                        "status": "<status_of_review>",        # "under_review", "approved" or "rejected"
 
                        "reviewer": "<user_id>",
 
                        "modified_at": "<date_time_of_review>" # iso 8601 date, server's timezone
 
                        "status" : "<status_of_review>",        # "under_review", "approved" or "rejected"
 
                        "reviewer" : "<user_id>",
 
                        "modified_at" : "<date_time_of_review>" # iso 8601 date, server's timezone
 
                      },
 
                      ...
 
                    ],
 
                    "revisions": [
 
                    "revisions" : [
 
                      "<raw_id>",
 
                      ...
 
                    ]
 
                  },
 
                  ...
 
                ]
 
            }
 
    error:  null
 
             }
 
    error : null
 

	
 
get_repos
 
^^^^^^^^^
 

	
 
List all existing repositories.
 
This command can only be executed using the api_key of a user with admin rights,
 
@@ -702,35 +862,35 @@ or that of a regular user with at least 
 

	
 
INPUT::
 

	
 
    id : <id_for_response>
 
    api_key : "<api_key>"
 
    method :  "get_repos"
 
    args:     { }
 
    args :    { }
 

	
 
OUTPUT::
 

	
 
    id : <id_given_in_input>
 
    result: [
 
    result : [
 
              {
 
                "repo_id" :          "<repo_id>",
 
                "repo_name" :        "<reponame>"
 
                "repo_name" :        "<reponame>",
 
                "repo_type" :        "<repo_type>",
 
                "clone_uri" :        "<clone_uri>",
 
                "private" :          "<bool>",
 
                "created_on" :       "<datetimecreated>",
 
                "description" :      "<description>",
 
                "landing_rev":       "<landing_rev>",
 
                "owner":             "<repo_owner>",
 
                "fork_of":           "<name_of_fork_parent>",
 
                "enable_downloads":  "<bool>",
 
                "enable_statistics": "<bool>",
 
                "landing_rev" :      "<landing_rev>",
 
                "owner" :            "<repo_owner>",
 
                "fork_of" :          "<name_of_fork_parent>",
 
                "enable_downloads" : "<bool>",
 
                "enable_statistics": "<bool>"
 
              },
 
 
            ]
 
    error:  null
 
             ]
 
    error : null
 

	
 
get_repo_nodes
 
^^^^^^^^^^^^^^
 

	
 
Return a list of files and directories for a given path at the given revision.
 
It is possible to specify ret_type to show only ``files`` or ``dirs``.
 
@@ -738,30 +898,30 @@ This command can only be executed using 
 

	
 
INPUT::
 

	
 
    id : <id_for_response>
 
    api_key : "<api_key>"
 
    method :  "get_repo_nodes"
 
    args:     {
 
                "repoid" : "<reponame or repo_id>"
 
                "revision"  : "<revision>",
 
    args :    {
 
                "repoid" : "<reponame or repo_id>",
 
                "revision" :  "<revision>",
 
                "root_path" : "<root_path>",
 
                "ret_type"  : "<ret_type> = Optional('all')"
 
                "ret_type" :  "<ret_type> = Optional('all')"
 
              }
 

	
 
OUTPUT::
 

	
 
    id : <id_given_in_input>
 
    result: [
 
    result : [
 
              {
 
                "name" :        "<name>"
 
                "type" :        "<type>",
 
                "name" :        "<name>",
 
                "type" :        "<type>"
 
              },
 
 
            ]
 
    error:  null
 
             ]
 
    error : null
 

	
 
create_repo
 
^^^^^^^^^^^
 

	
 
Create a repository. If the repository name contains "/", the repository will be
 
created in the repository group indicated by that path. Any such repository
 
@@ -775,45 +935,45 @@ Regular users cannot specify owner param
 

	
 
INPUT::
 

	
 
    id : <id_for_response>
 
    api_key : "<api_key>"
 
    method :  "create_repo"
 
    args:     {
 
    args :    {
 
                "repo_name" :        "<reponame>",
 
                "owner" :            "<owner_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_statistics": "<bool> = Optional(False)",
 
                "enable_downloads" : "<bool> = Optional(False)",
 
                "enable_statistics": "<bool> = Optional(False)"
 
              }
 

	
 
OUTPUT::
 

	
 
    id : <id_given_in_input>
 
    result: {
 
              "msg": "Created new repository `<reponame>`",
 
              "repo": {
 
    result : {
 
              "msg" : "Created new repository `<reponame>`",
 
              "repo" : {
 
                "repo_id" :          "<repo_id>",
 
                "repo_name" :        "<reponame>"
 
                "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_statistics": "<bool>",
 
              },
 
            }
 
    error:  null
 
                "landing_rev" :      "<landing_rev>",
 
                "owner" :            "<username or user_id>",
 
                "fork_of" :          "<name_of_fork_parent>",
 
                "enable_downloads" : "<bool>",
 
                "enable_statistics": "<bool>"
 
              }
 
             }
 
    error : null
 

	
 
update_repo
 
^^^^^^^^^^^
 

	
 
Update a repository.
 
This command can only be executed using the api_key of a user with admin rights,
 
@@ -822,54 +982,54 @@ Regular users cannot specify owner param
 

	
 
INPUT::
 

	
 
    id : <id_for_response>
 
    api_key : "<api_key>"
 
    method :  "update_repo"
 
    args:     {
 
                "repoid" :           "<reponame or repo_id>"
 
    args :    {
 
                "repoid" :           "<reponame or repo_id>",
 
                "name" :             "<reponame> = Optional('')",
 
                "group" :            "<group_id> = Optional(None)",
 
                "owner" :            "<owner_name_or_id = Optional(=apiuser)>",
 
                "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_statistics": "<bool> = Optional(False)",
 
                "enable_downloads" : "<bool> = Optional(False)",
 
                "enable_statistics": "<bool> = Optional(False)"
 
              }
 

	
 
OUTPUT::
 

	
 
    id : <id_given_in_input>
 
    result: {
 
              "msg": "updated repo ID:repo_id `<reponame>`",
 
              "repository": {
 
    result : {
 
              "msg" : "updated repo ID:repo_id `<reponame>`",
 
              "repository" : {
 
                "repo_id" :          "<repo_id>",
 
                "repo_name" :        "<reponame>"
 
                "repo_name" :        "<reponame>",
 
                "repo_type" :        "<repo_type>",
 
                "clone_uri" :        "<clone_uri>",
 
                "private":           "<bool>",
 
                "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>",
 
                "landing_rev" :      "<landing_rev>",
 
                "owner" :            "<username or user_id>",
 
                "fork_of" :          "<name_of_fork_parent>",
 
                "enable_downloads" : "<bool>",
 
                "enable_statistics": "<bool>",
 
                "last_changeset":    {
 
                                       "author":   "<full_author>",
 
                                       "date":     "<date_time_of_commit>",
 
                                       "message":  "<commit_message>",
 
                                       "raw_id":   "<raw_id>",
 
                "last_changeset" :   {
 
                                       "author" :  "<full_author>",
 
                                       "date" :    "<date_time_of_commit>",
 
                                       "message" : "<commit_message>",
 
                                       "raw_id" :  "<raw_id>",
 
                                       "revision": "<numeric_revision>",
 
                                       "short_id": "<short_id>"
 
                                     }
 
              },
 
            }
 
    error:  null
 
              }
 
             }
 
    error : null
 

	
 
fork_repo
 
^^^^^^^^^
 

	
 
Create a fork of the given repo. If using Celery, this will
 
return success message immediately and a fork will be created
 
@@ -881,31 +1041,30 @@ Regular users cannot specify owner param
 

	
 
INPUT::
 

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

	
 
                "private" :         "<bool>",
 
                "landing_rev" :     "<landing_rev>"
 
              }
 

	
 
OUTPUT::
 

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

	
 
delete_repo
 
^^^^^^^^^^^
 

	
 
Delete a repository.
 
This command can only be executed using the api_key of a user with admin rights,
 
@@ -914,76 +1073,76 @@ When ``forks`` param is set it is possib
 

	
 
INPUT::
 

	
 
    id : <id_for_response>
 
    api_key : "<api_key>"
 
    method :  "delete_repo"
 
    args:     {
 
    args :    {
 
                "repoid" : "<reponame or repo_id>",
 
                "forks"  : "`delete` or `detach` = Optional(None)"
 
                "forks" :  "`delete` or `detach` = Optional(None)"
 
              }
 

	
 
OUTPUT::
 

	
 
    id : <id_given_in_input>
 
    result: {
 
              "msg": "Deleted repository `<reponame>`",
 
              "success": true
 
            }
 
    error:  null
 
    result : {
 
              "msg" : "Deleted repository `<reponame>`",
 
              "success" : true
 
             }
 
    error : null
 

	
 
grant_user_permission
 
^^^^^^^^^^^^^^^^^^^^^
 

	
 
Grant permission for a user on the given repository, or update the existing one if found.
 
This command can only be executed using the api_key of a user with admin rights.
 

	
 
INPUT::
 

	
 
    id : <id_for_response>
 
    api_key : "<api_key>"
 
    method :  "grant_user_permission"
 
    args:     {
 
                "repoid" : "<reponame or repo_id>"
 
                "userid" : "<username or user_id>"
 
                "perm" :       "(repository.(none|read|write|admin))",
 
    args :    {
 
                "repoid" : "<reponame or repo_id>",
 
                "userid" : "<username or user_id>",
 
                "perm" :       "(repository.(none|read|write|admin))"
 
              }
 

	
 
OUTPUT::
 

	
 
    id : <id_given_in_input>
 
    result: {
 
    result : {
 
              "msg" : "Granted perm: `<perm>` for user: `<username>` in repo: `<reponame>`",
 
              "success": true
 
            }
 
    error:  null
 
              "success" : true
 
             }
 
    error : null
 

	
 
revoke_user_permission
 
^^^^^^^^^^^^^^^^^^^^^^
 

	
 
Revoke permission for a user on the given repository.
 
This command can only be executed using the api_key of a user with admin rights.
 

	
 
INPUT::
 

	
 
    id : <id_for_response>
 
    api_key : "<api_key>"
 
    method  : "revoke_user_permission"
 
    args:     {
 
                "repoid" : "<reponame or repo_id>"
 
    method :  "revoke_user_permission"
 
    args :    {
 
                "repoid" : "<reponame or repo_id>",
 
                "userid" : "<username or user_id>"
 
              }
 

	
 
OUTPUT::
 

	
 
    id : <id_given_in_input>
 
    result: {
 
    result : {
 
              "msg" : "Revoked perm for user: `<username>` in repo: `<reponame>`",
 
              "success": true
 
            }
 
    error:  null
 
              "success" : true
 
             }
 
    error : null
 

	
 
grant_user_group_permission
 
^^^^^^^^^^^^^^^^^^^^^^^^^^^
 

	
 
Grant permission for a user group on the given repository, or update the
 
existing one if found.
 
@@ -991,131 +1150,131 @@ This command can only be executed using 
 

	
 
INPUT::
 

	
 
    id : <id_for_response>
 
    api_key : "<api_key>"
 
    method :  "grant_user_group_permission"
 
    args:     {
 
                "repoid" : "<reponame or repo_id>"
 
                "usersgroupid" : "<user group id or name>"
 
                "perm" : "(repository.(none|read|write|admin))",
 
    args :    {
 
                "repoid" : "<reponame or repo_id>",
 
                "usersgroupid" : "<user group id or name>",
 
                "perm" : "(repository.(none|read|write|admin))"
 
              }
 

	
 
OUTPUT::
 

	
 
    id : <id_given_in_input>
 
    result: {
 
    result : {
 
              "msg" : "Granted perm: `<perm>` for group: `<usersgroupname>` in repo: `<reponame>`",
 
              "success": true
 
            }
 
    error:  null
 
              "success" : true
 
             }
 
    error : null
 

	
 
revoke_user_group_permission
 
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 

	
 
Revoke permission for a user group on the given repository.
 
This command can only be executed using the api_key of a user with admin rights.
 

	
 
INPUT::
 

	
 
    id : <id_for_response>
 
    api_key : "<api_key>"
 
    method  : "revoke_user_group_permission"
 
    args:     {
 
                "repoid" : "<reponame or repo_id>"
 
    method :  "revoke_user_group_permission"
 
    args :    {
 
                "repoid" : "<reponame or repo_id>",
 
                "usersgroupid" : "<user group id or name>"
 
              }
 

	
 
OUTPUT::
 

	
 
    id : <id_given_in_input>
 
    result: {
 
    result : {
 
              "msg" : "Revoked perm for group: `<usersgroupname>` in repo: `<reponame>`",
 
              "success": true
 
            }
 
    error:  null
 
              "success" : true
 
             }
 
    error : null
 

	
 
get_changesets
 
^^^^^^^^^^^^^^
 

	
 
Get changesets of a given repository. This command can only be executed using the api_key
 
of a user with read permissions to the repository.
 

	
 
INPUT::
 

	
 
    id : <id_for_response>
 
    api_key : "<api_key>"
 
    method  : "get_changesets"
 
    args:     {
 
    method :  "get_changesets"
 
    args :    {
 
                "repoid" : "<reponame or repo_id>",
 
                "start": "<revision number> = Optional(None)",
 
                "end": "<revision number> = Optional(None)",
 
                "start_date": "<date> = Optional(None)",    # in "%Y-%m-%dT%H:%M:%S" format
 
                "end_date": "<date> = Optional(None)",      # in "%Y-%m-%dT%H:%M:%S" format
 
                "branch_name": "<branch name filter> = Optional(None)",
 
                "reverse": "<bool> = Optional(False)",
 
                "with_file_list": "<bool> = Optional(False)"
 
                "start" : "<revision number> = Optional(None)",
 
                "end" : "<revision number> = Optional(None)",
 
                "start_date" : "<date> = Optional(None)",    # in "%Y-%m-%dT%H:%M:%S" format
 
                "end_date" : "<date> = Optional(None)",      # in "%Y-%m-%dT%H:%M:%S" format
 
                "branch_name" : "<branch name filter> = Optional(None)",
 
                "reverse" : "<bool> = Optional(False)",
 
                "with_file_list" : "<bool> = Optional(False)"
 
              }
 

	
 
OUTPUT::
 

	
 
    id : <id_given_in_input>
 
    result: [
 
    result : [
 
    {
 
      "raw_id": "<raw_id>",
 
      "short_id": "short_id": "<short_id>",
 
      "author": "<full_author>",
 
      "date": "<date_time_of_commit>",
 
      "message": "<commit_message>",
 
      "revision": "<numeric_revision>",
 
      "raw_id" : "<raw_id>",
 
      "short_id" : "<short_id>",
 
      "author" : "<full_author>",
 
      "date" : "<date_time_of_commit>",
 
      "message" : "<commit_message>",
 
      "revision" : "<numeric_revision>",
 
      <if with_file_list == True>
 
      "added": [<list of added files>],
 
      "changed": [<list of changed files>],
 
      "removed": [<list of removed files>]
 
      "added" : [<list of added files>],
 
      "changed" : [<list of changed files>],
 
      "removed" : [<list of removed files>]
 
    },
 
    ...
 
    ]
 
    error:  null
 
    error : null
 

	
 
get_changeset
 
^^^^^^^^^^^^^
 

	
 
Get information and review status for a given changeset. This command can only
 
be executed using the api_key of a user with read permissions to the
 
repository.
 

	
 
INPUT::
 

	
 
    id : <id_for_response>
 
    api_key : "<api_key>"
 
    method  : "get_changeset"
 
    args:     {
 
    method :  "get_changeset"
 
    args :    {
 
                "repoid" : "<reponame or repo_id>",
 
                "raw_id" : "<raw_id>",
 
                "with_reviews": "<bool> = Optional(False)"
 
                "with_reviews" : "<bool> = Optional(False)"
 
              }
 

	
 
OUTPUT::
 

	
 
    id : <id_given_in_input>
 
    result: {
 
              "author":   "<full_author>",
 
              "date":     "<date_time_of_commit>",
 
              "message":  "<commit_message>",
 
              "raw_id":   "<raw_id>",
 
    result : {
 
              "author" :  "<full_author>",
 
              "date" :    "<date_time_of_commit>",
 
              "message" : "<commit_message>",
 
              "raw_id" :  "<raw_id>",
 
              "revision": "<numeric_revision>",
 
              "short_id": "<short_id>",
 
              "reviews": [{
 
                    "reviewer":   "<username>",
 
                    "modified_at": "<date_time_of_review>",  # iso 8601 date, server's timezone
 
                    "status":   "<status_of_review>",        # "under_review", "approved" or "rejected"
 
              "reviews" : [{
 
                    "reviewer" :  "<username>",
 
                    "modified_at" : "<date_time_of_review>",  # iso 8601 date, server's timezone
 
                    "status" :  "<status_of_review>",        # "under_review", "approved" or "rejected"
 
                 },
 
                 ...
 
              ]
 
            }
 
    error:  null
 
             }
 
    error : null
 

	
 
Example output::
 

	
 
    {
 
      "id" : 1,
 
      "error" : null,
 
@@ -1152,90 +1311,90 @@ Get information and review status for a 
 
using the api_key of a user with read permissions to the original repository.
 

	
 
INPUT::
 

	
 
    id : <id_for_response>
 
    api_key : "<api_key>"
 
    method  : "get_pullrequest"
 
    args:     {
 
                "pullrequest_id" : "<pullrequest_id>",
 
    method :  "get_pullrequest"
 
    args :    {
 
                "pullrequest_id" : "<pullrequest_id>"
 
              }
 

	
 
OUTPUT::
 

	
 
    id : <id_given_in_input>
 
    result: {
 
        "status": "<pull_request_status>",
 
        "pull_request_id": <pull_request_id>,
 
        "description": "<pull_request_description>",
 
        "title": "<pull_request_title>",
 
        "url": "<pull_request_url>",
 
        "reviewers": [
 
    result : {
 
        "status" : "<pull_request_status>",
 
        "pull_request_id" : <pull_request_id>,
 
        "description" : "<pull_request_description>",
 
        "title" : "<pull_request_title>",
 
        "url" : "<pull_request_url>",
 
        "reviewers" : [
 
          {
 
            "username": "<user_name>",
 
            "username" : "<user_name>"
 
          },
 
          ...
 
        ],
 
        "org_repo_url": "<repo_url>",
 
        "org_ref_parts": [
 
        "org_repo_url" : "<repo_url>",
 
        "org_ref_parts" : [
 
          "<ref_type>",
 
          "<ref_name>",
 
          "<raw_id>"
 
        ],
 
        "other_ref_parts": [
 
        "other_ref_parts" : [
 
          "<ref_type>",
 
          "<ref_name>",
 
          "<raw_id>"
 
        ],
 
        "comments": [
 
        "comments" : [
 
          {
 
            "username": "<user_name>",
 
            "text": "<comment text>",
 
            "comment_id": "<comment_id>",
 
            "username" : "<user_name>",
 
            "text" : "<comment text>",
 
            "comment_id" : "<comment_id>"
 
          },
 
          ...
 
        ],
 
        "owner": "<username>",
 
        "statuses": [
 
        "owner" : "<username>",
 
        "statuses" : [
 
          {
 
            "status": "<status_of_review>",        # "under_review", "approved" or "rejected"
 
            "reviewer": "<user_name>",
 
            "modified_at": "<date_time_of_review>" # iso 8601 date, server's timezone
 
            "status" : "<status_of_review>",        # "under_review", "approved" or "rejected"
 
            "reviewer" : "<user_name>",
 
            "modified_at" : "<date_time_of_review>" # iso 8601 date, server's timezone
 
          },
 
          ...
 
        ],
 
        "revisions": [
 
        "revisions" : [
 
          "<raw_id>",
 
          ...
 
        ]
 
    },
 
    error:  null
 
    error : null
 

	
 
comment_pullrequest
 
^^^^^^^^^^^^^^^^^^^
 

	
 
Add comment, change status or close a given pull request. This command can only be executed
 
using the api_key of a user with read permissions to the original repository.
 

	
 
INPUT::
 

	
 
    id : <id_for_response>
 
    api_key : "<api_key>"
 
    method  : "comment_pullrequest"
 
    args:     {
 
                "pull_request_id":  "<pull_request_id>",
 
                "comment_msg":      Optional(''),
 
                "status":           Optional(None),     # "under_review", "approved" or "rejected"
 
                "close_pr":         Optional(False)",
 
    method :  "comment_pullrequest"
 
    args :    {
 
                "pull_request_id" : "<pull_request_id>",
 
                "comment_msg" :     Optional(''),
 
                "status" :          Optional(None),     # "under_review", "approved" or "rejected"
 
                "close_pr" :        Optional(False)"
 
              }
 

	
 
OUTPUT::
 

	
 
    id : <id_given_in_input>
 
    result: True
 
    error:  null
 
    result : True
 
    error : null
 

	
 

	
 
API access for web views
 
------------------------
 

	
 
Kallithea HTTP entry points can also be accessed without login using bearer
docs/conf.py
Show inline comments
 
@@ -44,13 +44,13 @@ source_suffix = '.rst'
 

	
 
# The master toctree document.
 
master_doc = 'index'
 

	
 
# General information about the project.
 
project = 'Kallithea'
 
copyright = '2010-2022 by various authors, licensed as GPLv3.'
 
copyright = '2010-2023 by various authors, licensed as GPLv3.'
 

	
 
# The version info for the project you're documenting, acts as replacement for
 
# |version| and |release|, also used in various other places throughout the
 
# built documents.
 
#
 
# The short X.Y version.
kallithea/controllers/admin/repo_groups.py
Show inline comments
 
@@ -73,12 +73,13 @@ class RepoGroupsController(base.BaseCont
 

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

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

	
 
@@ -143,13 +144,13 @@ class RepoGroupsController(base.BaseCont
 
        try:
 
            form_result = repo_group_form.to_python(dict(request.POST))
 
            gr = RepoGroupModel().create(
 
                group_name=form_result['group_name'],
 
                group_description=form_result['group_description'],
 
                parent=form_result['parent_group_id'],
 
                owner=request.authuser.user_id, # TODO: make editable
 
                owner=request.authuser.user_id,
 
                copy_permissions=form_result['group_copy_permissions']
 
            )
 
            meta.Session().commit()
 
            # TODO: in future action_logger(, '', '', '')
 
        except formencode.Invalid as errors:
 
            return htmlfill.render(
kallithea/controllers/api/api.py
Show inline comments
 
@@ -62,63 +62,53 @@ def store_update(updates, attr, name):
 
        updates[name] = attr
 

	
 

	
 
def get_user_or_error(userid):
 
    """
 
    Get user by id or name or return JsonRPCError if not found
 

	
 
    :param userid:
 
    """
 
    user = UserModel().get_user(userid)
 
    if user is None:
 
        raise JSONRPCError("user `%s` does not exist" % (userid,))
 
    return user
 

	
 

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

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

	
 

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

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

	
 

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

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

	
 

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

	
 
    :param permid:
 
    """
 
    perm = db.Permission.get_by_key(permid)
 
    if perm is None:
 
        raise JSONRPCError('permission `%s` does not exist' % (permid,))
 
    if prefix:
 
        if not perm.permission_name.startswith(prefix):
 
@@ -127,14 +117,12 @@ def get_perm_or_error(permid, prefix=Non
 
    return perm
 

	
 

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

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

	
 
@@ -162,36 +150,21 @@ class ApiController(JSONRPCController):
 
    def pull(self, repoid, clone_uri=None):
 
        """
 
        Triggers a pull from remote location on given repo. Can be used to
 
        automatically keep remote repos up to date. This command can be executed
 
        only using api_key belonging to user with admin rights
 

	
 
        :param repoid: repository name or repository id
 
        :type repoid: str or int
 
        :param clone_uri: repository URI to pull from (optional)
 
        :type clone_uri: str
 

	
 
        OUTPUT::
 

	
 
          id : <id_given_in_input>
 
          result : {
 
            "msg": "Pulled from `<repository name>`"
 
            "repository": "<repository name>"
 
          }
 
          error :  null
 

	
 
        ERROR OUTPUT::
 

	
 
          id : <id_given_in_input>
 
          result : null
 
          error :  {
 
            "Unable to pull changes from `<reponame>`"
 
          }
 

	
 
            id : <id_given_in_input>
 
            result : {
 
                "msg" : "Pulled from `<repository name>`",
 
                "repository" : "<repository name>"
 
            }
 
            error : null
 
        """
 

	
 
        repo = get_repo_or_error(repoid)
 

	
 
        try:
 
            ScmModel().pull_changes(repo.repo_name,
 
                                    request.authuser.username,
 
                                    request.ip_addr,
 
@@ -211,35 +184,21 @@ class ApiController(JSONRPCController):
 
        """
 
        Triggers rescan repositories action. If remove_obsolete is set
 
        than also delete repos that are in database but not in the filesystem.
 
        aka "clean zombies". This command can be executed only using api_key
 
        belonging to user with admin rights.
 

	
 
        :param remove_obsolete: deletes repositories from
 
            database that are not found on the filesystem
 
        :type remove_obsolete: Optional(bool)
 

	
 
        OUTPUT::
 

	
 
          id : <id_given_in_input>
 
          result : {
 
            'added': [<added repository name>,...]
 
            'removed': [<removed repository name>,...]
 
          }
 
          error :  null
 

	
 
        ERROR OUTPUT::
 

	
 
          id : <id_given_in_input>
 
          result : null
 
          error :  {
 
            'Error occurred during rescan repositories action'
 
          }
 

	
 
            id : <id_given_in_input>
 
            result : {
 
                'added': [<added repository name>,...]
 
                'removed': [<removed repository name>,...]
 
            }
 
            error : null
 
        """
 

	
 
        try:
 
            rm_obsolete = remove_obsolete
 
            added, removed = repo2db_mapper(ScmModel().repo_scan(),
 
                                            remove_obsolete=rm_obsolete)
 
            return {'added': added, 'removed': removed}
 
        except Exception:
 
@@ -251,32 +210,20 @@ class ApiController(JSONRPCController):
 
    def invalidate_cache(self, repoid):
 
        """
 
        Invalidate cache for repository.
 
        This command can be executed only using api_key belonging to user with admin
 
        rights or regular user that have write or admin or write access to repository.
 

	
 
        :param repoid: repository name or repository id
 
        :type repoid: str or int
 

	
 
        OUTPUT::
 

	
 
          id : <id_given_in_input>
 
          result : {
 
            'msg': Cache for repository `<repository name>` was invalidated,
 
            'repository': <repository name>
 
          }
 
          error :  null
 

	
 
        ERROR OUTPUT::
 

	
 
          id : <id_given_in_input>
 
          result : null
 
          error :  {
 
            'Error occurred during cache invalidation action'
 
          }
 

	
 
            id : <id_given_in_input>
 
            result : {
 
                'msg': Cache for repository `<repository name>` was invalidated,
 
                'repository': <repository name>
 
            }
 
            error : null
 
        """
 
        repo = get_repo_or_error(repoid)
 
        if not HasPermissionAny('hg.admin')():
 
            if not HasRepoPermissionLevel('write')(repo.repo_name):
 
                raise JSONRPCError('repository `%s` does not exist' % (repoid,))
 

	
 
@@ -298,29 +245,26 @@ class ApiController(JSONRPCController):
 
        Shows IP address as seen from Kallithea server, together with all
 
        defined IP addresses for given user. If userid is not passed data is
 
        returned for user who's calling this function.
 
        This command can be executed only using api_key belonging to user with
 
        admin rights.
 

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

	
 
        OUTPUT::
 

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

	
 
            error : null
 
        """
 
        if userid is None:
 
            userid = request.authuser.user_id
 
        user = get_user_or_error(userid)
 
        ips = db.UserIpMap.query().filter(db.UserIpMap.user == user).all()
 
        return dict(
 
@@ -333,67 +277,58 @@ class ApiController(JSONRPCController):
 

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

	
 

	
 
        OUTPUT::
 

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

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

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

	
 
        OUTPUT::
 

	
 
            id : <id_given_in_input>
 
            result: None if user does not exist or
 
                    {
 
            result : None if user does not exist or
 
                     {
 
                        "user_id" :     "<user_id>",
 
                        "api_key" :     "<api_key>",
 
                        "api_keys":     "[<list of all API keys including additional ones>]"
 
                        "username" :    "<username>",
 
                        "firstname":    "<firstname>",
 
                        "firstname" :   "<firstname>",
 
                        "lastname" :    "<lastname>",
 
                        "email" :       "<email>",
 
                        "emails":       "[<list of all emails including additional ones>]",
 
                        "ip_addresses": "[<ip_address_for_user>,...]",
 
                        "emails" :      "[<list of all emails including additional ones>]",
 
                        "active" :      "<bool: user active>",
 
                        "admin" :       "<bool: user is admin>",
 
                        "extern_name" : "<extern_name>",
 
                        "extern_type" : "<extern type>
 
                        "last_login":   "<last_login>",
 
                        "permissions": {
 
                            "global": ["hg.create.repository",
 
                                       "repository.read",
 
                                       "hg.register.manual_activate"],
 
                            "repositories": {"repo1": "repository.none"},
 
                            "repositories_groups": {"Group1": "group.read"}
 
                         },
 
                    }
 

	
 
            error:  null
 

	
 
                        "admin" :       "<bool: user is admin>",
 
                        "permissions" : {
 
                            "global" : ["hg.create.repository",
 
                                        "repository.read",
 
                                        "hg.register.manual_activate"],
 
                            "repositories" : {"repo1" : "repository.none"},
 
                            "repositories_groups" : {"Group1" : "group.read"},
 
                            "user_groups" : { "usrgrp1" : "usergroup.admin" }
 
                         }
 
                     }
 
            error : null
 
        """
 
        if not HasPermissionAny('hg.admin')():
 
            # make sure normal user does not pass someone else userid,
 
            # he is not allowed to do that
 
            if userid is not None and userid != request.authuser.user_id:
 
                raise JSONRPCError(
 
@@ -411,20 +346,18 @@ class ApiController(JSONRPCController):
 
    @HasPermissionAnyDecorator('hg.admin')
 
    def get_users(self):
 
        """
 
        Lists all existing users. This command can be executed only using api_key
 
        belonging to user with admin rights.
 

	
 

	
 
        OUTPUT::
 

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

	
 
        return [
 
            user.get_api_data()
 
            for user in db.User.query()
 
                .order_by(db.User.username)
 
                .filter_by(is_default_user=False)
 
        ]
 
@@ -436,55 +369,21 @@ class ApiController(JSONRPCController):
 
                    extern_type=db.User.DEFAULT_AUTH_TYPE,
 
                    extern_name=''):
 
        """
 
        Creates new user. Returns new user object. This command can
 
        be executed only using api_key belonging to user with admin rights.
 

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

	
 

	
 
        OUTPUT::
 

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

	
 
        ERROR OUTPUT::
 

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

	
 
                      "user" : <user_obj>
 
                     }
 
            error : null
 
        """
 

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

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

	
 
@@ -516,53 +415,21 @@ class ApiController(JSONRPCController):
 
                    active=None, admin=None,
 
                    extern_type=None, extern_name=None):
 
        """
 
        updates given user if such user exists. This command can
 
        be executed only using api_key belonging to user with admin rights.
 

	
 
        :param userid: userid to update
 
        :type userid: str or int
 
        :param username: new username
 
        :type username: str or int
 
        :param email: email
 
        :type email: str
 
        :param password: password
 
        :type password: Optional(str)
 
        :param firstname: firstname
 
        :type firstname: Optional(str)
 
        :param lastname: lastname
 
        :type lastname: Optional(str)
 
        :param active: active
 
        :type active: Optional(bool)
 
        :param admin: admin
 
        :type admin: Optional(bool)
 
        :param extern_name:
 
        :type extern_name: Optional(str)
 
        :param extern_type:
 
        :type extern_type: Optional(str)
 

	
 

	
 
        OUTPUT::
 

	
 
            id : <id_given_in_input>
 
            result: {
 
            result : {
 
                      "msg" : "updated user ID:<userid> <username>",
 
                      "user": <user_object>,
 
                    }
 
            error:  null
 

	
 
        ERROR OUTPUT::
 

	
 
          id : <id_given_in_input>
 
          result : null
 
          error :  {
 
            "failed to update user `<username>`"
 
          }
 

	
 
                      "user" : <user_object>
 
                     }
 
            error : null
 
        """
 

	
 
        user = get_user_or_error(userid)
 

	
 
        # only non optional arguments will be stored in updates
 
        updates = {}
 

	
 
        try:
 
@@ -593,32 +460,20 @@ class ApiController(JSONRPCController):
 
    @HasPermissionAnyDecorator('hg.admin')
 
    def delete_user(self, userid):
 
        """
 
        deletes given user if such user exists. This command can
 
        be executed only using api_key belonging to user with admin rights.
 

	
 
        :param userid: user to delete
 
        :type userid: str or int
 

	
 
        OUTPUT::
 

	
 
            id : <id_given_in_input>
 
            result: {
 
            result : {
 
                      "msg" : "deleted user ID:<userid> <username>",
 
                      "user": null
 
                    }
 
            error:  null
 

	
 
        ERROR OUTPUT::
 

	
 
          id : <id_given_in_input>
 
          result : null
 
          error :  {
 
            "failed to delete user ID:<userid> <username>"
 
          }
 

	
 
                      "user" : null
 
                     }
 
            error : null
 
        """
 
        user = get_user_or_error(userid)
 

	
 
        try:
 
            UserModel().delete(userid)
 
            meta.Session().commit()
 
@@ -636,27 +491,25 @@ class ApiController(JSONRPCController):
 
    def get_user_group(self, usergroupid):
 
        """
 
        Gets an existing user group. This command can be executed only using api_key
 
        belonging to user with admin rights or user who has at least
 
        read access to user group.
 

	
 
        :param usergroupid: id of user_group to edit
 
        :type usergroupid: str or int
 

	
 
        OUTPUT::
 

	
 
            id : <id_given_in_input>
 
            result : None if group not exist
 
                     {
 
                       "users_group_id" : "<id>",
 
                       "group_name" :     "<groupname>",
 
                       "active":          "<bool>",
 
                       "members" :  [<user_obj>,...]
 
                       "group_description" : "<description>",
 
                       "active" :         "<bool>",
 
                       "owner" :          "<username>",
 
                       "members" :        [<user_obj>,...]
 
                     }
 
            error : null
 

	
 
        """
 
        user_group = get_user_group_or_error(usergroupid)
 
        if not HasPermissionAny('hg.admin')():
 
            if not HasUserGroupPermissionLevel('read')(user_group.users_group_name):
 
                raise JSONRPCError('user group `%s` does not exist' % (usergroupid,))
 

	
 
@@ -667,20 +520,18 @@ class ApiController(JSONRPCController):
 
    def get_user_groups(self):
 
        """
 
        Lists all existing user groups. This command can be executed only using
 
        api_key belonging to user with admin rights or user who has at least
 
        read access to user group.
 

	
 

	
 
        OUTPUT::
 

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

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

	
 
    @HasPermissionAnyDecorator('hg.admin', 'hg.usergroup.create.true')
 
@@ -688,42 +539,21 @@ class ApiController(JSONRPCController):
 
                          owner=None, active=True):
 
        """
 
        Creates new user group. This command can be executed only using api_key
 
        belonging to user with admin rights or an user who has create user group
 
        permission
 

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

	
 
        OUTPUT::
 

	
 
            id : <id_given_in_input>
 
            result: {
 
                      "msg": "created new user group `<groupname>`",
 
                      "user_group": <user_group_object>
 
                    }
 
            error:  null
 

	
 
        ERROR OUTPUT::
 

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

	
 
            result : {
 
                      "msg" : "created new user group `<groupname>`",
 
                      "user_group" : <user_group_object>
 
                     }
 
            error : null
 
        """
 

	
 
        if UserGroupModel().get_by_name(group_name):
 
            raise JSONRPCError("user group `%s` already exist" % (group_name,))
 

	
 
        try:
 
            if owner is None:
 
                owner = request.authuser.user_id
 
@@ -745,40 +575,20 @@ class ApiController(JSONRPCController):
 
                          description=None, owner=None,
 
                          active=None):
 
        """
 
        Updates given usergroup.  This command can be executed only using api_key
 
        belonging to user with admin rights or an admin of given user group
 

	
 
        :param usergroupid: id of user group to update
 
        :type usergroupid: str or int
 
        :param group_name: name of new user group
 
        :type group_name: str
 
        :param description: group description
 
        :type description: str
 
        :param owner: owner of group.
 
        :type owner: Optional(str or int)
 
        :param active: group is active
 
        :type active: Optional(bool)
 

	
 
        OUTPUT::
 

	
 
          id : <id_given_in_input>
 
          result : {
 
            "msg": 'updated user group ID:<user group id> <user group name>',
 
            "user_group": <user_group_object>
 
            "msg" : 'updated user group ID:<user group id> <user group name>',
 
            "user_group" : <user_group_object>
 
          }
 
          error :  null
 

	
 
        ERROR OUTPUT::
 

	
 
          id : <id_given_in_input>
 
          result : null
 
          error :  {
 
            "failed to update user group `<user group name>`"
 
          }
 

	
 
          error : null
 
        """
 
        user_group = get_user_group_or_error(usergroupid)
 
        if not HasPermissionAny('hg.admin')():
 
            if not HasUserGroupPermissionLevel('admin')(user_group.users_group_name):
 
                raise JSONRPCError('user group `%s` does not exist' % (usergroupid,))
 

	
 
@@ -806,33 +616,19 @@ class ApiController(JSONRPCController):
 
    def delete_user_group(self, usergroupid):
 
        """
 
        Delete given user group by user group id or name.
 
        This command can be executed only using api_key
 
        belonging to user with admin rights or an admin of given user group
 

	
 
        :param usergroupid:
 
        :type usergroupid: int
 

	
 
        OUTPUT::
 

	
 
          id : <id_given_in_input>
 
          result : {
 
            "msg": "deleted user group ID:<user_group_id> <user_group_name>"
 
            "msg" : "deleted user group ID:<user_group_id> <user_group_name>"
 
          }
 
          error :  null
 

	
 
        ERROR OUTPUT::
 

	
 
          id : <id_given_in_input>
 
          result : null
 
          error :  {
 
            "failed to delete user group ID:<user_group_id> <user_group_name>"
 
            or
 
            "RepoGroup assigned to <repo_groups_list>"
 
          }
 

	
 
          error : null
 
        """
 
        user_group = get_user_group_or_error(usergroupid)
 
        if not HasPermissionAny('hg.admin')():
 
            if not HasUserGroupPermissionLevel('admin')(user_group.users_group_name):
 
                raise JSONRPCError('user group `%s` does not exist' % (usergroupid,))
 

	
 
@@ -856,38 +652,23 @@ class ApiController(JSONRPCController):
 

	
 
    # permission check inside
 
    def add_user_to_user_group(self, usergroupid, userid):
 
        """
 
        Adds a user to a user group. If user exists in that group success will be
 
        `false`. This command can be executed only using api_key
 
        belonging to user with admin rights  or an admin of given user group
 

	
 
        :param usergroupid:
 
        :type usergroupid: int
 
        :param userid:
 
        :type userid: int
 
        belonging to user with admin rights or an admin of a given user group
 

	
 
        OUTPUT::
 

	
 
          id : <id_given_in_input>
 
          result : {
 
              "success": True|False # depends on if member is in group
 
              "msg": "added member `<username>` to user group `<groupname>` |
 
                      User is already in that group"
 

	
 
          }
 
          error :  null
 

	
 
        ERROR OUTPUT::
 

	
 
          id : <id_given_in_input>
 
          result : null
 
          error :  {
 
            "failed to add member to user group `<user_group_name>`"
 
          }
 

	
 
            id : <id_given_in_input>
 
            result : {
 
                "success" : True|False # depends on if member is in group
 
                "msg" : "added member `<username>` to a user group `<groupname>` |
 
                         User is already in that group"
 
            }
 
            error : null
 
        """
 
        user = get_user_or_error(userid)
 
        user_group = get_user_group_or_error(usergroupid)
 
        if not HasPermissionAny('hg.admin')():
 
            if not HasUserGroupPermissionLevel('admin')(user_group.users_group_name):
 
                raise JSONRPCError('user group `%s` does not exist' % (usergroupid,))
 
@@ -917,26 +698,21 @@ class ApiController(JSONRPCController):
 
    def remove_user_from_user_group(self, usergroupid, userid):
 
        """
 
        Removes a user from a user group. If user is not in given group success will
 
        be `false`. This command can be executed only
 
        using api_key belonging to user with admin rights or an admin of given user group
 

	
 
        :param usergroupid:
 
        :param userid:
 

	
 

	
 
        OUTPUT::
 

	
 
            id : <id_given_in_input>
 
            result: {
 
                      "success":  True|False,  # depends on if member is in group
 
                      "msg": "removed member <username> from user group <groupname> |
 
                              User wasn't in group"
 
                    }
 
            error:  null
 

	
 
            result : {
 
                      "success" : True|False,  # depends on if member is in group
 
                      "msg" : "removed member <username> from user group <groupname> |
 
                               User wasn't in group"
 
                     }
 
            error : null
 
        """
 
        user = get_user_or_error(userid)
 
        user_group = get_user_group_or_error(usergroupid)
 
        if not HasPermissionAny('hg.admin')():
 
            if not HasUserGroupPermissionLevel('admin')(user_group.users_group_name):
 
                raise JSONRPCError('user group `%s` does not exist' % (usergroupid,))
 
@@ -964,72 +740,66 @@ class ApiController(JSONRPCController):
 
        """
 
        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 or regular user that have at least read access to repository.
 

	
 
        :param repoid: repository name or repository id
 
        :type repoid: str or int
 

	
 
        OUTPUT::
 

	
 
          id : <id_given_in_input>
 
          result : {
 
            {
 
                "repo_id" :          "<repo_id>",
 
                "repo_name" :        "<reponame>"
 
                "repo_type" :        "<repo_type>",
 
                "clone_uri" :        "<clone_uri>",
 
                "enable_downloads":  "<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" :     [
 
                                  {
 
                                    "name":     "<username>",
 
                                    "type" :    "user",
 
                                    "permission" : "repository.(read|write|admin)"
 
                                  },
 
 
                                  {
 
                                    "name":     "<usergroup name>",
 
                                    "type" :    "user_group",
 
                                    "permission" : "usergroup.(read|write|admin)"
 
                                  },
 
 
                                ]
 
                 "followers":   [<user_obj>, ...],
 
                 <if with_revision_names == True>
 
                 "tags": {
 
                            "<tagname>": "<raw_id>",
 
                            ...
 
                         },
 
                 "branches": {
 
                            "<branchname>": "<raw_id>",
 
                            ...
 
                         },
 
                 "bookmarks": {
 
                            "<bookmarkname>": "<raw_id>",
 
                            ...
 
                         },
 
            }
 
          }
 
          error :  null
 

	
 
            id : <id_given_in_input>
 
            result : {
 
                        "repo_id" :          "<repo_id>",
 
                        "repo_name" :        "<reponame>",
 
                        "repo_type" :        "<repo_type>",
 
                        "clone_uri" :        "<clone_uri>",
 
                        "enable_downloads" : "<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" :     [
 
                                            {
 
                                                "name" :    "<username>",
 
                                                "type" :    "user",
 
                                                "permission" : "repository.(read|write|admin)"
 
                                            },
 
 
                                            {
 
                                                "name" :    "<usergroup name>",
 
                                                "type" :    "user_group",
 
                                                "permission" : "usergroup.(read|write|admin)"
 
                                            },
 
 
                                        ],
 
                        "followers" :  [<user_obj>, ...],
 
                        <if with_revision_names == True>
 
                        "tags" : {
 
                                    "<tagname>" : "<raw_id>",
 
                                    ...
 
                                },
 
                        "branches" : {
 
                                    "<branchname>" : "<raw_id>",
 
                                    ...
 
                                },
 
                        "bookmarks" : {
 
                                    "<bookmarkname>" : "<raw_id>",
 
                                    ...
 
                                }
 
                     }
 
            error : null
 
        """
 
        repo = get_repo_or_error(repoid)
 

	
 
        if not HasPermissionAny('hg.admin')():
 
            if not HasRepoPermissionLevel('read')(repo.repo_name):
 
                raise JSONRPCError('repository `%s` does not exist' % (repoid,))
 
@@ -1070,34 +840,33 @@ class ApiController(JSONRPCController):
 
    def get_repos(self):
 
        """
 
        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.
 

	
 

	
 
        OUTPUT::
 

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

	
 
@@ -1112,33 +881,23 @@ class ApiController(JSONRPCController):
 
        """
 
        returns a list of nodes and it's children in a flat list for a given path
 
        at given revision. It's possible to specify ret_type to show only `files` or
 
        `dirs`.  This command can be executed only using api_key belonging to
 
        user with admin rights or regular user that have at least read access to repository.
 

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

	
 

	
 
        OUTPUT::
 

	
 
            id : <id_given_in_input>
 
            result: [
 
            result : [
 
                      {
 
                        "name" :        "<name>"
 
                        "type" :        "<type>",
 
                        "name" :        "<name>",
 
                        "type" :        "<type>"
 
                      },
 
 
                    ]
 
            error:  null
 
                     ]
 
            error : null
 
        """
 
        repo = get_repo_or_error(repoid)
 

	
 
        if not HasPermissionAny('hg.admin')():
 
            if not HasRepoPermissionLevel('read')(repo.repo_name):
 
                raise JSONRPCError('repository `%s` does not exist' % (repoid,))
 
@@ -1175,52 +934,20 @@ class ApiController(JSONRPCController):
 
        parent repository group must exist. For example "foo/bar/baz" require the groups
 
        "foo" and "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
 

	
 
        :param repo_name: repository name
 
        :type repo_name: str
 
        :param owner: user_id or username
 
        :type owner: Optional(str)
 
        :param repo_type: 'hg' or 'git'
 
        :type repo_type: Optional(str)
 
        :param description: repository description
 
        :type description: Optional(str)
 
        :param private:
 
        :type private: bool
 
        :param clone_uri:
 
        :type clone_uri: str
 
        :param landing_rev: <rev_type>:<rev>
 
        :type landing_rev: str
 
        :param enable_downloads:
 
        :type enable_downloads: bool
 
        :param enable_statistics:
 
        :type enable_statistics: bool
 
        :param copy_permissions: Copy permission from group that repository is
 
            being created.
 
        :type copy_permissions: bool
 

	
 
        OUTPUT::
 

	
 
            id : <id_given_in_input>
 
            result: {
 
                      "msg": "Created new repository `<reponame>`",
 
                      "success": true,
 
                      "task": "<celery task id or None if done sync>"
 
                    }
 
            error:  null
 

	
 
        ERROR OUTPUT::
 

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

	
 
            result : {
 
                      "msg" : "Created new repository `<reponame>`",
 
                      "success" : true
 
                     }
 
            error : null
 
        """
 
        group_name = None
 
        repo_name_parts = repo_name.split('/')
 
        if len(repo_name_parts) > 1:
 
            group_name = '/'.join(repo_name_parts[:-1])
 
            repo_group = db.RepoGroup.get_by_group_name(group_name)
 
@@ -1263,14 +990,14 @@ class ApiController(JSONRPCController):
 
                repo_type=repo_type,
 
                repo_description=description,
 
                repo_private=private,
 
                clone_uri=clone_uri,
 
                repo_group=group_name,
 
                repo_landing_rev=landing_rev,
 
                enable_statistics=enable_statistics,
 
                enable_downloads=enable_downloads,
 
                repo_enable_statistics=enable_statistics,
 
                repo_enable_downloads=enable_downloads,
 
                repo_copy_permissions=copy_permissions,
 
            )
 

	
 
            RepoModel().create(form_data=data, cur_user=owner.username)
 
            # no commit, it's done in RepoModel, or async via celery
 
            return dict(
 
@@ -1288,27 +1015,14 @@ class ApiController(JSONRPCController):
 
                    owner=None,
 
                    group=None,
 
                    description=None, private=None,
 
                    clone_uri=None, landing_rev=None,
 
                    enable_statistics=None,
 
                    enable_downloads=None):
 

	
 
        """
 
        Updates repo
 

	
 
        :param repoid: repository name or repository id
 
        :type repoid: str or int
 
        :param name:
 
        :param owner:
 
        :param group:
 
        :param description:
 
        :param private:
 
        :param clone_uri:
 
        :param landing_rev:
 
        :param enable_statistics:
 
        :param enable_downloads:
 
        """
 
        repo = get_repo_or_error(repoid)
 
        if not HasPermissionAny('hg.admin')():
 
            if not HasRepoPermissionLevel('admin')(repo.repo_name):
 
                raise JSONRPCError('repository `%s` does not exist' % (repoid,))
 

	
 
@@ -1362,45 +1076,35 @@ class ApiController(JSONRPCController):
 
        Creates a fork of given repo. In case of using celery this will
 
        immediately 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.
 

	
 
        :param repoid: repository name or repository id
 
        :type repoid: str or int
 
        :param fork_name:
 
        :param owner:
 
        :param description:
 
        :param copy_permissions:
 
        :param private:
 
        :param landing_rev:
 

	
 
        INPUT::
 

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

	
 
        OUTPUT::
 

	
 
            id : <id_given_in_input>
 
            result: {
 
                      "msg": "Created fork of `<reponame>` as `<forkname>`",
 
                      "success": true,
 
                      "task": "<celery task id or None if done sync>"
 
                    }
 
            error:  null
 

	
 
            result : {
 
                      "msg" : "Created fork of `<reponame>` as `<forkname>`",
 
                      "success" : true
 
                     }
 
            error : null
 
        """
 
        repo = get_repo_or_error(repoid)
 
        repo_name = repo.repo_name
 

	
 
        _repo = RepoModel().get_by_repo_name(fork_name)
 
        if _repo:
 
@@ -1469,26 +1173,20 @@ class ApiController(JSONRPCController):
 
        """
 
        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.
 
        When `forks` param is set it's possible to detach or delete forks of deleting
 
        repository
 

	
 
        :param repoid: repository name or repository id
 
        :type repoid: str or int
 
        :param forks: `detach` or `delete`, what do do with attached forks for repo
 
        :type forks: Optional(str)
 

	
 
        OUTPUT::
 

	
 
            id : <id_given_in_input>
 
            result: {
 
                      "msg": "Deleted repository `<reponame>`",
 
                      "success": true
 
                    }
 
            error:  null
 

	
 
            result : {
 
                      "msg" : "Deleted repository `<reponame>`",
 
                      "success" : true
 
                     }
 
            error : null
 
        """
 
        repo = get_repo_or_error(repoid)
 

	
 
        if not HasPermissionAny('hg.admin')():
 
            if not HasRepoPermissionLevel('admin')(repo.repo_name):
 
                raise JSONRPCError('repository `%s` does not exist' % (repoid,))
 
@@ -1523,26 +1221,20 @@ class ApiController(JSONRPCController):
 
    def grant_user_permission(self, repoid, userid, perm):
 
        """
 
        Grant permission for user on given repository, or update existing one
 
        if found. This command can be executed only using api_key belonging to user
 
        with admin rights.
 

	
 
        :param repoid: repository name or repository id
 
        :type repoid: str or int
 
        :param userid:
 
        :param perm: (repository.(none|read|write|admin))
 
        :type perm: str
 

	
 
        OUTPUT::
 

	
 
            id : <id_given_in_input>
 
            result: {
 
            result : {
 
                      "msg" : "Granted perm: `<perm>` for user: `<username>` in repo: `<reponame>`",
 
                      "success": true
 
                    }
 
            error:  null
 
                      "success" : true
 
                     }
 
            error : null
 
        """
 
        repo = get_repo_or_error(repoid)
 
        user = get_user_or_error(userid)
 
        perm = get_perm_or_error(perm)
 

	
 
        try:
 
@@ -1567,27 +1259,21 @@ class ApiController(JSONRPCController):
 
    @HasPermissionAnyDecorator('hg.admin')
 
    def revoke_user_permission(self, repoid, userid):
 
        """
 
        Revoke permission for user on given repository. This command can be executed
 
        only using api_key belonging to user with admin rights.
 

	
 
        :param repoid: repository name or repository id
 
        :type repoid: str or int
 
        :param userid:
 

	
 
        OUTPUT::
 

	
 
            id : <id_given_in_input>
 
            result: {
 
            result : {
 
                      "msg" : "Revoked perm for user: `<username>` in repo: `<reponame>`",
 
                      "success": true
 
                    }
 
            error:  null
 

	
 
                      "success" : true
 
                     }
 
            error : null
 
        """
 

	
 
        repo = get_repo_or_error(repoid)
 
        user = get_user_or_error(userid)
 
        try:
 
            RepoModel().revoke_user_permission(repo=repo, user=user)
 
            meta.Session().commit()
 
            return dict(
 
@@ -1608,37 +1294,20 @@ class ApiController(JSONRPCController):
 
    def grant_user_group_permission(self, repoid, usergroupid, perm):
 
        """
 
        Grant permission for user group on given repository, or update
 
        existing one if found. This command can be executed only using
 
        api_key belonging to user with admin rights.
 

	
 
        :param repoid: repository name or repository id
 
        :type repoid: str or int
 
        :param usergroupid: id of usergroup
 
        :type usergroupid: str or int
 
        :param perm: (repository.(none|read|write|admin))
 
        :type perm: str
 

	
 
        OUTPUT::
 

	
 
          id : <id_given_in_input>
 
          result : {
 
            "msg" : "Granted perm: `<perm>` for group: `<usersgroupname>` in repo: `<reponame>`",
 
            "success": true
 

	
 
          }
 
          error :  null
 

	
 
        ERROR OUTPUT::
 

	
 
          id : <id_given_in_input>
 
          result : null
 
          error :  {
 
            "failed to edit permission for user group: `<usergroup>` in repo `<repo>`'
 
          }
 

	
 
            id : <id_given_in_input>
 
            result : {
 
                "msg" : "Granted perm: `<perm>` for group: `<usersgroupname>` in repo: `<reponame>`",
 
                "success" : true
 
            }
 
            error : null
 
        """
 
        repo = get_repo_or_error(repoid)
 
        perm = get_perm_or_error(perm)
 
        user_group = get_user_group_or_error(usergroupid)
 
        if not HasPermissionAny('hg.admin')():
 
            if not HasRepoPermissionLevel('admin')(repo.repo_name):
 
@@ -1672,24 +1341,20 @@ class ApiController(JSONRPCController):
 
    # permission check inside
 
    def revoke_user_group_permission(self, repoid, usergroupid):
 
        """
 
        Revoke permission for user group on given repository. This command can be
 
        executed only using api_key belonging to user with admin rights.
 

	
 
        :param repoid: repository name or repository id
 
        :type repoid: str or int
 
        :param usergroupid:
 

	
 
        OUTPUT::
 

	
 
            id : <id_given_in_input>
 
            result: {
 
            result : {
 
                      "msg" : "Revoked perm for group: `<usersgroupname>` in repo: `<reponame>`",
 
                      "success": true
 
                    }
 
            error:  null
 
                      "success" : true
 
                     }
 
            error : null
 
        """
 
        repo = get_repo_or_error(repoid)
 
        user_group = get_user_group_or_error(usergroupid)
 
        if not HasPermissionAny('hg.admin')():
 
            if not HasRepoPermissionLevel('admin')(repo.repo_name):
 
                raise JSONRPCError('repository `%s` does not exist' % (repoid,))
 
@@ -1719,15 +1384,12 @@ class ApiController(JSONRPCController):
 

	
 
    @HasPermissionAnyDecorator('hg.admin')
 
    def get_repo_group(self, repogroupid):
 
        """
 
        Returns given repo group together with permissions, and repositories
 
        inside the group
 

	
 
        :param repogroupid: id/name of repository group
 
        :type repogroupid: str or int
 
        """
 
        repo_group = get_repo_group_or_error(repogroupid)
 

	
 
        members = []
 
        for user in repo_group.repo_group_to_perm:
 
            perm = user.permission.permission_name
 
@@ -1754,13 +1416,12 @@ class ApiController(JSONRPCController):
 
        return data
 

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

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

	
 
@@ -1770,40 +1431,20 @@ class ApiController(JSONRPCController):
 
                          parent=None,
 
                          copy_permissions=False):
 
        """
 
        Creates a repository group. This command can be executed only using
 
        api_key belonging to user with admin rights.
 

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

	
 
        OUTPUT::
 

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

	
 
        ERROR OUTPUT::
 

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

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

	
 
        if owner is None:
 
            owner = request.authuser.user_id
 
@@ -1832,20 +1473,24 @@ class ApiController(JSONRPCController):
 

	
 
    @HasPermissionAnyDecorator('hg.admin')
 
    def update_repo_group(self, repogroupid, group_name=None,
 
                          description=None,
 
                          owner=None,
 
                          parent=None):
 
        """
 
        TODO
 
        """
 
        repo_group = get_repo_group_or_error(repogroupid)
 
        parent_repo_group_id = None if parent is None else get_repo_group_or_error(parent).group_id
 

	
 
        updates = {}
 
        try:
 
            store_update(updates, group_name, 'group_name')
 
            store_update(updates, description, 'group_description')
 
            store_update(updates, owner, 'owner')
 
            store_update(updates, parent, 'parent_group')
 
            store_update(updates, parent_repo_group_id, 'parent_group_id')
 
            repo_group = RepoGroupModel().update(repo_group, updates)
 
            meta.Session().commit()
 
            return dict(
 
                msg='updated repository group ID:%s %s' % (repo_group.group_id,
 
                                                           repo_group.group_name),
 
                repo_group=repo_group.get_api_data()
 
@@ -1855,33 +1500,20 @@ class ApiController(JSONRPCController):
 
            raise JSONRPCError('failed to update repository group `%s`'
 
                               % (repogroupid,))
 

	
 
    @HasPermissionAnyDecorator('hg.admin')
 
    def delete_repo_group(self, repogroupid):
 
        """
 

	
 
        :param repogroupid: name or id of repository group
 
        :type repogroupid: str or int
 

	
 
        OUTPUT::
 

	
 
          id : <id_given_in_input>
 
          result : {
 
            'msg': 'deleted repo group ID:<repogroupid> <repogroupname>
 
            'repo_group': null
 
            'msg' : 'deleted repo group ID:<repogroupid> <repogroupname>
 
            'repo_group' : null
 
          }
 
          error :  null
 

	
 
        ERROR OUTPUT::
 

	
 
          id : <id_given_in_input>
 
          result : null
 
          error :  {
 
            "failed to delete repo group ID:<repogroupid> <repogroupname>"
 
          }
 

	
 
          error : null
 
        """
 
        repo_group = get_repo_group_or_error(repogroupid)
 

	
 
        try:
 
            RepoGroupModel().delete(repo_group)
 
            meta.Session().commit()
 
@@ -1902,39 +1534,21 @@ class ApiController(JSONRPCController):
 
        """
 
        Grant permission for user on given repository group, or update existing
 
        one if found. This command can be executed only using api_key belonging
 
        to user with admin rights, or user who has admin right to given repository
 
        group.
 

	
 
        :param repogroupid: name or id of repository group
 
        :type repogroupid: str or int
 
        :param userid:
 
        :param perm: (group.(none|read|write|admin))
 
        :type perm: str
 
        :param apply_to_children: 'none', 'repos', 'groups', 'all'
 
        :type apply_to_children: str
 

	
 
        OUTPUT::
 

	
 
            id : <id_given_in_input>
 
            result: {
 
            result : {
 
                      "msg" : "Granted perm: `<perm>` (recursive:<apply_to_children>) for user: `<username>` in repo group: `<repo_group_name>`",
 
                      "success": true
 
                    }
 
            error:  null
 

	
 
        ERROR OUTPUT::
 

	
 
          id : <id_given_in_input>
 
          result : null
 
          error :  {
 
            "failed to edit permission for user: `<userid>` in repo group: `<repo_group_name>`"
 
          }
 

	
 
                      "success" : true
 
                     }
 
            error : null
 
        """
 

	
 
        repo_group = get_repo_group_or_error(repogroupid)
 

	
 
        if not HasPermissionAny('hg.admin')():
 
            if not HasRepoGroupPermissionLevel('admin')(repo_group.group_name):
 
                raise JSONRPCError('repository group `%s` does not exist' % (repogroupid,))
 

	
 
@@ -1965,38 +1579,21 @@ class ApiController(JSONRPCController):
 
                                               apply_to_children='none'):
 
        """
 
        Revoke permission for user on given repository group. This command can
 
        be executed only using api_key belonging to user with admin rights, or
 
        user who has admin right to given repository group.
 

	
 
        :param repogroupid: name or id of repository group
 
        :type repogroupid: str or int
 
        :param userid:
 
        :type userid:
 
        :param apply_to_children: 'none', 'repos', 'groups', 'all'
 
        :type apply_to_children: str
 

	
 
        OUTPUT::
 

	
 
            id : <id_given_in_input>
 
            result: {
 
            result : {
 
                      "msg" : "Revoked perm (recursive:<apply_to_children>) for user: `<username>` in repo group: `<repo_group_name>`",
 
                      "success": true
 
                    }
 
            error:  null
 

	
 
        ERROR OUTPUT::
 

	
 
          id : <id_given_in_input>
 
          result : null
 
          error :  {
 
            "failed to edit permission for user: `<userid>` in repo group: `<repo_group_name>`"
 
          }
 

	
 
                      "success" : true
 
                     }
 
            error : null
 
        """
 

	
 
        repo_group = get_repo_group_or_error(repogroupid)
 

	
 
        if not HasPermissionAny('hg.admin')():
 
            if not HasRepoGroupPermissionLevel('admin')(repo_group.group_name):
 
                raise JSONRPCError('repository group `%s` does not exist' % (repogroupid,))
 

	
 
@@ -2028,39 +1625,20 @@ class ApiController(JSONRPCController):
 
        """
 
        Grant permission for user group on given repository group, or update
 
        existing one if found. This command can be executed only using
 
        api_key belonging to user with admin rights, or user who has admin
 
        right to given repository group.
 

	
 
        :param repogroupid: name or id of repository group
 
        :type repogroupid: str or int
 
        :param usergroupid: id of usergroup
 
        :type usergroupid: str or int
 
        :param perm: (group.(none|read|write|admin))
 
        :type perm: str
 
        :param apply_to_children: 'none', 'repos', 'groups', 'all'
 
        :type apply_to_children: str
 

	
 
        OUTPUT::
 

	
 
          id : <id_given_in_input>
 
          result : {
 
            "msg" : "Granted perm: `<perm>` (recursive:<apply_to_children>) for user group: `<usersgroupname>` in repo group: `<repo_group_name>`",
 
            "success": true
 

	
 
            "success" : true
 
          }
 
          error :  null
 

	
 
        ERROR OUTPUT::
 

	
 
          id : <id_given_in_input>
 
          result : null
 
          error :  {
 
            "failed to edit permission for user group: `<usergroup>` in repo group: `<repo_group_name>`"
 
          }
 

	
 
          error : null
 
        """
 
        repo_group = get_repo_group_or_error(repogroupid)
 
        perm = get_perm_or_error(perm, prefix='group.')
 
        user_group = get_user_group_or_error(usergroupid)
 
        if not HasPermissionAny('hg.admin')():
 
            if not HasRepoGroupPermissionLevel('admin')(repo_group.group_name):
 
@@ -2100,36 +1678,20 @@ class ApiController(JSONRPCController):
 
            apply_to_children='none'):
 
        """
 
        Revoke permission for user group on given repository. This command can be
 
        executed only using api_key belonging to user with admin rights, or
 
        user who has admin right to given repository group.
 

	
 
        :param repogroupid: name or id of repository group
 
        :type repogroupid: str or int
 
        :param usergroupid:
 
        :param apply_to_children: 'none', 'repos', 'groups', 'all'
 
        :type apply_to_children: str
 

	
 
        OUTPUT::
 

	
 
            id : <id_given_in_input>
 
            result: {
 
            result : {
 
                      "msg" : "Revoked perm (recursive:<apply_to_children>) for user group: `<usersgroupname>` in repo group: `<repo_group_name>`",
 
                      "success": true
 
                    }
 
            error:  null
 

	
 
        ERROR OUTPUT::
 

	
 
          id : <id_given_in_input>
 
          result : null
 
          error :  {
 
            "failed to edit permission for user group: `<usergroup>` in repo group: `<repo_group_name>`"
 
          }
 

	
 

	
 
                      "success" : true
 
                     }
 
            error : null
 
        """
 
        repo_group = get_repo_group_or_error(repogroupid)
 
        user_group = get_user_group_or_error(usergroupid)
 
        if not HasPermissionAny('hg.admin')():
 
            if not HasRepoGroupPermissionLevel('admin')(repo_group.group_name):
 
                raise JSONRPCError(
 
@@ -2159,29 +1721,23 @@ class ApiController(JSONRPCController):
 
                )
 
            )
 

	
 
    def get_gist(self, gistid):
 
        """
 
        Get given gist by id
 

	
 
        :param gistid: id of private or public gist
 
        :type gistid: str
 
        """
 
        gist = get_gist_or_error(gistid)
 
        if not HasPermissionAny('hg.admin')():
 
            if gist.owner_id != request.authuser.user_id:
 
                raise JSONRPCError('gist `%s` does not exist' % (gistid,))
 
        return gist.get_api_data()
 

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

	
 
        :param userid: user to get gists for
 
        :type userid: Optional(str or int)
 
        """
 
        if not HasPermissionAny('hg.admin')():
 
            # make sure normal user does not pass someone else userid,
 
            # he is not allowed to do that
 
            if userid is not None and userid != request.authuser.user_id:
 
                raise JSONRPCError(
 
@@ -2201,46 +1757,23 @@ class ApiController(JSONRPCController):
 
                .order_by(db.Gist.created_on.desc())
 
        ]
 

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

	
 
        """
 
        Creates new Gist
 

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

	
 
        OUTPUT::
 

	
 
          id : <id_given_in_input>
 
          result : {
 
            "msg": "created new gist",
 
            "gist": {}
 
            "msg" : "created new gist",
 
            "gist" : <gist_object>
 
          }
 
          error :  null
 

	
 
        ERROR OUTPUT::
 

	
 
          id : <id_given_in_input>
 
          result : null
 
          error :  {
 
            "failed to create gist"
 
          }
 

	
 
          error : null
 
        """
 
        try:
 
            if owner is None:
 
                owner = request.authuser.user_id
 

	
 
            owner = get_user_or_error(owner)
 
@@ -2262,32 +1795,20 @@ class ApiController(JSONRPCController):
 

	
 
    # permission check inside
 
    def delete_gist(self, gistid):
 
        """
 
        Deletes existing gist
 

	
 
        :param gistid: id of gist to delete
 
        :type gistid: str
 

	
 
        OUTPUT::
 

	
 
          id : <id_given_in_input>
 
          result : {
 
            "deleted gist ID: <gist_id>",
 
            "gist": null
 
            "msg" : "deleted gist ID: <gist_id>",
 
            "gist" : null
 
          }
 
          error :  null
 

	
 
        ERROR OUTPUT::
 

	
 
          id : <id_given_in_input>
 
          result : null
 
          error :  {
 
            "failed to delete gist ID:<gist_id>"
 
          }
 

	
 
          error : null
 
        """
 
        gist = get_gist_or_error(gistid)
 
        if not HasPermissionAny('hg.admin')():
 
            if gist.owner_id != request.authuser.user_id:
 
                raise JSONRPCError('gist `%s` does not exist' % (gistid,))
 

	
 
@@ -2303,12 +1824,15 @@ class ApiController(JSONRPCController):
 
            raise JSONRPCError('failed to delete gist ID:%s'
 
                               % (gist.gist_access_id,))
 

	
 
    # permission check inside
 
    def get_changesets(self, repoid, start=None, end=None, start_date=None,
 
                       end_date=None, branch_name=None, reverse=False, with_file_list=False, max_revisions=None):
 
        """
 
        TODO
 
        """
 
        repo = get_repo_or_error(repoid)
 
        if not HasRepoPermissionLevel('read')(repo.repo_name):
 
            raise JSONRPCError('Access denied to repo %s' % repo.repo_name)
 

	
 
        format = "%Y-%m-%dT%H:%M:%S"
 
        try:
 
@@ -2320,27 +1844,40 @@ class ApiController(JSONRPCController):
 
                                                 branch_name,
 
                                                 reverse, max_revisions)]
 
        except EmptyRepositoryError as e:
 
            raise JSONRPCError('Repository is empty')
 

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

	
 
        info = dict(changeset.as_dict())
 

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

	
 
        if with_comments:
 
            comments = ChangesetCommentsModel().get_comments(
 
                                repo.repo_id, changeset.raw_id)
 
            info["comments"] = comments
 

	
 
        if with_inline_comments:
 
            inline_comments = ChangesetCommentsModel().get_inline_comments(
 
                                repo.repo_id, changeset.raw_id)
 
            info["inline_comments"] = inline_comments
 

	
 
        return info
 

	
 
    # permission check inside
 
    def get_pullrequest(self, pullrequest_id):
 
        """
 
        Get given pull request by id
kallithea/controllers/pullrequests.py
Show inline comments
 
@@ -32,12 +32,13 @@ import formencode
 
import mercurial.unionrepo
 
from tg import request
 
from tg import tmpl_context as c
 
from tg.i18n import ugettext as _
 
from webob.exc import HTTPBadRequest, HTTPForbidden, HTTPFound, HTTPNotFound
 

	
 
import kallithea
 
import kallithea.lib.helpers as h
 
from kallithea.controllers import base
 
from kallithea.controllers.changeset import create_cs_pr_comment, delete_cs_pr_comment
 
from kallithea.lib import auth, diffs, webutils
 
from kallithea.lib.auth import HasRepoPermissionLevelDecorator, LoginRequired
 
from kallithea.lib.graphmod import graph_data
 
@@ -491,12 +492,14 @@ class PullrequestsController(base.BaseRe
 
                #c.merge_root = len(root_parents) > 1 # a range starting with a merge might deserve a warning
 
        except ChangesetDoesNotExistError: # probably because c.a_rev not found
 
            pass
 
        except IndexError: # probably because c.cs_ranges is empty, probably because revisions are missing
 
            pass
 

	
 
        rev_limit = safe_int(kallithea.CONFIG.get('next_iteration_rev_limit'), 0)
 

	
 
        avail_revs = set()
 
        avail_show = []
 
        c.cs_branch_name = c.cs_ref_name
 
        c.a_branch_name = None
 
        other_scm_instance = c.a_repo.scm_instance
 
        c.update_msg = ""
 
@@ -560,12 +563,20 @@ class PullrequestsController(base.BaseRe
 
            elif org_scm_instance.alias == 'git':
 
                c.cs_repo.scm_instance.get_changeset(c.cs_rev) # check it exists - raise ChangesetDoesNotExistError if not
 
                c.update_msg = _("Git pull requests don't support iterating yet.")
 
        except ChangesetDoesNotExistError:
 
            c.update_msg = _('Error: some changesets not found when displaying pull request from %s.') % c.cs_rev
 

	
 
        if rev_limit:
 
            if len(avail_revs) - 1 > rev_limit:
 
                c.update_msg = _('%d additional changesets are not shown.') % (len(avail_revs) - 1)
 
                avail_show = []
 
            elif len(avail_show) - 1 > rev_limit:
 
                c.update_msg = _('%d changesets available for merging are not shown.') % (len(avail_show) - len(avail_revs))
 
                avail_show = sorted(avail_revs, reverse=True)
 

	
 
        c.avail_revs = avail_revs
 
        c.avail_cs = [org_scm_instance.get_changeset(r) for r in avail_show]
 
        c.avail_jsdata = graph_data(org_scm_instance, avail_show)
 

	
 
        raw_ids = [x.raw_id for x in c.cs_ranges]
 
        c.cs_comments = c.cs_repo.get_comments(raw_ids)
kallithea/i18n/be/LC_MESSAGES/kallithea.po
Show inline comments
 
@@ -5,14 +5,14 @@ msgid ""
 
msgstr ""
 
"Report-Msgid-Bugs-To: translations@kallithea-scm.org\n"
 
"Language: be\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%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
 
"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
 
"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
 
"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
 

	
 
msgid "Repository not found in the filesystem"
 
msgstr "Рэпазітар не знойдзены на файлавай сістэме"
 

	
 
msgid "There are no changesets yet"
 
msgstr "Яшчэ не было змен"
 
@@ -1411,24 +1411,27 @@ msgstr "Пацвердзіце выдаленне гэтай групы карыстальнікаў"
 
msgid "Repository Groups Administration"
 
msgstr "Адміністраванне груп рэпазітароў"
 

	
 
msgid "Number of Top-level Repositories"
 
msgstr "Лік рэпазітароў верхняга ўзроўня"
 

	
 
msgid "Type of repository to create."
 
msgstr "Тып стваранага рэпазітара."
 

	
 
msgid "Repository URL"
 
msgstr "URL рэпазітара"
 

	
 
msgid ""
 
"Keep it short and to the point. Use a README file for longer descriptions."
 
msgstr ""
 
"Кароткае і асэнсаванае. Для разгорнутага апісання выкарыстоўвайце файл "
 
"README."
 

	
 
msgid "Optionally select a group to put this repository into."
 
msgstr "Апцыянальна абраць групу, у якую змясціць дадзены рэпазітар."
 

	
 
msgid "Type of repository to create."
 
msgstr "Тып стваранага рэпазітара."
 

	
 
msgid "Landing revision"
 
msgstr "Рэвізія для выгрузкі"
 

	
 
msgid "%s Creating Repository"
 
msgstr "Стварэнне рэпазітара %s"
 

	
 
@@ -1526,19 +1529,12 @@ msgstr "Занесці змены з аддаленага рэпазітара"
 
msgid "Confirm to pull changes from remote repository."
 
msgstr "Пацвердзіце спампоўку змен з аддаленага рэпазітара."
 

	
 
msgid "Remote repository"
 
msgstr "Аддалены рэпазітар"
 

	
 
msgid "Repository URL"
 
msgstr "URL рэпазітара"
 

	
 
msgid "Default revision for files page, downloads, whoosh and readme"
 
msgstr ""
 
"Рэвізія па змоўчанні, з якой будзе рабіцца выгрузка файлаў пры спампоўцы"
 

	
 
msgid "Change owner of this repository."
 
msgstr "Змяніць уладальніка рэпазітара."
 

	
 
msgid "Reset Statistics"
 
msgstr "Скід статыстыкі"
 

	
 
@@ -1888,17 +1884,17 @@ msgstr "Пошук усечаны"
 
msgid "No matching files"
 
msgstr "Няма супадзенняў"
 

	
 
msgid "Open New Pull Request from {0}"
 
msgstr "Стварыць новы pull-запыт з {0}"
 

	
 
msgid "Open New Pull Request for {0} &rarr; {1}"
 
msgstr "Стварыць новы pull-запыт для {0} &rarr; {1}"
 

	
 
msgid "Show Selected Changesets {0} &rarr; {1}"
 
msgstr "Паказаць выбраныя наборы змен: {0} &rarr; {1}"
 
msgid "Open New Pull Request for {0}"
 
msgstr "Стварыць новы pull-запыт для {0}"
 

	
 
msgid "Show Selected Changesets {0}"
 
msgstr "Паказаць выбраныя наборы змен {0}"
 

	
 
msgid "Selection Link"
 
msgstr "Спасылка выбару"
 

	
 
msgid "Collapse Diff"
 
msgstr "Згарнуць параўнанне"
kallithea/i18n/de/LC_MESSAGES/kallithea.po
Show inline comments
 
@@ -955,14 +955,14 @@ msgstr "Die Passwörter stimmen nicht überein"
 

	
 
msgid "Repository named %(repo)s already exists"
 
msgstr "Es gibt bereits ein Repository mit \"%(repo)s\""
 

	
 
msgid "Repository \"%(repo)s\" already exists in group \"%(group)s\""
 
msgstr ""
 
"Es gibt bereits ein Repository mit \"%(repo)s\" in der Gruppe \"%(group)s"
 
"\""
 
"Es gibt bereits ein Repository mit \"%(repo)s\" in der Gruppe "
 
"\"%(group)s\""
 

	
 
msgid "Repository group with name \"%(repo)s\" already exists"
 
msgstr "Eine Repositorygruppe mit dem Namen \"%(repo)s\" existiert bereits"
 

	
 
msgid "Invalid repository URL"
 
msgstr "Ungültige Repository-URL"
 
@@ -1612,12 +1612,15 @@ msgid ""
 
"private repositories and other groups if selected."
 
msgstr ""
 
"Setzen oder zurückziehen von Berechtigungen bezüglich aller "
 
"untergeordneten Elemente, einschließlich nicht-privater Repositories und "
 
"anderer Gruppen, falls ausgewählt."
 

	
 
msgid "Type name of user"
 
msgstr "Typname des Benutzers"
 

	
 
msgid "Remove this group"
 
msgstr "Diese Gruppe löschen"
 

	
 
msgid "Confirm to delete this group"
 
msgstr "Löschen der Gruppe bestätigen"
 

	
 
@@ -1627,15 +1630,21 @@ msgstr "Repository-Gruppe %s"
 
msgid "Repository Groups Administration"
 
msgstr "Repositorygruppenverwaltung"
 

	
 
msgid "Number of Top-level Repositories"
 
msgstr "Anzahl der Repositories oberster Ebene"
 

	
 
msgid "Type of repository to create."
 
msgstr "Repository Typ der erstellt werden soll."
 

	
 
msgid "Clone remote repository"
 
msgstr "Entferntes Repository clonen"
 

	
 
msgid "Repository URL"
 
msgstr "Repository URL"
 

	
 
msgid ""
 
"Optional: URL of a remote repository. If set, the repository will be "
 
"created as a clone from this URL."
 
msgstr ""
 
"Optional: URL eines entfernten Repositories. Falls gesetzt, dann wird das "
 
"Repository als Clon von dieser URL erstellt."
 
@@ -1648,15 +1657,12 @@ msgstr ""
 

	
 
msgid "Optionally select a group to put this repository into."
 
msgstr ""
 
"Wähle bei Bedarf eine Gruppe, der dieses Repository zugeordnet werden "
 
"soll."
 

	
 
msgid "Type of repository to create."
 
msgstr "Repository Typ der erstellt werden soll."
 

	
 
msgid "Landing revision"
 
msgstr "Start Revision"
 

	
 
msgid ""
 
"Default revision for files page, downloads, full text search index and "
 
"readme generation"
 
@@ -1791,46 +1797,22 @@ msgstr "Hole Änderungen vom entfernten Repository"
 
msgid "Confirm to pull changes from remote repository."
 
msgstr "Bestätige die Abholung von Änderungen vom entfernten Repository."
 

	
 
msgid "This repository does not have a remote repository URL."
 
msgstr "Für dieses Repository ist keine nicht-lokale URL angegeben."
 

	
 
msgid ""
 
"In case this repository is renamed or moved into another group the "
 
"repository URL changes.\n"
 
"                               Using the above permanent URL guarantees "
 
"that this repository always will be accessible on that URL.\n"
 
"                               This is useful for CI systems, or any "
 
"other cases that you need to hardcode the URL into a 3rd party service."
 
msgstr ""
 
"Falls dieses Repository umbenannt oder in eine andere Gruppe verschoben "
 
"wird, ändert sich seine URL.\n"
 
"Die Verwendung der permanenten URL garantiert, dass dieses Repository "
 
"immer über diese URL erreichbar sein wird.\n"
 
"Dies ist insbesondere für CI-Systeme oder in Fällen nützlich, in denen "
 
"die URL des Repositories bei Dritten dauerhaft eingetragen wird."
 

	
 
msgid "Remote repository"
 
msgstr "Entferntes Repository"
 

	
 
msgid "Repository URL"
 
msgstr "Repository URL"
 

	
 
msgid ""
 
"Optional: URL of a remote repository. If set, the repository can be "
 
"pulled from this URL."
 
msgstr ""
 
"Optional: URL eines entfernten Repositories. Falls gesetzt, dann kann das "
 
"Repository von dieser URL bezogen werden."
 

	
 
msgid "Default revision for files page, downloads, whoosh and readme"
 
msgstr "Standardrevision für Dateiseite, Downloads, Whoosh und Readme"
 

	
 
msgid "Type name of user"
 
msgstr "Typname des Benutzers"
 

	
 
msgid "Change owner of this repository."
 
msgstr "Besitzer des Repositorys ändern."
 

	
 
msgid "Processed commits"
 
msgstr "Verarbeitete Commits"
 

	
 
@@ -2493,11 +2475,8 @@ msgstr "Letzte Änderungen"
 
msgid "Quick Start"
 
msgstr "Schnelleinstieg"
 

	
 
msgid "Add or upload files directly via Kallithea"
 
msgstr "Dateien direkt über Kallithea hinzufügen oder hochladen"
 

	
 
msgid "Readme file from revision %s:%s"
 
msgstr "Liesmich-Datei von Revision %s:%s"
 

	
 
msgid "Download %s as %s"
 
msgstr "%s als %s herunterladen"
kallithea/i18n/el/LC_MESSAGES/kallithea.po
Show inline comments
 
@@ -574,12 +574,19 @@ msgstr "Παρακαλώ εισάγετε την διεύθυνση ηλεκτρονικού ταχυδρομείου"
 
msgid "Send email task created"
 
msgstr "Δημιουργήθηκε η εργασία της αποστολής ηλεκτρονικού ταχυδρομείου"
 

	
 
msgid "Hook already exists"
 
msgstr "Το άγκιστρο υπάρχει ήδη"
 

	
 
msgid ""
 
"Hook names with \".kallithea_\" are reserved for internal use. Please use "
 
"another hook name."
 
msgstr ""
 
"Άγκιστρα με το όνομα \".kallithea_\" είναι δεσμευμένα για εσωτερική "
 
"χρήση. Παρακαλώ δώστε άλλο όνομα στο άγκιστρο."
 

	
 
msgid "Added new hook"
 
msgstr "Προσθήκη νέου άγκιστρου"
 

	
 
msgid "Updated hooks"
 
msgstr "Τα άγκιστρα ενημερώθηκαν"
 

	
 
@@ -662,12 +669,18 @@ msgstr ""
 
"Το σετ αλλαγών ήταν πολύ μεγάλο και αποκόπηκε, χρησιμοποιήστε το μενού "
 
"διαφορών για να εμφανίσετε τις διαφορές"
 

	
 
msgid "No changes detected"
 
msgstr "Δεν εντοπίστηκαν αλλαγές"
 

	
 
msgid "Show whitespace changes"
 
msgstr "Εμφάνιση αλλαγής κενού"
 

	
 
msgid "Ignore whitespace changes"
 
msgstr "Αγνόηση αλλαγής κενού"
 

	
 
msgid "Increase diff context to %(num)s lines"
 
msgstr "Αύξηση του diff πλαισίου σε %(num)s γραμμές"
 

	
 
msgid "Deleted branch: %s"
 
msgstr "Διαγραφή κλάδου: %s"
 

	
 
@@ -779,12 +792,60 @@ msgstr "μετονομασία"
 
msgid "chmod"
 
msgstr "chmod"
 

	
 
msgid "SSH key is missing"
 
msgstr "Το κλειδί SSH λείπει"
 

	
 
msgid ""
 
"Invalid SSH key - it must have both a key type and a base64 part, like "
 
"'ssh-rsa ASRNeaZu4FA...xlJp='"
 
msgstr ""
 
"Άκυρο κλειδί SSH - πρέπει να έχει έναν τύπο κλειδιού καθώς και ένα τμήμα "
 
"base64, όπως \"ssh-rsa ASRNeaZu4FA ... xlJp =\""
 

	
 
msgid ""
 
"Invalid SSH key - it must start with key type 'ssh-rsa', 'ssh-dss', 'ssh-"
 
"ed448', or 'ssh-ed25519'"
 
msgstr ""
 
"Άκυρο κλειδί SSH - πρέπει να ξεκινά με τύπο κλειδιού 'ssh-rsa',ssh-"
 
"dss','ssh-ed448' ή 'ssh-ed25519'"
 

	
 
msgid "Invalid SSH key - unexpected characters in base64 part %r"
 
msgstr "Άκυρο κλειδί SSH - μη αναμενόμενοι χαρακτήρες στο τμήμα base64 %r"
 

	
 
msgid ""
 
"Invalid SSH key - base64 part %r seems truncated (it can't be decoded)"
 
msgstr ""
 
"Άκυρο κλειδί SSH - το base64 μέρος %r φαίνεται κομμένο (δεν μπορεί να "
 
"αποκωδικοποιηθεί)"
 

	
 
msgid ""
 
"Invalid SSH key - base64 part %r seems truncated (it contains a partial "
 
"string length)"
 
msgstr ""
 
"Άκυρο κλειδί SSH - το base64 μέρος %r φαίνεται κομμένο (περιέχει ένα "
 
"μερικό μήκος συμβολοσειράς)"
 

	
 
msgid ""
 
"Invalid SSH key - base64 part %r seems truncated (it is too short for "
 
"declared string length %s)"
 
msgstr ""
 
"Άκυρο κλειδί SSH - το τμήμα base64 %r φαίνεται να είναι κομμένο (είναι "
 
"πολύ μικρό για το δηλωμένο μήκος συμβολοσειράς %s)"
 

	
 
msgid ""
 
"Invalid SSH key - base64 part %r seems truncated (it contains too few "
 
"strings for a %s key)"
 
msgstr ""
 
"Άκυρο κλειδί SSH - το base64 τμήμα %r φαίνεται να είναι περικομμένο "
 
"(περιέχει πολύ λίγες συμβολοσειρές για ένα κλειδί %s)"
 

	
 
msgid "Invalid SSH key - it is a %s key but the base64 part contains %r"
 
msgstr ""
 
"Άκυρο κλειδί SSH - είναι ένα κλειδί %s αλλά το base64 μέρος περιέχει %r"
 

	
 
msgid "%d year"
 
msgid_plural "%d years"
 
msgstr[0] "%d έτος"
 
msgstr[1] "%d έτη"
 

	
 
msgid "%d month"
 
@@ -932,15 +993,36 @@ msgstr "Παρακαλώ εισάγετε έναν κωδικό πρόσβασης"
 
msgid "Enter %(min)i characters or more"
 
msgstr "Εισαγάγετε %(min)i χαρακτήρες ή περισσότερους"
 

	
 
msgid "Name must not contain only digits"
 
msgstr "Το όνομα δεν πρέπει να περιέχει μόνο ψηφία"
 

	
 
msgid ""
 
"[Comment] %(repo_name)s changeset %(short_id)s \"%(message_short)s\" on "
 
"%(branch)s by %(cs_author_username)s"
 
msgstr ""
 
"[Σχόλιο] %(repo_name)s changeset %(short_id)s \"%(message_short)s\" στο "
 
"%(branch)s από %(cs_author_username)s"
 

	
 
msgid "New user %(new_username)s registered"
 
msgstr "Καταχωρήθηκε νέος χρήστης %(new_username)s"
 

	
 
msgid ""
 
"[Review] %(repo_name)s PR %(pr_nice_id)s \"%(pr_title_short)s\" from "
 
"%(pr_source_branch)s by %(pr_owner_username)s"
 
msgstr ""
 
"[Κριτική] %(repo_name)s PR %(pr_nice_id)s \"%(pr_title_short)s\" στο "
 
"%(pr_source_branch)s από %(pr_owner_username)s"
 

	
 
msgid ""
 
"[Comment] %(repo_name)s PR %(pr_nice_id)s \"%(pr_title_short)s\" from "
 
"%(pr_source_branch)s by %(pr_owner_username)s"
 
msgstr ""
 
"[Σχόλιο] %(repo_name)s PR %(pr_nice_id)s \"%(pr_title_short)s\" στο "
 
"%(pr_source_branch)s από %(pr_owner_username)s"
 

	
 
msgid "Closing"
 
msgstr "Κλείσιμο"
 

	
 
msgid "Cannot create empty pull request"
 
msgstr "Δεν είναι δυνατή η δημιουργία κενής αίτησης έλξης"
 

	
 
@@ -1100,12 +1182,17 @@ msgstr "Το αποθετήριο \"%(repo)s\" υπάρχει ήδη στην ομάδα \"%(group)s\""
 
msgid "Repository group with name \"%(repo)s\" already exists"
 
msgstr "Η ομάδα αποθετηρίου με το όνομα \"%(repo)s\" υπάρχει ήδη"
 

	
 
msgid "Invalid repository URL"
 
msgstr "Μη έγκυρη διεύθυνση URL αποθετηρίου"
 

	
 
msgid "Invalid repository URL. It must be a valid http, https, or ssh URL"
 
msgstr ""
 
"Μη έγκυρο αποθετήριο URL. Πρέπει να είναι μια έγκυρη http, https ή ssh "
 
"διεύθυνση URL"
 

	
 
msgid "Fork has to be the same type as parent"
 
msgstr "Ο κλώνος πρέπει να έχει τον ίδιο τύπο με τον γονέα του"
 

	
 
msgid "You don't have permissions to create repository in this group"
 
msgstr "Δεν έχετε δικαιώματα δημιουργίας αποθετηρίου σε αυτήν την ομάδα"
 

	
 
@@ -1820,12 +1907,15 @@ msgid ""
 
"private repositories and other groups if selected."
 
msgstr ""
 
"Ορίστε ή ανακαλέστε τα θυγατρικά δικαιώματα αυτής της ομάδας, "
 
"συμπεριλαμβανομένων των μη ιδιωτικών αποθετηρίων και άλλων ομάδων, εάν "
 
"επιλεγεί."
 

	
 
msgid "Type name of user"
 
msgstr "Πληκτρολογήστε το όνομα του χρήστη"
 

	
 
msgid "Remove this group"
 
msgstr "Κατάργηση αυτής της ομάδας"
 

	
 
msgid "Confirm to delete this group"
 
msgstr "Επιβεβαιώστε για να διαγράψετε αυτή την ομάδα"
 

	
 
@@ -1835,15 +1925,21 @@ msgstr "Ομάδα αποθετηρίων %s"
 
msgid "Repository Groups Administration"
 
msgstr "Διαχείριση Ομάδων Αποθετηρίου"
 

	
 
msgid "Number of Top-level Repositories"
 
msgstr "Αριθμός αποθετηρίων ανώτατου επιπέδου"
 

	
 
msgid "Type of repository to create."
 
msgstr "Τύπος αποθετηρίου προς δημιουργία."
 

	
 
msgid "Clone remote repository"
 
msgstr "Κλωνοποίηση απομακρυσμένου αποθετηρίου"
 

	
 
msgid "Repository URL"
 
msgstr "URL Αποθετηρίου"
 

	
 
msgid ""
 
"Optional: URL of a remote repository. If set, the repository will be "
 
"created as a clone from this URL."
 
msgstr ""
 
"Προαιρετικό: Διεύθυνση URL ενός απομακρυσμένου αποθετηρίου. Εάν οριστεί, "
 
"το αποθετήριο θα δημιουργηθεί ως κλώνος από αυτήν τη διεύθυνση URL."
 
@@ -1855,15 +1951,12 @@ msgstr ""
 
"μεγαλύτερες περιγραφές."
 

	
 
msgid "Optionally select a group to put this repository into."
 
msgstr ""
 
"Προαιρετικά, επιλέξτε μια ομάδα για να τοποθετήσετε αυτό το αποθετήριο."
 

	
 
msgid "Type of repository to create."
 
msgstr "Τύπος αποθετηρίου προς δημιουργία."
 

	
 
msgid "Landing revision"
 
msgstr "Αναθεώρηση εκφόρτωσης"
 

	
 
msgid ""
 
"Default revision for files page, downloads, full text search index and "
 
"readme generation"
 
@@ -2005,49 +2098,22 @@ msgid "This repository does not have a r
 
msgstr ""
 
"Αυτό το αποθετήριο δεν έχει διεύθυνση URL απομακρυσμένου αποθετηρίου."
 

	
 
msgid "Permanent URL"
 
msgstr "Μόνιμη διεύθυνση URL"
 

	
 
msgid ""
 
"In case this repository is renamed or moved into another group the "
 
"repository URL changes.\n"
 
"                               Using the above permanent URL guarantees "
 
"that this repository always will be accessible on that URL.\n"
 
"                               This is useful for CI systems, or any "
 
"other cases that you need to hardcode the URL into a 3rd party service."
 
msgstr ""
 
"Η διεύθυνση URL του αποθετηρίου αλλάζει όταν αυτό μετονομαστεί ή "
 
"μετακινηθεί σε άλλη ομάδα.\n"
 
"                               Η χρήση της παραπάνω μόνιμης διεύθυνσης "
 
"URL εγγυάται ότι αυτό το αποθετήριο θα είναι πάντα προσβάσιμο σε αυτήν τη "
 
"διεύθυνση URL.\n"
 
"                               Αυτό είναι χρήσιμο για συστήματα CI ή για "
 
"άλλες περιπτώσεις που χρειάζεστε να κωδικοποιήσετε τη διεύθυνση URL σε "
 
"κάποια υπηρεσία τρίτου μέρους."
 

	
 
msgid "Remote repository"
 
msgstr "Απομακρυσμένο αποθετήριο"
 

	
 
msgid "Repository URL"
 
msgstr "URL Αποθετηρίου"
 

	
 
msgid ""
 
"Optional: URL of a remote repository. If set, the repository can be "
 
"pulled from this URL."
 
msgstr ""
 
"Προαιρετικό: Διεύθυνση URL ενός απομακρυσμένου αποθετηρίου. Εάν οριστεί, "
 
"το αποθετήριο μπορεί να τραβηχτεί από αυτήν τη διεύθυνση URL."
 

	
 
msgid "Default revision for files page, downloads, whoosh and readme"
 
msgstr ""
 
"Προεπιλεγμένη αναθεώρηση για τη σελίδα αρχείων, λήψεων, Whoosh και readme"
 

	
 
msgid "Type name of user"
 
msgstr "Πληκτρολογήστε το όνομα του χρήστη"
 

	
 
msgid "Change owner of this repository."
 
msgstr "Αλλάξτε τον κάτοχο αυτού του αποθετηρίου."
 

	
 
msgid "Processed commits"
 
msgstr "Επεξεργασμένα commits"
 

	
 
@@ -2133,12 +2199,15 @@ msgstr ""
 
"Ιδιωτικό κλειδί για το σύστημα reCaptcha. Ο καθορισμός αυτής της τιμής θα "
 
"ενεργοποιήσει το captcha κατά την εγγραφή."
 

	
 
msgid "Save Settings"
 
msgstr "Αποθήκευση Ρυθμίσεων"
 

	
 
msgid "Custom Hooks are not enabled"
 
msgstr "Τα προσαρμοσμένα άγκιστρα δεν είναι ενεργά"
 

	
 
msgid "Failed to remove hook"
 
msgstr "Απέτυχε η αφαίρεση γάντζου"
 

	
 
msgid "Rescan options"
 
msgstr "Επιλογές Επανασάρωσης"
 

	
 
@@ -2161,12 +2230,24 @@ msgstr ""
 
"Επιλέξτε αυτό για να φορτώσετε ξανά τα δεδομένα και να καταργήστε την "
 
"cache για όλα τα αποθετήρια."
 

	
 
msgid "Install Git hooks"
 
msgstr "Εγκατάσταση Git hooks"
 

	
 
msgid "Install and overwrite Git hooks"
 
msgstr "Εγκατάσταση και επανεγγραφή Git hooks"
 

	
 
msgid ""
 
"Install Kallithea's internal hooks for all Git repositories. Existing "
 
"hooks that don't seem to come from Kallithea will be disabled by renaming "
 
"to .bak extension."
 
msgstr ""
 
"Εγκαταστήστε τα εσωτερικά hooks της Kallithea για όλα τα αποθετήρια Git. "
 
"Τα υπάρχοντα hooks που δεν φαίνεται να προέρχονται από την Kallithea θα "
 
"απενεργοποιηθούν με τη μετονομασία σε επέκταση .bak."
 

	
 
msgid "Rescan Repositories"
 
msgstr "Επανασάρωση αποθετηρίων"
 

	
 
msgid "Index build option"
 
msgstr "Επιλογή δημιουργίας ευρετηρίου"
 

	
 
@@ -2662,17 +2743,17 @@ msgstr "Περικομμένη αναζήτηση"
 
msgid "No matching files"
 
msgstr "Δεν υπάρχουν αρχεία που να ταιριάζουν"
 

	
 
msgid "Open New Pull Request from {0}"
 
msgstr "Άνοιγμα νέας αίτησης έλξης από {0}"
 

	
 
msgid "Open New Pull Request for {0} &rarr; {1}"
 
msgstr "Άνοιγμα νέου αιτήματος έλξης για {0} &rarr; {1}"
 

	
 
msgid "Show Selected Changesets {0} &rarr; {1}"
 
msgstr "Εμφάνιση Επιλεγμένων Σετ Αλλαγών {0} &rarr; {1}"
 
msgid "Open New Pull Request for {0}"
 
msgstr "Άνοιγμα νέου αιτήματος έλξης για {0}"
 

	
 
msgid "Show Selected Changesets {0}"
 
msgstr "Εμφάνιση Επιλεγμένων Σετ Αλλαγών {0}"
 

	
 
msgid "Selection Link"
 
msgstr "Σύνδεσμος Επιλογής"
 

	
 
msgid "Collapse Diff"
 
msgstr "Σύμπτυξη Διαφοράς"
 
@@ -2769,12 +2850,15 @@ msgstr ""
 
"Κατάσταση συνόλου αλλαγών: %s από %s\n"
 
"Κάντε κλικ για να ανοίξετε το συσχετισμένο αίτημα έλξης %s"
 

	
 
msgid "Changeset status: %s by %s"
 
msgstr "Κατάσταση σετ αλλαγών: %s από %s"
 

	
 
msgid "(No commit message)"
 
msgstr "(Χωρίς κείμενο commit)"
 

	
 
msgid "Expand commit message"
 
msgstr "Ανάπτυξη μηνύματος commit"
 

	
 
msgid "%s comments"
 
msgstr "%s σχόλια"
 

	
 
@@ -2931,12 +3015,18 @@ msgstr "Αρχείο μετά"
 
msgid "Show full diff for this file"
 
msgstr "Εμφάνιση πλήρους διαφοράς για αυτό το αρχείο"
 

	
 
msgid "Show full side-by-side diff for this file"
 
msgstr "Εμφάνιση πλήρους διαφοράς δίπλα-δίπλα για αυτό το αρχείο"
 

	
 
msgid "Raw diff for this file"
 
msgstr "Διαφορές για αυτό το αρχείο"
 

	
 
msgid "Download diff for this file"
 
msgstr "Μεταφόρτωση διαφοράς για αυτό το αρχείο"
 

	
 
msgid "Show inline comments"
 
msgstr "Εμφάνιση ενσωματωμένων σχολίων"
 

	
 
msgid "No changesets"
 
msgstr "Χωρίς σετ αλλαγών"
 

	
 
@@ -3617,11 +3707,8 @@ msgstr "Προσθέστε ή ανεβάστε αρχεία απευθείας μέσω Καλλιθέας"
 
msgid "Push new repository"
 
msgstr "Ώθηση νέου αποθετηρίου"
 

	
 
msgid "Existing repository?"
 
msgstr "Υπάρχον αποθετήριο;"
 

	
 
msgid "Readme file from revision %s:%s"
 
msgstr "Αρχείο Readme από την αναθεώρηση %s:%s"
 

	
 
msgid "Download %s as %s"
 
msgstr "Λήψη %s ως %s"
kallithea/i18n/es/LC_MESSAGES/kallithea.po
Show inline comments
 
@@ -10,12 +10,15 @@ msgstr ""
 
"Content-Transfer-Encoding: 8bit\n"
 
"Plural-Forms: nplurals=2; plural=n != 1;\n"
 

	
 
msgid "There are no changesets yet"
 
msgstr "Aún no hay cambios"
 

	
 
msgid "SSH access is disabled."
 
msgstr "El acceso mediante SSH está desactivado."
 

	
 
msgid "None"
 
msgstr "Ninguno"
 

	
 
msgid "(closed)"
 
msgstr "(cerrado)"
 

	
 
@@ -74,12 +77,15 @@ msgstr "El cambio era demasiado grande y se redució..."
 
msgid "%s %s feed"
 
msgstr "%s%s canal"
 

	
 
msgid "Click here to add new file"
 
msgstr "Haga clic aquí para añadir un archivo nuevo"
 

	
 
msgid "There are no files yet."
 
msgstr "Aún no hay archivos."
 

	
 
msgid "%s at %s"
 
msgstr "%s en %s"
 

	
 
msgid "You can only delete files with revision being a valid branch"
 
msgstr ""
 
"Sólo puede borrar archivos si la revisión pertenece a una rama válida"
 
@@ -208,12 +214,15 @@ msgstr "No hay descripción"
 
msgid "Pull request updated"
 
msgstr "Petición pull actualizada"
 

	
 
msgid "Successfully deleted pull request"
 
msgstr "Petición pull eliminada correctamente"
 

	
 
msgid "Revision %s not found in %s"
 
msgstr "No se ha encontrado la revisión %s en %s"
 

	
 
msgid "This pull request has already been merged to %s."
 
msgstr "La petición pull ya ha sido incluida a %s."
 

	
 
msgid "This pull request has been closed and can not be updated."
 
msgstr "La petición pull esta cerrada y no se puede actualizar."
 

	
kallithea/i18n/fr/LC_MESSAGES/kallithea.po
Show inline comments
 
@@ -235,13 +235,13 @@ msgid "Peer branches"
 
msgstr "Branches appairées"
 

	
 
msgid "Bookmarks"
 
msgstr "Signets"
 

	
 
msgid "Error creating pull request: %s"
 
msgstr "Erreur de création de la demande de pull : %s"
 
msgstr "Erreur de création de la demande de pull : %s"
 

	
 
msgid "Error occurred while creating pull request"
 
msgstr "Une erreur est survenue durant la création de la pull request"
 

	
 
msgid "Successfully opened new pull request"
 
msgstr "La requête de pull a été ouverte avec succès"
 
@@ -283,13 +283,13 @@ msgstr ""
 
"Les modifications additionnelles suivantes sont disponibles sur %s :"
 

	
 
msgid "No additional changesets found for iterating on this pull request."
 
msgstr "Pas de changeset additionnel trouvé pour cette requête de pull."
 

	
 
msgid "Note: Branch %s has another head: %s."
 
msgstr "Note : La branche %s a une autre tête : %s."
 
msgstr "Note : La branche %s a une autre tête : %s."
 

	
 
msgid "Git pull requests don't support iterating yet."
 
msgstr ""
 
"Les itérations des requêtes de pull Git ne sont pas encore supportées."
 

	
 
msgid ""
 
@@ -368,13 +368,13 @@ msgstr "Les données du gist on été mises à jour avec succès"
 

	
 
msgid "Error occurred during update of gist %s"
 
msgstr "Une erreur est survenue durant la mise à jour du gist %s"
 

	
 
msgid "You can't edit this user since it's crucial for entire application"
 
msgstr ""
 
"Vous ne pouvez pas éditer cet utilisateur ; il est nécessaire pour le bon "
 
"Vous ne pouvez pas éditer cet utilisateur ; il est nécessaire pour le bon "
 
"fonctionnement de l’application"
 

	
 
msgid "Your account was updated successfully"
 
msgstr "Votre compte a été mis à jour avec succès"
 

	
 
msgid "Error occurred during update of user %s"
 
@@ -383,13 +383,13 @@ msgstr "Une erreur est survenue durant la mise à jour de l'utilisateur %s"
 
msgid "Error occurred during update of user password"
 
msgstr ""
 
"Une erreur est survenue durant la mise à jour du mot de passe de "
 
"l'utilisateur"
 

	
 
msgid "Added email %s to user"
 
msgstr "L’e-mail « %s » a été ajouté à l’utilisateur"
 
msgstr "L’e-mail « %s » a été ajouté à l’utilisateur"
 

	
 
msgid "An error occurred during email saving"
 
msgstr "Une erreur est survenue durant l’enregistrement de l’e-mail"
 

	
 
msgid "Removed email from user"
 
msgstr "L’e-mail a été enlevé de l’utilisateur"
 
@@ -558,13 +558,13 @@ msgstr "Réglages des gestionnaires de versions mis à jour"
 
msgid "Error occurred while updating application settings"
 
msgstr ""
 
"Une erreur est survenue durant la mise à jour des réglages de "
 
"l'application"
 

	
 
msgid "Repositories successfully rescanned. Added: %s. Removed: %s."
 
msgstr "Dépôts ré-analysés avec succès. Ajouté: %s. Supprimé: %s."
 
msgstr "Dépôts ré-analysés avec succès. Ajouté : %s. Supprimé : %s."
 

	
 
msgid "Invalidated %s repositories"
 
msgstr "%s dépôts invalidés"
 

	
 
msgid "Updated application settings"
 
msgstr "Réglages mis à jour"
 
@@ -587,14 +587,14 @@ msgid "Hook already exists"
 
msgstr "Le hook existe déjà"
 

	
 
msgid ""
 
"Hook names with \".kallithea_\" are reserved for internal use. Please use "
 
"another hook name."
 
msgstr ""
 
"Les noms de hook avec \".kallithea_\" sont réservés pour un usage interne. "
 
"Merci de choisir un autre nom pour le hook."
 
"Les noms de hook avec \".kallithea_\" sont réservés pour un usage "
 
"interne. Merci de choisir un autre nom pour le hook."
 

	
 
msgid "Added new hook"
 
msgstr "Le nouveau hook a été ajouté"
 

	
 
msgid "Updated hooks"
 
msgstr "Hooks mis à jour"
 
@@ -676,13 +676,13 @@ msgid "Binary file"
 
msgstr "Fichier binaire"
 

	
 
msgid ""
 
"Changeset was too big and was cut off, use diff menu to display this diff"
 
msgstr ""
 
"Cet ensemble de changements était trop gros pour être affiché et a été "
 
"découpé, utilisez le menu « diff » pour afficher les différences"
 
"découpé, utilisez le menu « diff » pour afficher les différences"
 

	
 
msgid "No changes detected"
 
msgstr "Aucun changement détecté"
 

	
 
msgid "Show whitespace changes"
 
msgstr "Afficher les modifications d'espaces et de tabulations"
 
@@ -691,16 +691,16 @@ msgid "Ignore whitespace changes"
 
msgstr "Ignorer les modifications d'espaces et de tabulations"
 

	
 
msgid "Increase diff context to %(num)s lines"
 
msgstr "Augmenter le contexte du diff à %(num)s lignes"
 

	
 
msgid "Deleted branch: %s"
 
msgstr "Branche supprimée: %s"
 
msgstr "Branche supprimée : %s"
 

	
 
msgid "Created tag: %s"
 
msgstr "Étiquette créée: %s"
 
msgstr "Étiquette créée : %s"
 

	
 
msgid "Changeset %s not found"
 
msgstr "Ensemble de changements %s non trouvé"
 

	
 
msgid "Show all combined changesets %s->%s"
 
msgstr "Afficher les changements combinés %s->%s"
 
@@ -838,24 +838,25 @@ msgstr ""
 
"taille partielle)"
 

	
 
msgid ""
 
"Invalid SSH key - base64 part %r seems truncated (it is too short for "
 
"declared string length %s)"
 
msgstr ""
 
"Clé SSH invalide – la partie base64 %r semble tronquée (elle est trop court "
 
"pour la taille déclarée %s)"
 
"Clé SSH invalide – la partie base64 %r semble tronquée (elle est trop "
 
"court pour la taille déclarée %s)"
 

	
 
msgid ""
 
"Invalid SSH key - base64 part %r seems truncated (it contains too few "
 
"strings for a %s key)"
 
msgstr ""
 
"Clé SSH invalide – la partie base64 %r semble tronquée (elle ne contient pas "
 
"assez de parties pour une clé %s)"
 
"Clé SSH invalide – la partie base64 %r semble tronquée (elle ne contient "
 
"pas assez de parties pour une clé %s)"
 

	
 
msgid "Invalid SSH key - it is a %s key but the base64 part contains %r"
 
msgstr "Clé SSH invalide – c'est une clé %s mais la partie base64 contient %r"
 
msgstr ""
 
"Clé SSH invalide – c'est une clé %s mais la partie base64 contient %r"
 

	
 
msgid "%d year"
 
msgid_plural "%d years"
 
msgstr[0] "%d an"
 
msgstr[1] "%d ans"
 

	
 
@@ -1016,30 +1017,30 @@ msgid "Name must not contain only digits
 
msgstr "Le nom ne doit pas contenir seulement des chiffres"
 

	
 
msgid ""
 
"[Comment] %(repo_name)s changeset %(short_id)s \"%(message_short)s\" on "
 
"%(branch)s by %(cs_author_username)s"
 
msgstr ""
 
"[Commentaire] Changeset %(short_id)s « %(message_short)s » de %(repo_name)s "
 
"dans %(branch)s par %(cs_author_username)s"
 
"[Commentaire] Changeset %(short_id)s « %(message_short)s » de "
 
"%(repo_name)s dans %(branch)s par %(cs_author_username)s"
 

	
 
msgid "New user %(new_username)s registered"
 
msgstr "Nouvel utilisateur %(new_username)s enregistré"
 

	
 
msgid ""
 
"[Review] %(repo_name)s PR %(pr_nice_id)s \"%(pr_title_short)s\" from "
 
"%(pr_source_branch)s by %(pr_owner_username)s"
 
msgstr ""
 
"[Revue] %(repo_name)s PR %(pr_nice_id)s « %(pr_title_short)s » depuis "
 
"[Revue] %(repo_name)s PR %(pr_nice_id)s « %(pr_title_short)s » depuis "
 
"%(pr_source_branch)s par %(pr_owner_username)s"
 

	
 
msgid ""
 
"[Comment] %(repo_name)s PR %(pr_nice_id)s \"%(pr_title_short)s\" from "
 
"%(pr_source_branch)s by %(pr_owner_username)s"
 
msgstr ""
 
"[Commentaire] %(repo_name)s PR %(pr_nice_id)s « %(pr_title_short)s » "
 
"[Commentaire] %(repo_name)s PR %(pr_nice_id)s « %(pr_title_short)s » "
 
"depuis %(pr_source_branch)s par %(pr_owner_username)s"
 

	
 
msgid "Closing"
 
msgstr "Fermeture"
 

	
 
msgid "Cannot create empty pull request"
 
@@ -1053,16 +1054,16 @@ msgstr ""
 
"de fusionner une révision plus vieille de %s vers %s"
 

	
 
msgid "You are not authorized to create the pull request"
 
msgstr "Vous n'êtes pas autorisé à créer cette requête de pull"
 

	
 
msgid "Missing changesets since the previous iteration:"
 
msgstr "Changeset manquant depuis la précédente itération :"
 
msgstr "Changeset manquant depuis la précédente itération :"
 

	
 
msgid "New changesets on %s %s since the previous iteration:"
 
msgstr "Nouveau changeset sur %s %s depuis la précédente itération :"
 
msgstr "Nouveau changeset sur %s %s depuis la précédente itération :"
 

	
 
msgid "Ancestor didn't change - diff since previous iteration:"
 
msgstr "L'ancêtre n'a pas changé - diff depuis l'itération précédente :"
 

	
 
msgid ""
 
"This iteration is based on another %s revision and there is no simple "
 
@@ -1078,45 +1079,45 @@ msgid "Closed, next iteration: %s ."
 
msgstr "Fermé, itération suivante : %s."
 

	
 
msgid "latest tip"
 
msgstr "Dernier sommet"
 

	
 
msgid "SSH key %r is invalid: %s"
 
msgstr "La clé SSH %r est invalide : %s"
 
msgstr "La clé SSH %r est invalide : %s"
 

	
 
msgid "SSH key %s is already used by %s"
 
msgstr "La clé SSH %s est déjà utilisée par %s"
 

	
 
msgid "SSH key with fingerprint %r found"
 
msgstr "Clé SSH avec l'empreinte %r trouvée"
 

	
 
msgid ""
 
"You can't remove this user since it is crucial for the entire application"
 
msgstr ""
 
"Vous ne pouvez pas supprimer cet utilisateur ; il est nécessaire pour le "
 
"Vous ne pouvez pas supprimer cet utilisateur ; il est nécessaire pour le "
 
"bon fonctionnement de l’application"
 

	
 
msgid ""
 
"User \"%s\" still owns %s repositories and cannot be removed. Switch "
 
"owners or remove those repositories: %s"
 
msgstr ""
 
"L’utilisateur \"%s\" possède %s dépôts et ne peut être supprimé. Changez "
 
"les propriétaires ou supprimez ces dépôts : %s"
 
"les propriétaires ou supprimez ces dépôts : %s"
 

	
 
msgid ""
 
"User \"%s\" still owns %s repository groups and cannot be removed. Switch "
 
"owners or remove those repository groups: %s"
 
msgstr ""
 
"L’utilisateur \"%s\" possède %s groupes de dépôt et ne peut être "
 
"supprimé. Changez les propriétaires ou supprimez ces dépôts : %s"
 
"supprimé. Changez les propriétaires ou supprimez ces dépôts : %s"
 

	
 
msgid ""
 
"User \"%s\" still owns %s user groups and cannot be removed. Switch "
 
"owners or remove those user groups: %s"
 
msgstr ""
 
"L’utilisateur « %s » possède %s groupes d'utilisateurs et ne peut pas "
 
"L’utilisateur « %s » possède %s groupes d'utilisateurs et ne peut pas "
 
"être supprimé. Changez les propriétaires de ces groupes d'utilisateurs ou "
 
"supprimez-les : %s"
 

	
 
msgid "Password reset link"
 
msgstr "Lien de remise à zéro du mot de passe"
 

	
 
@@ -1131,16 +1132,16 @@ msgstr ""
 
"réinitialisation du mot de passe."
 

	
 
msgid "Value cannot be an empty list"
 
msgstr "Cette valeur ne peut être une liste vide"
 

	
 
msgid "Username \"%(username)s\" already exists"
 
msgstr "Le nom d’utilisateur « %(username)s » existe déjà"
 
msgstr "Le nom d’utilisateur « %(username)s » existe déjà"
 

	
 
msgid "Username \"%(username)s\" cannot be used"
 
msgstr "Le nom d’utilisateur « %(username)s » n’est pas valide"
 
msgstr "Le nom d’utilisateur « %(username)s » n’est pas valide"
 

	
 
msgid ""
 
"Username may only contain alphanumeric characters underscores, periods or "
 
"dashes and must begin with an alphanumeric character or underscore"
 
msgstr ""
 
"Le nom d'utilisateur ne peut contenir que des caractères alphanumériques, "
 
@@ -1148,19 +1149,19 @@ msgstr ""
 
"caractère alphanumérique ou un underscore"
 

	
 
msgid "The input is not valid"
 
msgstr "L'entrée n'est pas valide"
 

	
 
msgid "Username %(username)s is not valid"
 
msgstr "Le nom d’utilisateur « %(username)s » n’est pas valide"
 
msgstr "Le nom d’utilisateur « %(username)s » n’est pas valide"
 

	
 
msgid "Invalid user group name"
 
msgstr "Nom de groupe d'utilisateurs invalide"
 

	
 
msgid "User group \"%(usergroup)s\" already exists"
 
msgstr "Le groupe d'utilisateurs «%(usergroup)s» existe déjà"
 
msgstr "Le groupe d'utilisateurs « %(usergroup)s » existe déjà"
 

	
 
msgid ""
 
"user group name may only contain alphanumeric characters underscores, "
 
"periods or dashes and must begin with alphanumeric character"
 
msgstr ""
 
"Le nom du groupe d'utilisateurs ne peut contenir que des caractères "
 
@@ -1168,16 +1169,16 @@ msgstr ""
 
"commencer avec un caractère alphanumérique"
 

	
 
msgid "Cannot assign this group as parent"
 
msgstr "Impossible d’assigner ce groupe en tant que parent"
 

	
 
msgid "Group \"%(group_name)s\" already exists"
 
msgstr "Le groupe « %(group_name)s » existe déjà"
 
msgstr "Le groupe « %(group_name)s » existe déjà"
 

	
 
msgid "Repository with name \"%(group_name)s\" already exists"
 
msgstr "Un dépôt portant le nom « %(group_name)s » existe déjà"
 
msgstr "Un dépôt portant le nom « %(group_name)s » existe déjà"
 

	
 
msgid "Invalid characters (non-ascii) in password"
 
msgstr "Caractères incorrects (non-ASCII) dans le mot de passe"
 

	
 
msgid "Invalid old password"
 
msgstr "Ancien mot de passe invalide"
 
@@ -1186,29 +1187,30 @@ msgid "Passwords do not match"
 
msgstr "Les mots de passe ne correspondent pas"
 

	
 
msgid "Invalid username or password"
 
msgstr "Nom d'utilisateur ou mot de passe invalide"
 

	
 
msgid "Repository name %(repo)s is not allowed"
 
msgstr "Le nom de dépôt « %(repo)s » n’est pas autorisé"
 
msgstr "Le nom de dépôt « %(repo)s » n’est pas autorisé"
 

	
 
msgid "Repository named %(repo)s already exists"
 
msgstr "Un dépôt portant le nom « %(repo)s » existe déjà"
 
msgstr "Un dépôt portant le nom « %(repo)s » existe déjà"
 

	
 
msgid "Repository \"%(repo)s\" already exists in group \"%(group)s\""
 
msgstr "Le dépôt « %(repo)s » existe déjà dans le groupe « %(group)s »"
 
msgstr "Le dépôt « %(repo)s » existe déjà dans le groupe « %(group)s »"
 

	
 
msgid "Repository group with name \"%(repo)s\" already exists"
 
msgstr "Un groupe de dépôts avec le nom «%(repo)s» existe déjà"
 
msgstr "Un groupe de dépôts avec le nom « %(repo)s » existe déjà"
 

	
 
msgid "Invalid repository URL"
 
msgstr "URL de dépôt invalide"
 

	
 
msgid "Invalid repository URL. It must be a valid http, https, or ssh URL"
 
msgstr ""
 
"URL de dépôt invalide. Ce doit être une URL valide de type http, https ou ssh"
 
"URL de dépôt invalide. Ce doit être une URL valide de type http, https ou "
 
"ssh"
 

	
 
msgid "Fork has to be the same type as parent"
 
msgstr "Le fork doit être du même type que le parent"
 

	
 
msgid "You don't have permissions to create repository in this group"
 
msgstr "Vous n’avez pas la permission de créer un dépôt dans ce"
 
@@ -1228,13 +1230,13 @@ msgid "This is not a valid path"
 
msgstr "Ceci n’est pas un chemin valide"
 

	
 
msgid "This email address is already in use"
 
msgstr "Cette adresse e-mail est déjà enregistrée"
 

	
 
msgid "Email address \"%(email)s\" not found"
 
msgstr "L’adresse e-mail « %(email)s » n’existe pas"
 
msgstr "L’adresse e-mail « %(email)s » n’existe pas"
 

	
 
msgid ""
 
"The LDAP Login attribute of the CN must be specified - this is the name "
 
"of the attribute that is equivalent to \"username\""
 
msgstr ""
 
"L’attribut Login du CN doit être spécifié. Cet attribut correspond au nom "
 
@@ -1305,16 +1307,16 @@ msgid "Password"
 
msgstr "Mot de passe"
 

	
 
msgid "Stay logged in after browser restart"
 
msgstr "Rester connecté après un redémarrage du navigateur"
 

	
 
msgid "Forgot your password?"
 
msgstr "Mot de passe oublié?"
 
msgstr "Mot de passe oublié ?"
 

	
 
msgid "Don't have an account?"
 
msgstr "Vous n’avez pas de compte?"
 
msgstr "Vous n’avez pas de compte ?"
 

	
 
msgid "Sign In"
 
msgstr "Connexion"
 

	
 
msgid "Password Reset"
 
msgstr "Remettre le mot de passe à zéro"
 
@@ -1593,19 +1595,19 @@ msgid "Show Permissions"
 
msgstr "Afficher les permissions"
 

	
 
msgid "Built-in"
 
msgstr "Inclus"
 

	
 
msgid "Confirm to reset this API key: %s"
 
msgstr "Confirmer la remise à zéro de cette clé d'API: %s"
 
msgstr "Confirmer la remise à zéro de cette clé d'API : %s"
 

	
 
msgid "Expired"
 
msgstr "a expiré"
 

	
 
msgid "Confirm to remove this API key: %s"
 
msgstr "Confirmer la suppression de cette clé d'API: %s"
 
msgstr "Confirmer la suppression de cette clé d'API : %s"
 

	
 
msgid "Remove"
 
msgstr "Supprimer"
 

	
 
msgid "No additional API keys specified"
 
msgstr "Pas de clés d'API supplémentaires spécifiées"
 
@@ -1642,13 +1644,13 @@ msgstr ""
 
"puisse plus être utilisée.\n"
 

	
 
msgid "Primary"
 
msgstr "Primaire"
 

	
 
msgid "Confirm to delete this email: %s"
 
msgstr "Veuillez confirmer la suppression de l’e-mail : %s"
 
msgstr "Veuillez confirmer la suppression de l’e-mail : %s"
 

	
 
msgid "No additional emails specified."
 
msgstr "Pas d'adresse e-mail supplémentaires spécifiées."
 

	
 
msgid "New email address"
 
msgstr "Nouvelle adresse e-mail"
 
@@ -1692,13 +1694,13 @@ msgid "Fingerprint"
 
msgstr "Empreinte"
 

	
 
msgid "Last Used"
 
msgstr "Dernière utilisation"
 

	
 
msgid "Confirm to remove this SSH key: %s"
 
msgstr "Confirmer la suppression de cette clé SSH: %s"
 
msgstr "Confirmer la suppression de cette clé SSH : %s"
 

	
 
msgid "No SSH keys have been added"
 
msgstr "Aucune clé SSH n'a été ajoutée"
 

	
 
msgid "New SSH key"
 
msgstr "Nouvelle clé SSH"
 
@@ -1818,13 +1820,13 @@ msgid "Registration"
 
msgstr "Enregistrement"
 

	
 
msgid "External auth account activation"
 
msgstr "Activation de l'authentification externe"
 

	
 
msgid "Confirm to delete this IP address: %s"
 
msgstr "Confirmer la suppression de cette adresse IP: %s"
 
msgstr "Confirmer la suppression de cette adresse IP : %s"
 

	
 
msgid "All IP addresses are allowed."
 
msgstr "Toutes les adresses IP sont autorisées."
 

	
 
msgid "New IP address"
 
msgstr "Nouvelle adresse IP"
 
@@ -1857,13 +1859,13 @@ msgid "Advanced"
 
msgstr "Avancé"
 

	
 
msgid "Permissions"
 
msgstr "Permissions"
 

	
 
msgid "Repository Group: %s"
 
msgstr "Groupe de dépôts: %s"
 
msgstr "Groupe de dépôts : %s"
 

	
 
msgid "Top level repositories"
 
msgstr "Dépôts de niveau supérieur"
 

	
 
msgid "Total repositories"
 
msgstr "Dépôts totaux"
 
@@ -1873,14 +1875,14 @@ msgstr "Groupes enfants"
 

	
 
msgid "Created on"
 
msgstr "Créé le"
 

	
 
msgid "Confirm to delete this group: %s with %s repository"
 
msgid_plural "Confirm to delete this group: %s with %s repositories"
 
msgstr[0] "Confirmer la suppression de ce groupe : %s avec %s dépôt"
 
msgstr[1] "Confirmer la suppression de ce groupe : %s avec %s dépôts"
 
msgstr[0] "Confirmer la suppression de ce groupe : %s avec %s dépôt"
 
msgstr[1] "Confirmer la suppression de ce groupe : %s avec %s dépôts"
 

	
 
msgid "Delete this repository group"
 
msgstr "Supprimer ce groupe de dépôts"
 

	
 
msgid "Not visible"
 
msgstr "Non visible"
 
@@ -1916,12 +1918,15 @@ msgid ""
 
"Set or revoke permission to all children of that group, including non-"
 
"private repositories and other groups if selected."
 
msgstr ""
 
"Ajouter ou révoquer la permission pour tous les enfants de ce groupe, y "
 
"compris les dépôts non-privés et les autres groupes si sélectionné."
 

	
 
msgid "Type name of user"
 
msgstr "Saisir le nom de l'utilisateur"
 

	
 
msgid "Remove this group"
 
msgstr "Supprimer ce groupe"
 

	
 
msgid "Confirm to delete this group"
 
msgstr "Confirmer la suppression de ce groupe"
 

	
 
@@ -1931,15 +1936,21 @@ msgstr "Groupe de dépôts %s"
 
msgid "Repository Groups Administration"
 
msgstr "Administration des groupes de dépôts"
 

	
 
msgid "Number of Top-level Repositories"
 
msgstr "Nombre de dépôts de niveau supérieur"
 

	
 
msgid "Type of repository to create."
 
msgstr "Type de dépôt à créer."
 

	
 
msgid "Clone remote repository"
 
msgstr "Cloner le dépôt distant"
 

	
 
msgid "Repository URL"
 
msgstr "URL du dépôt"
 

	
 
msgid ""
 
"Optional: URL of a remote repository. If set, the repository will be "
 
"created as a clone from this URL."
 
msgstr ""
 
"Optionnel : URL d'un dépôt distant. Si renseigné, le dépôt sera créé "
 
"comme un clone à partir de cette URL."
 
@@ -1950,15 +1961,12 @@ msgstr ""
 
"Gardez cette description précise et concise. Utilisez un fichier README "
 
"pour des descriptions plus détaillées."
 

	
 
msgid "Optionally select a group to put this repository into."
 
msgstr "Sélectionnez un groupe (optionel) dans lequel sera placé le dépôt."
 

	
 
msgid "Type of repository to create."
 
msgstr "Type de dépôt à créer."
 

	
 
msgid "Landing revision"
 
msgstr "Révision d’arrivée"
 

	
 
msgid ""
 
"Default revision for files page, downloads, full text search index and "
 
"readme generation"
 
@@ -1974,13 +1982,13 @@ msgid "Creating repository"
 
msgstr "Création du dépôt"
 

	
 
msgid ""
 
"Repository \"%(repo_name)s\" is being created, you will be redirected "
 
"when this process is finished.repo_name"
 
msgstr ""
 
"Le dépôt « %(repo_name)s » est en cours de création, vous allez être "
 
"Le dépôt « %(repo_name)s » est en cours de création, vous allez être "
 
"redirigé quand cette opération sera terminée."
 

	
 
msgid ""
 
"We're sorry but error occurred during this operation. Please check your "
 
"Kallithea server logs, or contact administrator."
 
msgstr ""
 
@@ -2097,49 +2105,22 @@ msgstr ""
 
msgid "This repository does not have a remote repository URL."
 
msgstr "Ce dépôt n'a pas d'URL de dépôt distant."
 

	
 
msgid "Permanent URL"
 
msgstr "URL permanente"
 

	
 
msgid ""
 
"In case this repository is renamed or moved into another group the "
 
"repository URL changes.\n"
 
"                               Using the above permanent URL guarantees "
 
"that this repository always will be accessible on that URL.\n"
 
"                               This is useful for CI systems, or any "
 
"other cases that you need to hardcode the URL into a 3rd party service."
 
msgstr ""
 
"Si ce dépôt est renommé ou déplacé dans un autre groupe, l'URL du dépôt "
 
"change.\n"
 
"                               L'utilisation de l'URL permanente ci-"
 
"dessus garantit que ce dépôt sera toujours accessible via cette URL.\n"
 
"                               Cela peut être utile pour les systèmes "
 
"d'intégration continue, ou dans tous les cas où vous devez saisir l'URL "
 
"« en dur » dans un service tiers."
 

	
 
msgid "Remote repository"
 
msgstr "Dépôt distant"
 

	
 
msgid "Repository URL"
 
msgstr "URL du dépôt"
 

	
 
msgid ""
 
"Optional: URL of a remote repository. If set, the repository can be "
 
"pulled from this URL."
 
msgstr ""
 
"Optionel : URL d'un dépôt distant. Si renseigné, le dépôt sera pullé à "
 
"partir de cette URL."
 

	
 
msgid "Default revision for files page, downloads, whoosh and readme"
 
msgstr ""
 
"Révision par défaut pour les pages de fichiers, de téléchargements, de "
 
"recherche et de documentation"
 

	
 
msgid "Type name of user"
 
msgstr "Saisir le nom de l'utilisateur"
 

	
 
msgid "Change owner of this repository."
 
msgstr "Changer le propriétaire de ce dépôt."
 

	
 
msgid "Processed commits"
 
msgstr "Commits traités"
 

	
 
@@ -2245,15 +2226,15 @@ msgstr "Git Hooks"
 

	
 
msgid ""
 
"Kallithea has no support for custom Git hooks. Kallithea will use Git "
 
"post-receive hooks internally. Installation of these hooks is managed in "
 
"%s."
 
msgstr ""
 
"Kallithea ne supporte pas les hooks Git personnalisés. Kallithea utilise des "
 
"hooks Git de post-réception en interne. L'installation de ces hooks est "
 
"gérée dans %s."
 
"Kallithea ne supporte pas les hooks Git personnalisés. Kallithea utilise "
 
"des hooks Git de post-réception en interne. L'installation de ces hooks "
 
"est gérée dans %s."
 

	
 
msgid "Custom Hooks are not enabled"
 
msgstr "Les Hooks personnalisés ne sont pas activés"
 

	
 
msgid "Failed to remove hook"
 
msgstr "Erreur lors de la suppression du hook"
 
@@ -2296,15 +2277,15 @@ msgstr "Installer et surcharger des hook
 

	
 
msgid ""
 
"Install Kallithea's internal hooks for all Git repositories. Existing "
 
"hooks that don't seem to come from Kallithea will be disabled by renaming "
 
"to .bak extension."
 
msgstr ""
 
"Installe les hooks internes de Kallithea pour tous les dépôts Git. Les hooks "
 
"existants qui ne semblent pas être livrés avec Kallithea seront désactivés "
 
"en les renommant avec l'extension .bak."
 
"Installe les hooks internes de Kallithea pour tous les dépôts Git. Les "
 
"hooks existants qui ne semblent pas être livrés avec Kallithea seront "
 
"désactivés en les renommant avec l'extension .bak."
 

	
 
msgid "Rescan Repositories"
 
msgstr "Relancer le scan des dépôts"
 

	
 
msgid "Index build option"
 
msgstr "Option de construction de l'index"
 
@@ -2449,28 +2430,29 @@ msgid ""
 
"                                                    {system_user}  name "
 
"of the Kallithea system user,\n"
 
"                                                    {hostname}  server "
 
"hostname\n"
 
"                                                    "
 
msgstr ""
 
"Modèle de construction d'URL de clone. Par exemple : "
 
"'{scheme}://{user}@{netloc}/{repo}'.\n"
 
"Modèle de construction d'URL de clone. Par exemple : '{scheme}://{user}"
 
"@{netloc}/{repo}'.\n"
 
"                                                       Les variables "
 
"suivantes sont disponibles :\n"
 
"                                                        {scheme}    'http' "
 
"ou 'https' envoyé à partir du serveur Kallithea en cours d'utilisation,\n"
 
"                                                        {scheme}    "
 
"'http' ou 'https' envoyé à partir du serveur Kallithea en cours "
 
"d'utilisation,\n"
 
"                                                        {user}     nom de "
 
"l'utilisateur courant,\n"
 
"                                                        {netloc}    "
 
"emplacement réseau/hôte du serveur Kallithea en cours d'utilisation,\n"
 
"                                                        {repo}    nom "
 
"complet du dépôt,\n"
 
"                                                        {repoid}    ID du "
 
"dépôt, peut être utilisé pour cloner par ID,\n"
 
"                                                        {system_user}  nom "
 
"de l'utilisateur système Kallithea,\n"
 
"                                                        {system_user}  "
 
"nom de l'utilisateur système Kallithea,\n"
 
"                                                        {hostname}  nom "
 
"d'hôte du serveur\n"
 
"                                                    "
 

	
 
msgid ""
 
"Schema for constructing SSH clone URL, eg. 'ssh://{system_user}"
 
@@ -2506,13 +2488,13 @@ msgid "Show public repository icon on re
 
msgstr "Afficher l’icône de dépôt public sur les dépôts"
 

	
 
msgid "Show private repository icon on repositories"
 
msgstr "Afficher l’icône de dépôt privé sur les dépôts"
 

	
 
msgid "Show public/private icons next to repository names."
 
msgstr "Afficher l’icône « public/privé » à côté du nom des dépôts."
 
msgstr "Afficher l’icône « public/privé » à côté du nom des dépôts."
 

	
 
msgid "Meta Tagging"
 
msgstr "Meta-tagging"
 

	
 
msgid ""
 
"Parses meta tags from the repository description field and turns them "
 
@@ -2597,13 +2579,13 @@ msgid "Last Login"
 
msgstr "Dernière connexion"
 

	
 
msgid "Member of User Groups"
 
msgstr "Membre des groupes d'utilisateurs"
 

	
 
msgid "Confirm to delete this user: %s"
 
msgstr "Voulez-vous vraiment supprimer l’utilisateur « %s » ?"
 
msgstr "Voulez-vous vraiment supprimer l’utilisateur « %s » ?"
 

	
 
msgid "Delete this user"
 
msgstr "Supprimer cet utilisateur"
 

	
 
msgid "Inherited from %s"
 
msgstr "Hérité de %s"
 
@@ -2711,13 +2693,13 @@ msgid "Not Logged In"
 
msgstr "Non connecté"
 

	
 
msgid "Login to Your Account"
 
msgstr "Connexion à votre compte"
 

	
 
msgid "Forgot password?"
 
msgstr "Mot de passe oublié?"
 
msgstr "Mot de passe oublié ?"
 

	
 
msgid "Log Out"
 
msgstr "Se déconnecter"
 

	
 
msgid "Parent rev."
 
msgstr "Révision parente"
 
@@ -2797,17 +2779,17 @@ msgstr "Recherche tronquée"
 
msgid "No matching files"
 
msgstr "Aucun fichier correspondant"
 

	
 
msgid "Open New Pull Request from {0}"
 
msgstr "Ouvrir une nouvelle requête de pull à partir de {0}"
 

	
 
msgid "Open New Pull Request for {0} &rarr; {1}"
 
msgstr "Ouvrir une nouvelle requête de pull pour {0} &rarr; {1}"
 

	
 
msgid "Show Selected Changesets {0} &rarr; {1}"
 
msgstr "Afficher les changesets sélectionnés {0} &rarr; {1}"
 
msgid "Open New Pull Request for {0}"
 
msgstr "Ouvrir une nouvelle requête de pull pour {0}"
 

	
 
msgid "Show Selected Changesets {0}"
 
msgstr "Afficher les changesets sélectionnés {0}"
 

	
 
msgid "Selection Link"
 
msgstr "Lien vers la sélection"
 

	
 
msgid "Collapse Diff"
 
msgstr "Replier le Diff"
 
@@ -3144,16 +3126,16 @@ msgid "Subscribe to %s atom feed"
 
msgstr "S’abonner au flux ATOM de %s"
 

	
 
msgid "Creating"
 
msgstr "En cours de création"
 

	
 
msgid "Mention in Comment on Changeset \"%s\""
 
msgstr "Mention dans le commentaire sur le changeset « %s »"
 
msgstr "Mention dans le commentaire sur le changeset « %s »"
 

	
 
msgid "Comment on Changeset \"%s\""
 
msgstr "Commentaire sur le changeset « %s »"
 
msgstr "Commentaire sur le changeset « %s »"
 

	
 
msgid "Changeset on"
 
msgstr "Changeset sur"
 

	
 
msgid "branch"
 
msgstr "branche"
 
@@ -3206,16 +3188,16 @@ msgid ""
 
"message."
 
msgstr ""
 
"Si vous n'avez pas demandé la réinitialisation de votre mot de passe, ne "
 
"tenez pas compte de ce message."
 

	
 
msgid "Mention on Pull Request %s \"%s\" by %s"
 
msgstr "Mention sur la requête de pull %s « %s » par %s"
 
msgstr "Mention sur la requête de pull %s « %s » par %s"
 

	
 
msgid "Added as Reviewer of Pull Request %s \"%s\" by %s"
 
msgstr "Ajouté comme relecteur de la requête de pull %s « %s » par %s"
 
msgstr "Ajouté comme relecteur de la requête de pull %s « %s » par %s"
 

	
 
msgid "Pull request"
 
msgstr "Requête de pull"
 

	
 
msgid "from"
 
msgstr "depuis"
 
@@ -3224,19 +3206,19 @@ msgid "to"
 
msgstr "vers"
 

	
 
msgid "View Pull Request"
 
msgstr "Afficher la requête de pull"
 

	
 
msgid "Mention in Comment on Pull Request %s \"%s\""
 
msgstr "Mention dans le commentaire sur la requête de pull %s « %s »"
 
msgstr "Mention dans le commentaire sur la requête de pull %s « %s »"
 

	
 
msgid "Pull Request %s \"%s\" Closed"
 
msgstr "Requête de pull %s « %s » fermée"
 
msgstr "Requête de pull %s « %s » fermée"
 

	
 
msgid "Comment on Pull Request %s \"%s\""
 
msgstr "Commentaire sur la requête de pull %s « %s »"
 
msgstr "Commentaire sur la requête de pull %s « %s »"
 

	
 
msgid "New User Registration"
 
msgstr "Nouvel enregistrement d'utilisateur"
 

	
 
msgid "Full Name"
 
msgstr "Nom complet"
 
@@ -3760,11 +3742,8 @@ msgstr "Ajouter ou téléverser des fichiers directement via Kallithea"
 
msgid "Push new repository"
 
msgstr "Pusher le nouveau dépôt"
 

	
 
msgid "Existing repository?"
 
msgstr "Le dépôt existe déjà ?"
 

	
 
msgid "Readme file from revision %s:%s"
 
msgstr "Fichier Lisez-moi de la revision %s:%s"
 

	
 
msgid "Download %s as %s"
 
msgstr "Télécharge %s comme %s"
kallithea/i18n/ja/LC_MESSAGES/kallithea.po
Show inline comments
 
@@ -1409,26 +1409,29 @@ msgstr "このグループを削除してもよろしいですか?: %s"
 
msgid "Repository Groups Administration"
 
msgstr "リポジトリグループ管理"
 

	
 
msgid "Number of Top-level Repositories"
 
msgstr "トップレベルリポジトリ数"
 

	
 
msgid "Type of repository to create."
 
msgstr "作成するリポジトリの種別を指定します"
 

	
 
msgid "Clone remote repository"
 
msgstr "リモートリポジトリをクローン"
 

	
 
msgid "Repository URL"
 
msgstr "リポジトリURL"
 

	
 
msgid ""
 
"Keep it short and to the point. Use a README file for longer descriptions."
 
msgstr ""
 
"短く要点を絞ってください。長い説明にはREADMEファイルを利用してください。"
 

	
 
msgid "Optionally select a group to put this repository into."
 
msgstr "オプション:このリポジトリが属するグループを選択します"
 

	
 
msgid "Type of repository to create."
 
msgstr "作成するリポジトリの種別を指定します"
 

	
 
msgid "Landing revision"
 
msgstr "ランディングリビジョン"
 

	
 
msgid ""
 
"Default revision for files page, downloads, full text search index and "
 
"readme generation"
 
@@ -1544,44 +1547,22 @@ msgstr "リモートリポジトリから変更を取り込む"
 
msgid "Confirm to pull changes from remote repository."
 
msgstr "リモートリポジトリから変更を取り込んでもよろしいですか?"
 

	
 
msgid "This repository does not have a remote repository URL."
 
msgstr "このリポジトリにリモートURLは設定されていません"
 

	
 
msgid ""
 
"In case this repository is renamed or moved into another group the "
 
"repository URL changes.\n"
 
"                               Using the above permanent URL guarantees "
 
"that this repository always will be accessible on that URL.\n"
 
"                               This is useful for CI systems, or any "
 
"other cases that you need to hardcode the URL into a 3rd party service."
 
msgstr ""
 
"通常、リポジトリの名前を変更したり、別のグループに移動すると、リポジトリの"
 
"URLが変わります。\n"
 
"上のURLを使えば、常にリポジトリにアクセスできます。\n"
 
"この機能は、CIを使っている場合や、3rd pirtyのサービス向けにURLを固定化した"
 
"いときに便利です。"
 

	
 
msgid "Remote repository"
 
msgstr "リモートリポジトリ"
 

	
 
msgid "Repository URL"
 
msgstr "リポジトリURL"
 

	
 
msgid ""
 
"Optional: URL of a remote repository. If set, the repository can be "
 
"pulled from this URL."
 
msgstr ""
 
"オプション: リモートリポジトリのURLです。設定した場合、このURLから変更を取"
 
"得することができます。"
 

	
 
msgid "Default revision for files page, downloads, whoosh and readme"
 
msgstr ""
 
"ファイルページ、ダウンロード、検索、READMEのデフォルトのリビジョンを指定し"
 
"ます"
 

	
 
msgid "Change owner of this repository."
 
msgstr "リポジトリの所有者を変更"
 

	
 
msgid "Processed commits"
 
msgstr "処理済みコミット数"
 

	
 
@@ -2054,16 +2035,16 @@ msgstr "検索結果は省略されています"
 
msgid "No matching files"
 
msgstr "マッチするファイルはありません"
 

	
 
msgid "Open New Pull Request from {0}"
 
msgstr "新しいプルリクエストを{0}から作成"
 

	
 
msgid "Open New Pull Request for {0} &rarr; {1}"
 
msgid "Open New Pull Request for {0}"
 
msgstr "{0} &rarr; {1}から新しいプルリクエストを作成する"
 

	
 
msgid "Show Selected Changesets {0} &rarr; {1}"
 
msgid "Show Selected Changesets {0}"
 
msgstr "選択したチェンジセット{0} &rarr; {0}を表示"
 

	
 
msgid "Collapse Diff"
 
msgstr "差分をたたむ"
 

	
 
msgid "Expand Diff"
 
@@ -2754,11 +2735,8 @@ msgstr "Kallithea経由で直接ファイルを追加またはアップロード"
 
msgid "Push new repository"
 
msgstr "新しいリポジトリをプッシュ"
 

	
 
msgid "Existing repository?"
 
msgstr "存在するリポジトリをプッシュ"
 

	
 
msgid "Readme file from revision %s:%s"
 
msgstr "リビジョン %s:%s の README ファイル"
 

	
 
msgid "Download %s as %s"
 
msgstr "%s を %s でダウンロード"
kallithea/i18n/nb_NO/LC_MESSAGES/kallithea.po
Show inline comments
 
@@ -893,12 +893,15 @@ msgstr "Tilbakekall"
 
msgid "Add new"
 
msgstr "Legg til ny"
 

	
 
msgid "Both"
 
msgstr "Begge"
 

	
 
msgid "Type name of user"
 
msgstr "Skriv inn brukerens navn"
 

	
 
msgid "Remove this group"
 
msgstr "Fjern denne gruppen"
 

	
 
msgid "Confirm to delete this group"
 
msgstr "Bekreft sletting av denne gruppen"
 

	
 
@@ -952,15 +955,12 @@ msgstr "Ekstra felter avskrudd."
 
msgid "Private Repository"
 
msgstr "Privat pakkebrønn"
 

	
 
msgid "Fork of repository"
 
msgstr "Forgrening av pakkebrønn"
 

	
 
msgid "Type name of user"
 
msgstr "Skriv inn brukerens navn"
 

	
 
msgid "Processed commits"
 
msgstr "Behandlede innsendelser"
 

	
 
msgid "Processed progress"
 
msgstr "Behandlingsframdrift"
 

	
kallithea/i18n/nl_BE/LC_MESSAGES/kallithea.po
Show inline comments
 
@@ -212,14 +212,14 @@ msgstr "Fork van repository"
 
msgid "Repository page size"
 
msgstr "Repository paginagrootte"
 

	
 
msgid "Open New Pull Request from {0}"
 
msgstr "Open nieuwe pull request vanaf {0}"
 

	
 
msgid "Show Selected Changesets {0} &rarr; {1}"
 
msgstr "Toon geselecteerde changesets {0} &rarr; {1}"
 
msgid "Show Selected Changesets {0}"
 
msgstr "Toon geselecteerde changesets {0}"
 

	
 
msgid "Select changeset"
 
msgstr "Selecteer een changeset"
 

	
 
msgid "Specify changeset"
 
msgstr "Specifieer een changeset"
kallithea/i18n/pl/LC_MESSAGES/kallithea.po
Show inline comments
 
@@ -5,14 +5,14 @@ msgid ""
 
msgstr ""
 
"Report-Msgid-Bugs-To: translations@kallithea-scm.org\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"
 
"Plural-Forms: nplurals=3; plural=n==1 ? 0 : n%10>=2 && n%10<=4 && "
 
"(n%100<10 || n%100>=20) ? 1 : 2;\n"
 

	
 
msgid ""
 
"CSRF token leak has been detected - all form tokens have been expired"
 
msgstr ""
 
"Wykryto wyciek tokenu CSRF — wszystkie tokeny formularza zostały "
 
"unieważnione"
 
@@ -173,12 +173,15 @@ msgstr "Twój link zresetowania hasła został wysłany"
 
msgid "Invalid password reset token"
 
msgstr "Nieprawidłowy token resetowania hasła"
 

	
 
msgid "Successfully updated password"
 
msgstr "Pomyślnie zaktualizowano hasło"
 

	
 
msgid "Invalid reviewer \"%s\" specified"
 
msgstr "Podano nieprawidłowego recenzenta \"%s\""
 

	
 
msgid "%s (closed)"
 
msgstr "%s (zamknięty)"
 

	
 
msgid "Changeset"
 
msgstr "Grupy zmian"
 

	
 
@@ -1503,23 +1506,23 @@ msgstr "Usuń tę grupę repozytoriów"
 
msgid "Add new"
 
msgstr "Dodaj nowe"
 

	
 
msgid "Both"
 
msgstr "Oba"
 

	
 
msgid "Type of repository to create."
 
msgstr "Rodzaj repozytorium do stworzenia."
 

	
 
msgid ""
 
"Keep it short and to the point. Use a README file for longer descriptions."
 
msgstr ""
 
"Powinna być krótka i na temat. Użyj pliku README dla dłuższych opisów."
 

	
 
msgid "Optionally select a group to put this repository into."
 
msgstr "Opcjonalnie wybierz grupę do wprowadzenia tego repozytorium."
 

	
 
msgid "Type of repository to create."
 
msgstr "Rodzaj repozytorium do stworzenia."
 

	
 
msgid "Landing revision"
 
msgstr "Docelowa rewizja"
 

	
 
msgid "Remote"
 
msgstr "Zdalnie"
 

	
 
@@ -1559,15 +1562,12 @@ msgstr "Wpisz krótką etykietę"
 
msgid "New field description"
 
msgstr "Nowy opis pola"
 

	
 
msgid "Enter description of a field"
 
msgstr "Wprowadź opis pola"
 

	
 
msgid "Default revision for files page, downloads, whoosh and readme"
 
msgstr "Wersja domyślna dla plików stronicowania, pobierania plików, readme"
 

	
 
msgid "Change owner of this repository."
 
msgstr "Zmiana właściciela tego repozytorium."
 

	
 
msgid "Hooks"
 
msgstr "Aktualizacja"
 

	
kallithea/i18n/pt/LC_MESSAGES/kallithea.po
Show inline comments
 
@@ -886,24 +886,24 @@ msgid_plural "Confirm to delete this gro
 
msgstr[0] "Confirme para apagar este grupo: %s com %s repositório"
 
msgstr[1] "Confirme para apagar este grupo: %s com %s repositórios"
 

	
 
msgid "Add new"
 
msgstr "Adicionar novo"
 

	
 
msgid "Type of repository to create."
 
msgstr "Tipo de repositório a criar."
 

	
 
msgid ""
 
"Keep it short and to the point. Use a README file for longer descriptions."
 
msgstr ""
 
"Seja sucinto e objetivo. Use um ficheiro README para descrições mais "
 
"longas."
 

	
 
msgid "Optionally select a group to put this repository into."
 
msgstr "Opcionalmente selecione um grupo no qual pôr esse repositório."
 

	
 
msgid "Type of repository to create."
 
msgstr "Tipo de repositório a criar."
 

	
 
msgid "Landing revision"
 
msgstr "Revisão de pouso"
 

	
 
msgid "Remote"
 
msgstr "Remoto"
 

	
 
@@ -940,17 +940,12 @@ msgstr "Entre com o rótulo curto"
 
msgid "New field description"
 
msgstr "Nova descrição de campo"
 

	
 
msgid "Enter description of a field"
 
msgstr "Entre com a descrição de um campo"
 

	
 
msgid "Default revision for files page, downloads, whoosh and readme"
 
msgstr ""
 
"Revisão predefinida para página de ficheiros, descarregamentos, whoosh e "
 
"readme"
 

	
 
msgid "Change owner of this repository."
 
msgstr "Mudar o dono desse repositório."
 

	
 
msgid "Hooks"
 
msgstr "Ganchos"
 

	
kallithea/i18n/pt_BR/LC_MESSAGES/kallithea.po
Show inline comments
 
@@ -886,24 +886,24 @@ msgid_plural "Confirm to delete this gro
 
msgstr[0] "Confirme para excluir este grupo: %s com %s repositório"
 
msgstr[1] "Confirme para excluir este grupo: %s com %s repositórios"
 

	
 
msgid "Add new"
 
msgstr "Adicionar novo"
 

	
 
msgid "Type of repository to create."
 
msgstr "Tipo de repositório a criar."
 

	
 
msgid ""
 
"Keep it short and to the point. Use a README file for longer descriptions."
 
msgstr ""
 
"Seja sucinto e objetivo. Use um arquivo README para descrições mais "
 
"longas."
 

	
 
msgid "Optionally select a group to put this repository into."
 
msgstr "Opcionalmente selecione um grupo no qual colocar esse repositório."
 

	
 
msgid "Type of repository to create."
 
msgstr "Tipo de repositório a criar."
 

	
 
msgid "Landing revision"
 
msgstr "Revisão de pouso"
 

	
 
msgid "Remote"
 
msgstr "Remoto"
 

	
 
@@ -940,15 +940,12 @@ msgstr "Entre com o rótulo curto"
 
msgid "New field description"
 
msgstr "Nova descrição de campo"
 

	
 
msgid "Enter description of a field"
 
msgstr "Entre com a descrição de um campo"
 

	
 
msgid "Default revision for files page, downloads, whoosh and readme"
 
msgstr "Revisão padrão para página de arquivos, downloads, whoosh e readme"
 

	
 
msgid "Change owner of this repository."
 
msgstr "Mudar o dono desse repositório."
 

	
 
msgid "Hooks"
 
msgstr "Ganchos"
 

	
kallithea/i18n/ru/LC_MESSAGES/kallithea.po
Show inline comments
 
@@ -5,14 +5,14 @@ msgid ""
 
msgstr ""
 
"Report-Msgid-Bugs-To: translations@kallithea-scm.org\n"
 
"Language: ru\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%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
 
"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
 
"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
 
"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
 

	
 
msgid ""
 
"CSRF token leak has been detected - all form tokens have been expired"
 
msgstr "Обнаружена утечка CSRF-токена — истёк срок действия токенов форм"
 

	
 
msgid "Repository not found in the filesystem"
 
@@ -931,12 +931,19 @@ msgstr "Пожалуйста, введите пароль"
 
msgid "Enter %(min)i characters or more"
 
msgstr "Введите не менее %(min)i символов"
 

	
 
msgid "Name must not contain only digits"
 
msgstr "Имя не может состоять только из цифр"
 

	
 
msgid ""
 
"[Comment] %(repo_name)s changeset %(short_id)s \"%(message_short)s\" on "
 
"%(branch)s by %(cs_author_username)s"
 
msgstr ""
 
"[Комментарий] к набору изменений %(short_id)s «%(message_short)s» "
 
"репозитория %(repo_name)s в %(branch)s от %(cs_author_username)s"
 

	
 
msgid "New user %(new_username)s registered"
 
msgstr "Новый пользователь \"%(new_username)s\" зарегистрирован"
 

	
 
msgid ""
 
"[Review] %(repo_name)s PR %(pr_nice_id)s \"%(pr_title_short)s\" from "
 
"%(pr_source_branch)s by %(pr_owner_username)s"
 
@@ -1808,12 +1815,15 @@ msgid ""
 
"Set or revoke permission to all children of that group, including non-"
 
"private repositories and other groups if selected."
 
msgstr ""
 
"Установить или отозвать права всех дочерних элементов этой группы, "
 
"включая публичные репозитории и другие группы, если они выбраны."
 

	
 
msgid "Type name of user"
 
msgstr "Введите имя пользователя"
 

	
 
msgid "Remove this group"
 
msgstr "Удалить группу"
 

	
 
msgid "Confirm to delete this group"
 
msgstr "Подтвердите удаление этой группы пользователей"
 

	
 
@@ -1823,15 +1833,21 @@ msgstr "Группа репозиториев %s"
 
msgid "Repository Groups Administration"
 
msgstr "Администрирование групп репозиториев"
 

	
 
msgid "Number of Top-level Repositories"
 
msgstr "Число репозиториев верхнего уровня"
 

	
 
msgid "Type of repository to create."
 
msgstr "Тип создаваемого репозитория."
 

	
 
msgid "Clone remote repository"
 
msgstr "Клонировать удалённый репозиторий"
 

	
 
msgid "Repository URL"
 
msgstr "URL репозитория"
 

	
 
msgid ""
 
"Optional: URL of a remote repository. If set, the repository will be "
 
"created as a clone from this URL."
 
msgstr ""
 
"Опционально: URL удалённого репозитория. Если параметр задан, то будет "
 
"создан клон репозитория, расположенного по этому адресу."
 
@@ -1841,15 +1857,12 @@ msgid ""
 
msgstr ""
 
"Короткое и осмысленное. Для развернутого описания используйте файл README."
 

	
 
msgid "Optionally select a group to put this repository into."
 
msgstr "Опционально выбрать группу, в которую поместить данный репозиторий."
 

	
 
msgid "Type of repository to create."
 
msgstr "Тип создаваемого репозитория."
 

	
 
msgid "Landing revision"
 
msgstr "Ревизия для выгрузки"
 

	
 
msgid ""
 
"Default revision for files page, downloads, full text search index and "
 
"readme generation"
 
@@ -1986,48 +1999,22 @@ msgstr "Подтвердите применение изменений из удалённого репозитория."
 
msgid "This repository does not have a remote repository URL."
 
msgstr "Данный репозиторий не имеет URL удалённого репозитория."
 

	
 
msgid "Permanent URL"
 
msgstr "Постоянный URL"
 

	
 
msgid ""
 
"In case this repository is renamed or moved into another group the "
 
"repository URL changes.\n"
 
"                               Using the above permanent URL guarantees "
 
"that this repository always will be accessible on that URL.\n"
 
"                               This is useful for CI systems, or any "
 
"other cases that you need to hardcode the URL into a 3rd party service."
 
msgstr ""
 
"В случае, когда репозиторий переименовывается или перемещается в другую "
 
"группу, URL репозитория изменяется.\n"
 
"                               Использование постоянного URL гарантирует, "
 
"что данный репозиторий всегда будет доступен по этому URL.\n"
 
"                               Это может быть полезно в CI-системах, или "
 
"в любом другом случае, требующем встраивания URL в код ПО."
 

	
 
msgid "Remote repository"
 
msgstr "Удалённый репозиторий"
 

	
 
msgid "Repository URL"
 
msgstr "URL репозитория"
 

	
 
msgid ""
 
"Optional: URL of a remote repository. If set, the repository can be "
 
"pulled from this URL."
 
msgstr ""
 
"Опционально: URL удалённого репозитория. Если задан, то репозиторий можно "
 
"получить по заданному адресу."
 

	
 
msgid "Default revision for files page, downloads, whoosh and readme"
 
msgstr ""
 
"Ревизия по умолчанию, из которой будет производиться выгрузка файлов при "
 
"скачивании"
 

	
 
msgid "Type name of user"
 
msgstr "Введите имя пользователя"
 

	
 
msgid "Change owner of this repository."
 
msgstr "Изменить владельца репозитория."
 

	
 
msgid "Processed commits"
 
msgstr "Обработанные фиксации"
 

	
 
@@ -2642,17 +2629,17 @@ msgstr "Поиск усечен"
 
msgid "No matching files"
 
msgstr "Нет совпадений"
 

	
 
msgid "Open New Pull Request from {0}"
 
msgstr "Открыть новый pull-запрос от {0}"
 

	
 
msgid "Open New Pull Request for {0} &rarr; {1}"
 
msgstr "Открыть новый pull-запрос для {0} &rarr; {1}"
 

	
 
msgid "Show Selected Changesets {0} &rarr; {1}"
 
msgstr "Показать выбранные наборы изменений: {0} &rarr; {1}"
 
msgid "Open New Pull Request for {0}"
 
msgstr "Открыть новый pull-запрос для {0}"
 

	
 
msgid "Show Selected Changesets {0}"
 
msgstr "Показать выбранные наборы изменений: {0}"
 

	
 
msgid "Selection Link"
 
msgstr "Ссылка выбора"
 

	
 
msgid "Collapse Diff"
 
msgstr "Свернуть сравнение"
 
@@ -3595,11 +3582,8 @@ msgstr "Добавить или загрузить файлы через Kallithea"
 
msgid "Push new repository"
 
msgstr "Отправить новый репозиторий"
 

	
 
msgid "Existing repository?"
 
msgstr "Существующий репозиторий?"
 

	
 
msgid "Readme file from revision %s:%s"
 
msgstr "Файл readme из ревизии %s:%s"
 

	
 
msgid "Download %s as %s"
 
msgstr "Скачать %s как %s"
kallithea/i18n/uk/LC_MESSAGES/kallithea.po
Show inline comments
 
@@ -5,14 +5,14 @@ msgid ""
 
msgstr ""
 
"Report-Msgid-Bugs-To: translations@kallithea-scm.org\n"
 
"Language: uk\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%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
 
"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
 
"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
 
"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
 

	
 
msgid ""
 
"CSRF token leak has been detected - all form tokens have been expired"
 
msgstr "Виявлено витік токенів CSRF - всі маркери форми минули"
 

	
 
msgid "Repository not found in the filesystem"
 
@@ -1051,12 +1051,15 @@ msgid ""
 
"Set or revoke permission to all children of that group, including non-"
 
"private repositories and other groups if selected."
 
msgstr ""
 
"Встановіть або скасуйте дозвіл для всіх дітей цієї групи, включно з "
 
"неприватними репозиторіями та іншими групами, якщо вони вибрані."
 

	
 
msgid "Type name of user"
 
msgstr "Введіть ім'я користувача"
 

	
 
msgid "Remove this group"
 
msgstr "Видалити цю групу"
 

	
 
msgid "Confirm to delete this group"
 
msgstr "Підтвердіть, щоб видалити цю групу"
 

	
 
@@ -1066,21 +1069,24 @@ msgstr "Група репозиторію: %s"
 
msgid "Repository Groups Administration"
 
msgstr "Адміністрування Груп Репозиторіїв"
 

	
 
msgid "Number of Top-level Repositories"
 
msgstr "Кількість репозиторіїв верхнього рівня"
 

	
 
msgid "Type of repository to create."
 
msgstr "Тип сховища для створення."
 

	
 
msgid "Clone remote repository"
 
msgstr "Клонувати віддалений репозиторій"
 

	
 
msgid "Repository URL"
 
msgstr "URL репозиторію"
 

	
 
msgid "Optionally select a group to put this repository into."
 
msgstr "За бажанням виберіть групу, в яку буде розміщено цей репозиторій."
 

	
 
msgid "Type of repository to create."
 
msgstr "Тип сховища для створення."
 

	
 
msgid "Landing revision"
 
msgstr "Цільова редакція"
 

	
 
msgid ""
 
"Default revision for files page, downloads, full text search index and "
 
"readme generation"
 
@@ -1202,25 +1208,19 @@ msgstr "Підтвердьте, щоб витягнути зміни з віддаленого сховища."
 
msgid "This repository does not have a remote repository URL."
 
msgstr "У цьому сховищі немає URL-адреси віддаленого сховища."
 

	
 
msgid "Remote repository"
 
msgstr "Віддалений репозиторій"
 

	
 
msgid "Repository URL"
 
msgstr "URL репозиторію"
 

	
 
msgid ""
 
"Optional: URL of a remote repository. If set, the repository can be "
 
"pulled from this URL."
 
msgstr ""
 
"Опціонально: URL-адреса віддаленого сховища. Якщо встановлено, сховище "
 
"можна витягнути з цієї URL-адреси."
 

	
 
msgid "Type name of user"
 
msgstr "Введіть ім'я користувача"
 

	
 
msgid "Change owner of this repository."
 
msgstr "Змінити власника цього сховища."
 

	
 
msgid "Processed commits"
 
msgstr "Оброблені коміти"
 

	
kallithea/i18n/zh_CN/LC_MESSAGES/kallithea.po
Show inline comments
 
@@ -566,13 +566,13 @@ msgstr "没有在该版本库组中创建版本库的权限"
 
msgid "This is not a valid path"
 
msgstr "不是一个合法的路径"
 

	
 
msgid ""
 
"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 必须指定 - 这个名字作为用户名"
 
msgstr "LDAP 登录属性的 CN 必须指定 - 这个名字作为用户名"
 

	
 
msgid "Repository"
 
msgstr "版本库"
 

	
 
msgid "Description"
 
msgstr "描述"
 
@@ -712,22 +712,22 @@ msgid "Created on"
 
msgstr "创建于"
 

	
 
msgid "Confirm to delete this group: %s with %s repository"
 
msgid_plural "Confirm to delete this group: %s with %s repositories"
 
msgstr[0] "确认删除这个版本库组:%s包含%s个版本库"
 

	
 
msgid "Type of repository to create."
 
msgstr "要创建的版本库类型。"
 

	
 
msgid ""
 
"Keep it short and to the point. Use a README file for longer descriptions."
 
msgstr "保持简短。用README文件来写更长的描述。"
 

	
 
msgid "Optionally select a group to put this repository into."
 
msgstr "可选的选择一个组将版本库放到其中。"
 

	
 
msgid "Type of repository to create."
 
msgstr "要创建的版本库类型。"
 

	
 
msgid "Landing revision"
 
msgstr "默认修订"
 

	
 
msgid "Remote"
 
msgstr "远程"
 

	
 
@@ -740,15 +740,12 @@ msgstr "从公共日志删除"
 
msgid "Confirm to delete this repository: %s"
 
msgstr "确认删除版本库:%s"
 

	
 
msgid "Key"
 
msgstr "键"
 

	
 
msgid "Default revision for files page, downloads, whoosh and readme"
 
msgstr "文件浏览、下载、whoosh和README的默认修订版本"
 

	
 
msgid "Change owner of this repository."
 
msgstr "修改这个版本库的所有者。"
 

	
 
msgid "Hooks"
 
msgstr "钩子"
 

	
 
@@ -860,14 +857,14 @@ msgstr "停止关注该版本库"
 
msgid "Start following this repository"
 
msgstr "开始关注该版本库"
 

	
 
msgid "Group"
 
msgstr "组"
 

	
 
msgid "Show Selected Changesets {0} &rarr; {1}"
 
msgstr "显示选中的修订集 {0} &rarr; {1}"
 
msgid "Show Selected Changesets {0}"
 
msgstr "显示选中的修订集 {0}"
 

	
 
msgid "Select changeset"
 
msgstr "选择修订集"
 

	
 
msgid "Specify changeset"
 
msgstr "指定修订集"
 
@@ -943,13 +940,13 @@ msgid "Comment"
 
msgstr "评论"
 

	
 
msgid "You need to be logged in to comment."
 
msgstr "您必须登录才能评论。"
 

	
 
msgid "Login now"
 
msgstr "现在登陆"
 
msgstr "现在登录"
 

	
 
msgid "Hide"
 
msgstr "隐藏"
 

	
 
msgid "%d comment"
 
msgid_plural "%d comments"
kallithea/model/db.py
Show inline comments
 
@@ -910,13 +910,13 @@ class Repository(meta.Base, BaseDbModel)
 

	
 
    STATE_CREATED = 'repo_state_created'
 
    STATE_PENDING = 'repo_state_pending'
 
    STATE_ERROR = 'repo_state_error'
 

	
 
    repo_id = Column(Integer(), primary_key=True)
 
    repo_name = Column(Unicode(255), nullable=False, unique=True)
 
    repo_name = Column(Unicode(255), nullable=False, unique=True)  # full path, must be updated (based on get_new_name) when name or path changes
 
    repo_state = Column(String(255), nullable=False)
 

	
 
    clone_uri = Column(String(255), nullable=True) # FIXME: not nullable?
 
    repo_type = Column(String(255), nullable=False) # 'hg' or 'git'
 
    owner_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=False)
 
    private = Column(Boolean(), nullable=False)
 
@@ -1334,13 +1334,13 @@ class RepoGroup(meta.Base, BaseDbModel):
 
        _table_args_default_dict,
 
    )
 

	
 
    SEP = ' &raquo; '
 

	
 
    group_id = Column(Integer(), primary_key=True)
 
    group_name = Column(Unicode(255), nullable=False, unique=True) # full path
 
    group_name = Column(Unicode(255), nullable=False, unique=True)  # full path, must be updated (based on get_new_name) when name or path changes
 
    parent_group_id = Column('group_parent_id', Integer(), ForeignKey('groups.group_id'), nullable=True)
 
    group_description = Column(Unicode(10000), nullable=False)
 
    owner_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=False)
 
    created_on = Column(DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
 

	
 
    repo_group_to_perm = relationship('UserRepoGroupToPerm', cascade='all', order_by='UserRepoGroupToPerm.group_to_perm_id')
 
@@ -1911,12 +1911,13 @@ class ChangesetComment(meta.Base, BaseDb
 
            return self.pull_request.url(anchor=anchor)
 

	
 
    def __json__(self):
 
        return dict(
 
            comment_id=self.comment_id,
 
            username=self.author.username,
 
            created_on=self.created_on.replace(microsecond=0),
 
            text=self.text,
 
        )
 

	
 
    def deletable(self):
 
        return self.created_on > datetime.datetime.now() - datetime.timedelta(minutes=5)
 

	
kallithea/model/forms.py
Show inline comments
 
@@ -170,12 +170,13 @@ def RepoGroupForm(edit=False, old_data=N
 
                         v.ValidRegex(msg=_('Name must not contain only digits'))(r'(?!^\d+$)^.+$'))
 
        group_description = v.UnicodeString(strip=True, min=1,
 
                                            not_empty=False)
 
        group_copy_permissions = v.StringBoolean(if_missing=False)
 

	
 
        if edit:
 
            owner = All(v.UnicodeString(not_empty=True), v.ValidRepoUser())
 
            # FIXME: do a special check that we cannot move a group to one of
 
            # its children
 
            pass
 

	
 
        parent_group_id = All(v.CanCreateGroup(can_create_in_root),
 
                              v.OneOf(repo_group_ids, hideList=False,
kallithea/model/repo.py
Show inline comments
 
@@ -708,19 +708,16 @@ def create_repo(form_data, cur_user):
 
    clone_uri = form_data.get('clone_uri')
 
    repo_group = form_data['repo_group']
 
    landing_rev = form_data['repo_landing_rev']
 
    copy_fork_permissions = form_data.get('copy_permissions')
 
    copy_group_permissions = form_data.get('repo_copy_permissions')
 
    fork_of = form_data.get('fork_parent_id')
 
    enable_statistics = form_data['repo_enable_statistics']
 
    enable_downloads = form_data['repo_enable_downloads']
 
    state = form_data.get('repo_state', db.Repository.STATE_PENDING)
 

	
 
    # repo creation defaults, private and repo_type are filled in form
 
    defs = db.Setting.get_default_repo_settings(strip_prefix=True)
 
    enable_statistics = defs.get('repo_enable_statistics')
 
    enable_downloads = defs.get('repo_enable_downloads')
 

	
 
    try:
 
        db_repo = RepoModel()._create_repo(
 
            repo_name=repo_name_full,
 
            repo_type=repo_type,
 
            description=description,
 
            owner=owner,
kallithea/model/repo_group.py
Show inline comments
 
@@ -275,51 +275,52 @@ class RepoGroupModel(object):
 

	
 
        return updates
 

	
 
    def update(self, repo_group, repo_group_args):
 
        try:
 
            repo_group = db.RepoGroup.guess_instance(repo_group)
 
            old_path = repo_group.full_path
 
            old_path = repo_group.full_path  # aka .group_name
 

	
 
            # change properties
 
            if 'owner' in repo_group_args:
 
                repo_group.owner = db.User.get_by_username(repo_group_args['owner'])
 
            if 'group_description' in repo_group_args:
 
                repo_group.group_description = repo_group_args['group_description']
 
            if 'parent_group_id' in repo_group_args:
 
                repo_group.parent_group_id = repo_group_args['parent_group_id']
 

	
 
            if 'parent_group_id' in repo_group_args:
 
                assert repo_group_args['parent_group_id'] != '-1', repo_group_args  # RepoGroupForm should have converted to None
 
                repo_group.parent_group = db.RepoGroup.get(repo_group_args['parent_group_id'])
 
                new_parent_group = db.RepoGroup.get(repo_group_args['parent_group_id'])
 
                if new_parent_group is not repo_group.parent_group:
 
                    repo_group.parent_group = new_parent_group
 
                    repo_group.group_name = repo_group.get_new_name(repo_group.name)
 
                    log.debug('Moving repo group %s to %s', old_path, repo_group.group_name)
 
            if 'group_name' in repo_group_args:
 
                group_name = repo_group_args['group_name']
 
                if kallithea.lib.utils2.repo_name_slug(group_name) != group_name:
 
                    raise Exception('invalid repo group name %s' % group_name)
 
                repo_group.group_name = repo_group.get_new_name(group_name)
 
                if repo_group.name != group_name:
 
                    repo_group.group_name = repo_group.get_new_name(group_name)
 
                    log.debug('Renaming repo group %s to %s', old_path, repo_group.group_name)
 
            new_path = repo_group.full_path
 
            meta.Session().add(repo_group)
 

	
 
            # iterate over all members of this groups and do fixes
 
            # if obj is a repoGroup also fix the name of the group according
 
            # to the parent
 
            # if obj is a Repo fix it's name
 
            # this can be potentially heavy operation
 
            # Iterate over all members of this repo group and update the full
 
            # path (repo_name and group_name) based on the (already updated)
 
            # full path of the parent.
 
            # This can potentially be a heavy operation.
 
            for obj in repo_group.recursive_groups_and_repos():
 
                # set the value from it's parent
 
                if obj is repo_group:
 
                    continue  # already updated and logged
 
                if isinstance(obj, db.RepoGroup):
 
                    new_name = obj.get_new_name(obj.name)
 
                    log.debug('Fixing group %s to new name %s'
 
                                % (obj.group_name, new_name))
 
                    log.debug('Fixing repo group %s to new name %s', obj.group_name, new_name)
 
                    obj.group_name = new_name
 
                elif isinstance(obj, db.Repository):
 
                    # we need to get all repositories from this new group and
 
                    # rename them accordingly to new group path
 
                    new_name = obj.get_new_name(obj.just_name)
 
                    log.debug('Fixing repo %s to new name %s'
 
                                % (obj.repo_name, new_name))
 
                    log.debug('Fixing repo %s to new name %s', obj.repo_name, new_name)
 
                    obj.repo_name = new_name
 

	
 
            # Rename in file system
 
            self._rename_group(old_path, new_path)
 

	
 
            return repo_group
 
        except Exception:
 
            log.error(traceback.format_exc())
 
            raise
kallithea/templates/about.html
Show inline comments
 
@@ -21,27 +21,33 @@
 
  v 3.0 (GPLv3)</a>.</p>
 

	
 
  <p>Kallithea is copyrighted by various authors, including but not
 
  necessarily limited to the following:</p>
 
  <ul>
 

	
 
  <li>Copyright &copy; 2012&ndash;2021, Mads Kiilerich</li>
 
  <li>Copyright &copy; 2012&ndash;2023, Mads Kiilerich</li>
 
  <li>Copyright &copy; 2019&ndash;2020, 2022&ndash;2023, Manuel Jacob</li>
 
  <li>Copyright &copy; 2023, Mathias De Mare</li>
 
  <li>Copyright &copy; 2016&ndash;2017, 2020, 2022, Asterios Dimitriou</li>
 
  <li>Copyright &copy; 2022, Jaime Marquínez Ferrándiz</li>
 
  <li>Copyright &copy; 2022, Louis Bertrand</li>
 
  <li>Copyright &copy; 2022, toras9000</li>
 
  <li>Copyright &copy; 2022, yzqzss</li>
 
  <li>Copyright &copy; 2022, МАН69К</li>
 
  <li>Copyright &copy; 2014&ndash;2021, Thomas De Schampheleire</li>
 
  <li>Copyright &copy; 2015&ndash;2017, 2019&ndash;2021, Étienne Gilli</li>
 
  <li>Copyright &copy; 2018&ndash;2021, ssantos</li>
 
  <li>Copyright &copy; 2019&ndash;2021, Private</li>
 
  <li>Copyright &copy; 2020&ndash;2021, fresh</li>
 
  <li>Copyright &copy; 2020&ndash;2021, robertus</li>
 
  <li>Copyright &copy; 2021, Eugenia Russell</li>
 
  <li>Copyright &copy; 2021, Michalis</li>
 
  <li>Copyright &copy; 2021, vs</li>
 
  <li>Copyright &copy; 2021, Александр</li>
 
  <li>Copyright &copy; 2016&ndash;2017, 2020, Asterios Dimitriou</li>
 
  <li>Copyright &copy; 2017&ndash;2020, Allan Nordhøy</li>
 
  <li>Copyright &copy; 2017, 2020, Anton Schur</li>
 
  <li>Copyright &copy; 2019&ndash;2020, Manuel Jacob</li>
 
  <li>Copyright &copy; 2020, Artem</li>
 
  <li>Copyright &copy; 2020, David Ignjić</li>
 
  <li>Copyright &copy; 2020, Dennis Fink</li>
 
  <li>Copyright &copy; 2020, J. Lavoie</li>
 
  <li>Copyright &copy; 2020, Ross Thomas</li>
 
  <li>Copyright &copy; 2020, Tim Ooms</li>
kallithea/templates/admin/repo_groups/repo_group_edit_settings.html
Show inline comments
 
@@ -6,12 +6,19 @@ ${h.form(url('update_repos_group',group_
 
            <div>
 
                ${h.text('group_name',class_='form-control')}
 
            </div>
 
        </div>
 

	
 
        <div class="form-group">
 
            <label class="control-label" for="owner">${_('Owner')}:</label>
 
            <div>
 
               ${h.text('owner',class_='form-control', placeholder=_('Type name of user'))}
 
            </div>
 
        </div>
 

	
 
        <div class="form-group">
 
            <label class="control-label" for="group_description">${_('Description')}:</label>
 
            <div>
 
                ${h.textarea('group_description',cols=23,rows=5,class_='form-control')}
 
            </div>
 
        </div>
 

	
 
@@ -44,8 +51,9 @@ ${h.end_form()}
 
<script>
 
    'use strict';
 
    $(document).ready(function(){
 
        $("#parent_group_id").select2({
 
            'dropdownAutoWidth': true
 
        });
 
        SimpleUserAutoComplete($('#owner'));
 
    });
 
</script>
kallithea/templates/admin/repos/repo_add_base.html
Show inline comments
 
## -*- coding: utf-8 -*-
 

	
 
${h.form(url('repos'))}
 
<div class="form">
 
    <div class="form">
 
        <div class="form-group">
 
            <label class="control-label" for="repo_name">${_('Name')}:</label>
 
            <div>
 
                ${h.text('repo_name',class_='form-control')}
 
            </div>
 
        </div>
 
        <div id="remote_clone" class="form-group">
 
        <div class="form-group">
 
            <label class="control-label" for="repo_type">${_('Type')}:</label>
 
            <div>
 
                ${h.select('repo_type','hg',c.backends,class_='form-control')}
 
                <span class="help-block">${_('Type of repository to create.')}</span>
 
            </div>
 
        </div>
 
        <div class="form-group">
 
            <label class="control-label" for="clone_uri">${_('Clone remote repository')}:</label>
 
            <div>
 
                ${h.text('clone_uri',class_='form-control')}
 
                ${h.text('clone_uri',class_='form-control', placeholder=_('Repository URL'))}
 
                <span class="help-block">
 
                    ${_('Optional: URL of a remote repository. If set, the repository will be created as a clone from this URL.')}
 
                </span>
 
            </div>
 
        </div>
 
        <div class="form-group">
 
@@ -36,63 +41,71 @@ ${h.form(url('repos'))}
 
            <div>
 
                ${h.checkbox('repo_copy_permissions',value="True")}
 
                <span class="help-block">${_('Copy permission set from parent repository group.')}</span>
 
            </div>
 
        </div>
 
        <div class="form-group">
 
            <label class="control-label" for="repo_type">${_('Type')}:</label>
 
            <div>
 
                ${h.select('repo_type','hg',c.backends,class_='form-control')}
 
                <span class="help-block">${_('Type of repository to create.')}</span>
 
            </div>
 
        </div>
 
        <div class="form-group">
 
            <label class="control-label" for="repo_landing_rev">${_('Landing revision')}:</label>
 
            <div>
 
                ${h.select('repo_landing_rev','',c.landing_revs,class_='form-control')}
 
                ${h.select('repo_landing_rev',None,c.landing_revs,class_='form-control')}
 
                <span class="help-block">${_('Default revision for files page, downloads, full text search index and readme generation')}</span>
 
            </div>
 
        </div>
 
        <div class="form-group">
 
            <label class="control-label" for="repo_private">${_('Private repository')}:</label>
 
            <div>
 
                ${h.checkbox('repo_private',value="True")}
 
                <span class="help-block">${_('Private repositories are only visible to people explicitly added as collaborators.')}</span>
 
            </div>
 
        </div>
 
        <div class="form-group">
 
            <label class="control-label" for="repo_enable_statistics">${_('Enable statistics')}:</label>
 
            <div>
 
                ${h.checkbox('repo_enable_statistics',value="True")}
 
                <span class="help-block">${_('Enable statistics window on summary page.')}</span>
 
            </div>
 
        </div>
 
        <div class="form-group">
 
            <label class="control-label" for="repo_enable_downloads">${_('Enable downloads')}:</label>
 
            <div>
 
                ${h.checkbox('repo_enable_downloads',value="True")}
 
                <span class="help-block">${_('Enable download menu on summary page.')}</span>
 
            </div>
 
        </div>
 
        <div class="form-group">
 
            <div class="buttons">
 
                ${h.submit('add',_('Add'),class_="btn btn-default")}
 
            </div>
 
        </div>
 
</div>
 
    </div>
 
${h.end_form()}
 

	
 
<script>
 
    'use strict';
 
    $(document).ready(function(){
 
        $('#repo_type').select2({
 
            'minimumResultsForSearch': -1
 
        });
 
        $('#repo_group').select2({
 
            'dropdownAutoWidth': true
 
        });
 

	
 
        function setCopyPermsOption(group_val){
 
            if(group_val != "-1"){
 
                $('#copy_perms').show();
 
            }
 
            else{
 
                $('#copy_perms').hide();
 
            }
 
        }
 

	
 
        $("#repo_group").select2({
 
            'dropdownAutoWidth': true
 
        });
 

	
 
        setCopyPermsOption($('#repo_group').val());
 
        $("#repo_group").on("change", function(e) {
 
        $('#repo_group').on("change", function(e) {
 
            setCopyPermsOption(e.val);
 
        });
 

	
 
        $("#repo_type").select2({
 
            'minimumResultsForSearch': -1
 
        });
 
        $("#repo_landing_rev").select2({
 
        $('#repo_landing_rev').select2({
 
            'minimumResultsForSearch': -1
 
        });
 
        $('#repo_name').focus();
 
    });
 
</script>
 
${h.end_form()}
kallithea/templates/admin/repos/repo_edit_settings.html
Show inline comments
 
${h.form(url('update_repo', repo_name=c.repo_info.repo_name))}
 
    <div class="form">
 
            <div class="form-group">
 
                <label class="control-label" for="repo_name">${_('Name')}:</label>
 
                <div>
 
                    ${h.text('repo_name',class_='form-control')}
 
                </div>
 
        <div class="form-group">
 
            <label class="control-label" for="repo_name">${_('Name')}:</label>
 
            <div>
 
                ${h.text('repo_name',class_='form-control')}
 
            </div>
 
        </div>
 
        <div class="form-group">
 
            <label class="control-label" for="permanent_url">${_('Permanent URL')}:</label>
 
            <div>
 
                ${h.text('permanent_url',class_='form-control', readonly='1')}
 
                <span class="help-block">
 
                    ${_('''In case this repository is renamed or moved into another group the repository URL changes.
 
                           Using the above permanent URL guarantees that this repository always will be accessible on that URL.
 
                           This is useful for CI systems, or any other cases that you need to hardcode the URL into a 3rd party service.''')}
 
                </span>
 
            </div>
 
        </div>
 
        <div class="form-group">
 
            <label class="control-label" for="clone_uri">${_('Remote repository')}:</label>
 
            <div>
 
                ${h.text('clone_uri',class_='form-control', placeholder=_('Repository URL'))}
 
                ${h.hidden('clone_uri_hidden', c.repo_info.clone_uri_hidden)}
 
                <span class="help-block">
 
                    ${_('Optional: URL of a remote repository. If set, the repository can be pulled from this URL.')}
 
                </span>
 
            </div>
 
        </div>
 
        <div class="form-group">
 
            <label class="control-label" for="repo_description">${_('Description')}:</label>
 
            <div>
 
                ${h.textarea('repo_description',class_='form-control')}
 
                <span class="help-block">${_('Keep it short and to the point. Use a README file for longer descriptions.')}</span>
 
            </div>
 
        </div>
 
        <div class="form-group">
 
            <label class="control-label" for="repo_group">${_('Repository group')}:</label>
 
            <div>
 
                ${h.select('repo_group',None,c.repo_groups,class_='form-control')}
 
                <span class="help-block">${_('Optionally select a group to put this repository into.')}</span>
 
            </div>
 
        </div>
 
        <div class="form-group">
 
            <label class="control-label" for="repo_landing_rev">${_('Landing revision')}:</label>
 
            <div>
 
                ${h.select('repo_landing_rev',None,c.landing_revs,class_='form-control')}
 
                <span class="help-block">${_('Default revision for files page, downloads, full text search index and readme generation')}</span>
 
            </div>
 
        </div>
 
        <div class="form-group">
 
            <label class="control-label" for="owner">${_('Owner')}:</label>
 
            <div>
 
                ${h.text('owner',class_='form-control', placeholder=_('Type name of user'))}
 
                <span class="help-block">${_('Change owner of this repository.')}</span>
 
            </div>
 
        </div>
 
        <div class="form-group">
 
            <label class="control-label" for="repo_private">${_('Private repository')}:</label>
 
            <div>
 
                ${h.checkbox('repo_private',value="True")}
 
                <span class="help-block">${_('Private repositories are only visible to people explicitly added as collaborators.')}</span>
 
            </div>
 
        </div>
 
        <div class="form-group">
 
            <label class="control-label" for="repo_enable_statistics">${_('Enable statistics')}:</label>
 
            <div>
 
                ${h.checkbox('repo_enable_statistics',value="True")}
 
                <span class="help-block">${_('Enable statistics window on summary page.')}</span>
 
            </div>
 
        </div>
 
        <div class="form-group">
 
            <label class="control-label" for="repo_enable_downloads">${_('Enable downloads')}:</label>
 
            <div>
 
                ${h.checkbox('repo_enable_downloads',value="True")}
 
                <span class="help-block">${_('Enable download menu on summary page.')}</span>
 
            </div>
 
        </div>
 

	
 
        %if c.visual.repository_fields:
 
          ## EXTRA FIELDS
 
          %for field in c.repo_fields:
 
            <div class="form-group">
 
                <label class="control-label" for="permanent_url">${_('Permanent URL')}:</label>
 
                <div>
 
                    ${h.text('permanent_url',class_='form-control', readonly='1')}
 
                    <span class="help-block">
 
                        ${_('''In case this repository is renamed or moved into another group the repository URL changes.
 
                               Using the above permanent URL guarantees that this repository always will be accessible on that URL.
 
                               This is useful for CI systems, or any other cases that you need to hardcode the URL into a 3rd party service.''')}
 
                    </span>
 
                </div>
 
            </div>
 
            <div class="form-group">
 
                <label class="control-label" for="clone_uri">${_('Remote repository')}:</label>
 
                <label class="control-label" for="${field.field_key_prefixed}">${field.field_label} (${field.field_key}):</label>
 
                <div>
 
                  <div id="alter_clone_uri">
 
                        ${h.text('clone_uri',class_='form-control', placeholder=_('Repository URL'))}
 
                        ${h.hidden('clone_uri_hidden', c.repo_info.clone_uri_hidden)}
 
                  </div>
 
                  <span id="alter_clone_uri_help_block" class="help-block">
 
                    ${_('Optional: URL of a remote repository. If set, the repository can be pulled from this URL.')}
 
                  </span>
 
                </div>
 
            </div>
 
            <div class="form-group">
 
                <label class="control-label" for="repo_group">${_('Repository group')}:</label>
 
                <div>
 
                    ${h.select('repo_group','',c.repo_groups,class_='form-control')}
 
                    <span class="help-block">${_('Optionally select a group to put this repository into.')}</span>
 
                </div>
 
            </div>
 
            <div class="form-group">
 
                <label class="control-label" for="repo_landing_rev">${_('Landing revision')}:</label>
 
                <div>
 
                    ${h.select('repo_landing_rev','',c.landing_revs,class_='form-control')}
 
                    <span class="help-block">${_('Default revision for files page, downloads, whoosh and readme')}</span>
 
                </div>
 
            </div>
 
            <div class="form-group">
 
                <label class="control-label" for="owner">${_('Owner')}:</label>
 
                <div>
 
                   ${h.text('owner',class_='form-control', placeholder=_('Type name of user'))}
 
                   <span class="help-block">${_('Change owner of this repository.')}</span>
 
                    ${h.text(field.field_key_prefixed, field.field_value, class_='form-control')}
 
                    %if field.field_desc:
 
                      <span class="help-block">${field.field_desc}</span>
 
                    %endif
 
                </div>
 
            </div>
 
            <div class="form-group">
 
                <label class="control-label" for="repo_description">${_('Description')}:</label>
 
                <div>
 
                    ${h.textarea('repo_description',class_='form-control')}
 
                    <span class="help-block">${_('Keep it short and to the point. Use a README file for longer descriptions.')}</span>
 
                </div>
 
            </div>
 

	
 
            <div class="form-group">
 
                <label class="control-label" for="repo_private">${_('Private repository')}:</label>
 
                <div>
 
                    ${h.checkbox('repo_private',value="True")}
 
                    <span class="help-block">${_('Private repositories are only visible to people explicitly added as collaborators.')}</span>
 
                </div>
 
            </div>
 
            <div class="form-group">
 
                <label class="control-label" for="repo_enable_statistics">${_('Enable statistics')}:</label>
 
                <div>
 
                    ${h.checkbox('repo_enable_statistics',value="True")}
 
                    <span class="help-block">${_('Enable statistics window on summary page.')}</span>
 
                </div>
 
          %endfor
 
        %endif
 
        <div class="form-group">
 
            <div class="buttons">
 
                ${h.submit('save',_('Save'),class_="btn btn-default")}
 
                ${h.reset('reset',_('Reset'),class_="btn btn-default")}
 
            </div>
 
            <div class="form-group">
 
                <label class="control-label" for="repo_enable_downloads">${_('Enable downloads')}:</label>
 
                <div>
 
                    ${h.checkbox('repo_enable_downloads',value="True")}
 
                    <span class="help-block">${_('Enable download menu on summary page.')}</span>
 
                </div>
 
            </div>
 

	
 
            %if c.visual.repository_fields:
 
              ## EXTRA FIELDS
 
              %for field in c.repo_fields:
 
                <div class="form-group">
 
                    <label class="control-label" for="${field.field_key_prefixed}">${field.field_label} (${field.field_key}):</label>
 
                    <div>
 
                        ${h.text(field.field_key_prefixed, field.field_value, class_='form-control')}
 
                        %if field.field_desc:
 
                          <span class="help-block">${field.field_desc}</span>
 
                        %endif
 
                    </div>
 
                </div>
 
              %endfor
 
            %endif
 
            <div class="form-group">
 
                <div class="buttons">
 
                    ${h.submit('save',_('Save'),class_="btn btn-default")}
 
                    ${h.reset('reset',_('Reset'),class_="btn btn-default")}
 
                </div>
 
            </div>
 
        </div>
 
    </div>
 
    ${h.end_form()}
 
${h.end_form()}
 

	
 
<script>
 
    'use strict';
 
    $(document).ready(function(){
 
        $('#repo_landing_rev').select2({
 
        $('#repo_group').select2({
 
            'dropdownAutoWidth': true
 
        });
 
        $('#repo_group').select2({
 
        $('#repo_landing_rev').select2({
 
            'dropdownAutoWidth': true
 
        });
 

	
 
        // autocomplete
 
        SimpleUserAutoComplete($('#owner'));
 
    });
kallithea/templates/base/base.html
Show inline comments
 
@@ -20,13 +20,13 @@
 
        %if c.visual.show_version:
 
            <a class="navbar-link" href="${h.url('kallithea_project_url')}" target="_blank">Kallithea</a> ${c.kallithea_version},
 
        %else:
 
            <a class="navbar-link" href="${h.url('kallithea_project_url')}" target="_blank">Kallithea</a>,
 
        %endif
 
        which is
 
        <a class="navbar-link" href="${h.canonical_url('about')}#copyright">&copy; 2010&ndash;2022 by various authors &amp; licensed under GPLv3</a>.
 
        <a class="navbar-link" href="${h.canonical_url('about')}#copyright">&copy; 2010&ndash;2023 by various authors &amp; licensed under GPLv3</a>.
 
        %if c.issues_url:
 
            &ndash; <a class="navbar-link" href="${c.issues_url}" target="_blank">${_('Support')}</a>
 
        %endif
 
    </span>
 
</div>
 

	
kallithea/templates/base/root.html
Show inline comments
 
@@ -35,14 +35,14 @@
 
                'Group': ${h.jshtml(_('Group'))},
 
                'Loading ...': ${h.jshtml(_('Loading ...'))},
 
                'loading ...': ${h.jshtml(_('loading ...'))},
 
                'Search truncated': ${h.jshtml(_('Search truncated'))},
 
                'No matching files': ${h.jshtml(_('No matching files'))},
 
                'Open New Pull Request from {0}': ${h.jshtml(_('Open New Pull Request from {0}'))},
 
                'Open New Pull Request for {0} &rarr; {1}': ${h.js(_('Open New Pull Request for {0} &rarr; {1}'))},
 
                'Show Selected Changesets {0} &rarr; {1}': ${h.js(_('Show Selected Changesets {0} &rarr; {1}'))},
 
                'Open New Pull Request for {0}': ${h.js(_('Open New Pull Request for {0}'))},
 
                'Show Selected Changesets {0}': ${h.js(_('Show Selected Changesets {0}'))},
 
                'Selection Link': ${h.jshtml(_('Selection Link'))},
 
                'Collapse Diff': ${h.jshtml(_('Collapse Diff'))},
 
                'Expand Diff': ${h.jshtml(_('Expand Diff'))},
 
                'No revisions': ${h.jshtml(_('No revisions'))},
 
                'Type name of user or member to grant permission': ${h.jshtml(_('Type name of user or member to grant permission'))},
 
                'Failed to revoke permission': ${h.jshtml(_('Failed to revoke permission'))},
kallithea/templates/changelog/changelog.html
Show inline comments
 
@@ -106,19 +106,19 @@ ${self.repo_context_bar('changelog', c.f
 
                        if ($checked_checkboxes.length > 1 || singlerange) {
 
                            var rev_start = $checked_checkboxes.last().prop('name');
 
                            $('#rev_range_container').prop('href',
 
                                pyroutes.url('changeset_home', {'repo_name': ${h.js(c.repo_name)},
 
                                                                'revision': rev_start + '...' + rev_end}));
 
                            $('#rev_range_container').html(
 
                                 _TM['Show Selected Changesets {0} &rarr; {1}'].format(rev_start.substr(0, 12), rev_end.substr(0, 12)));
 
                                 _TM['Show Selected Changesets {0}'].format(rev_start.substr(0, 12) + ' &rarr; ' + rev_end.substr(0, 12)));
 
                            $('#rev_range_container').show();
 
                            $('#open_new_pr').prop('href', pyroutes.url('pullrequest_home',
 
                                                                        {'repo_name': ${h.js(c.repo_name)},
 
                                                                         'rev_start': rev_start,
 
                                                                         'rev_end': rev_end}));
 
                            $('#open_new_pr').html(_TM['Open New Pull Request for {0} &rarr; {1}'].format(rev_start.substr(0, 12), rev_end.substr(0, 12)));
 
                            $('#open_new_pr').html(_TM['Open New Pull Request for {0}'].format(rev_start.substr(0, 12) + ' &rarr; ' + rev_end.substr(0, 12)));
 
                        } else {
 
                            $('#open_new_pr').prop('href', pyroutes.url('pullrequest_home',
 
                                                                        {'repo_name': ${h.js(c.repo_name)},
 
                                                                         'rev_end': rev_end}));
 
                            $('#open_new_pr').html(_TM['Open New Pull Request from {0}'].format(rev_end.substr(0, 12)));
 
                        }
kallithea/templates/summary/summary.html
Show inline comments
 
@@ -221,13 +221,13 @@ hg push ${c.clone_repo_url}
 
</div>
 

	
 
%if c.readme_data:
 
<div id="readme" class="anchor">
 
</div>
 
<div class="panel panel-primary">
 
    <div class="panel-heading" title="${_('Readme file from revision %s:%s') % (c.db_repo.landing_rev[0], c.db_repo.landing_rev[1])}">
 
    <div class="panel-heading" title="${_('Readme file from %s') % (c.db_repo.landing_rev[1])}">
 
        <div class="panel-title">
 
            <a href="${h.url('files_home',repo_name=c.repo_name,revision='tip',f_path=c.readme_file)}">${c.readme_file}</a>
 
        </div>
 
    </div>
 
    <div class="readme panel-body">
 
        ${c.readme_data|n}
kallithea/tests/api/api_base.py
Show inline comments
 
@@ -13,12 +13,13 @@
 
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
 

	
 
"""
 
Tests for the JSON-RPC web api.
 
"""
 

	
 
import datetime
 
import os
 
import random
 
import re
 
import string
 
from typing import Sized
 

	
 
@@ -257,13 +258,13 @@ class _BaseTestApi(object):
 
        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_remote(self):
 
        # Note: pulling from local repos is a mis-feature - it will bypass access control
 
        # Note: pulling from local repos is a misfeature - it will bypass access control
 
        # ... but ok, if the path already has been set in the database
 
        repo_name = 'test_pull'
 
        r = fixture.create_repo(repo_name, repo_type=self.REPO_TYPE)
 
        # hack around that clone_uri can't be set to to a local path
 
        # (as shown by test_api_create_repo_clone_uri_local)
 
        r.clone_uri = os.path.join(db.Ui.get_by_key('paths', '/').ui_value, self.REPO)
 
@@ -781,30 +782,79 @@ class _BaseTestApi(object):
 
        expected = response.json['result']
 
        try:
 
            self._compare_ok(id_, expected, given=response.body)
 
        finally:
 
            RepoModel().revoke_user_permission(self.REPO, self.TEST_USER_LOGIN)
 

	
 
    def test_api_create_repo(self):
 
        repo_name = 'api-repo'
 
    @base.parametrize('changing_attr,updates', [
 
        ('owner', {'owner': base.TEST_USER_REGULAR_LOGIN}),
 
        ('description', {'description': 'new description'}),
 
        ('clone_uri', {'clone_uri': 'http://example.com/repo'}), # will fail - pulling from non-existing repo should fail
 
        ('clone_uri', {'clone_uri': '/repo'}), # will fail - pulling from local repo was a misfeature - it would bypass access control
 
        ('clone_uri', {'clone_uri': None}),
 
        ('landing_rev', {'landing_rev': 'branch:master'}),
 
        ('private', {'private': True}),
 
        ('enable_statistics', {'enable_statistics': True}),
 
        ('enable_downloads', {'enable_downloads': True}),
 
        ('repo_group', {'group': 'test_group_for_update'}),
 
    ])
 
    def test_api_create_repo(self, changing_attr, updates):
 
        repo_name = repo_name_full = 'new_repo'
 

	
 
        if changing_attr == 'repo_group':
 
            group_name = updates['group']
 
            fixture.create_repo_group(group_name)
 
            repo_name_full = '/'.join([group_name, repo_name])
 
            updates = {}
 

	
 
        id_, params = _build_data(self.apikey, 'create_repo',
 
                                  repo_name=repo_name,
 
                                  owner=base.TEST_USER_ADMIN_LOGIN,
 
                                  repo_type=self.REPO_TYPE,
 
        )
 
                                  repo_type=self.REPO_TYPE, repo_name=repo_name_full, **updates)
 
        response = api_call(self, params)
 

	
 
        repo = RepoModel().get_by_repo_name(repo_name)
 
        assert repo is not None
 
        ret = {
 
            'msg': 'Created new repository `%s`' % repo_name,
 
            'success': True,
 
        }
 
        expected = ret
 
        self._compare_ok(id_, expected, given=response.body)
 
        fixture.destroy_repo(repo_name)
 
        try:
 
            expected = {
 
                'msg': 'Created new repository `%s`' % repo_name_full,
 
                'success': True}
 
            if changing_attr == 'clone_uri' and updates['clone_uri']:
 
                expected = 'failed to create repository `%s`' % repo_name
 
                self._compare_error(id_, expected, given=response.body)
 
                return
 
            else:
 
                self._compare_ok(id_, expected, given=response.body)
 

	
 
            repo = db.Repository.get_by_repo_name(repo_name_full)
 
            assert repo is not None
 

	
 
            expected_data = {
 
                    'clone_uri': None,
 
                    'created_on': repo.created_on,
 
                    'description': repo_name,
 
                    'enable_downloads': False,
 
                    'enable_statistics': False,
 
                    'fork_of': None,
 
                    'landing_rev': ['rev', 'tip'],
 
                    'last_changeset': {'author': '',
 
                                       'date': datetime.datetime(1970, 1, 1, 0, 0),
 
                                       'message': '',
 
                                       'raw_id': '0000000000000000000000000000000000000000',
 
                                       'revision': -1,
 
                                       'short_id': '000000000000'},
 
                    'owner': 'test_admin',
 
                    'private': False,
 
                    'repo_id': repo.repo_id,
 
                    'repo_name': repo_name_full,
 
                    'repo_type': self.REPO_TYPE,
 
            }
 
            expected_data.update(updates)
 
            if changing_attr == 'landing_rev':
 
                expected_data['landing_rev'] = expected_data['landing_rev'].split(':', 1)
 
            assert repo.get_api_data() == expected_data
 
        finally:
 
            fixture.destroy_repo(repo_name_full)
 
            if changing_attr == 'repo_group':
 
                fixture.destroy_repo_group(group_name)
 

	
 
    @base.parametrize('repo_name', [
 
        '',
 
        '.',
 
        '..',
 
        ':',
 
@@ -824,13 +874,13 @@ class _BaseTestApi(object):
 
        else:
 
            expected = "failed to create repository `%s`" % repo_name
 
            self._compare_error(id_, expected, given=response.body)
 
        fixture.destroy_repo(repo_name)
 

	
 
    def test_api_create_repo_clone_uri_local(self):
 
        # cloning from local repos was a mis-feature - it would bypass access control
 
        # cloning from local repos was a misfeature - it would bypass access control
 
        # TODO: introduce other test coverage of actual remote cloning
 
        clone_uri = os.path.join(base.TESTS_TMP_PATH, self.REPO)
 
        repo_name = 'api-repo'
 
        id_, params = _build_data(self.apikey, 'create_repo',
 
                                  repo_name=repo_name,
 
                                  owner=base.TEST_USER_ADMIN_LOGIN,
 
@@ -1001,15 +1051,16 @@ class _BaseTestApi(object):
 
        self._compare_error(id_, expected, given=response.body)
 

	
 
    @base.parametrize('changing_attr,updates', [
 
        ('owner', {'owner': base.TEST_USER_REGULAR_LOGIN}),
 
        ('description', {'description': 'new description'}),
 
        ('clone_uri', {'clone_uri': 'http://example.com/repo'}), # will fail - pulling from non-existing repo should fail
 
        ('clone_uri', {'clone_uri': '/repo'}), # will fail - pulling from local repo was a mis-feature - it would bypass access control
 
        ('clone_uri', {'clone_uri': '/repo'}), # will fail - pulling from local repo was a misfeature - it would bypass access control
 
        ('clone_uri', {'clone_uri': None}),
 
        ('landing_rev', {'landing_rev': 'branch:master'}),
 
        ('private', {'private': True}),
 
        ('enable_statistics', {'enable_statistics': True}),
 
        ('enable_downloads', {'enable_downloads': True}),
 
        ('name', {'name': 'new_repo_name'}),
 
        ('repo_group', {'group': 'test_group_for_update'}),
 
    ])
 
    def test_api_update_repo(self, changing_attr, updates):
 
@@ -1017,37 +1068,49 @@ class _BaseTestApi(object):
 
        repo = fixture.create_repo(repo_name, repo_type=self.REPO_TYPE)
 
        if changing_attr == 'repo_group':
 
            fixture.create_repo_group(updates['group'])
 

	
 
        id_, params = _build_data(self.apikey, 'update_repo',
 
                                  repoid=repo_name, **updates)
 
        response = api_call(self, params)
 

	
 
        if changing_attr == 'name':
 
            repo_name = updates['name']
 
        if changing_attr == 'repo_group':
 
            repo_name = '/'.join([updates['group'], repo_name])
 
        expected = {
 
            'msg': 'updated repo ID:%s %s' % (repo.repo_id, repo_name),
 
            'repository': repo.get_api_data()
 
        }
 
        expected['repository'].update(updates)
 
        if changing_attr == 'clone_uri' and updates['clone_uri'] is None:
 
            expected['repository']['clone_uri'] = ''
 
        if changing_attr == 'landing_rev':
 
            expected['repository']['landing_rev'] = expected['repository']['landing_rev'].split(':', 1)
 
        if changing_attr == 'name':
 
            expected['repository']['repo_name'] = expected['repository'].pop('name')
 
        if changing_attr == 'repo_group':
 
            expected['repository']['repo_name'] = expected['repository'].pop('group') + '/' + repo.repo_name
 

	
 
        response = api_call(self, params)
 

	
 
        try:
 
            if changing_attr == 'clone_uri' and updates['clone_uri']:
 
                expected = 'failed to update repo `%s`' % repo_name
 
                self._compare_error(id_, expected, given=response.body)
 
            else:
 
                expected = {
 
                    'msg': 'updated repo ID:%s %s' % (repo.repo_id, repo_name),
 
                    'repository': repo.get_api_data()
 
                }
 
                self._compare_ok(id_, expected, given=response.body)
 
        finally:
 
            fixture.destroy_repo(repo_name)
 
            if changing_attr == 'repo_group':
 
                fixture.destroy_repo_group(updates['group'])
 

	
 
    @base.parametrize('changing_attr,updates', [
 
        ('owner', {'owner': base.TEST_USER_REGULAR_LOGIN}),
 
        ('description', {'description': 'new description'}),
 
        ('clone_uri', {'clone_uri': 'http://example.com/repo'}), # will fail - pulling from non-existing repo should fail
 
        ('clone_uri', {'clone_uri': '/repo'}), # will fail - pulling from local repo was a mis-feature - it would bypass access control
 
        ('clone_uri', {'clone_uri': '/repo'}), # will fail - pulling from local repo was a misfeature - it would bypass access control
 
        ('clone_uri', {'clone_uri': None}),
 
        ('landing_rev', {'landing_rev': 'branch:master'}),
 
        ('enable_statistics', {'enable_statistics': True}),
 
        ('enable_downloads', {'enable_downloads': True}),
 
        ('name', {'name': 'new_repo_name'}),
 
        ('repo_group', {'group': 'test_group_for_update'}),
 
@@ -1785,12 +1848,53 @@ class _BaseTestApi(object):
 

	
 
        expected = 'failed to edit permission for user group: `%s` in repo: `%s`' % (
 
            TEST_USER_GROUP, self.REPO
 
        )
 
        self._compare_error(id_, expected, given=response.body)
 

	
 
    @base.parametrize('changing_attr,updates', [
 
        ('owner', {'owner': base.TEST_USER_REGULAR_LOGIN}),
 
        ('description', {'description': 'new description'}),
 
        ('group_name', {'group_name': 'new_repo_name'}),
 
        ('parent', {'parent': 'test_group_for_update'}),
 
    ])
 
    def test_api_update_repo_group(self, changing_attr, updates):
 
        group_name = 'lololo'
 
        repo_group = fixture.create_repo_group(group_name)
 

	
 
        new_group_name = group_name
 
        if changing_attr == 'group_name':
 
            assert repo_group.parent_group_id is None  # lazy assumption for this test
 
            new_group_name = updates['group_name']
 
        if changing_attr == 'parent':
 
            new_group_name = '/'.join([updates['parent'], group_name.rsplit('/', 1)[-1]])
 

	
 
        expected = {
 
            'msg': 'updated repository group ID:%s %s' % (repo_group.group_id, new_group_name),
 
            'repo_group': repo_group.get_api_data()
 
        }
 
        expected['repo_group'].update(updates)
 
        if 'description' in updates:
 
            expected['repo_group']['group_description'] = expected['repo_group'].pop('description')
 

	
 
        if changing_attr == 'parent':
 
            new_parent = fixture.create_repo_group(updates['parent'])
 
            expected['repo_group']['parent_group'] = expected['repo_group'].pop('parent')
 
            expected['repo_group']['group_name'] = new_group_name
 

	
 
        id_, params = _build_data(self.apikey, 'update_repo_group',
 
                                  repogroupid=group_name, **updates)
 
        response = api_call(self, params)
 

	
 
        try:
 
            self._compare_ok(id_, expected, given=response.body)
 
        finally:
 
            if changing_attr == 'parent':
 
                fixture.destroy_repo_group(new_parent.group_id)
 
            fixture.destroy_repo_group(new_group_name)
 

	
 
    @base.parametrize('name,perm,apply_to_children', [
 
        ('none', 'group.none', 'none'),
 
        ('read', 'group.read', 'none'),
 
        ('write', 'group.write', 'none'),
 
        ('admin', 'group.admin', 'none'),
 

	
 
@@ -2372,31 +2476,78 @@ class _BaseTestApi(object):
 
        id_, params = _build_data(self.apikey, 'get_changeset',
 
                                  repoid=self.REPO, raw_id=self.TEST_REVISION)
 
        response = api_call(self, params)
 
        result = ext_json.loads(response.body)["result"]
 
        assert result["raw_id"] == self.TEST_REVISION
 
        assert "reviews" not in result
 
        assert "comments" not in result
 
        assert "inline_comments" not in result
 

	
 
    def test_api_get_changeset_with_reviews(self):
 
        reviewobjs = fixture.review_changeset(self.REPO, self.TEST_REVISION, "approved")
 
        id_, params = _build_data(self.apikey, 'get_changeset',
 
                                  repoid=self.REPO, raw_id=self.TEST_REVISION,
 
                                  with_reviews=True)
 
        response = api_call(self, params)
 
        result = ext_json.loads(response.body)["result"]
 
        assert result["raw_id"] == self.TEST_REVISION
 
        assert "reviews" in result
 
        assert "comments" not in result
 
        assert "inline_comments" not in result
 
        assert len(result["reviews"]) == 1
 
        review = result["reviews"][0]
 
        expected = {
 
            'status': 'approved',
 
            'modified_at': reviewobjs[0].modified_at.replace(microsecond=0).isoformat(),
 
            'reviewer': 'test_admin',
 
        }
 
        assert review == expected
 

	
 
    def test_api_get_changeset_with_comments(self):
 
        commentobj = fixture.add_changeset_comment(self.REPO, self.TEST_REVISION, "example changeset comment")
 
        id_, params = _build_data(self.apikey, 'get_changeset',
 
                                  repoid=self.REPO, raw_id=self.TEST_REVISION,
 
                                  with_comments=True)
 
        response = api_call(self, params)
 
        result = ext_json.loads(response.body)["result"]
 
        assert result["raw_id"] == self.TEST_REVISION
 
        assert "reviews" not in result
 
        assert "comments" in result
 
        assert "inline_comments" not in result
 
        comment = result["comments"][-1]
 
        expected = {
 
            'comment_id': commentobj.comment_id,
 
            'text': 'example changeset comment',
 
            'username': 'test_admin',
 
            'created_on': commentobj.created_on.replace(microsecond=0).isoformat(),
 
        }
 
        assert comment == expected
 

	
 
    def test_api_get_changeset_with_inline_comments(self):
 
        commentobj = fixture.add_changeset_comment(self.REPO, self.TEST_REVISION, "example inline comment", f_path='vcs/__init__.py', line_no="n3")
 
        id_, params = _build_data(self.apikey, 'get_changeset',
 
                                  repoid=self.REPO, raw_id=self.TEST_REVISION,
 
                                  with_inline_comments=True)
 
        response = api_call(self, params)
 
        result = ext_json.loads(response.body)["result"]
 
        assert result["raw_id"] == self.TEST_REVISION
 
        assert "reviews" not in result
 
        assert "comments" not in result
 
        assert "inline_comments" in result
 
        expected = [
 
            ['vcs/__init__.py', {
 
                'n3': [{
 
                    'comment_id': commentobj.comment_id,
 
                    'text': 'example inline comment',
 
                    'username': 'test_admin',
 
                    'created_on': commentobj.created_on.replace(microsecond=0).isoformat(),
 
                }]
 
            }]
 
        ]
 
        assert result["inline_comments"] == expected
 

	
 
    def test_api_get_changeset_that_does_not_exist(self):
 
        """ Fetch changeset status for non-existant changeset.
 
        revision id is the above git hash used in the test above with the
 
        last 3 nibbles replaced with 0xf.  Should not exist for git _or_ hg.
 
        """
 
        id_, params = _build_data(self.apikey, 'get_changeset',
 
@@ -2433,13 +2584,14 @@ class _BaseTestApi(object):
 
            "url": "/%s/pull-request/%s/_/%s" % (self.REPO, pull_request_id, "stable"),
 
            "reviewers": [{"username": "test_regular"}],
 
            "org_repo_url": "http://localhost:80/%s" % self.REPO,
 
            "org_ref_parts": ["branch", "stable", self.TEST_PR_SRC],
 
            "other_ref_parts": ["branch", "default", self.TEST_PR_DST],
 
            "comments": [{"username": base.TEST_USER_ADMIN_LOGIN, "text": "",
 
                         "comment_id": pullrequest.comments[0].comment_id}],
 
                          "comment_id": pullrequest.comments[0].comment_id,
 
                          "created_on": "2000-01-01T00:00:00"}],
 
            "owner": base.TEST_USER_ADMIN_LOGIN,
 
            "statuses": [{"status": "under_review", "reviewer": base.TEST_USER_ADMIN_LOGIN, "modified_at": "2000-01-01T00:00:00"} for i in range(0, len(self.TEST_PR_REVISIONS))],
 
            "title": "get test",
 
            "revisions": self.TEST_PR_REVISIONS,
 
            "created_on": "2000-01-01T00:00:00",
 
            "updated_on": "2000-01-01T00:00:00",
kallithea/tests/fixture.py
Show inline comments
 
@@ -92,12 +92,14 @@ class Fixture(object):
 
            repo_name=None,
 
            repo_type='hg',
 
            clone_uri='',
 
            repo_group='-1',
 
            repo_description='DESC',
 
            repo_private=False,
 
            repo_enable_statistics=False,
 
            repo_enable_downloads=False,
 
            repo_landing_rev='rev:tip',
 
            repo_copy_permissions=False,
 
            repo_state=db.Repository.STATE_CREATED,
 
        )
 
        defs.update(custom)
 
        if 'repo_name_full' not in custom:
 
@@ -324,12 +326,17 @@ class Fixture(object):
 
    def review_changeset(self, repo, revision, status, author=TEST_USER_ADMIN_LOGIN):
 
        comment = ChangesetCommentsModel().create("review comment", repo, author, revision=revision, send_email=False)
 
        csm = ChangesetStatusModel().set_status(repo, db.ChangesetStatus.STATUS_APPROVED, author, comment, revision=revision)
 
        meta.Session().commit()
 
        return csm
 

	
 
    def add_changeset_comment(self, repo, revision, text, author=TEST_USER_ADMIN_LOGIN, f_path=None, line_no=None):
 
        comment = ChangesetCommentsModel().create(text, repo, author, revision=revision, f_path=f_path, line_no=line_no, send_email=False)
 
        meta.Session().commit()
 
        return comment
 

	
 
    def create_pullrequest(self, testcontroller, repo_name, pr_src_rev, pr_dst_rev, title='title'):
 
        org_ref = 'branch:stable:%s' % pr_src_rev
 
        other_ref = 'branch:default:%s' % pr_dst_rev
 
        with test_context(testcontroller.app): # needed to be able to mock request user and routes.url
 
            org_repo = other_repo = db.Repository.get_by_repo_name(repo_name)
 
            owner_user = db.User.get_by_username(TEST_USER_ADMIN_LOGIN)
kallithea/tests/functional/test_admin_repo_groups.py
Show inline comments
 
@@ -50,12 +50,13 @@ class TestRepoGroupsController(base.Test
 
        response.mustcontain('name="group_name" type="text" value="%s"' % group_name)
 
        response.mustcontain('<!-- for: group_description -->')
 

	
 
        # edit
 
        response = self.app.post(base.url('update_repos_group', group_name=group_name),
 
                                         {'group_name': group_name,
 
                                         'owner': base.TEST_USER_REGULAR2_LOGIN,
 
                                         'group_description': 'lolo',
 
                                          '_session_csrf_secret_token': self.session_csrf_secret_token()})
 
        self.checkSessionFlash(response, 'Updated repository group %s' % group_name)
 
        response = response.follow()
 
        response.mustcontain('name="group_name" type="text" value="%s"' % group_name)
 
        response.mustcontain(no='<!-- for: group_description -->')
setup.py
Show inline comments
 
@@ -72,12 +72,14 @@ requirements = [
 
    "paginate >= 0.5, < 0.6",
 
    "paginate_sqlalchemy >= 0.3.0, < 0.4",
 
    "bcrypt >= 3.1.0, < 3.2",
 
    "pip >= 20.0, < 999",
 
    "chardet >= 3",
 
]
 
if sys.version_info < (3, 8):
 
    requirements.append("importlib-metadata < 5")
 

	
 
dependency_links = [
 
]
 

	
 
classifiers = [
 
    'Development Status :: 4 - Beta',
0 comments (0 inline, 0 general)