release: merge back stable branch into default

This commit is contained in:
Marcin Kuzminski 2020-03-24 14:38:15 +01:00
commit 54de45c2dd
125 changed files with 3361 additions and 2113 deletions

View file

@ -57,5 +57,5 @@ Each lines should represent a single name e.g `repo_name_1` or `repo_group/repo_
Run this line from CLI to execute the code from the `repo_delete_task.py` file and
exit the ishell after the execution::
echo "%run repo_delete_task.py" | rccontrol ishell Enterprise-1
echo "%run repo_delete_task.py" | rccontrol ishell enterprise-1

View file

@ -124,6 +124,7 @@ permission issues could occur. To do this edit the ``/etc/apache2/envvars``
LogLevel info
# allows custom host names, prevents 400 errors on checkout
HttpProtocolOptions Unsafe
# Most likely this will be: /home/user/.rccontrol/enterprise-1/mod_dav_svn.conf
Include /home/user/.rccontrol/enterprise-1/mod_dav_svn.conf
</VirtualHost>

View file

@ -48,7 +48,7 @@ uses, or if required it can be a different one. We recommend to use the same dat
To switch to reds-based user sessions uncomment the following section in
To switch to redis-based user sessions uncomment the following section in
your :file:`/home/{user}/.rccontrol/{instance-id}/rhodecode.ini` file.
.. code-block:: ini

View file

@ -33,6 +33,15 @@ To get |RCE| up and running, run through the below steps:
Do you accept the RhodeCode Control license?
Press [Y] to accept license and [V] to view license text: y
.. important::
We recommend running RhodeCode as a non-root user, such as `rhodecode`;
this user must have a proper home directory.
Either log in as that user to install the software, or do it as root
with `sudo -i -u rhodecode ./RhodeCode-installer-linux-*`
3. Install a VCS Server, and configure it to start at boot.
.. code-block:: bash

View file

@ -193,6 +193,10 @@ Fixes
Upgrade notes
^^^^^^^^^^^^^
- Major Celery Version upgrade. The 4.18.X release includes a major Celery version.
It's recommended to run `rccontrol self-stop && rccontrol self-init` after the
upgrade to ensure celery workers are restarted and updated.
- New Automation task. We've changed the logic for updating latest change inside repository group.
New logic includes scanning for changes in all nested objects. Since this is a heavy task
a new dedicated scheduler task has been created to update it automatically on a scheduled base.
@ -218,6 +222,11 @@ Upgrade notes
Please review vcsserver.ini settings under:
`rc_cache.repo_object.backend = dogpile.cache.rc.redis_msgpack`
- Gunicorn configuration now moved to .ini files.
Upgrading to 4.18.X will overwrite the gunicorn_conf.py file. If there are any custom changes in that file
they will be lost. Recommended way to configure gunicorn is now via the .ini files. Please check `rhodecode.template.ini` file
for example gunicorn configuration.
- New memory monitoring for Gunicorn workers. Starting from 4.18 release a option was added
to limit the maximum amount of memory used by a worker.
Please review new settings in `[server:main]` section for memory management in both

View file

@ -0,0 +1,60 @@
|RCE| 4.18.1 |RNS|
------------------
Release Date
^^^^^^^^^^^^
- 2020-01-20
New Features
^^^^^^^^^^^^
General
^^^^^^^
- API: invalidate license cache on set_license_key call.
- API: add send_email flag for comments api to allow commenting without email notification.
- API: added pull requests versions into returned API data.
- Dashboard: fixed jumping of text in grid loading by new loading indicator.
- Installation: add few extra defaults that makes RhodeCode nicer out of the box.
- Pull Requests: small code cleanup to define other type of merge username.
RC_MERGE_USER_NAME_ATTR env variable defines what should be used from user as merge username.
- Gists: cleanup UI and make the gist access id use monospace according to the new UI.
Security
^^^^^^^^
- Repository permission: properly flush permission caches on set private mode of repository.
Otherwise we get cached values still in place until it expires.
- Repository permission: add set/un-set of private repository from permissions page.
- Permissions: flush all user permissions in case of default user permission changes.
Performance
^^^^^^^^^^^
- Caches: used more efficient way of fetching all users for permissions invalidation.
- Issue trackers: optimized performance of fetching issue tracker patterns.
Fixes
^^^^^
- SSH: fixed SSH problems with EE edition.
- Branch permissions: remove emtpy tooltips on branch permission entries.
- Core: fixed cython compat inspect that caused some API calls to not work correctly in EE release.
- Audit logger: use copy of params we later modify to prevent from modification by the store
function of parameters that we only use for reading.
- Users: fixed wrong mention of readme in user description help block.
- Issue trackers: fixed wrong examples in patterns.
- Issue trackers: fixed missing option to get back to inherited settings.
Upgrade notes
^^^^^^^^^^^^^
- Scheduled release addressing problems in 4.18.X releases.

View file

@ -0,0 +1,49 @@
|RCE| 4.18.2 |RNS|
------------------
Release Date
^^^^^^^^^^^^
- 2020-01-28
New Features
^^^^^^^^^^^^
General
^^^^^^^
- Permissions: add better help text about default permissions, and correlation with anonymous access enabled.
- Mentions: markdown renderer now wraps username in hovercard logic allowing checking the mentioned user.
- Documentation: added note about hard restart due to celery update.
- Maintenance: run rebuildfncache for Mercurial in maintenance command.
Security
^^^^^^^^
Performance
^^^^^^^^^^^
- Authentication: cache plugins for auth and their settings in the auth_registry for single request.
This heavily influences SVN performance on multiple-file commits.
Fixes
^^^^^
- Descriptions: fixed rendering problem with certain meta-tags in repo description.
- Emails: fixed fonts rendering problems in Outlook.
- Emails: fixed bug in test email sending.
- Summary: fixed styling of readme indicator.
- Flash: fixed display problem with flash messages on error pages.
Upgrade notes
^^^^^^^^^^^^^
- Scheduled release addressing problems in 4.18.X releases.

View file

@ -0,0 +1,64 @@
|RCE| 4.18.3 |RNS|
------------------
Release Date
^^^^^^^^^^^^
- 2020-03-24
New Features
^^^^^^^^^^^^
- LDAP: added nested user groups sync which was planned in 4.18.X but didn't
make it to the release. New option for sync is available in the LDAP configuration.
General
^^^^^^^
- API: added branch permissions functions.
- Pull requests: added creating indicator to let users know they should wait until PR is creating.
- Pull requests: allow super-admins to force change state of locked PRs.
- Users/User groups: in edit mode we now show the actual name of what we're editing.
- SSH: allow generation of legacy SSH keys for older systems and Windows users.
- File store: don't response with cookie data on file-store download response.
- File store: use our own logic for setting content-type. This solves a problem
when previously used resolver set different content-type+content-encoding which
is an incorrect behaviour.
- My Account: show info about password usage for external accounts e.g github/google etc
We now recommend using auth-tokens instead of actual passwords.
- Repositories: in description field we now show mention of metatags only if they
are enabled.
Security
^^^^^^^^
- Remote sync: don't expose credentials in displayed URLs.
Remote links url had visible credentials displayed in the link.
This was used for web-view and not needed anymore.
Performance
^^^^^^^^^^^
- Full text search: significantly improved GIT commit indexing performance by reducing
number of calls to the vcsserver.
Fixes
^^^^^
- Mercurial: fixed cases of lookup of branches that are exactly 20 character long.
- SVN: allow legacy (pre SVN 1.7) extraction of post commit data.
- GIT: use non-unicode author extraction as it's returned as bytes from backend, and
we can get an unicode errors while there's some non-ascii characters.
- GIT: use safe configparser for git submodules to prevent from errors on submodules with % sign.
- System info: fixed UI problem with new version update info screen.
Upgrade notes
^^^^^^^^^^^^^
- Scheduled release addressing problems in 4.18.X releases.

View file

@ -9,6 +9,9 @@ Release Notes
.. toctree::
:maxdepth: 1
release-notes-4.18.3.rst
release-notes-4.18.2.rst
release-notes-4.18.1.rst
release-notes-4.18.0.rst
release-notes-4.17.4.rst
release-notes-4.17.3.rst

View file

@ -18,7 +18,6 @@
# RhodeCode Enterprise Edition, including its added features, Support services,
# and proprietary license terms, please see https://rhodecode.com/licenses/
import inspect
import itertools
import logging
import sys
@ -186,10 +185,12 @@ def request_view(request):
Main request handling method. It handles all logic to call a specific
exposed method
"""
# cython compatible inspect
from rhodecode.config.patches import inspect_getargspec
inspect = inspect_getargspec()
# check if we can find this session using api_key, get_by_auth_token
# search not expired tokens only
try:
api_user = User.get_by_auth_token(request.rpc_api_key)

View file

@ -56,5 +56,6 @@ class TestGetMethod(object):
'request': '<RequiredType>',
'resolves_comment_id': '<Optional:None>',
'status': '<Optional:None>',
'userid': '<Optional:<OptionalAttr:apiuser>>'}]
'userid': '<Optional:<OptionalAttr:apiuser>>',
'send_email': '<Optional:True>'}]
assert_ok(id_, expected, given=response.body)

View file

@ -66,7 +66,7 @@ class TestGrantUserGroupPermission(object):
perm=perm)
response = api_call(self.app, params)
expected = 'permission `%s` does not exist' % (perm,)
expected = 'permission `%s` does not exist.' % (perm,)
assert_error(id_, expected, given=response.body)
@mock.patch.object(RepoModel, 'grant_user_group_permission', crash)

View file

@ -132,8 +132,7 @@ class TestGrantUserGroupPermissionFromRepoGroup(object):
RepoGroupModel().revoke_user_group_permission(
repo_group.group_id, user_group.users_group_id)
else:
expected = 'repository group `%s` does not exist' % (
repo_group.name,)
expected = 'repository group `%s` does not exist' % (repo_group.name,)
assert_error(id_, expected, given=response.body)
def test_api_grant_user_group_permission_to_repo_group_wrong_permission(
@ -149,7 +148,7 @@ class TestGrantUserGroupPermissionFromRepoGroup(object):
perm=perm)
response = api_call(self.app, params)
expected = 'permission `%s` does not exist' % (perm,)
expected = 'permission `%s` does not exist. Permission should start with prefix: `group.`' % (perm,)
assert_error(id_, expected, given=response.body)
@mock.patch.object(RepoGroupModel, 'grant_user_group_permission', crash)

View file

@ -65,7 +65,7 @@ class TestGrantUserPermission(object):
perm=perm)
response = api_call(self.app, params)
expected = 'permission `%s` does not exist' % (perm,)
expected = 'permission `%s` does not exist.' % (perm,)
assert_error(id_, expected, given=response.body)
@mock.patch.object(RepoModel, 'grant_user_permission', crash)

View file

@ -132,7 +132,7 @@ class TestGrantUserPermissionFromRepoGroup(object):
perm=perm)
response = api_call(self.app, params)
expected = 'permission `%s` does not exist' % (perm,)
expected = 'permission `%s` does not exist. Permission should start with prefix: `group.`' % (perm,)
assert_error(id_, expected, given=response.body)
@mock.patch.object(RepoGroupModel, 'grant_user_permission', crash)

View file

@ -130,7 +130,7 @@ class TestGrantUserPermissionFromUserGroup(object):
perm=perm)
response = api_call(self.app, params)
expected = 'permission `%s` does not exist' % perm
expected = 'permission `%s` does not exist. Permission should start with prefix: `usergroup.`' % perm
assert_error(id_, expected, given=response.body)
def test_api_grant_user_permission_to_user_group_exception_when_adding(

View file

@ -308,7 +308,11 @@ def get_perm_or_error(permid, prefix=None):
perm = PermissionModel.cls.get_by_key(permid)
if perm is None:
raise JSONRPCError('permission `%s` does not exist' % (permid,))
msg = 'permission `{}` does not exist.'.format(permid)
if prefix:
msg += ' Permission should start with prefix: `{}`'.format(prefix)
raise JSONRPCError(msg)
if prefix:
if not perm.permission_name.startswith(prefix):
raise JSONRPCError('permission `%s` is invalid, '
@ -351,12 +355,12 @@ def get_pull_request_or_error(pullrequestid):
def build_commit_data(commit, detail_level):
parsed_diff = []
if detail_level == 'extended':
for f in commit.added:
parsed_diff.append(_get_commit_dict(filename=f.path, op='A'))
for f in commit.changed:
parsed_diff.append(_get_commit_dict(filename=f.path, op='M'))
for f in commit.removed:
parsed_diff.append(_get_commit_dict(filename=f.path, op='D'))
for f_path in commit.added_paths:
parsed_diff.append(_get_commit_dict(filename=f_path, op='A'))
for f_path in commit.changed_paths:
parsed_diff.append(_get_commit_dict(filename=f_path, op='M'))
for f_path in commit.removed_paths:
parsed_diff.append(_get_commit_dict(filename=f_path, op='D'))
elif detail_level == 'full':
from rhodecode.lib.diffs import DiffProcessor

View file

@ -73,6 +73,7 @@ def get_pull_request(request, apiuser, pullrequestid, repoid=Optional(None),
"status" : "<status>",
"created_on": "<date_time_created>",
"updated_on": "<date_time_updated>",
"versions": "<number_or_versions_of_pr>",
"commit_ids": [
...
"<commit_id>",
@ -452,7 +453,7 @@ def comment_pull_request(
message=Optional(None), commit_id=Optional(None), status=Optional(None),
comment_type=Optional(ChangesetComment.COMMENT_TYPE_NOTE),
resolves_comment_id=Optional(None), extra_recipients=Optional([]),
userid=Optional(OAttr('apiuser'))):
userid=Optional(OAttr('apiuser')), send_email=Optional(True)):
"""
Comment on the pull request specified with the `pullrequestid`,
in the |repo| specified by the `repoid`, and optionally change the
@ -483,6 +484,8 @@ def comment_pull_request(
:type extra_recipients: Optional(list)
:param userid: Comment on the pull request as this user
:type userid: Optional(str or int)
:param send_email: Define if this comment should also send email notification
:type send_email: Optional(bool)
Example output:
@ -527,6 +530,7 @@ def comment_pull_request(
comment_type = Optional.extract(comment_type)
resolves_comment_id = Optional.extract(resolves_comment_id)
extra_recipients = Optional.extract(extra_recipients)
send_email = Optional.extract(send_email, binary=True)
if not message and not status:
raise JSONRPCError(
@ -587,7 +591,8 @@ def comment_pull_request(
comment_type=comment_type,
resolves_comment_id=resolves_comment_id,
auth_user=auth_user,
extra_recipients=extra_recipients
extra_recipients=extra_recipients,
send_email=send_email
)
if allowed_to_change_status and status:

View file

@ -1551,7 +1551,7 @@ def comment_commit(
request, apiuser, repoid, commit_id, message, status=Optional(None),
comment_type=Optional(ChangesetComment.COMMENT_TYPE_NOTE),
resolves_comment_id=Optional(None), extra_recipients=Optional([]),
userid=Optional(OAttr('apiuser'))):
userid=Optional(OAttr('apiuser')), send_email=Optional(True)):
"""
Set a commit comment, and optionally change the status of the commit.
@ -1575,6 +1575,8 @@ def comment_commit(
:type extra_recipients: Optional(list)
:param userid: Set the user name of the comment creator.
:type userid: Optional(str or int)
:param send_email: Define if this comment should also send email notification
:type send_email: Optional(bool)
Example error output:
@ -1610,6 +1612,7 @@ def comment_commit(
comment_type = Optional.extract(comment_type)
resolves_comment_id = Optional.extract(resolves_comment_id)
extra_recipients = Optional.extract(extra_recipients)
send_email = Optional.extract(send_email, binary=True)
allowed_statuses = [x[0] for x in ChangesetStatus.STATUSES]
if status and status not in allowed_statuses:
@ -1639,7 +1642,8 @@ def comment_commit(
comment_type=comment_type,
resolves_comment_id=resolves_comment_id,
auth_user=apiuser,
extra_recipients=extra_recipients
extra_recipients=extra_recipients,
send_email=send_email
)
if status:
# also do a status change

View file

@ -18,7 +18,6 @@
# RhodeCode Enterprise Edition, including its added features, Support services,
# and proprietary license terms, please see https://rhodecode.com/licenses/
import inspect
import logging
import itertools
import base64
@ -334,6 +333,9 @@ def get_method(request, apiuser, pattern=Optional('*')):
]
error : null
"""
from rhodecode.config.patches import inspect_getargspec
inspect = inspect_getargspec()
if not has_superadmin_permission(apiuser):
raise JSONRPCForbidden()

View file

@ -37,7 +37,7 @@ from rhodecode.model import user_group
from rhodecode.model import user
from rhodecode.model.db import User
from rhodecode.model.scm import ScmModel
from rhodecode.model.settings import VcsSettingsModel
from rhodecode.model.settings import VcsSettingsModel, IssueTrackerSettingsModel
from rhodecode.model.repo import ReadmeFinder
log = logging.getLogger(__name__)
@ -226,6 +226,7 @@ class RepoAppView(BaseAppView):
self.db_repo_name = self.db_repo.repo_name
self.db_repo_pull_requests = ScmModel().get_pull_requests(self.db_repo)
self.db_repo_artifacts = ScmModel().get_artifacts(self.db_repo)
self.db_repo_patterns = IssueTrackerSettingsModel(repo=self.db_repo)
def _handle_missing_requirements(self, error):
log.error(

View file

@ -573,7 +573,7 @@ class AdminSettingsView(BaseAppView):
email_kwargs = {
'date': datetime.datetime.now(),
'user': c.rhodecode_user
'user': self._rhodecode_db_user
}
(subject, headers, email_body,

View file

@ -872,7 +872,10 @@ class UsersView(UserAppView):
c.active = 'ssh_keys_generate'
comment = 'RhodeCode-SSH {}'.format(c.user.email or '')
c.private, c.public = SshKeyModel().generate_keypair(comment=comment)
private_format = self.request.GET.get('private_format') \
or SshKeyModel.DEFAULT_PRIVATE_KEY_FORMAT
c.private, c.public = SshKeyModel().generate_keypair(
comment=comment, private_format=private_format)
return self._get_template_context(c)

View file

@ -33,6 +33,7 @@ from rhodecode.lib import audit_logger
from rhodecode.lib.auth import (
CSRFRequired, NotAnonymous, HasRepoPermissionAny, HasRepoGroupPermissionAny,
LoginRequired)
from rhodecode.lib.vcs.conf.mtypes import get_mimetypes_db
from rhodecode.model.db import Session, FileStore, UserApiKeys
log = logging.getLogger(__name__)
@ -46,6 +47,15 @@ class FileStoreView(BaseAppView):
self.storage = utils.get_file_storage(self.request.registry.settings)
return c
def _guess_type(self, file_name):
"""
Our own type guesser for mimetypes using the rich DB
"""
if not hasattr(self, 'db'):
self.db = get_mimetypes_db()
_content_type, _encoding = self.db.guess_type(file_name, strict=False)
return _content_type, _encoding
def _serve_file(self, file_uid):
if not self.storage.exists(file_uid):
@ -92,7 +102,18 @@ class FileStoreView(BaseAppView):
FileStore.bump_access_counter(file_uid)
file_path = self.storage.store_path(file_uid)
return FileResponse(file_path)
content_type = 'application/octet-stream'
content_encoding = None
_content_type, _encoding = self._guess_type(file_path)
if _content_type:
content_type = _content_type
# For file store we don't submit any session data, this logic tells the
# Session lib to skip it
setattr(self.request, '_file_response', True)
return FileResponse(file_path, request=self.request,
content_type=content_type, content_encoding=content_encoding)
@LoginRequired()
@NotAnonymous()

View file

@ -105,11 +105,11 @@ class TestGistsController(TestController):
g4 = create_gist('gist4', gist_type='private').gist_access_id
response = self.app.get(route_path('gists_show'))
response.mustcontain('gist: %s' % g1.gist_access_id)
response.mustcontain('gist: %s' % g2.gist_access_id)
response.mustcontain('gist: %s' % g3.gist_access_id)
response.mustcontain(g1.gist_access_id)
response.mustcontain(g2.gist_access_id)
response.mustcontain(g3.gist_access_id)
response.mustcontain('gist3-desc')
response.mustcontain(no=['gist: %s' % g4])
response.mustcontain(no=[g4])
# Expiration information should be visible
expires_tag = '%s' % h.age_component(
@ -122,7 +122,7 @@ class TestGistsController(TestController):
response = self.app.get(route_path('gists_show', params=dict(private=1)))
# and privates
response.mustcontain('gist: %s' % gist.gist_access_id)
response.mustcontain(gist.gist_access_id)
def test_index_show_all(self, create_gist):
self.log_user()
@ -136,7 +136,7 @@ class TestGistsController(TestController):
assert len(GistModel.get_all()) == 4
# and privates
for gist in GistModel.get_all():
response.mustcontain('gist: %s' % gist.gist_access_id)
response.mustcontain(gist.gist_access_id)
def test_index_show_all_hidden_from_regular(self, create_gist):
self.log_user(TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS)
@ -150,7 +150,7 @@ class TestGistsController(TestController):
# since we don't have access to private in this view, we
# should see nothing
for gist in GistModel.get_all():
response.mustcontain(no=['gist: %s' % gist.gist_access_id])
response.mustcontain(no=[gist.gist_access_id])
def test_create(self):
self.log_user()

View file

@ -33,7 +33,7 @@ def assert_and_get_main_filter_content(result):
if data_item['type'] == 'search':
display_val = data_item['value_display']
if data_item['id'] == -1:
assert 'File search for:' in display_val, display_val
assert 'File content search for:' in display_val, display_val
elif data_item['id'] == -2:
assert 'Commit search for:' in display_val, display_val
else:

View file

@ -453,7 +453,7 @@ class HomeView(BaseAppView, DataGridAppView):
qry = query
return {'q': qry, 'type': 'content'}
label = u'File search for `{}`'.format(h.escape(query))
label = u'File content search for `{}`'.format(h.escape(query))
file_qry = {
'id': -10,
'value': query,
@ -497,7 +497,7 @@ class HomeView(BaseAppView, DataGridAppView):
qry = query
return {'q': qry, 'type': 'content'}
label = u'File search for `{}`'.format(query)
label = u'File content search for `{}`'.format(query)
file_qry = {
'id': -30,
'value': query,
@ -541,7 +541,7 @@ class HomeView(BaseAppView, DataGridAppView):
{
'id': -1,
'value': query,
'value_display': u'File search for: `{}`'.format(query),
'value_display': u'File content search for: `{}`'.format(query),
'value_icon': '<i class="icon-code"></i>',
'type': 'search',
'subtype': 'global',

View file

@ -25,6 +25,10 @@ def includeme(config):
name='hovercard_user',
pattern='/_hovercard/user/{user_id}')
config.add_route(
name='hovercard_username',
pattern='/_hovercard/username/{username}')
config.add_route(
name='hovercard_user_group',
pattern='/_hovercard/user_group/{user_group_id}')

View file

@ -63,6 +63,19 @@ class HoverCardsView(BaseAppView):
c.user = User.get_or_404(user_id)
return self._get_template_context(c)
@LoginRequired()
@view_config(
route_name='hovercard_username', request_method='GET', xhr=True,
renderer='rhodecode:templates/hovercards/hovercard_user.mako')
def hovercard_username(self):
c = self.load_default_context()
username = self.request.matchdict['username']
c.user = User.get_by_username(username)
if not c.user:
raise HTTPNotFound()
return self._get_template_context(c)
@LoginRequired()
@view_config(
route_name='hovercard_user_group', request_method='GET', xhr=True,

View file

@ -108,7 +108,8 @@ class TestLoginController(object):
def test_login_regular_forbidden_when_super_admin_restriction(self):
from rhodecode.authentication.plugins.auth_rhodecode import RhodeCodeAuthPlugin
with fixture.auth_restriction(RhodeCodeAuthPlugin.AUTH_RESTRICTION_SUPER_ADMIN):
with fixture.auth_restriction(self.app._pyramid_registry,
RhodeCodeAuthPlugin.AUTH_RESTRICTION_SUPER_ADMIN):
response = self.app.post(route_path('login'),
{'username': 'test_regular',
'password': 'test12'})
@ -118,7 +119,8 @@ class TestLoginController(object):
def test_login_regular_forbidden_when_scope_restriction(self):
from rhodecode.authentication.plugins.auth_rhodecode import RhodeCodeAuthPlugin
with fixture.scope_restriction(RhodeCodeAuthPlugin.AUTH_RESTRICTION_SCOPE_VCS):
with fixture.scope_restriction(self.app._pyramid_registry,
RhodeCodeAuthPlugin.AUTH_RESTRICTION_SCOPE_VCS):
response = self.app.post(route_path('login'),
{'username': 'test_regular',
'password': 'test12'})

View file

@ -76,6 +76,7 @@ class MyAccountView(BaseAppView, DataGridAppView):
def my_account_profile(self):
c = self.load_default_context()
c.active = 'profile'
c.extern_type = c.user.extern_type
return self._get_template_context(c)
@LoginRequired()

View file

@ -72,8 +72,11 @@ class MyAccountSshKeysView(BaseAppView, DataGridAppView):
c.active = 'ssh_keys_generate'
if c.ssh_key_generator_enabled:
private_format = self.request.GET.get('private_format') \
or SshKeyModel.DEFAULT_PRIVATE_KEY_FORMAT
comment = 'RhodeCode-SSH {}'.format(c.user.email or '')
c.private, c.public = SshKeyModel().generate_keypair(comment=comment)
c.private, c.public = SshKeyModel().generate_keypair(
comment=comment, private_format=private_format)
c.target_form_url = h.route_path(
'my_account_ssh_keys', _query=dict(default_key=c.public))
return self._get_template_context(c)

View file

@ -28,6 +28,7 @@ from rhodecode.lib import helpers as h
from rhodecode.lib import audit_logger
from rhodecode.lib.auth import (
LoginRequired, HasRepoGroupPermissionAnyDecorator, CSRFRequired)
from rhodecode.model.db import User
from rhodecode.model.permission import PermissionModel
from rhodecode.model.repo_group import RepoGroupModel
from rhodecode.model.forms import RepoGroupPermsForm
@ -96,7 +97,13 @@ class RepoGroupPermissionsView(RepoGroupAppView):
Session().commit()
h.flash(_('Repository Group permissions updated'), category='success')
PermissionModel().flush_user_permission_caches(changes)
affected_user_ids = None
if changes.get('default_user_changed', False):
# if we change the default user, we need to flush everyone permissions
affected_user_ids = User.get_all_user_ids()
PermissionModel().flush_user_permission_caches(
changes, affected_user_ids=affected_user_ids)
raise HTTPFound(
h.route_path('edit_repo_group_perms',

View file

@ -28,6 +28,8 @@ from rhodecode.lib import helpers as h
from rhodecode.lib import audit_logger
from rhodecode.lib.auth import (
LoginRequired, HasRepoPermissionAnyDecorator, CSRFRequired)
from rhodecode.lib.utils2 import str2bool
from rhodecode.model.db import User
from rhodecode.model.forms import RepoPermsForm
from rhodecode.model.meta import Session
from rhodecode.model.permission import PermissionModel
@ -89,7 +91,12 @@ class RepoSettingsPermissionsView(RepoAppView):
Session().commit()
h.flash(_('Repository access permissions updated'), category='success')
PermissionModel().flush_user_permission_caches(changes)
affected_user_ids = None
if changes.get('default_user_changed', False):
# if we change the default user, we need to flush everyone permissions
affected_user_ids = User.get_all_user_ids()
PermissionModel().flush_user_permission_caches(
changes, affected_user_ids=affected_user_ids)
raise HTTPFound(
h.route_path('edit_repo_perms', repo_name=self.db_repo_name))
@ -104,9 +111,11 @@ class RepoSettingsPermissionsView(RepoAppView):
_ = self.request.translate
self.load_default_context()
private_flag = str2bool(self.request.POST.get('private'))
try:
RepoModel().update(
self.db_repo, **{'repo_private': True, 'repo_name': self.db_repo_name})
self.db_repo, **{'repo_private': private_flag, 'repo_name': self.db_repo_name})
Session().commit()
h.flash(_('Repository `{}` private mode set successfully').format(self.db_repo_name),
@ -116,7 +125,11 @@ class RepoSettingsPermissionsView(RepoAppView):
h.flash(_('Error occurred during update of repository {}').format(
self.db_repo_name), category='error')
# NOTE(dan): we change repo private mode we need to notify all USERS
affected_user_ids = User.get_all_user_ids()
PermissionModel().trigger_permission_flush(affected_user_ids)
return {
'redirect_url': h.route_path('edit_repo_perms', repo_name=self.db_repo_name),
'private': True
'private': private_flag
}

View file

@ -275,6 +275,20 @@ class RepoPullRequestsView(RepoAppView, DataGridAppView):
c.state_progressing = pull_request.is_state_changing()
_new_state = {
'created': PullRequest.STATE_CREATED,
}.get(self.request.GET.get('force_state'))
if c.is_super_admin and _new_state:
with pull_request.set_state(PullRequest.STATE_UPDATING, final_state=_new_state):
h.flash(
_('Pull Request state was force changed to `{}`').format(_new_state),
category='success')
Session().commit()
raise HTTPFound(h.route_path(
'pullrequest_show', repo_name=self.db_repo_name,
pull_request_id=pull_request_id))
version = self.request.GET.get('version')
from_version = self.request.GET.get('from_version') or version
merge_checks = self.request.GET.get('merge_checks')

View file

@ -31,7 +31,7 @@ from rhodecode.lib.auth import (
LoginRequired, HasRepoPermissionAnyDecorator, CSRFRequired)
from rhodecode.model.forms import IssueTrackerPatternsForm
from rhodecode.model.meta import Session
from rhodecode.model.settings import IssueTrackerSettingsModel, SettingsModel
from rhodecode.model.settings import SettingsModel
log = logging.getLogger(__name__)
@ -53,7 +53,7 @@ class RepoSettingsIssueTrackersView(RepoAppView):
c.active = 'issuetracker'
c.data = 'data'
c.settings_model = IssueTrackerSettingsModel(repo=self.db_repo)
c.settings_model = self.db_repo_patterns
c.global_patterns = c.settings_model.get_global_settings()
c.repo_patterns = c.settings_model.get_repo_settings()
@ -79,7 +79,7 @@ class RepoSettingsIssueTrackersView(RepoAppView):
def repo_issuetracker_delete(self):
_ = self.request.translate
uid = self.request.POST.get('uid')
repo_settings = IssueTrackerSettingsModel(repo=self.db_repo_name)
repo_settings = self.db_repo_patterns
try:
repo_settings.delete_entries(uid)
except Exception:
@ -113,7 +113,7 @@ class RepoSettingsIssueTrackersView(RepoAppView):
def repo_issuetracker_update(self):
_ = self.request.translate
# Save inheritance
repo_settings = IssueTrackerSettingsModel(repo=self.db_repo_name)
repo_settings = self.db_repo_patterns
inherited = (
self.request.POST.get('inherit_global_issuetracker') == "inherited")
repo_settings.inherit_global_settings = inherited

View file

@ -24,6 +24,7 @@
# LogLevel info
# # allows custom host names, prevents 400 errors on checkout
# HttpProtocolOptions Unsafe
# # Most likely this will be: /home/user/.rccontrol/enterprise-1/mod_dav_svn.conf
# Include /path/to/generated/mod_dav_svn.conf
# </VirtualHost>
#

View file

@ -150,6 +150,7 @@ class RhodeCodeAuthPluginBase(object):
def __init__(self, plugin_id):
self._plugin_id = plugin_id
self._settings = {}
def __str__(self):
return self.get_id()
@ -226,17 +227,26 @@ class RhodeCodeAuthPluginBase(object):
"""
return AuthnPluginSettingsSchemaBase()
def get_settings(self):
"""
Returns the plugin settings as dictionary.
"""
def _propagate_settings(self, raw_settings):
settings = {}
raw_settings = SettingsModel().get_all_settings()
for node in self.get_settings_schema():
settings[node.name] = self.get_setting_by_name(
node.name, plugin_cached_settings=raw_settings)
return settings
def get_settings(self, use_cache=True):
"""
Returns the plugin settings as dictionary.
"""
if self._settings != {} and use_cache:
return self._settings
raw_settings = SettingsModel().get_all_settings()
settings = self._propagate_settings(raw_settings)
self._settings = settings
return self._settings
def get_setting_by_name(self, name, default=None, plugin_cached_settings=None):
"""
Returns a plugin setting by name.
@ -594,19 +604,19 @@ class AuthLdapBase(object):
if not full_resolve:
return '{}:{}'.format(host, port)
log.debug('LDAP: Resolving IP for LDAP host %s', host)
log.debug('LDAP: Resolving IP for LDAP host `%s`', host)
try:
ip = socket.gethostbyname(host)
log.debug('Got LDAP server %s ip %s', host, ip)
log.debug('LDAP: Got LDAP host `%s` ip %s', host, ip)
except Exception:
raise LdapConnectionError(
'Failed to resolve host: `{}`'.format(host))
raise LdapConnectionError('Failed to resolve host: `{}`'.format(host))
log.debug('LDAP: Checking if IP %s is accessible', ip)
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
s.connect((ip, int(port)))
s.shutdown(socket.SHUT_RD)
log.debug('LDAP: connection to %s successful', ip)
except Exception:
raise LdapConnectionError(
'Failed to connect to host: `{}:{}`'.format(host, port))
@ -667,7 +677,7 @@ def loadplugin(plugin_id):
def get_authn_registry(registry=None):
registry = registry or get_current_registry()
authn_registry = registry.getUtility(IAuthnPluginRegistry)
authn_registry = registry.queryUtility(IAuthnPluginRegistry)
return authn_registry
@ -690,6 +700,7 @@ def authenticate(username, password, environ=None, auth_type=None,
headers_only = environ and not (username and password)
authn_registry = get_authn_registry(registry)
plugins_to_check = authn_registry.get_plugins_for_authentication()
log.debug('Starting ordered authentication chain using %s plugins',
[x.name for x in plugins_to_check])

View file

@ -145,16 +145,16 @@ class AuthLdap(AuthLdapBase):
log.debug('Trying simple_bind with password and given login DN: %r',
self.LDAP_BIND_DN)
ldap_conn.simple_bind_s(self.LDAP_BIND_DN, self.LDAP_BIND_PASS)
log.debug('simple_bind successful')
return ldap_conn
def fetch_attrs_from_simple_bind(self, server, dn, username, password):
try:
log.debug('Trying simple bind with %r', dn)
server.simple_bind_s(dn, safe_str(password))
user = server.search_ext_s(
_dn, attrs = server.search_ext_s(
dn, ldap.SCOPE_BASE, '(objectClass=*)', )[0]
_, attrs = user
return attrs
except ldap.INVALID_CREDENTIALS:
@ -206,7 +206,7 @@ class AuthLdap(AuthLdapBase):
break
else:
raise LdapPasswordError(
'Failed to authenticate user `{}`'
'Failed to authenticate user `{}` '
'with given password'.format(username))
except ldap.NO_SUCH_OBJECT:
@ -249,7 +249,7 @@ class LdapSettingsSchema(AuthnPluginSettingsSchemaBase):
colander.Int(),
default=389,
description=_('Custom port that the LDAP server is listening on. '
'Default value is: 389, use 689 for LDAPS(SSL)'),
'Default value is: 389, use 689 for LDAPS (SSL)'),
preparer=strip_whitespace,
title=_('Port'),
validator=colander.Range(min=0, max=65536),
@ -272,7 +272,7 @@ class LdapSettingsSchema(AuthnPluginSettingsSchemaBase):
'uid=root,cn=users,dc=mydomain,dc=com, or admin@mydomain.com'),
missing='',
preparer=strip_whitespace,
title=_('Account'),
title=_('Bind account'),
widget='string')
dn_pass = colander.SchemaNode(
colander.String(),
@ -280,7 +280,7 @@ class LdapSettingsSchema(AuthnPluginSettingsSchemaBase):
description=_('Password to authenticate for given user DN.'),
missing='',
preparer=strip_whitespace,
title=_('Password'),
title=_('Bind account password'),
widget='password')
tls_kind = colander.SchemaNode(
colander.String(),
@ -318,7 +318,7 @@ class LdapSettingsSchema(AuthnPluginSettingsSchemaBase):
colander.String(),
default='',
description=_('Base DN to search. Dynamic bind is supported. Add `$login` marker '
'in it to be replaced with current user credentials \n'
'in it to be replaced with current user username \n'
'(e.g., dc=mydomain,dc=com, or ou=Users,dc=mydomain,dc=com)'),
missing='',
preparer=strip_whitespace,

View file

@ -38,6 +38,7 @@ class AuthenticationPluginRegistry(object):
def __init__(self, settings):
self._plugins = {}
self._plugins_for_auth = None
self._fallback_plugin = settings.get(self.fallback_plugin_key, None)
def add_authn_plugin(self, config, plugin):
@ -63,6 +64,10 @@ class AuthenticationPluginRegistry(object):
if plugin.uid == plugin_uid:
return plugin
def invalidate_plugins_for_auth(self):
log.debug('Invalidating cached plugins for authentication')
self._plugins_for_auth = None
def get_plugins_for_authentication(self):
"""
Returns a list of plugins which should be consulted when authenticating
@ -70,6 +75,9 @@ class AuthenticationPluginRegistry(object):
Additionally it includes the fallback plugin from the INI file, if
`rhodecode.auth_plugin_fallback` is set to a plugin ID.
"""
if self._plugins_for_auth is not None:
return self._plugins_for_auth
plugins = []
# Add all enabled and active plugins to the list. We iterate over the
@ -80,6 +88,9 @@ class AuthenticationPluginRegistry(object):
plugin = self.get_plugin(plugin_id)
if plugin is not None and plugin.is_active(
plugin_cached_settings=raw_settings):
# inject settings into plugin, we can re-use the DB fetched settings here
plugin._settings = plugin._propagate_settings(raw_settings)
plugins.append(plugin)
# Add the fallback plugin from ini file.
@ -89,6 +100,8 @@ class AuthenticationPluginRegistry(object):
self._fallback_plugin)
plugin = self.get_plugin(self._fallback_plugin)
if plugin is not None and plugin not in plugins:
plugin._settings = plugin._propagate_settings(raw_settings)
plugins.append(plugin)
return plugins
self._plugins_for_auth = plugins
return self._plugins_for_auth

View file

@ -99,11 +99,12 @@ class AuthnPluginViewBase(BaseAppView):
for name, value in valid_data.items():
self.plugin.create_or_update_setting(name, value)
Session().commit()
SettingsModel().invalidate_settings_cache()
# Display success message and redirect.
h.flash(_('Auth settings updated successfully.'), category='success')
redirect_to = self.request.resource_path(
self.context, route_name='auth_home')
redirect_to = self.request.resource_path(self.context, route_name='auth_home')
return HTTPFound(redirect_to)
@ -159,7 +160,7 @@ class AuthSettingsView(BaseAppView):
'auth_plugins', plugins)
Session().add(setting)
Session().commit()
SettingsModel().invalidate_settings_cache()
h.flash(_('Auth settings updated successfully.'), category='success')
except formencode.Invalid as errors:
e = errors.error_dict or {}
@ -174,6 +175,6 @@ class AuthSettingsView(BaseAppView):
h.flash(_('Error occurred during update of auth settings.'),
category='error')
redirect_to = self.request.resource_path(
self.context, route_name='auth_home')
redirect_to = self.request.resource_path(self.context, route_name='auth_home')
return HTTPFound(redirect_to)

View file

@ -95,3 +95,5 @@ def inspect_getargspec():
return inspect.ArgSpec(args, varargs, varkw, func.func_defaults)
inspect.getargspec = custom_getargspec
return inspect

File diff suppressed because it is too large Load diff

View file

@ -151,20 +151,22 @@ def _store_log(action_name, action_data, user_id, username, user_data,
def store_web(*args, **kwargs):
if 'action_data' not in kwargs:
kwargs['action_data'] = {}
kwargs['action_data'].update({
'source': SOURCE_WEB
})
action_data = {}
org_action_data = kwargs.pop('action_data', {})
action_data.update(org_action_data)
action_data['source'] = SOURCE_WEB
kwargs['action_data'] = action_data
return store(*args, **kwargs)
def store_api(*args, **kwargs):
if 'action_data' not in kwargs:
kwargs['action_data'] = {}
kwargs['action_data'].update({
'source': SOURCE_API
})
action_data = {}
org_action_data = kwargs.pop('action_data', {})
action_data.update(org_action_data)
action_data['source'] = SOURCE_API
kwargs['action_data'] = action_data
return store(*args, **kwargs)

View file

@ -24,7 +24,6 @@ authentication and permission libraries
import os
import time
import inspect
import collections
import fnmatch
import hashlib
@ -2013,6 +2012,7 @@ class PermsFunction(object):
self.user_group_name = None
def __bool__(self):
import inspect
frame = inspect.currentframe()
stack_trace = traceback.format_stack(frame)
log.error('Checking bool value on a class instance of perm '

View file

@ -211,8 +211,9 @@ def vcs_operation_context(
class BasicAuth(AuthBasicAuthenticator):
def __init__(self, realm, authfunc, registry, auth_http_code=None,
initial_call_detection=False, acl_repo_name=None):
initial_call_detection=False, acl_repo_name=None, rc_realm=''):
self.realm = realm
self.rc_realm = rc_realm
self.initial_call = initial_call_detection
self.authfunc = authfunc
self.registry = registry
@ -227,7 +228,7 @@ class BasicAuth(AuthBasicAuthenticator):
return HTTPForbidden
def get_rc_realm(self):
return safe_str(self.registry.rhodecode_settings.get('rhodecode_realm'))
return safe_str(self.rc_realm)
def build_authentication(self):
head = WWW_AUTHENTICATE.tuples('Basic realm="%s"' % self.realm)
@ -288,7 +289,7 @@ def attach_context_attributes(context, request, user_id=None):
"""
config = request.registry.settings
rc_config = SettingsModel().get_all_settings(cache=True)
rc_config = SettingsModel().get_all_settings(cache=True, from_request=False)
context.rc_config = rc_config
context.rhodecode_version = rhodecode.__version__
context.rhodecode_edition = config.get('rhodecode.edition')

View file

@ -66,11 +66,12 @@ markdown_tags = [
markdown_attrs = {
"*": ["class", "style", "align"],
"img": ["src", "alt", "title"],
"a": ["href", "alt", "title", "name"],
"a": ["href", "alt", "title", "name", "data-hovercard-alt", "data-hovercard-url"],
"abbr": ["title"],
"acronym": ["title"],
"pre": ["lang"],
"input": ["type", "disabled", "checked"]
"input": ["type", "disabled", "checked"],
"strong": ["title", "data-hovercard-alt", "data-hovercard-url"],
}
standard_styles = [

View file

@ -421,9 +421,20 @@ class DbManage(object):
:param skip_existing:
"""
defaults = [
('auth_plugins',
'egg:rhodecode-enterprise-ce#token,egg:rhodecode-enterprise-ce#rhodecode',
'list'),
for k, v, t in [('auth_plugins', 'egg:rhodecode-enterprise-ce#rhodecode', 'list'),
('auth_rhodecode_enabled', 'True', 'bool')]:
('auth_authtoken_enabled',
'True',
'bool'),
('auth_rhodecode_enabled',
'True',
'bool'),
]
for k, v, t in defaults:
if (skip_existing and
SettingsModel().get_setting_by_name(k) is not None):
log.debug('Skipping option %s', k)
@ -568,19 +579,32 @@ class DbManage(object):
('title', '', 'unicode'),
('pre_code', '', 'unicode'),
('post_code', '', 'unicode'),
# Visual
('show_public_icon', True, 'bool'),
('show_private_icon', True, 'bool'),
('stylify_metatags', False, 'bool'),
('dashboard_items', 100, 'int'),
('admin_grid_items', 25, 'int'),
('markup_renderer', 'markdown', 'unicode'),
('show_version', True, 'bool'),
('use_gravatar', False, 'bool'),
('gravatar_url', User.DEFAULT_GRAVATAR_URL, 'unicode'),
('clone_uri_tmpl', Repository.DEFAULT_CLONE_URI, 'unicode'),
('support_url', '', 'unicode'),
('update_url', RhodeCodeSetting.DEFAULT_UPDATE_URL, 'unicode'),
('show_revision_number', True, 'bool'),
('show_sha_length', 12, 'int'),
('use_gravatar', False, 'bool'),
('gravatar_url', User.DEFAULT_GRAVATAR_URL, 'unicode'),
('clone_uri_tmpl', Repository.DEFAULT_CLONE_URI, 'unicode'),
('clone_uri_ssh_tmpl', Repository.DEFAULT_CLONE_URI_SSH, 'unicode'),
('support_url', '', 'unicode'),
('update_url', RhodeCodeSetting.DEFAULT_UPDATE_URL, 'unicode'),
# VCS Settings
('pr_merge_enabled', True, 'bool'),
('use_outdated_comments', True, 'bool'),
('diff_cache', True, 'bool'),
]
for key, val, type_ in settings:

View file

@ -598,9 +598,10 @@ class _Message(object):
* ``category``: the category specified when the message was created.
"""
def __init__(self, category, message):
def __init__(self, category, message, sub_data=None):
self.category = category
self.message = message
self.sub_data = sub_data or {}
def __str__(self):
return self.message
@ -663,7 +664,17 @@ class Flash(object):
# of strings.
for cat in self.categories:
for msg in session.pop_flash(queue=cat):
messages.append(_Message(cat, msg))
sub_data = {}
if hasattr(msg, 'rsplit'):
flash_data = msg.rsplit('|DELIM|', 1)
org_message = flash_data[0]
if len(flash_data) > 1:
sub_data = json.loads(flash_data[1])
else:
org_message = msg
messages.append(_Message(cat, org_message, sub_data=sub_data))
# Map messages from the default queue to the 'notice' category.
for msg in session.pop_flash():
messages.append(_Message('notice', msg))
@ -673,25 +684,16 @@ class Flash(object):
def json_alerts(self, session=None, request=None):
payloads = []
messages = flash.pop_messages(session=session, request=request)
if messages:
for message in messages:
subdata = {}
if hasattr(message.message, 'rsplit'):
flash_data = message.message.rsplit('|DELIM|', 1)
org_message = flash_data[0]
if len(flash_data) > 1:
subdata = json.loads(flash_data[1])
else:
org_message = message.message
payloads.append({
'message': {
'message': u'{}'.format(org_message),
'level': message.category,
'force': True,
'subdata': subdata
}
})
messages = flash.pop_messages(session=session, request=request) or []
for message in messages:
payloads.append({
'message': {
'message': u'{}'.format(message.message),
'level': message.category,
'force': True,
'subdata': message.sub_data
}
})
return json.dumps(payloads)
def __call__(self, message, category=None, ignore_duplicate=True,
@ -1514,6 +1516,9 @@ def get_active_pattern_entries(repo_name):
return active_entries
pr_pattern_re = re.compile(r'(?:(?:^!)|(?: !))(\d+)')
def process_patterns(text_string, repo_name, link_format='html', active_entries=None):
allowed_formats = ['html', 'rst', 'markdown',
@ -1522,7 +1527,10 @@ def process_patterns(text_string, repo_name, link_format='html', active_entries=
raise ValueError('Link format can be only one of:{} got {}'.format(
allowed_formats, link_format))
active_entries = active_entries or get_active_pattern_entries(repo_name)
if active_entries is None:
log.debug('Fetch active patterns for repo: %s', repo_name)
active_entries = get_active_pattern_entries(repo_name)
issues_data = []
new_text = text_string
@ -1537,11 +1545,14 @@ def process_patterns(text_string, repo_name, link_format='html', active_entries=
log.debug('issue tracker entry: uid: `%s` PAT:%s URL:%s PREFIX:%s',
uid, entry['pat'], entry['url'], entry['pref'])
try:
pattern = re.compile(r'%s' % entry['pat'])
except re.error:
log.exception('issue tracker pattern: `%s` failed to compile', entry['pat'])
continue
if entry.get('pat_compiled'):
pattern = entry['pat_compiled']
else:
try:
pattern = re.compile(r'%s' % entry['pat'])
except re.error:
log.exception('issue tracker pattern: `%s` failed to compile', entry['pat'])
continue
data_func = partial(
_process_url_func, repo_name=repo_name, entry=entry, uid=uid,
@ -1569,7 +1580,7 @@ def process_patterns(text_string, repo_name, link_format='html', active_entries=
pr_url_func = partial(
_process_url_func, repo_name=repo_name, entry=pr_entry, uid=None,
link_format=link_format+'+hovercard')
new_text = re.compile(r'(?:(?:^!)|(?: !))(\d+)').sub(pr_url_func, new_text)
new_text = pr_pattern_re.sub(pr_url_func, new_text)
log.debug('processed !pr pattern')
return new_text, issues_data
@ -1580,6 +1591,7 @@ def urlify_commit_message(commit_text, repository=None, active_pattern_entries=N
Parses given text message and makes proper links.
issues are linked to given issue-server, and rest is a commit link
"""
def escaper(_text):
return _text.replace('<', '&lt;').replace('>', '&gt;')
@ -1636,7 +1648,7 @@ def renderer_from_filename(filename, exclude=None):
def render(source, renderer='rst', mentions=False, relative_urls=None,
repo_name=None):
repo_name=None, active_pattern_entries=None):
def maybe_convert_relative_links(html_source):
if relative_urls:
@ -1651,7 +1663,8 @@ def render(source, renderer='rst', mentions=False, relative_urls=None,
if repo_name:
# process patterns on comments if we pass in repo name
source, issues = process_patterns(
source, repo_name, link_format='rst')
source, repo_name, link_format='rst',
active_entries=active_pattern_entries)
return literal(
'<div class="rst-block">%s</div>' %
@ -1662,7 +1675,8 @@ def render(source, renderer='rst', mentions=False, relative_urls=None,
if repo_name:
# process patterns on comments if we pass in repo name
source, issues = process_patterns(
source, repo_name, link_format='markdown')
source, repo_name, link_format='markdown',
active_entries=active_pattern_entries)
return literal(
'<div class="markdown-block">%s</div>' %

View file

@ -47,6 +47,14 @@ log = logging.getLogger(__name__)
# default renderer used to generate automated comments
DEFAULT_COMMENTS_RENDERER = 'rst'
try:
from lxml.html import fromstring
from lxml.html import tostring
except ImportError:
log.exception('Failed to import lxml')
fromstring = None
tostring = None
class CustomHTMLTranslator(writers.html4css1.HTMLTranslator):
"""
@ -81,11 +89,7 @@ def relative_links(html_source, server_paths):
if not html_source:
return html_source
try:
from lxml.html import fromstring
from lxml.html import tostring
except ImportError:
log.exception('Failed to import lxml')
if not fromstring and tostring:
return html_source
try:
@ -210,6 +214,8 @@ class MarkupRenderer(object):
URL_PAT = re.compile(r'(http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]'
r'|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+)')
MENTION_PAT = re.compile(MENTIONS_REGEX)
extensions = ['markdown.extensions.codehilite', 'markdown.extensions.extra',
'markdown.extensions.def_list', 'markdown.extensions.sane_lists']
@ -347,6 +353,26 @@ class MarkupRenderer(object):
return cls.URL_PAT.sub(url_func, text)
@classmethod
def convert_mentions(cls, text, mode):
mention_pat = cls.MENTION_PAT
def wrapp(match_obj):
uname = match_obj.groups()[0]
hovercard_url = "pyroutes.url('hovercard_username', {'username': '%s'});" % uname
if mode == 'markdown':
tmpl = '<strong class="tooltip-hovercard" data-hovercard-alt="{uname}" data-hovercard-url="{hovercard_url}">@{uname}</strong>'
elif mode == 'rst':
tmpl = ' **@{uname}** '
else:
raise ValueError('mode must be rst or markdown')
return tmpl.format(**{'uname': uname,
'hovercard_url': hovercard_url})
return mention_pat.sub(wrapp, text).strip()
@classmethod
def plain(cls, source, universal_newline=True, leading_newline=True):
source = safe_unicode(source)
@ -378,12 +404,7 @@ class MarkupRenderer(object):
cls.extensions, cls.output_format)
if mentions:
mention_pat = re.compile(MENTIONS_REGEX)
def wrapp(match_obj):
uname = match_obj.groups()[0]
return ' **@%(uname)s** ' % {'uname': uname}
mention_hl = mention_pat.sub(wrapp, source).strip()
mention_hl = cls.convert_mentions(source, mode='markdown')
# we extracted mentions render with this using Mentions false
return cls.markdown(mention_hl, safe=safe, flavored=flavored,
mentions=False)
@ -409,12 +430,7 @@ class MarkupRenderer(object):
@classmethod
def rst(cls, source, safe=True, mentions=False, clean_html=False):
if mentions:
mention_pat = re.compile(MENTIONS_REGEX)
def wrapp(match_obj):
uname = match_obj.groups()[0]
return ' **@%(uname)s** ' % {'uname': uname}
mention_hl = mention_pat.sub(wrapp, source).strip()
mention_hl = cls.convert_mentions(source, mode='rst')
# we extracted mentions render with this using Mentions false
return cls.rst(mention_hl, safe=safe, mentions=False)
@ -443,7 +459,7 @@ class MarkupRenderer(object):
except Exception:
log.exception('Error when rendering RST')
if safe:
log.debug('Fallbacking to render in plain mode')
log.debug('Fallback to render in plain mode')
return cls.plain(source)
else:
raise

View file

@ -133,15 +133,16 @@ class SimpleVCS(object):
self.config = config
# re-populated by specialized middleware
self.repo_vcs_config = base.Config()
self.rhodecode_settings = SettingsModel().get_all_settings(cache=True)
registry.rhodecode_settings = self.rhodecode_settings
rc_settings = SettingsModel().get_all_settings(cache=True, from_request=False)
realm = rc_settings.get('rhodecode_realm') or 'RhodeCode AUTH'
# authenticate this VCS request using authfunc
auth_ret_code_detection = \
str2bool(self.config.get('auth_ret_code_detection', False))
self.authenticate = BasicAuth(
'', authenticate, registry, config.get('auth_ret_code'),
auth_ret_code_detection)
auth_ret_code_detection, rc_realm=realm)
self.ip_addr = '0.0.0.0'
@LazyProperty

View file

@ -32,10 +32,14 @@ def BeakerSessionFactoryConfig(**options):
def session_callback(request, response):
exception = getattr(request, 'exception', None)
if (exception is None or self._cookie_on_exception) and self.accessed():
file_response = getattr(request, '_file_response', None)
if file_response is None \
and (exception is None or self._cookie_on_exception) \
and self.accessed():
self.persist()
headers = self.__dict__['_headers']
if headers['set_cookie'] and headers['cookie_out']:
if headers.get('set_cookie') and headers.get('cookie_out'):
response.headerlist.append(('Set-Cookie', headers['cookie_out']))
request.add_response_callback(session_callback)

View file

@ -148,6 +148,15 @@ class HGUpdateCaches(MaintenanceTask):
return res
class HGRebuildFnCaches(MaintenanceTask):
human_name = 'HG rebuild fn caches'
def run(self):
instance = self.db_repo.scm_instance()
res = instance.hg_rebuild_fn_cache()
return res
class SVNVerify(MaintenanceTask):
human_name = 'SVN Verify repo'
@ -162,7 +171,7 @@ class RepoMaintenance(object):
Performs maintenance of repository based on it's type
"""
tasks = {
'hg': [HGVerify, HGUpdateCaches],
'hg': [HGVerify, HGUpdateCaches, HGRebuildFnCaches],
'git': [GitFSCK, GitGC, GitRepack],
'svn': [SVNVerify],
}

View file

@ -432,8 +432,11 @@ class GitCommit(base.BaseCommit):
"""
if not self.parents:
return list(self._get_file_nodes())
return AddedFileNodesGenerator(
[n for n in self._get_paths_for_status('added')], self)
return AddedFileNodesGenerator(self.added_paths, self)
@LazyProperty
def added_paths(self):
return [n for n in self._get_paths_for_status('added')]
@LazyProperty
def changed(self):
@ -442,8 +445,11 @@ class GitCommit(base.BaseCommit):
"""
if not self.parents:
return []
return ChangedFileNodesGenerator(
[n for n in self._get_paths_for_status('modified')], self)
return ChangedFileNodesGenerator(self.changed_paths, self)
@LazyProperty
def changed_paths(self):
return [n for n in self._get_paths_for_status('modified')]
@LazyProperty
def removed(self):
@ -452,8 +458,11 @@ class GitCommit(base.BaseCommit):
"""
if not self.parents:
return []
return RemovedFileNodesGenerator(
[n for n in self._get_paths_for_status('deleted')], self)
return RemovedFileNodesGenerator(self.removed_paths, self)
@LazyProperty
def removed_paths(self):
return [n for n in self._get_paths_for_status('deleted')]
def _get_submodule_url(self, submodule_path):
git_modules_path = '.gitmodules'
@ -472,7 +481,7 @@ class GitCommit(base.BaseCommit):
for line in _content.splitlines():
yield line
parser = configparser.ConfigParser()
parser = configparser.RawConfigParser()
parser.read_file(iter_content(submodules_node.content))
for section in parser.sections():

View file

@ -372,18 +372,30 @@ class MercurialCommit(base.BaseCommit):
"""
Returns list of added ``FileNode`` objects.
"""
return AddedFileNodesGenerator([n for n in self.status[1]], self)
return AddedFileNodesGenerator(self.added_paths, self)
@LazyProperty
def added_paths(self):
return [n for n in self.status[1]]
@property
def changed(self):
"""
Returns list of modified ``FileNode`` objects.
"""
return ChangedFileNodesGenerator([n for n in self.status[0]], self)
return ChangedFileNodesGenerator(self.changed_paths, self)
@LazyProperty
def changed_paths(self):
return [n for n in self.status[0]]
@property
def removed(self):
"""
Returns list of removed ``FileNode`` objects.
"""
return RemovedFileNodesGenerator([n for n in self.status[2]], self)
return RemovedFileNodesGenerator(self.removed_paths, self)
@LazyProperty
def removed_paths(self):
return [n for n in self.status[2]]

View file

@ -290,6 +290,12 @@ class MercurialRepository(BaseRepository):
self._remote.invalidate_vcs_cache()
return update_cache
def hg_rebuild_fn_cache(self):
update_cache = self._remote.hg_rebuild_fn_cache()
self._remote.invalidate_vcs_cache()
return update_cache
def get_common_ancestor(self, commit_id1, commit_id2, repo2):
if commit_id1 == commit_id2:
return commit_id1

View file

@ -218,18 +218,27 @@ class SubversionCommit(base.BaseCommit):
@property
def added(self):
return nodes.AddedFileNodesGenerator(
self._changes_cache['added'], self)
return nodes.AddedFileNodesGenerator(self.added_paths, self)
@LazyProperty
def added_paths(self):
return [n for n in self._changes_cache['added']]
@property
def changed(self):
return nodes.ChangedFileNodesGenerator(
self._changes_cache['changed'], self)
return nodes.ChangedFileNodesGenerator(self.changed_paths, self)
@LazyProperty
def changed_paths(self):
return [n for n in self._changes_cache['changed']]
@property
def removed(self):
return nodes.RemovedFileNodesGenerator(
self._changes_cache['removed'], self)
return nodes.RemovedFileNodesGenerator(self.removed_paths, self)
@LazyProperty
def removed_paths(self):
return [n for n in self._changes_cache['removed']]
def _date_from_svn_properties(properties):

View file

@ -18,6 +18,19 @@
# RhodeCode Enterprise Edition, including its added features, Support services,
# and proprietary license terms, please see https://rhodecode.com/licenses/
DEFAULTS = {
'encodings_map': {'.gz': 'gzip',
'.Z': 'compress',
'.bz2': 'bzip2',
'.xz': 'xz'},
'suffix_map': {'.svgz': '.svg.gz',
'.tgz': '.tar.gz',
'.taz': '.tar.gz',
'.tz': '.tar.gz',
'.tbz2': '.tar.bz2',
'.txz': '.tar.xz'},
}
TYPES_MAP = [
{'.jpg': 'image/jpg',
'.mid': 'audio/midi',
@ -1203,4 +1216,6 @@ def get_mimetypes_db(extra_types=None):
types_map[1].update(extra_types)
db = mimetypes.MimeTypes()
db.types_map = types_map
db.encodings_map.update(DEFAULTS['encodings_map'])
db.suffix_map.update(DEFAULTS['suffix_map'])
return db

View file

@ -1024,6 +1024,17 @@ class User(Base, BaseModel):
qry = qry.filter(User.active == true())
return qry.all()
@classmethod
def get_all_user_ids(cls, only_active=True):
"""
Returns all users IDs
"""
qry = Session().query(User.user_id)
if only_active:
qry = qry.filter(User.active == true())
return [x.user_id for x in qry]
@classmethod
def get_default_user(cls, cache=False, refresh=False):
user = User.get_by_username(User.DEFAULT_USER, cache=cache)
@ -3890,8 +3901,8 @@ class _SetState(object):
self._current_state = None
def __enter__(self):
log.debug('StateLock: entering set state context, setting state to: `%s`',
self._pr_state)
log.debug('StateLock: entering set state context of pr %s, setting state to: `%s`',
self._pr, self._pr_state)
self.set_pr_state(self._pr_state)
return self
@ -3901,8 +3912,9 @@ class _SetState(object):
return None
self.set_pr_state(self._org_state)
log.debug('StateLock: exiting set state context, setting state to: `%s`',
self._org_state)
log.debug('StateLock: exiting set state context of pr %s, setting state to: `%s`',
self._pr, self._org_state)
@property
def state(self):
return self._current_state
@ -4285,6 +4297,7 @@ class PullRequest(Base, _PullRequestBase):
def __json__(self):
return {
'revisions': self.revisions,
'versions': self.versions_count
}
def calculated_review_status(self):
@ -4307,6 +4320,14 @@ class PullRequest(Base, _PullRequestBase):
vcs_obj = self.target_repo.scm_instance()
return vcs_obj.get_shadow_instance(shadow_repository_path)
@property
def versions_count(self):
"""
return number of versions this PR have, e.g a PR that once been
updated will have 2 versions
"""
return self.versions.count() + 1
class PullRequestVersion(Base, _PullRequestBase):
__tablename__ = 'pull_request_versions'

View file

@ -21,8 +21,7 @@
"""
permissions model for RhodeCode
"""
import collections
import logging
import traceback
@ -557,6 +556,27 @@ class PermissionModel(BaseModel):
self.sa.rollback()
raise
def get_users_with_repo_write(self, db_repo):
write_plus = ['repository.write', 'repository.admin']
default_user_id = User.get_default_user().user_id
user_write_permissions = collections.OrderedDict()
# write+ and DEFAULT user for inheritance
for perm in db_repo.permissions():
if perm.permission in write_plus or perm.user_id == default_user_id:
user_write_permissions[perm.user_id] = perm
return user_write_permissions
def get_user_groups_with_repo_write(self, db_repo):
write_plus = ['repository.write', 'repository.admin']
user_group_write_permissions = collections.OrderedDict()
# write+ and DEFAULT user for inheritance
for p in db_repo.permission_user_groups():
if p.permission in write_plus:
user_group_write_permissions[p.users_group_id] = p
return user_group_write_permissions
def trigger_permission_flush(self, affected_user_ids):
events.trigger(events.UserPermissionsChange(affected_user_ids))

View file

@ -26,6 +26,8 @@ pull request model for RhodeCode
import json
import logging
import os
import datetime
import urllib
import collections
@ -632,6 +634,7 @@ class PullRequestModel(BaseModel):
repo_id = pull_request.target_repo.repo_id
use_rebase = self._use_rebase_for_merging(pull_request)
close_branch = self._close_branch_before_merging(pull_request)
user_name = self._user_name_for_merging(pull_request, user)
target_ref = self._refresh_reference(
pull_request.target_ref_parts, target_vcs)
@ -647,7 +650,6 @@ class PullRequestModel(BaseModel):
target_vcs.config.set(
'rhodecode', 'RC_SCM_DATA', json.dumps(extras))
user_name = user.short_contact
merge_state = target_vcs.merge(
repo_id, workspace_id, target_ref, source_vcs,
pull_request.source_ref_parts,
@ -1664,6 +1666,16 @@ class PullRequestModel(BaseModel):
return False
def _user_name_for_merging(self, pull_request, user):
env_user_name_attr = os.environ.get('RC_MERGE_USER_NAME_ATTR', '')
if env_user_name_attr and hasattr(user, env_user_name_attr):
user_name_attr = env_user_name_attr
else:
user_name_attr = 'short_contact'
user_name = getattr(user, user_name_attr)
return user_name
def _close_branch_before_merging(self, pull_request):
repo_type = pull_request.target_repo.repo_type
if repo_type == 'hg':

View file

@ -38,8 +38,7 @@ from rhodecode.lib.user_log_filter import user_log_filter
from rhodecode.lib.utils import make_db_config
from rhodecode.lib.utils2 import (
safe_str, safe_unicode, remove_prefix, obfuscate_url_pw,
get_current_rhodecode_user, safe_int, datetime_to_time,
action_logger_generic)
get_current_rhodecode_user, safe_int, action_logger_generic)
from rhodecode.lib.vcs.backends import get_backend
from rhodecode.model import BaseModel
from rhodecode.model.db import (
@ -199,9 +198,11 @@ class RepoModel(BaseModel):
def get_repos_as_dict(self, repo_list=None, admin=False,
super_user_actions=False, short_name=None):
_render = get_current_request().get_partial_renderer(
'rhodecode:templates/data_table/_dt_elements.mako')
c = _render.get_call_context()
h = _render.get_helpers()
def quick_menu(repo_name):
return _render('quick_menu', repo_name)
@ -258,7 +259,7 @@ class RepoModel(BaseModel):
"name": repo_lnk(repo.repo_name, repo.repo_type, repo.repo_state,
repo.private, repo.archived, repo.fork),
"desc": desc(repo.description),
"desc": desc(h.escape(repo.description)),
"last_change": last_change(repo.updated_on),
@ -619,13 +620,26 @@ class RepoModel(BaseModel):
changes = {
'added': [],
'updated': [],
'deleted': []
'deleted': [],
'default_user_changed': None
}
repo = self._get_repo(repo)
# update permissions
for member_id, perm, member_type in perm_updates:
member_id = int(member_id)
if member_type == 'user':
member_name = User.get(member_id).username
if member_name == User.DEFAULT_USER:
# NOTE(dan): detect if we changed permissions for default user
perm_obj = self.sa.query(UserRepoToPerm) \
.filter(UserRepoToPerm.user_id == member_id) \
.filter(UserRepoToPerm.repository == repo) \
.scalar()
if perm_obj and perm_obj.permission.permission_name != perm:
changes['default_user_changed'] = True
# this updates also current one if found
self.grant_user_permission(
repo=repo, user=member_id, perm=perm)

View file

@ -41,7 +41,7 @@ from rhodecode.model.db import (_hash_key, func, or_, in_filter_generator,
UserGroup, Repository)
from rhodecode.model.settings import VcsSettingsModel, SettingsModel
from rhodecode.lib.caching_query import FromCache
from rhodecode.lib.utils2 import action_logger_generic, datetime_to_time
from rhodecode.lib.utils2 import action_logger_generic
log = logging.getLogger(__name__)
@ -353,7 +353,8 @@ class RepoGroupModel(BaseModel):
changes = {
'added': [],
'updated': [],
'deleted': []
'deleted': [],
'default_user_changed': None
}
def _set_perm_user(obj, user, perm):
@ -430,6 +431,15 @@ class RepoGroupModel(BaseModel):
member_id = int(member_id)
if member_type == 'user':
member_name = User.get(member_id).username
if isinstance(obj, RepoGroup) and obj == repo_group and member_name == User.DEFAULT_USER:
# NOTE(dan): detect if we changed permissions for default user
perm_obj = self.sa.query(UserRepoGroupToPerm) \
.filter(UserRepoGroupToPerm.user_id == member_id) \
.filter(UserRepoGroupToPerm.group == repo_group) \
.scalar()
if perm_obj and perm_obj.permission.permission_name != perm:
changes['default_user_changed'] = True
# this updates also current one if found
_set_perm_user(obj, user=member_id, perm=perm)
elif member_type == 'user_group':
@ -698,8 +708,6 @@ class RepoGroupModel(BaseModel):
for repo_group in repo_groups:
repo_group.update_commit_cache()
def get_repo_groups_as_dict(self, repo_group_list=None, admin=False,
super_user_actions=False):
@ -753,7 +761,7 @@ class RepoGroupModel(BaseModel):
"last_changeset": "",
"last_changeset_raw": "",
"desc": desc(group.group_description, group.personal),
"desc": desc(h.escape(group.group_description), group.personal),
"top_level_repos": 0,
"owner": user_profile(group.User.username)
}

View file

@ -21,9 +21,11 @@
import os
import hashlib
import logging
import re
from collections import namedtuple
from functools import wraps
import bleach
from pyramid.threadlocal import get_current_request, get_current_registry
from rhodecode.lib import rc_cache
from rhodecode.lib.utils2 import (
@ -210,7 +212,23 @@ class SettingsModel(BaseModel):
invalidation_namespace = CacheKey.SETTINGS_INVALIDATION_NAMESPACE
CacheKey.set_invalidate(invalidation_namespace)
def get_all_settings(self, cache=False):
def get_all_settings(self, cache=False, from_request=True):
from rhodecode.authentication.base import get_authn_registry
# defines if we use GLOBAL, or PER_REPO
repo = self._get_repo(self.repo) if self.repo else None
key = "settings_repo.{}".format(repo.repo_id) if repo else "settings_app"
# initially try the requests context, this is the fastest
# we only fetch global config
if from_request:
request = get_current_request()
if request and not repo and hasattr(request, 'call_context') and hasattr(request.call_context, 'rc_config'):
rc_config = request.call_context.rc_config
if rc_config:
return rc_config
region = rc_cache.get_or_create_region('sql_cache_short')
invalidation_namespace = CacheKey.SETTINGS_INVALIDATION_NAMESPACE
@ -226,9 +244,6 @@ class SettingsModel(BaseModel):
}
return settings
repo = self._get_repo(self.repo) if self.repo else None
key = "settings_repo.{}".format(repo.repo_id) if repo else "settings_app"
inv_context_manager = rc_cache.InvalidationContext(
uid='cache_settings', invalidation_namespace=invalidation_namespace)
with inv_context_manager as invalidation_context:
@ -240,6 +255,11 @@ class SettingsModel(BaseModel):
# are anyway very short lived and it's a safest way.
region = rc_cache.get_or_create_region('sql_cache_short')
region.invalidate()
registry = get_current_registry()
if registry:
authn_registry = get_authn_registry(registry)
if authn_registry:
authn_registry.invalidate_plugins_for_auth()
result = _get_all_settings('rhodecode_settings', key)
log.debug('Fetching app settings for key: %s took: %.4fs', key,
@ -360,9 +380,15 @@ class IssueTrackerSettingsModel(object):
for uid in issuetracker_entries:
url_data = qs.get(self._get_keyname('url', uid, 'rhodecode_'))
pat = qs.get(self._get_keyname('pat', uid, 'rhodecode_'))
try:
pat_compiled = re.compile(r'%s' % pat)
except re.error:
pat_compiled = None
issuetracker_entries[uid] = AttributeDict({
'pat': qs.get(
self._get_keyname('pat', uid, 'rhodecode_')),
'pat': pat,
'pat_compiled': pat_compiled,
'url': url_cleaner(
qs.get(self._get_keyname('url', uid, 'rhodecode_')) or ''),
'pref': bleach.clean(

View file

@ -38,6 +38,7 @@ log = logging.getLogger(__name__)
class SshKeyModel(BaseModel):
cls = UserSshKeys
DEFAULT_PRIVATE_KEY_FORMAT = 'pkcs8'
def parse_key(self, key_data):
"""
@ -66,16 +67,23 @@ class SshKeyModel(BaseModel):
log.error("Key Parse error: %s", err)
raise
def generate_keypair(self, comment=None):
def generate_keypair(self, comment=None, private_format=DEFAULT_PRIVATE_KEY_FORMAT):
key = rsa.generate_private_key(
backend=crypto_default_backend(),
public_exponent=65537,
key_size=2048
)
if private_format == self.DEFAULT_PRIVATE_KEY_FORMAT:
private_format = crypto_serialization.PrivateFormat.PKCS8
else:
# legacy format that can be used by older systems, use if pkcs8 have
# problems
private_format = crypto_serialization.PrivateFormat.TraditionalOpenSSL
private_key = key.private_bytes(
crypto_serialization.Encoding.PEM,
crypto_serialization.PrivateFormat.PKCS8,
private_format,
crypto_serialization.NoEncryption())
public_key = key.public_key().public_bytes(
crypto_serialization.Encoding.OpenSSH,

View file

@ -134,14 +134,11 @@ div.markdown-block h2 {
div.markdown-block h1 {
font-size: 32px;
margin: 15px 0 15px 0 !important;
padding-bottom: 5px !important;
}
div.markdown-block h2 {
font-size: 24px !important;
margin: 34px 0 10px 0 !important;
padding-top: 15px !important;
padding-bottom: 8px !important;
}
div.markdown-block h3 {
@ -174,6 +171,21 @@ div.markdown-block hr {
margin-bottom: 13px;
}
div.markdown-block blockquote {
color: #424242 !important;
padding: 8px 21px;
margin: 12px 0;
border-left: 4px solid @grey6;
}
div.markdown-block blockquote p {
color: #424242 !important;
padding: 0 !important;
margin: 0 !important;
line-height: 1.5;
}
div.markdown-block ol,
div.markdown-block ul,
div.markdown-block p,
@ -181,13 +193,11 @@ div.markdown-block blockquote,
div.markdown-block dl,
div.markdown-block li,
div.markdown-block table {
margin: 3px 0px 13px 0px !important;
color: #424242 !important;
font-size: 13px !important;
font-family: @text-regular;
font-weight: normal !important;
overflow: visible !important;
line-height: 140% !important;
}
div.markdown-block pre {
@ -245,14 +255,6 @@ div.markdown-block ol li {
list-style: decimal !important;
}
/*
div.markdown-block a,
div.markdown-block a:visited {
color: #4183C4 !important;
background-color: inherit;
text-decoration: none;
}
*/
div.markdown-block #message {
.border-radius(@border-radius);

View file

@ -87,6 +87,10 @@ body {
border-left: @border-thickness solid @border-default-color;
}
.cursor-pointer {
cursor: pointer;
}
input + .action-link, .action-link.first{
border-left: none;
}

View file

@ -3,9 +3,13 @@
.readme-title {
border: 1px solid @grey6;
padding: 10px 5px;
font-weight: 600;
margin-top: 30px;
margin-bottom: -1px;
a {
font-weight: 600;
font-size: 13px
}
}
div.readme_box {
@ -34,14 +38,11 @@ div.readme_box h2 {
div.readme_box h1 {
font-size: 32px;
margin: 15px 0 15px 0 !important;
padding-bottom: 5px !important;
}
div.readme_box h2 {
font-size: 24px !important;
margin: 34px 0 10px 0 !important;
padding-top: 15px !important;
padding-bottom: 8px !important;
}
div.readme_box h3 {
@ -74,6 +75,20 @@ div.readme_box hr {
margin-bottom: 13px;
}
div.readme_box blockquote {
color: #424242 !important;
padding: 8px 21px;
margin: 12px 0;
border-left: 4px solid @grey6;
}
div.readme_box blockquote p {
color: #424242 !important;
padding: 0 !important;
margin: 0 !important;
line-height: 1.5;
}
div.readme_box ol,
div.readme_box ul,
div.readme_box p,
@ -81,13 +96,11 @@ div.readme_box blockquote,
div.readme_box dl,
div.readme_box li,
div.readme_box table {
margin: 3px 0px 13px 0px !important;
color: #424242 !important;
font-size: 13px !important;
font-family: @text-regular;
font-weight: normal !important;
overflow: visible !important;
line-height: 140% !important;
}
div.readme_box pre {

View file

@ -373,6 +373,17 @@ table#repo_list_table {
min-width: 600px;
}
#no_grid_data {
text-align: center;
}
#grid_data_loading {
text-align: center;
font-weight: 600;
font-size: 16px;
padding: 80px 20px;
}
// Keyboard mappings
table.keyboard-mappings {
th {
@ -538,5 +549,12 @@ table.compare_view_commits {
img{
vertical-align: middle;
}
&.td-expire {
width: 200px;
}
&.td-gist-type {
width: 100px;
}
}
}

View file

@ -593,7 +593,7 @@ address {
}
.help-block-inline {
margin: 0;
margin: 0 !important;
}
// help block text

View file

@ -11,28 +11,38 @@ var _TM = {
'All Authors': 'All Authors',
'All individual reviewers must vote.': 'All individual reviewers must vote.',
'All reviewers must vote.': 'All reviewers must vote.',
'Are you sure to close this pull request without merging?': 'Are you sure to close this pull request without merging?',
'At least {0} reviewer must vote.': 'At least {0} reviewer must vote.',
'At least {0} reviewers must vote.': 'At least {0} reviewers must vote.',
'Author is not allowed to be a reviewer.': 'Author is not allowed to be a reviewer.',
'Changed files': 'Changed files',
'Close': 'Close',
'Collapse all files': 'Collapse all files',
'Collapse {0} commit': 'Collapse {0} commit',
'Collapse {0} commits': 'Collapse {0} commits',
'Comment text will be set automatically based on currently selected status ({0}) ...': 'Comment text will be set automatically based on currently selected status ({0}) ...',
'Commit Authors are not allowed to be a reviewer.': 'Commit Authors are not allowed to be a reviewer.',
'Context file: ': 'Context file: ',
'Delete this comment?': 'Delete this comment?',
'Diff to Commit ': 'Diff to Commit ',
'Expand all files': 'Expand all files',
'Expand {0} commit': 'Expand {0} commit',
'Expand {0} commits': 'Expand {0} commits',
'Fetching repository state failed. Error code: {0} {1}. Try <a href="{2}">refreshing</a> this page.': 'Fetching repository state failed. Error code: {0} {1}. Try <a href="{2}">refreshing</a> this page.',
'Fetching repository state failed. Error code: {0} {1}. Try refreshing this page.': 'Fetching repository state failed. Error code: {0} {1}. Try refreshing this page.',
'Follow': 'Follow',
'Force updating...': 'Force updating...',
'Hide full context diff': 'Hide full context diff',
'Hide whitespace changes': 'Hide whitespace changes',
'Invite reviewers to this discussion': 'Invite reviewers to this discussion',
'Leave a comment on line {0}.': 'Leave a comment on line {0}.',
'Leave a comment, or click resolve button to resolve TODO comment #{0}': 'Leave a comment, or click resolve button to resolve TODO comment #{0}',
'Leave a resolution comment, or click resolve button to resolve TODO comment #{0}': 'Leave a resolution comment, or click resolve button to resolve TODO comment #{0}',
'Lifetime': 'Lifetime',
'Loading ...': 'Loading ...',
'Loading failed': 'Loading failed',
'Loading more results...': 'Loading more results...',
'Loading...': 'Loading...',
'No bookmarks available yet.': 'No bookmarks available yet.',
'No branches available yet.': 'No branches available yet.',
'No forks available yet.': 'No forks available yet.',
@ -41,7 +51,9 @@ var _TM = {
'No matching files': 'No matching files',
'No pull requests available yet.': 'No pull requests available yet.',
'No repositories available yet.': 'No repositories available yet.',
'No repositories present.': 'No repositories present.',
'No repository groups available yet.': 'No repository groups available yet.',
'No repository groups present.': 'No repository groups present.',
'No results': 'No results',
'No ssh keys available yet.': 'No ssh keys available yet.',
'No tags available yet.': 'No tags available yet.',
@ -125,9 +137,11 @@ var _TM = {
'{0} min': '{0} min',
'{0} month': '{0} month',
'{0} months': '{0} months',
'{0} of {1} repositories': '{0} of {1} repositories',
'{0} of {1} repository groups': '{0} of {1} repository groups',
'{0} out of {1} ssh keys': '{0} out of {1} ssh keys',
'{0} out of {1} users': '{0} out of {1} users',
'{0} repositories': '{0} repositories',
'{0} repository groups': '{0} repository groups',
'{0} results are available, use up and down arrow keys to navigate.': '{0} results are available, use up and down arrow keys to navigate.',
'{0} sec': '{0} sec',

View file

@ -11,28 +11,38 @@ var _TM = {
'All Authors': 'All Authors',
'All individual reviewers must vote.': 'All individual reviewers must vote.',
'All reviewers must vote.': 'All reviewers must vote.',
'Are you sure to close this pull request without merging?': 'Are you sure to close this pull request without merging?',
'At least {0} reviewer must vote.': 'At least {0} reviewer must vote.',
'At least {0} reviewers must vote.': 'At least {0} reviewers must vote.',
'Author is not allowed to be a reviewer.': 'Author is not allowed to be a reviewer.',
'Changed files': 'Changed files',
'Close': 'Close',
'Collapse all files': 'Collapse all files',
'Collapse {0} commit': 'Collapse {0} commit',
'Collapse {0} commits': 'Collapse {0} commits',
'Comment text will be set automatically based on currently selected status ({0}) ...': 'Comment text will be set automatically based on currently selected status ({0}) ...',
'Commit Authors are not allowed to be a reviewer.': 'Commit Authors are not allowed to be a reviewer.',
'Context file: ': 'Context file: ',
'Delete this comment?': 'Delete this comment?',
'Diff to Commit ': 'Diff to Commit ',
'Expand all files': 'Expand all files',
'Expand {0} commit': 'Expand {0} commit',
'Expand {0} commits': 'Expand {0} commits',
'Fetching repository state failed. Error code: {0} {1}. Try <a href="{2}">refreshing</a> this page.': 'Fetching repository state failed. Error code: {0} {1}. Try <a href="{2}">refreshing</a> this page.',
'Fetching repository state failed. Error code: {0} {1}. Try refreshing this page.': 'Fetching repository state failed. Error code: {0} {1}. Try refreshing this page.',
'Follow': 'Follow',
'Force updating...': 'Force updating...',
'Hide full context diff': 'Hide full context diff',
'Hide whitespace changes': 'Hide whitespace changes',
'Invite reviewers to this discussion': 'Invite reviewers to this discussion',
'Leave a comment on line {0}.': 'Leave a comment on line {0}.',
'Leave a comment, or click resolve button to resolve TODO comment #{0}': 'Leave a comment, or click resolve button to resolve TODO comment #{0}',
'Leave a resolution comment, or click resolve button to resolve TODO comment #{0}': 'Leave a resolution comment, or click resolve button to resolve TODO comment #{0}',
'Lifetime': 'Lebensdauer',
'Loading ...': 'Loading ...',
'Loading failed': 'Loading failed',
'Loading more results...': 'Loading more results...',
'Loading...': 'Loading...',
'No bookmarks available yet.': 'No bookmarks available yet.',
'No branches available yet.': 'No branches available yet.',
'No forks available yet.': 'No forks available yet.',
@ -41,7 +51,9 @@ var _TM = {
'No matching files': 'No matching files',
'No pull requests available yet.': 'No pull requests available yet.',
'No repositories available yet.': 'No repositories available yet.',
'No repositories present.': 'No repositories present.',
'No repository groups available yet.': 'No repository groups available yet.',
'No repository groups present.': 'No repository groups present.',
'No results': 'No results',
'No ssh keys available yet.': 'No ssh keys available yet.',
'No tags available yet.': 'No tags available yet.',
@ -125,9 +137,11 @@ var _TM = {
'{0} min': '{0} min',
'{0} month': '{0} month',
'{0} months': '{0} months',
'{0} of {1} repositories': '{0} of {1} repositories',
'{0} of {1} repository groups': '{0} of {1} repository groups',
'{0} out of {1} ssh keys': '{0} out of {1} ssh keys',
'{0} out of {1} users': '{0} out of {1} users',
'{0} repositories': '{0} repositories',
'{0} repository groups': '{0} repository groups',
'{0} results are available, use up and down arrow keys to navigate.': '{0} results are available, use up and down arrow keys to navigate.',
'{0} sec': '{0} sec',

View file

@ -11,28 +11,38 @@ var _TM = {
'All Authors': 'All Authors',
'All individual reviewers must vote.': 'All individual reviewers must vote.',
'All reviewers must vote.': 'All reviewers must vote.',
'Are you sure to close this pull request without merging?': 'Are you sure to close this pull request without merging?',
'At least {0} reviewer must vote.': 'At least {0} reviewer must vote.',
'At least {0} reviewers must vote.': 'At least {0} reviewers must vote.',
'Author is not allowed to be a reviewer.': 'Author is not allowed to be a reviewer.',
'Changed files': 'Changed files',
'Close': 'Close',
'Collapse all files': 'Collapse all files',
'Collapse {0} commit': 'Collapse {0} commit',
'Collapse {0} commits': 'Collapse {0} commits',
'Comment text will be set automatically based on currently selected status ({0}) ...': 'Comment text will be set automatically based on currently selected status ({0}) ...',
'Commit Authors are not allowed to be a reviewer.': 'Commit Authors are not allowed to be a reviewer.',
'Context file: ': 'Context file: ',
'Delete this comment?': 'Delete this comment?',
'Diff to Commit ': 'Diff to Commit ',
'Expand all files': 'Expand all files',
'Expand {0} commit': 'Expand {0} commit',
'Expand {0} commits': 'Expand {0} commits',
'Fetching repository state failed. Error code: {0} {1}. Try <a href="{2}">refreshing</a> this page.': 'Fetching repository state failed. Error code: {0} {1}. Try <a href="{2}">refreshing</a> this page.',
'Fetching repository state failed. Error code: {0} {1}. Try refreshing this page.': 'Fetching repository state failed. Error code: {0} {1}. Try refreshing this page.',
'Follow': 'Follow',
'Force updating...': 'Force updating...',
'Hide full context diff': 'Hide full context diff',
'Hide whitespace changes': 'Hide whitespace changes',
'Invite reviewers to this discussion': 'Invite reviewers to this discussion',
'Leave a comment on line {0}.': 'Leave a comment on line {0}.',
'Leave a comment, or click resolve button to resolve TODO comment #{0}': 'Leave a comment, or click resolve button to resolve TODO comment #{0}',
'Leave a resolution comment, or click resolve button to resolve TODO comment #{0}': 'Leave a resolution comment, or click resolve button to resolve TODO comment #{0}',
'Lifetime': 'Lifetime',
'Loading ...': 'Loading ...',
'Loading failed': 'Loading failed',
'Loading more results...': 'Loading more results...',
'Loading...': 'Loading...',
'No bookmarks available yet.': 'No bookmarks available yet.',
'No branches available yet.': 'No branches available yet.',
'No forks available yet.': 'No forks available yet.',
@ -41,7 +51,9 @@ var _TM = {
'No matching files': 'No matching files',
'No pull requests available yet.': 'No pull requests available yet.',
'No repositories available yet.': 'No repositories available yet.',
'No repositories present.': 'No repositories present.',
'No repository groups available yet.': 'No repository groups available yet.',
'No repository groups present.': 'No repository groups present.',
'No results': 'No results',
'No ssh keys available yet.': 'No ssh keys available yet.',
'No tags available yet.': 'No tags available yet.',
@ -125,9 +137,11 @@ var _TM = {
'{0} min': '{0} min',
'{0} month': '{0} month',
'{0} months': '{0} months',
'{0} of {1} repositories': '{0} of {1} repositories',
'{0} of {1} repository groups': '{0} of {1} repository groups',
'{0} out of {1} ssh keys': '{0} out of {1} ssh keys',
'{0} out of {1} users': '{0} out of {1} users',
'{0} repositories': '{0} repositories',
'{0} repository groups': '{0} repository groups',
'{0} results are available, use up and down arrow keys to navigate.': '{0} results are available, use up and down arrow keys to navigate.',
'{0} sec': '{0} sec',

View file

@ -11,28 +11,38 @@ var _TM = {
'All Authors': 'All Authors',
'All individual reviewers must vote.': 'All individual reviewers must vote.',
'All reviewers must vote.': 'All reviewers must vote.',
'Are you sure to close this pull request without merging?': 'Are you sure to close this pull request without merging?',
'At least {0} reviewer must vote.': 'At least {0} reviewer must vote.',
'At least {0} reviewers must vote.': 'At least {0} reviewers must vote.',
'Author is not allowed to be a reviewer.': 'Author is not allowed to be a reviewer.',
'Changed files': 'Changed files',
'Close': 'Close',
'Collapse all files': 'Collapse all files',
'Collapse {0} commit': 'Collapse {0} commit',
'Collapse {0} commits': 'Collapse {0} commits',
'Comment text will be set automatically based on currently selected status ({0}) ...': 'Comment text will be set automatically based on currently selected status ({0}) ...',
'Commit Authors are not allowed to be a reviewer.': 'Commit Authors are not allowed to be a reviewer.',
'Context file: ': 'Context file: ',
'Delete this comment?': 'Delete this comment?',
'Diff to Commit ': 'Diff to Commit ',
'Expand all files': 'Expand all files',
'Expand {0} commit': 'Expand {0} commit',
'Expand {0} commits': 'Expand {0} commits',
'Fetching repository state failed. Error code: {0} {1}. Try <a href="{2}">refreshing</a> this page.': 'Fetching repository state failed. Error code: {0} {1}. Try <a href="{2}">refreshing</a> this page.',
'Fetching repository state failed. Error code: {0} {1}. Try refreshing this page.': 'Fetching repository state failed. Error code: {0} {1}. Try refreshing this page.',
'Follow': 'Follow',
'Force updating...': 'Force updating...',
'Hide full context diff': 'Hide full context diff',
'Hide whitespace changes': 'Hide whitespace changes',
'Invite reviewers to this discussion': 'Invite reviewers to this discussion',
'Leave a comment on line {0}.': 'Leave a comment on line {0}.',
'Leave a comment, or click resolve button to resolve TODO comment #{0}': 'Leave a comment, or click resolve button to resolve TODO comment #{0}',
'Leave a resolution comment, or click resolve button to resolve TODO comment #{0}': 'Leave a resolution comment, or click resolve button to resolve TODO comment #{0}',
'Lifetime': 'De por vida',
'Loading ...': 'Loading ...',
'Loading failed': 'Loading failed',
'Loading more results...': 'Loading more results...',
'Loading...': 'Loading...',
'No bookmarks available yet.': 'No bookmarks available yet.',
'No branches available yet.': 'No branches available yet.',
'No forks available yet.': 'No forks available yet.',
@ -41,7 +51,9 @@ var _TM = {
'No matching files': 'No matching files',
'No pull requests available yet.': 'No pull requests available yet.',
'No repositories available yet.': 'No repositories available yet.',
'No repositories present.': 'No repositories present.',
'No repository groups available yet.': 'No repository groups available yet.',
'No repository groups present.': 'No repository groups present.',
'No results': 'No results',
'No ssh keys available yet.': 'No ssh keys available yet.',
'No tags available yet.': 'No tags available yet.',
@ -125,9 +137,11 @@ var _TM = {
'{0} min': '{0} min',
'{0} month': '{0} month',
'{0} months': '{0} months',
'{0} of {1} repositories': '{0} of {1} repositories',
'{0} of {1} repository groups': '{0} of {1} repository groups',
'{0} out of {1} ssh keys': '{0} out of {1} ssh keys',
'{0} out of {1} users': '{0} out of {1} users',
'{0} repositories': '{0} repositories',
'{0} repository groups': '{0} repository groups',
'{0} results are available, use up and down arrow keys to navigate.': '{0} results are available, use up and down arrow keys to navigate.',
'{0} sec': '{0} sec',

View file

@ -11,28 +11,38 @@ var _TM = {
'All Authors': 'All Authors',
'All individual reviewers must vote.': 'All individual reviewers must vote.',
'All reviewers must vote.': 'All reviewers must vote.',
'Are you sure to close this pull request without merging?': 'Are you sure to close this pull request without merging?',
'At least {0} reviewer must vote.': 'At least {0} reviewer must vote.',
'At least {0} reviewers must vote.': 'At least {0} reviewers must vote.',
'Author is not allowed to be a reviewer.': 'Author is not allowed to be a reviewer.',
'Changed files': 'Changed files',
'Close': 'Close',
'Collapse all files': 'Collapse all files',
'Collapse {0} commit': 'Collapse {0} commit',
'Collapse {0} commits': 'Collapse {0} commits',
'Comment text will be set automatically based on currently selected status ({0}) ...': 'Comment text will be set automatically based on currently selected status ({0}) ...',
'Commit Authors are not allowed to be a reviewer.': 'Commit Authors are not allowed to be a reviewer.',
'Context file: ': 'Context file: ',
'Delete this comment?': 'Delete this comment?',
'Diff to Commit ': 'Diff to Commit ',
'Expand all files': 'Expand all files',
'Expand {0} commit': 'Expand {0} commit',
'Expand {0} commits': 'Expand {0} commits',
'Fetching repository state failed. Error code: {0} {1}. Try <a href="{2}">refreshing</a> this page.': 'Fetching repository state failed. Error code: {0} {1}. Try <a href="{2}">refreshing</a> this page.',
'Fetching repository state failed. Error code: {0} {1}. Try refreshing this page.': 'Fetching repository state failed. Error code: {0} {1}. Try refreshing this page.',
'Follow': 'Follow',
'Force updating...': 'Force updating...',
'Hide full context diff': 'Hide full context diff',
'Hide whitespace changes': 'Hide whitespace changes',
'Invite reviewers to this discussion': 'Invite reviewers to this discussion',
'Leave a comment on line {0}.': 'Leave a comment on line {0}.',
'Leave a comment, or click resolve button to resolve TODO comment #{0}': 'Leave a comment, or click resolve button to resolve TODO comment #{0}',
'Leave a resolution comment, or click resolve button to resolve TODO comment #{0}': 'Leave a resolution comment, or click resolve button to resolve TODO comment #{0}',
'Lifetime': 'Durée de vie',
'Loading ...': 'Loading ...',
'Loading failed': 'Loading failed',
'Loading more results...': 'Loading more results...',
'Loading...': 'Loading...',
'No bookmarks available yet.': 'No bookmarks available yet.',
'No branches available yet.': 'No branches available yet.',
'No forks available yet.': 'No forks available yet.',
@ -41,7 +51,9 @@ var _TM = {
'No matching files': 'No matching files',
'No pull requests available yet.': 'No pull requests available yet.',
'No repositories available yet.': 'No repositories available yet.',
'No repositories present.': 'No repositories present.',
'No repository groups available yet.': 'No repository groups available yet.',
'No repository groups present.': 'No repository groups present.',
'No results': 'No results',
'No ssh keys available yet.': 'No ssh keys available yet.',
'No tags available yet.': 'No tags available yet.',
@ -125,9 +137,11 @@ var _TM = {
'{0} min': '{0} min',
'{0} month': '{0} month',
'{0} months': '{0} mois',
'{0} of {1} repositories': '{0} of {1} repositories',
'{0} of {1} repository groups': '{0} of {1} repository groups',
'{0} out of {1} ssh keys': '{0} out of {1} ssh keys',
'{0} out of {1} users': '{0} out of {1} users',
'{0} repositories': '{0} repositories',
'{0} repository groups': '{0} repository groups',
'{0} results are available, use up and down arrow keys to navigate.': '{0} results are available, use up and down arrow keys to navigate.',
'{0} sec': '{0} sec',

View file

@ -11,28 +11,38 @@ var _TM = {
'All Authors': 'All Authors',
'All individual reviewers must vote.': 'All individual reviewers must vote.',
'All reviewers must vote.': 'All reviewers must vote.',
'Are you sure to close this pull request without merging?': 'Are you sure to close this pull request without merging?',
'At least {0} reviewer must vote.': 'At least {0} reviewer must vote.',
'At least {0} reviewers must vote.': 'At least {0} reviewers must vote.',
'Author is not allowed to be a reviewer.': 'Author is not allowed to be a reviewer.',
'Changed files': 'Changed files',
'Close': 'Close',
'Collapse all files': 'Collapse all files',
'Collapse {0} commit': 'Collapse {0} commit',
'Collapse {0} commits': 'Collapse {0} commits',
'Comment text will be set automatically based on currently selected status ({0}) ...': 'Comment text will be set automatically based on currently selected status ({0}) ...',
'Commit Authors are not allowed to be a reviewer.': 'Commit Authors are not allowed to be a reviewer.',
'Context file: ': 'Context file: ',
'Delete this comment?': 'Delete this comment?',
'Diff to Commit ': 'Diff to Commit ',
'Expand all files': 'Expand all files',
'Expand {0} commit': 'Expand {0} commit',
'Expand {0} commits': 'Expand {0} commits',
'Fetching repository state failed. Error code: {0} {1}. Try <a href="{2}">refreshing</a> this page.': 'Fetching repository state failed. Error code: {0} {1}. Try <a href="{2}">refreshing</a> this page.',
'Fetching repository state failed. Error code: {0} {1}. Try refreshing this page.': 'Fetching repository state failed. Error code: {0} {1}. Try refreshing this page.',
'Follow': 'Segui',
'Force updating...': 'Force updating...',
'Hide full context diff': 'Hide full context diff',
'Hide whitespace changes': 'Hide whitespace changes',
'Invite reviewers to this discussion': 'Invite reviewers to this discussion',
'Leave a comment on line {0}.': 'Leave a comment on line {0}.',
'Leave a comment, or click resolve button to resolve TODO comment #{0}': 'Leave a comment, or click resolve button to resolve TODO comment #{0}',
'Leave a resolution comment, or click resolve button to resolve TODO comment #{0}': 'Leave a resolution comment, or click resolve button to resolve TODO comment #{0}',
'Lifetime': 'a vita',
'Loading ...': 'Caricamento ...',
'Loading failed': 'Loading failed',
'Loading more results...': 'Loading more results...',
'Loading...': 'Loading...',
'No bookmarks available yet.': 'No bookmarks available yet.',
'No branches available yet.': 'No branches available yet.',
'No forks available yet.': 'No forks available yet.',
@ -41,7 +51,9 @@ var _TM = {
'No matching files': 'Nessuna corrispondenza tra i file',
'No pull requests available yet.': 'No pull requests available yet.',
'No repositories available yet.': 'No repositories available yet.',
'No repositories present.': 'No repositories present.',
'No repository groups available yet.': 'No repository groups available yet.',
'No repository groups present.': 'No repository groups present.',
'No results': 'Nessun risultato',
'No ssh keys available yet.': 'No ssh keys available yet.',
'No tags available yet.': 'No tags available yet.',
@ -125,9 +137,11 @@ var _TM = {
'{0} min': '{0} min',
'{0} month': '{0} month',
'{0} months': '{0} months',
'{0} of {1} repositories': '{0} of {1} repositories',
'{0} of {1} repository groups': '{0} of {1} repository groups',
'{0} out of {1} ssh keys': '{0} out of {1} ssh keys',
'{0} out of {1} users': '{0} out of {1} users',
'{0} repositories': '{0} repositories',
'{0} repository groups': '{0} repository groups',
'{0} results are available, use up and down arrow keys to navigate.': '{0} results are available, use up and down arrow keys to navigate.',
'{0} sec': '{0} sec',

View file

@ -11,28 +11,38 @@ var _TM = {
'All Authors': 'All Authors',
'All individual reviewers must vote.': 'All individual reviewers must vote.',
'All reviewers must vote.': 'All reviewers must vote.',
'Are you sure to close this pull request without merging?': 'Are you sure to close this pull request without merging?',
'At least {0} reviewer must vote.': 'At least {0} reviewer must vote.',
'At least {0} reviewers must vote.': 'At least {0} reviewers must vote.',
'Author is not allowed to be a reviewer.': 'Author is not allowed to be a reviewer.',
'Changed files': 'Changed files',
'Close': 'Close',
'Collapse all files': 'Collapse all files',
'Collapse {0} commit': 'Collapse {0} commit',
'Collapse {0} commits': 'Collapse {0} commits',
'Comment text will be set automatically based on currently selected status ({0}) ...': '選択したステータス ({0}) を元にコメントが自動的に設定されます...',
'Commit Authors are not allowed to be a reviewer.': 'Commit Authors are not allowed to be a reviewer.',
'Context file: ': 'Context file: ',
'Delete this comment?': 'Delete this comment?',
'Diff to Commit ': 'Diff to Commit ',
'Expand all files': 'Expand all files',
'Expand {0} commit': 'Expand {0} commit',
'Expand {0} commits': 'Expand {0} commits',
'Fetching repository state failed. Error code: {0} {1}. Try <a href="{2}">refreshing</a> this page.': 'Fetching repository state failed. Error code: {0} {1}. Try <a href="{2}">refreshing</a> this page.',
'Fetching repository state failed. Error code: {0} {1}. Try refreshing this page.': 'Fetching repository state failed. Error code: {0} {1}. Try refreshing this page.',
'Follow': 'フォロー',
'Force updating...': 'Force updating...',
'Hide full context diff': 'Hide full context diff',
'Hide whitespace changes': 'Hide whitespace changes',
'Invite reviewers to this discussion': 'Invite reviewers to this discussion',
'Leave a comment on line {0}.': 'Leave a comment on line {0}.',
'Leave a comment, or click resolve button to resolve TODO comment #{0}': 'Leave a comment, or click resolve button to resolve TODO comment #{0}',
'Leave a resolution comment, or click resolve button to resolve TODO comment #{0}': 'Leave a resolution comment, or click resolve button to resolve TODO comment #{0}',
'Lifetime': '有効期間',
'Loading ...': '読み込み中...',
'Loading failed': '読み込み失敗',
'Loading more results...': '結果を読み込み中...',
'Loading...': 'Loading...',
'No bookmarks available yet.': 'まだブックマークがありません。',
'No branches available yet.': 'まだブランチがありません。',
'No forks available yet.': 'No forks available yet.',
@ -41,7 +51,9 @@ var _TM = {
'No matching files': 'マッチするファイルはありません',
'No pull requests available yet.': 'まだプルリクエストがありません。',
'No repositories available yet.': 'まだリポジトリがありません。',
'No repositories present.': 'No repositories present.',
'No repository groups available yet.': 'まだリポジトリグループがありません。',
'No repository groups present.': 'No repository groups present.',
'No results': '結果がありません',
'No ssh keys available yet.': 'No ssh keys available yet.',
'No tags available yet.': 'まだタグがありません。',
@ -125,9 +137,11 @@ var _TM = {
'{0} min': '{0} 分',
'{0} month': '{0} ヶ月',
'{0} months': '{0} months',
'{0} of {1} repositories': '{0} of {1} repositories',
'{0} of {1} repository groups': '{0} of {1} repository groups',
'{0} out of {1} ssh keys': '{0} out of {1} ssh keys',
'{0} out of {1} users': '{0} out of {1} users',
'{0} repositories': '{0} repositories',
'{0} repository groups': '{0} repository groups',
'{0} results are available, use up and down arrow keys to navigate.': '{0} 件の結果があります。矢印キーの上下で選択できます。',
'{0} sec': '{0} 秒',

View file

@ -5,28 +5,38 @@ _gettext('Adding new reviewers is forbidden.');
_gettext('All Authors');
_gettext('All individual reviewers must vote.');
_gettext('All reviewers must vote.');
_gettext('Are you sure to close this pull request without merging?');
_gettext('At least {0} reviewer must vote.');
_gettext('At least {0} reviewers must vote.');
_gettext('Author is not allowed to be a reviewer.');
_gettext('Changed files');
_gettext('Close');
_gettext('Collapse all files');
_gettext('Collapse {0} commit');
_gettext('Collapse {0} commits');
_gettext('Comment text will be set automatically based on currently selected status ({0}) ...');
_gettext('Commit Authors are not allowed to be a reviewer.');
_gettext('Context file: ');
_gettext('Delete this comment?');
_gettext('Diff to Commit ');
_gettext('Expand all files');
_gettext('Expand {0} commit');
_gettext('Expand {0} commits');
_gettext('Fetching repository state failed. Error code: {0} {1}. Try <a href="{2}">refreshing</a> this page.');
_gettext('Fetching repository state failed. Error code: {0} {1}. Try refreshing this page.');
_gettext('Follow');
_gettext('Force updating...');
_gettext('Hide full context diff');
_gettext('Hide whitespace changes');
_gettext('Invite reviewers to this discussion');
_gettext('Leave a comment on line {0}.');
_gettext('Leave a comment, or click resolve button to resolve TODO comment #{0}');
_gettext('Leave a resolution comment, or click resolve button to resolve TODO comment #{0}');
_gettext('Lifetime');
_gettext('Loading ...');
_gettext('Loading failed');
_gettext('Loading more results...');
_gettext('Loading...');
_gettext('No bookmarks available yet.');
_gettext('No branches available yet.');
_gettext('No forks available yet.');
@ -35,7 +45,9 @@ _gettext('No matches found');
_gettext('No matching files');
_gettext('No pull requests available yet.');
_gettext('No repositories available yet.');
_gettext('No repositories present.');
_gettext('No repository groups available yet.');
_gettext('No repository groups present.');
_gettext('No results');
_gettext('No ssh keys available yet.');
_gettext('No tags available yet.');
@ -119,9 +131,11 @@ _gettext('{0} hours');
_gettext('{0} min');
_gettext('{0} month');
_gettext('{0} months');
_gettext('{0} of {1} repositories');
_gettext('{0} of {1} repository groups');
_gettext('{0} out of {1} ssh keys');
_gettext('{0} out of {1} users');
_gettext('{0} repositories');
_gettext('{0} repository groups');
_gettext('{0} results are available, use up and down arrow keys to navigate.');
_gettext('{0} sec');

View file

@ -11,28 +11,38 @@ var _TM = {
'All Authors': 'All Authors',
'All individual reviewers must vote.': 'All individual reviewers must vote.',
'All reviewers must vote.': 'All reviewers must vote.',
'Are you sure to close this pull request without merging?': 'Are you sure to close this pull request without merging?',
'At least {0} reviewer must vote.': 'At least {0} reviewer must vote.',
'At least {0} reviewers must vote.': 'At least {0} reviewers must vote.',
'Author is not allowed to be a reviewer.': 'Author is not allowed to be a reviewer.',
'Changed files': 'Changed files',
'Close': 'Zamknij',
'Collapse all files': 'Collapse all files',
'Collapse {0} commit': 'Collapse {0} commit',
'Collapse {0} commits': 'Collapse {0} commits',
'Comment text will be set automatically based on currently selected status ({0}) ...': 'Comment text will be set automatically based on currently selected status ({0}) ...',
'Commit Authors are not allowed to be a reviewer.': 'Commit Authors are not allowed to be a reviewer.',
'Context file: ': 'Context file: ',
'Delete this comment?': 'Delete this comment?',
'Diff to Commit ': 'Diff to Commit ',
'Expand all files': 'Expand all files',
'Expand {0} commit': 'Expand {0} commit',
'Expand {0} commits': 'Expand {0} commits',
'Fetching repository state failed. Error code: {0} {1}. Try <a href="{2}">refreshing</a> this page.': 'Fetching repository state failed. Error code: {0} {1}. Try <a href="{2}">refreshing</a> this page.',
'Fetching repository state failed. Error code: {0} {1}. Try refreshing this page.': 'Fetching repository state failed. Error code: {0} {1}. Try refreshing this page.',
'Follow': 'Obserwuj',
'Force updating...': 'Force updating...',
'Hide full context diff': 'Hide full context diff',
'Hide whitespace changes': 'Hide whitespace changes',
'Invite reviewers to this discussion': 'Invite reviewers to this discussion',
'Leave a comment on line {0}.': 'Leave a comment on line {0}.',
'Leave a comment, or click resolve button to resolve TODO comment #{0}': 'Leave a comment, or click resolve button to resolve TODO comment #{0}',
'Leave a resolution comment, or click resolve button to resolve TODO comment #{0}': 'Leave a resolution comment, or click resolve button to resolve TODO comment #{0}',
'Lifetime': 'Czas życia',
'Loading ...': 'Ładuję...',
'Loading failed': 'Loading failed',
'Loading more results...': 'Loading more results...',
'Loading...': 'Loading...',
'No bookmarks available yet.': 'No bookmarks available yet.',
'No branches available yet.': 'No branches available yet.',
'No forks available yet.': 'No forks available yet.',
@ -41,7 +51,9 @@ var _TM = {
'No matching files': 'Nie ma plików pasujących',
'No pull requests available yet.': 'No pull requests available yet.',
'No repositories available yet.': 'No repositories available yet.',
'No repositories present.': 'No repositories present.',
'No repository groups available yet.': 'No repository groups available yet.',
'No repository groups present.': 'No repository groups present.',
'No results': 'No results',
'No ssh keys available yet.': 'No ssh keys available yet.',
'No tags available yet.': 'No tags available yet.',
@ -125,9 +137,11 @@ var _TM = {
'{0} min': '{0} min',
'{0} month': '{0} month',
'{0} months': '{0} months',
'{0} of {1} repositories': '{0} of {1} repositories',
'{0} of {1} repository groups': '{0} of {1} repository groups',
'{0} out of {1} ssh keys': '{0} out of {1} ssh keys',
'{0} out of {1} users': '{0} out of {1} users',
'{0} repositories': '{0} repositories',
'{0} repository groups': '{0} repository groups',
'{0} results are available, use up and down arrow keys to navigate.': '{0} results are available, use up and down arrow keys to navigate.',
'{0} sec': '{0} sec',

View file

@ -11,28 +11,38 @@ var _TM = {
'All Authors': 'All Authors',
'All individual reviewers must vote.': 'All individual reviewers must vote.',
'All reviewers must vote.': 'All reviewers must vote.',
'Are you sure to close this pull request without merging?': 'Are you sure to close this pull request without merging?',
'At least {0} reviewer must vote.': 'At least {0} reviewer must vote.',
'At least {0} reviewers must vote.': 'At least {0} reviewers must vote.',
'Author is not allowed to be a reviewer.': 'Author is not allowed to be a reviewer.',
'Changed files': 'Changed files',
'Close': 'Close',
'Collapse all files': 'Collapse all files',
'Collapse {0} commit': 'Collapse {0} commit',
'Collapse {0} commits': 'Collapse {0} commits',
'Comment text will be set automatically based on currently selected status ({0}) ...': 'Comment text will be set automatically based on currently selected status ({0}) ...',
'Commit Authors are not allowed to be a reviewer.': 'Commit Authors are not allowed to be a reviewer.',
'Context file: ': 'Context file: ',
'Delete this comment?': 'Delete this comment?',
'Diff to Commit ': 'Diff to Commit ',
'Expand all files': 'Expand all files',
'Expand {0} commit': 'Expand {0} commit',
'Expand {0} commits': 'Expand {0} commits',
'Fetching repository state failed. Error code: {0} {1}. Try <a href="{2}">refreshing</a> this page.': 'Fetching repository state failed. Error code: {0} {1}. Try <a href="{2}">refreshing</a> this page.',
'Fetching repository state failed. Error code: {0} {1}. Try refreshing this page.': 'Fetching repository state failed. Error code: {0} {1}. Try refreshing this page.',
'Follow': 'Seguir',
'Force updating...': 'Force updating...',
'Hide full context diff': 'Hide full context diff',
'Hide whitespace changes': 'Hide whitespace changes',
'Invite reviewers to this discussion': 'Invite reviewers to this discussion',
'Leave a comment on line {0}.': 'Leave a comment on line {0}.',
'Leave a comment, or click resolve button to resolve TODO comment #{0}': 'Leave a comment, or click resolve button to resolve TODO comment #{0}',
'Leave a resolution comment, or click resolve button to resolve TODO comment #{0}': 'Leave a resolution comment, or click resolve button to resolve TODO comment #{0}',
'Lifetime': 'Tempo de Vida',
'Loading ...': 'Carregando...',
'Loading failed': 'Loading failed',
'Loading more results...': 'Loading more results...',
'Loading...': 'Loading...',
'No bookmarks available yet.': 'No bookmarks available yet.',
'No branches available yet.': 'No branches available yet.',
'No forks available yet.': 'No forks available yet.',
@ -41,7 +51,9 @@ var _TM = {
'No matching files': 'Nenhum arquivo encontrado',
'No pull requests available yet.': 'No pull requests available yet.',
'No repositories available yet.': 'No repositories available yet.',
'No repositories present.': 'No repositories present.',
'No repository groups available yet.': 'No repository groups available yet.',
'No repository groups present.': 'No repository groups present.',
'No results': 'No results',
'No ssh keys available yet.': 'No ssh keys available yet.',
'No tags available yet.': 'No tags available yet.',
@ -125,9 +137,11 @@ var _TM = {
'{0} min': '{0} min',
'{0} month': '{0} month',
'{0} months': '{0} months',
'{0} of {1} repositories': '{0} of {1} repositories',
'{0} of {1} repository groups': '{0} of {1} repository groups',
'{0} out of {1} ssh keys': '{0} out of {1} ssh keys',
'{0} out of {1} users': '{0} out of {1} users',
'{0} repositories': '{0} repositories',
'{0} repository groups': '{0} repository groups',
'{0} results are available, use up and down arrow keys to navigate.': '{0} results are available, use up and down arrow keys to navigate.',
'{0} sec': '{0} sec',

View file

@ -11,28 +11,38 @@ var _TM = {
'All Authors': 'All Authors',
'All individual reviewers must vote.': 'All individual reviewers must vote.',
'All reviewers must vote.': 'All reviewers must vote.',
'Are you sure to close this pull request without merging?': 'Are you sure to close this pull request without merging?',
'At least {0} reviewer must vote.': 'At least {0} reviewer must vote.',
'At least {0} reviewers must vote.': 'At least {0} reviewers must vote.',
'Author is not allowed to be a reviewer.': 'Author is not allowed to be a reviewer.',
'Changed files': 'Changed files',
'Close': 'Close',
'Collapse all files': 'Collapse all files',
'Collapse {0} commit': 'Collapse {0} commit',
'Collapse {0} commits': 'Collapse {0} commits',
'Comment text will be set automatically based on currently selected status ({0}) ...': 'Comment text will be set automatically based on currently selected status ({0}) ...',
'Commit Authors are not allowed to be a reviewer.': 'Commit Authors are not allowed to be a reviewer.',
'Context file: ': 'Context file: ',
'Delete this comment?': 'Delete this comment?',
'Diff to Commit ': 'Diff to Commit ',
'Expand all files': 'Expand all files',
'Expand {0} commit': 'Expand {0} commit',
'Expand {0} commits': 'Expand {0} commits',
'Fetching repository state failed. Error code: {0} {1}. Try <a href="{2}">refreshing</a> this page.': 'Fetching repository state failed. Error code: {0} {1}. Try <a href="{2}">refreshing</a> this page.',
'Fetching repository state failed. Error code: {0} {1}. Try refreshing this page.': 'Fetching repository state failed. Error code: {0} {1}. Try refreshing this page.',
'Follow': 'Наблюдать',
'Force updating...': 'Force updating...',
'Hide full context diff': 'Hide full context diff',
'Hide whitespace changes': 'Hide whitespace changes',
'Invite reviewers to this discussion': 'Invite reviewers to this discussion',
'Leave a comment on line {0}.': 'Leave a comment on line {0}.',
'Leave a comment, or click resolve button to resolve TODO comment #{0}': 'Leave a comment, or click resolve button to resolve TODO comment #{0}',
'Leave a resolution comment, or click resolve button to resolve TODO comment #{0}': 'Leave a resolution comment, or click resolve button to resolve TODO comment #{0}',
'Lifetime': 'Срок',
'Loading ...': 'Загрузка...',
'Loading failed': 'Loading failed',
'Loading more results...': 'Loading more results...',
'Loading...': 'Loading...',
'No bookmarks available yet.': 'No bookmarks available yet.',
'No branches available yet.': 'No branches available yet.',
'No forks available yet.': 'No forks available yet.',
@ -41,7 +51,9 @@ var _TM = {
'No matching files': 'Нет совпадений',
'No pull requests available yet.': 'No pull requests available yet.',
'No repositories available yet.': 'No repositories available yet.',
'No repositories present.': 'No repositories present.',
'No repository groups available yet.': 'No repository groups available yet.',
'No repository groups present.': 'No repository groups present.',
'No results': 'No results',
'No ssh keys available yet.': 'No ssh keys available yet.',
'No tags available yet.': 'No tags available yet.',
@ -125,9 +137,11 @@ var _TM = {
'{0} min': '{0} min',
'{0} month': '{0} month',
'{0} months': '{0} months',
'{0} of {1} repositories': '{0} of {1} repositories',
'{0} of {1} repository groups': '{0} of {1} repository groups',
'{0} out of {1} ssh keys': '{0} out of {1} ssh keys',
'{0} out of {1} users': '{0} out of {1} users',
'{0} repositories': '{0} repositories',
'{0} repository groups': '{0} repository groups',
'{0} results are available, use up and down arrow keys to navigate.': '{0} results are available, use up and down arrow keys to navigate.',
'{0} sec': '{0} sec',

View file

@ -11,28 +11,38 @@ var _TM = {
'All Authors': 'All Authors',
'All individual reviewers must vote.': 'All individual reviewers must vote.',
'All reviewers must vote.': 'All reviewers must vote.',
'Are you sure to close this pull request without merging?': 'Are you sure to close this pull request without merging?',
'At least {0} reviewer must vote.': 'At least {0} reviewer must vote.',
'At least {0} reviewers must vote.': 'At least {0} reviewers must vote.',
'Author is not allowed to be a reviewer.': 'Author is not allowed to be a reviewer.',
'Changed files': 'Changed files',
'Close': 'Close',
'Collapse all files': 'Collapse all files',
'Collapse {0} commit': 'Collapse {0} commit',
'Collapse {0} commits': 'Collapse {0} commits',
'Comment text will be set automatically based on currently selected status ({0}) ...': 'Comment text will be set automatically based on currently selected status ({0}) ...',
'Commit Authors are not allowed to be a reviewer.': 'Commit Authors are not allowed to be a reviewer.',
'Context file: ': 'Context file: ',
'Delete this comment?': 'Delete this comment?',
'Diff to Commit ': 'Diff to Commit ',
'Expand all files': 'Expand all files',
'Expand {0} commit': 'Expand {0} commit',
'Expand {0} commits': 'Expand {0} commits',
'Fetching repository state failed. Error code: {0} {1}. Try <a href="{2}">refreshing</a> this page.': 'Fetching repository state failed. Error code: {0} {1}. Try <a href="{2}">refreshing</a> this page.',
'Fetching repository state failed. Error code: {0} {1}. Try refreshing this page.': 'Fetching repository state failed. Error code: {0} {1}. Try refreshing this page.',
'Follow': 'Follow',
'Force updating...': 'Force updating...',
'Hide full context diff': 'Hide full context diff',
'Hide whitespace changes': 'Hide whitespace changes',
'Invite reviewers to this discussion': 'Invite reviewers to this discussion',
'Leave a comment on line {0}.': 'Leave a comment on line {0}.',
'Leave a comment, or click resolve button to resolve TODO comment #{0}': 'Leave a comment, or click resolve button to resolve TODO comment #{0}',
'Leave a resolution comment, or click resolve button to resolve TODO comment #{0}': 'Leave a resolution comment, or click resolve button to resolve TODO comment #{0}',
'Lifetime': '终身',
'Loading ...': 'Loading ...',
'Loading failed': 'Loading failed',
'Loading more results...': 'Loading more results...',
'Loading...': 'Loading...',
'No bookmarks available yet.': 'No bookmarks available yet.',
'No branches available yet.': 'No branches available yet.',
'No forks available yet.': 'No forks available yet.',
@ -41,7 +51,9 @@ var _TM = {
'No matching files': 'No matching files',
'No pull requests available yet.': 'No pull requests available yet.',
'No repositories available yet.': 'No repositories available yet.',
'No repositories present.': 'No repositories present.',
'No repository groups available yet.': 'No repository groups available yet.',
'No repository groups present.': 'No repository groups present.',
'No results': 'No results',
'No ssh keys available yet.': 'No ssh keys available yet.',
'No tags available yet.': 'No tags available yet.',
@ -125,9 +137,11 @@ var _TM = {
'{0} min': '{0} min',
'{0} month': '{0} month',
'{0} months': '{0} months',
'{0} of {1} repositories': '{0} of {1} repositories',
'{0} of {1} repository groups': '{0} of {1} repository groups',
'{0} out of {1} ssh keys': '{0} out of {1} ssh keys',
'{0} out of {1} users': '{0} out of {1} users',
'{0} repositories': '{0} repositories',
'{0} repository groups': '{0} repository groups',
'{0} results are available, use up and down arrow keys to navigate.': '{0} results are available, use up and down arrow keys to navigate.',
'{0} sec': '{0} sec',

View file

@ -31,6 +31,7 @@ function registerRCRoutes() {
pyroutes.register('repo_integrations_create', '/%(repo_name)s/settings/integrations/%(integration)s/new', ['repo_name', 'integration']);
pyroutes.register('repo_integrations_edit', '/%(repo_name)s/settings/integrations/%(integration)s/%(integration_id)s', ['repo_name', 'integration', 'integration_id']);
pyroutes.register('hovercard_user', '/_hovercard/user/%(user_id)s', ['user_id']);
pyroutes.register('hovercard_username', '/_hovercard/username/%(username)s', ['username']);
pyroutes.register('hovercard_user_group', '/_hovercard/user_group/%(user_group_id)s', ['user_group_id']);
pyroutes.register('hovercard_pull_request', '/_hovercard/pull_request/%(pull_request_id)s', ['pull_request_id']);
pyroutes.register('hovercard_repo_commit', '/_hovercard/commit/%(repo_name)s/%(commit_id)s', ['repo_name', 'commit_id']);

View file

@ -299,6 +299,10 @@ var tooltipActivate = function () {
var altHovercard =$origin.data('hovercardAlt');
if (hovercardUrl !== undefined && hovercardUrl !== "") {
if (hovercardUrl.substr(0,12) === 'pyroutes.url'){
hovercardUrl = eval(hovercardUrl)
}
var loaded = loadHoverCard(hovercardUrl, altHovercard, function (data) {
instance.content(data);
})

View file

@ -102,7 +102,7 @@ $(document).ready(function() {
{ data: {"_": "author",
"sort": "author_raw"}, title: "${_("Author")}", width: "250px", className: "td-user" },
{ data: {"_": "type",
"sort": "type"}, title: "${_("Type")}", width: "70px", className: "td-tags" },
"sort": "type"}, title: "${_("Type")}", width: "100px", className: "td-gist-type" },
{ data: {"_": "access_id",
"sort": "access_id"}, title: "${_("Name")}", width:"150px", className: "td-componentname" },
{ data: {"_": "description",
@ -110,7 +110,7 @@ $(document).ready(function() {
{ data: {"_": "created_on",
"sort": "created_on_raw"}, title: "${_("Created on")}", className: "td-time" },
{ data: {"_": "expires",
"sort": "expires"}, title: "${_("Expires")}", className: "td-exp" }
"sort": "expires"}, title: "${_("Expires")}", width: "200px", className: "td-expire" }
],
language: {
paginate: DEFAULT_GRID_PAGINATION,

View file

@ -71,7 +71,7 @@
<div class="input">
${h.text('description', class_='medium', placeholder=_('Description'))}
${h.hidden('lifetime')}
${h.select('role', '', c.role_options)}
${h.select('role', request.GET.get('token_role', ''), c.role_options)}
% if c.allow_scoped_tokens:
${h.hidden('scope_repo_id')}

View file

@ -4,7 +4,9 @@
% if c.extern_type != 'rhodecode':
<p>${_('Your user account details are managed by an external source. Details cannot be managed here.')}
<br/>${_('Source type')}: <strong>${c.extern_type}</strong>
<br/>${_('For VCS access please generate')}
<a href="${h.route_path('my_account_auth_tokens', _query={'token_role':'token_role_vcs'})}">Authentication Token</a> or <a href="${h.route_path('my_account_ssh_keys_generate')}">SSH Key</a>.
<br/>${_('Source type')}: <strong>${c.extern_type}</strong>
</p>
% else:
${c.form.render() | n}

View file

@ -8,6 +8,17 @@
</div>
<div class="panel-body fields">
%if c.extern_type != 'rhodecode':
<% readonly = "readonly" %>
<% disabled = " disabled" %>
<div class="alert-warning" style="margin:0px 0px 20px 0px; padding: 10px">
<strong>${_('This user was created from external source (%s). Editing some of the settings is limited.' % c.extern_type)}</strong>
</div>
<div style="margin:-10px 0px 20px 0px;">
${_('For VCS access please generate')}
<a href="${h.route_path('my_account_auth_tokens', _query={'token_role':'token_role_vcs'})}">Authentication Token</a> or <a href="${h.route_path('my_account_ssh_keys_generate')}">SSH Key</a>.
</div>
%endif
<div class="field">
<div class="label">
${_('Photo')}:

View file

@ -11,13 +11,19 @@
%if c.extern_type != 'rhodecode':
<% readonly = "readonly" %>
<% disabled = "disabled" %>
<% disabled = " disabled" %>
<div class="alert-warning" style="margin:0px 0px 20px 0px; padding: 10px">
<strong>${_('This user was created from external source (%s). Editing some of the settings is limited.' % c.extern_type)}</strong>
</div>
<div style="margin:-10px 0px 20px 0px;">
${_('For VCS access please generate')}
<a href="${h.route_path('my_account_auth_tokens', _query={'token_role':'token_role_vcs'})}">Authentication Token</a> or <a href="${h.route_path('my_account_ssh_keys_generate')}">SSH Key</a>.
</div>
%endif
%if c.extern_type != 'rhodecode':
<div class="infoform">
<div class="fields">
<p>${_('Your user account details are managed by an external source. Details cannot be managed here.')}
<br/>${_('Source type')}: <strong>${c.extern_type}</strong>
</p>
<div class="field">
<div class="label">
<label for="username">${_('Username')}:</label>

View file

@ -55,7 +55,13 @@
<div class="textarea editor">
${h.textarea('group_description',cols=23,rows=5,class_="medium")}
<% metatags_url = h.literal('''<a href="#metatagsShow" onclick="$('#meta-tags-desc').toggle();return false">meta-tags</a>''') %>
<span class="help-block">${_('Plain text format with support of {metatags}').format(metatags=metatags_url)|n}</span>
<span class="help-block">
% if c.visual.stylify_metatags:
${_('Plain text format with {metatags} support.').format(metatags=metatags_url)|n}
% else:
${_('Plain text format.')}
% endif
</span>
<span id="meta-tags-desc" style="display: none">
<%namespace name="dt" file="/data_table/_dt_elements.mako"/>
${dt.metatags_help()}

View file

@ -28,7 +28,7 @@
<div class="sidebar">
<ul class="nav nav-pills nav-stacked">
<li class="${h.is_active('settings', c.active)}"><a href="${h.route_path('edit_repo_group', repo_group_name=c.repo_group.group_name)}">${_('Settings')}</a></li>
<li class="${h.is_active('permissions', c.active)}"><a href="${h.route_path('edit_repo_group_perms', repo_group_name=c.repo_group.group_name)}">${_('Permissions')}</a></li>
<li class="${h.is_active('permissions', c.active)}"><a href="${h.route_path('edit_repo_group_perms', repo_group_name=c.repo_group.group_name)}">${_('Access Permissions')}</a></li>
<li class="${h.is_active('advanced', c.active)}"><a href="${h.route_path('edit_repo_group_advanced', repo_group_name=c.repo_group.group_name)}">${_('Advanced')}</a></li>
<li class="${h.is_active('integrations', c.active)}"><a href="${h.route_path('repo_group_integrations_home', repo_group_name=c.repo_group.group_name)}">${_('Integrations')}</a></li>
</ul>

View file

@ -61,7 +61,12 @@
${base.gravatar(_user.email, 16, user=_user, tooltip=True)}
<span class="user">
% if _user.username == h.DEFAULT_USER:
${h.DEFAULT_USER} <span class="user-perm-help-text"> - ${_('permission for all other users')}</span>
${h.DEFAULT_USER}
% if _user.active:
<span class="user-perm-help-text"> - ${_('permission for other logged in and anonymous users')}</span>
% else:
<span class="user-perm-help-text"> - ${_('permission for other logged in users')}</span>
% endif
% else:
${h.link_to_user(_user.username)}
%if getattr(_user, 'duplicate_perm', None):
@ -104,7 +109,12 @@
${base.gravatar(_user.email, 16, user=_user, tooltip=True)}
<span class="user">
% if _user.username == h.DEFAULT_USER:
${h.DEFAULT_USER} <span class="user-perm-help-text"> - ${_('permission for all other users')}</span>
${h.DEFAULT_USER}
% if _user.active:
<span class="user-perm-help-text"> - ${_('permission for other logged in and anonymous users')}</span>
% else:
<span class="user-perm-help-text"> - ${_('permission for other logged in users')}</span>
% endif
% else:
${h.link_to_user(_user.username)}
%if getattr(_user, 'duplicate_perm', None):

View file

@ -59,7 +59,13 @@
${c.form.render_error(request, c.form['repo_group_description'])|n}
<% metatags_url = h.literal('''<a href="#metatagsShow" onclick="$('#meta-tags-desc').toggle();return false">meta-tags</a>''') %>
<span class="help-block">${_('Plain text format with support of {metatags}').format(metatags=metatags_url)|n}</span>
<span class="help-block">
% if c.visual.stylify_metatags:
${_('Plain text format with {metatags} support.').format(metatags=metatags_url)|n}
% else:
${_('Plain text format.')}
% endif
</span>
<span id="meta-tags-desc" style="display: none">
<%namespace name="dt" file="/data_table/_dt_elements.mako"/>
${dt.metatags_help()}

View file

@ -68,7 +68,14 @@ ${h.secure_form(h.route_path('repo_create'), request=request)}
<div class="textarea editor">
${h.textarea('repo_description',cols=23,rows=5,class_="medium")}
<% metatags_url = h.literal('''<a href="#metatagsShow" onclick="$('#meta-tags-desc').toggle();return false">meta-tags</a>''') %>
<span class="help-block">${_('Plain text format with support of {metatags}. Add a README file for longer descriptions').format(metatags=metatags_url)|n}</span>
<span class="help-block">
% if c.visual.stylify_metatags:
${_('Plain text format with {metatags} support.').format(metatags=metatags_url)|n}
% else:
${_('Plain text format.')}
% endif
${_('Add a README file for longer descriptions')}
</span>
<span id="meta-tags-desc" style="display: none">
<%namespace name="dt" file="/data_table/_dt_elements.mako"/>
${dt.metatags_help()}

View file

@ -23,43 +23,48 @@
<div class="panel-heading">
<h3 class="panel-title">${_('Inherited Issue Tracker Patterns')}</h3>
</div>
<div class="panel-body">
<table class="rctable issuetracker readonly">
<tr>
<th>${_('Description')}</th>
<th>${_('Pattern')}</th>
<th>${_('Url')}</th>
<th>${_('Prefix')}</th>
<th ></th>
</tr>
%for uid, entry in c.global_patterns.items():
<tr id="${uid}">
<td class="td-description issuetracker_desc">
<span class="entry">
${entry.desc}
</span>
</td>
<td class="td-regex issuetracker_pat">
<span class="entry">
${entry.pat}
</span>
</td>
<td class="td-url issuetracker_url">
<span class="entry">
${entry.url}
</span>
</td>
<td class="td-prefix issuetracker_pref">
<span class="entry">
${entry.pref}
</span>
</td>
<td class="td-action">
</td>
</tr>
%endfor
</table>
<div class="panel-body">
<table class="rctable issuetracker readonly">
<tr>
<th>${_('Description')}</th>
<th>${_('Pattern')}</th>
<th>${_('Url')}</th>
<th>${_('Prefix')}</th>
<th></th>
</tr>
% for uid, entry in c.global_patterns.items():
<tr id="${uid}">
<td class="td-description issuetracker_desc">
<span class="entry">
${entry.desc}
</span>
</td>
<td class="td-regex issuetracker_pat">
<span class="entry">
${entry.pat}
</span>
</td>
<td class="td-url issuetracker_url">
<span class="entry">
${entry.url}
</span>
</td>
<td class="td-prefix issuetracker_pref">
<span class="entry">
${entry.pref}
</span>
</td>
<td class="td-action">
</td>
</tr>
% endfor
</table>
<div class="buttons">
<button type="submit" class="btn btn-primary save-inheritance" id="save">${_('Save')}</button>
</div>
</div>
</div>
</div>
@ -77,7 +82,6 @@
)}
<div class="buttons">
<button type="submit" class="btn btn-primary save-inheritance" id="save">${_('Save')}</button>
<button type="reset" class="btn reset-inheritance">${_('Reset')}</button>
</div>
</div>
</div>

View file

@ -58,7 +58,11 @@
<td class="private_repo_msg">
${base.gravatar(h.DEFAULT_USER_EMAIL, 16)}
${h.DEFAULT_USER} - ${_('only users/user groups explicitly added here will have access')}</td>
<td></td>
<td class="td-action">
<span class="tooltip btn btn-link btn-default" onclick="setPrivateRepo(false); return false" title="${_('Private repositories are only visible to people explicitly added as collaborators. Default permissions wont apply')}">
${_('un-set private mode')}
</span>
</td>
<td class="quick_repo_menu">
% if c.rhodecode_user.is_admin:
<i class="icon-more"></i>
@ -83,7 +87,12 @@
${base.gravatar(_user.email, 16, user=_user, tooltip=True)}
<span class="user">
% if _user.username == h.DEFAULT_USER:
${h.DEFAULT_USER} <span class="user-perm-help-text"> - ${_('permission for all other users')}</span>
${h.DEFAULT_USER}
% if _user.active:
<span class="user-perm-help-text"> - ${_('permission for other logged in and anonymous users')}</span>
% else:
<span class="user-perm-help-text"> - ${_('permission for other logged in users')}</span>
% endif
% else:
${h.link_to_user(_user.username)}
%if getattr(_user, 'duplicate_perm', None):
@ -106,7 +115,7 @@
${_('Remove')}
</span>
%elif _user.username == h.DEFAULT_USER:
<span class="tooltip btn btn-link btn-default" onclick="enablePrivateRepo(); return false" title="${_('Private repositories are only visible to people explicitly added as collaborators.')}">
<span class="tooltip btn btn-link btn-default" onclick="setPrivateRepo(true); return false" title="${_('Private repositories are only visible to people explicitly added as collaborators. Default permissions wont apply')}">
${_('set private mode')}
</span>
%endif
@ -204,9 +213,10 @@
});
quick_repo_menu();
var enablePrivateRepo = function () {
var setPrivateRepo = function (private) {
var postData = {
'csrf_token': CSRF_TOKEN
'csrf_token': CSRF_TOKEN,
'private': private
};
var success = function(o) {

View file

@ -174,7 +174,14 @@
${c.form.render_error(request, c.form['repo_description'])|n}
<% metatags_url = h.literal('''<a href="#metatagsShow" onclick="$('#meta-tags-desc').toggle();return false">meta-tags</a>''') %>
<span class="help-block">${_('Plain text format with support of {metatags}. Add a README file for longer descriptions').format(metatags=metatags_url)|n}</span>
<span class="help-block">
% if c.visual.stylify_metatags:
${_('Plain text format with {metatags} support.').format(metatags=metatags_url)|n}
% else:
${_('Plain text format.')}
% endif
${_('Add a README file for longer descriptions')}
</span>
<span id="meta-tags-desc" style="display: none">
<%namespace name="dt" file="/data_table/_dt_elements.mako"/>
${dt.metatags_help()}

View file

@ -1,5 +1,5 @@
<div id="update_notice" style="display: none; margin: -40px 0px 20px 0px">
<div id="update_notice" style="display: none; margin: 0px 0px 30px 0px">
<div>${_('Checking for updates...')}</div>
</div>

View file

@ -18,7 +18,11 @@
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">${_('User Group: %s') % c.user_group.users_group_name}</h3>
<h3 class="panel-title">
<i class="icon-user-group" title="${_('User group')}"></i>
${h.link_to_group(c.user_group.users_group_name)}
- ${_('Advanced')}
</h3>
</div>
<div class="panel-body">
${base.dt_info_panel(elems)}

View file

@ -2,7 +2,11 @@
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">${_('User Group Permissions')}</h3>
<h3 class="panel-title">
<i class="icon-user-group" title="${_('User group')}"></i>
${h.link_to_group(c.user_group.users_group_name)}
- ${_('Access Permissions')}
</h3>
</div>
<div class="panel-body">
${h.secure_form(h.route_path('edit_user_group_perms_update', user_group_id=c.user_group.users_group_id), request=request)}
@ -63,7 +67,12 @@
${base.gravatar(_user.email, 16, user=_user, tooltip=True)}
<span class="user">
% if _user.username == h.DEFAULT_USER:
${h.DEFAULT_USER} <span class="user-perm-help-text"> - ${_('permission for all other users')}</span>
${h.DEFAULT_USER}
% if _user.active:
<span class="user-perm-help-text"> - ${_('permission for other logged in and anonymous users')}</span>
% else:
<span class="user-perm-help-text"> - ${_('permission for other logged in users')}</span>
% endif
% else:
${h.link_to_user(_user.username)}
%if getattr(_user, 'duplicate_perm', None):
@ -106,7 +115,12 @@
${base.gravatar(_user.email, 16, user=_user, tooltip=True)}
<span class="user">
% if _user.username == h.DEFAULT_USER:
${h.DEFAULT_USER} <span class="user-perm-help-text"> - ${_('permission for all other users')}</span>
${h.DEFAULT_USER}
% if _user.active:
<span class="user-perm-help-text"> - ${_('permission for other logged in and anonymous users')}</span>
% else:
<span class="user-perm-help-text"> - ${_('permission for other logged in users')}</span>
% endif
% else:
${h.link_to_user(_user.username)}
%if getattr(_user, 'duplicate_perm', None):

Some files were not shown because too many files have changed in this diff Show more