##// END OF EJS Templates
merge with beta
marcink -
r2825:f7a52d54 merge default
parent child Browse files
Show More
@@ -0,0 +1,51 b''
1 import logging
2 import datetime
3
4 from sqlalchemy import *
5 from sqlalchemy.exc import DatabaseError
6 from sqlalchemy.orm import relation, backref, class_mapper
7 from sqlalchemy.orm.session import Session
8 from sqlalchemy.ext.declarative import declarative_base
9
10 from rhodecode.lib.dbmigrate.migrate import *
11 from rhodecode.lib.dbmigrate.migrate.changeset import *
12
13 from rhodecode.model.meta import Base
14 from rhodecode.model import meta
15
16 log = logging.getLogger(__name__)
17
18
19 def upgrade(migrate_engine):
20 """
21 Upgrade operations go here.
22 Don't create your own engine; bind migrate_engine to your metadata
23 """
24
25 #==========================================================================
26 # CHANGESET_COMMENTS
27 #==========================================================================
28 from rhodecode.lib.dbmigrate.schema.db_1_4_0 import ChangesetComment
29 tbl_name = ChangesetComment.__tablename__
30 tbl = Table(tbl_name,
31 MetaData(bind=migrate_engine), autoload=True,
32 autoload_with=migrate_engine)
33 col = tbl.columns.revision
34
35 # remove nullability from revision field
36 col.alter(nullable=True)
37
38 #==========================================================================
39 # REPOSITORY
40 #==========================================================================
41 from rhodecode.lib.dbmigrate.schema.db_1_4_0 import Repository
42 tbl = Repository.__table__
43 updated_on = Column('updated_on', DateTime(timezone=False),
44 nullable=True, unique=None)
45 # create created on column for future lightweight main page
46 updated_on.create(table=tbl)
47
48
49 def downgrade(migrate_engine):
50 meta = MetaData()
51 meta.bind = migrate_engine
@@ -0,0 +1,20 b''
1 ## -*- coding: utf-8 -*-
2 <%inherit file="main.html"/>
3
4 User <b>${pr_user_created}</b> opened pull request for repository
5 ${pr_repo_url} and wants you to review changes.
6
7 <div>title: ${pr_title}</div>
8 <div>description:</div>
9 <p>
10 ${body}
11 </p>
12
13 <div>revisions for reviewing</div>
14 <ul>
15 %for r in pr_revisions:
16 <li>${r}</li>
17 %endfor
18 </ul>
19
20 View this pull request here: ${pr_url}
@@ -0,0 +1,15 b''
1 ## -*- coding: utf-8 -*-
2 <%inherit file="main.html"/>
3
4 User <b>${pr_comment_user}</b> commented on pull request #${pr_id} for
5 repository ${pr_target_repo}
6
7 <p>
8 ${body}
9
10 %if status_change:
11 <span>New status -> ${status_change}</span>
12 %endif
13 </p>
14
15 View this comment here: ${pr_comment_url}
@@ -0,0 +1,116 b''
1 import os
2 import unittest
3 import functools
4 from rhodecode.tests import *
5
6
7 from rhodecode.model.repos_group import ReposGroupModel
8 from rhodecode.model.repo import RepoModel
9 from rhodecode.model.db import RepoGroup, Repository, User
10 from rhodecode.model.user import UserModel
11
12 from rhodecode.lib.auth import AuthUser
13 from rhodecode.model.meta import Session
14
15
16 def _make_group(path, desc='desc', parent_id=None,
17 skip_if_exists=False):
18
19 gr = RepoGroup.get_by_group_name(path)
20 if gr and skip_if_exists:
21 return gr
22 if isinstance(parent_id, RepoGroup):
23 parent_id = parent_id.group_id
24 gr = ReposGroupModel().create(path, desc, parent_id)
25 return gr
26
27
28 def _make_repo(name, repos_group=None, repo_type='hg'):
29 return RepoModel().create_repo(name, repo_type, 'desc',
30 TEST_USER_ADMIN_LOGIN,
31 repos_group=repos_group)
32
33
34 def _destroy_project_tree(test_u1_id):
35 Session.remove()
36 repos_group = RepoGroup.get_by_group_name(group_name='g0')
37 for el in reversed(repos_group.recursive_groups_and_repos()):
38 if isinstance(el, Repository):
39 RepoModel().delete(el)
40 elif isinstance(el, RepoGroup):
41 ReposGroupModel().delete(el, force_delete=True)
42
43 u = User.get(test_u1_id)
44 Session().delete(u)
45 Session().commit()
46
47
48 def _create_project_tree():
49 """
50 Creates a tree of groups and repositories to test permissions
51
52 structure
53 [g0] - group `g0` with 3 subgroups
54 |
55 |__[g0_1] group g0_1 with 2 groups 0 repos
56 | |
57 | |__[g0_1_1] group g0_1_1 with 1 group 2 repos
58 | | |__<g0/g0_1/g0_1_1/g0_1_1_r1>
59 | | |__<g0/g0_1/g0_1_1/g0_1_1_r2>
60 | |__<g0/g0_1/g0_1_r1>
61 |
62 |__[g0_2] 2 repos
63 | |
64 | |__<g0/g0_2/g0_2_r1>
65 | |__<g0/g0_2/g0_2_r2>
66 |
67 |__[g0_3] 1 repo
68 |
69 |_<g0/g0_3/g0_3_r1>
70
71 """
72 test_u1 = UserModel().create_or_update(
73 username=u'test_u1', password=u'qweqwe',
74 email=u'test_u1@rhodecode.org', firstname=u'test_u1', lastname=u'test_u1'
75 )
76 g0 = _make_group('g0')
77 g0_1 = _make_group('g0_1', parent_id=g0)
78 g0_1_1 = _make_group('g0_1_1', parent_id=g0_1)
79 g0_1_1_r1 = _make_repo('g0/g0_1/g0_1_1/g0_1_1_r1', repos_group=g0_1_1)
80 g0_1_1_r2 = _make_repo('g0/g0_1/g0_1_1/g0_1_1_r2', repos_group=g0_1_1)
81 g0_1_r1 = _make_repo('g0/g0_1/g0_1_r1', repos_group=g0_1)
82 g0_2 = _make_group('g0_2', parent_id=g0)
83 g0_2_r1 = _make_repo('g0/g0_2/g0_2_r1', repos_group=g0_2)
84 g0_2_r2 = _make_repo('g0/g0_2/g0_2_r2', repos_group=g0_2)
85 g0_3 = _make_group('g0_3', parent_id=g0)
86 g0_3_r1 = _make_repo('g0/g0_3/g0_3_r1', repos_group=g0_3)
87 return test_u1
88
89
90 def expected_count(group_name, objects=False):
91 repos_group = RepoGroup.get_by_group_name(group_name=group_name)
92 objs = repos_group.recursive_groups_and_repos()
93 if objects:
94 return objs
95 return len(objs)
96
97
98 def _check_expected_count(items, repo_items, expected):
99 should_be = len(items + repo_items)
100 there_are = len(expected)
101 assert should_be == there_are, ('%s != %s' % ((items + repo_items), expected))
102
103
104 def check_tree_perms(obj_name, repo_perm, prefix, expected_perm):
105 assert repo_perm == expected_perm, ('obj:`%s` got perm:`%s` should:`%s`'
106 % (obj_name, repo_perm, expected_perm))
107
108
109 def _get_perms(filter_='', recursive=True, key=None, test_u1_id=None):
110 test_u1 = AuthUser(user_id=test_u1_id)
111 for k, v in test_u1.permissions[key].items():
112 if recursive and k.startswith(filter_):
113 yield k, v
114 elif not recursive:
115 if k == filter_:
116 yield k, v
@@ -0,0 +1,161 b''
1 import os
2 import unittest
3 import functools
4 from rhodecode.tests import *
5
6 from rhodecode.model.repos_group import ReposGroupModel
7 from rhodecode.model.db import RepoGroup, Repository, User
8
9 from rhodecode.model.meta import Session
10 from nose.tools import with_setup
11 from rhodecode.tests.models.common import _create_project_tree, check_tree_perms, \
12 _get_perms, _check_expected_count, expected_count, _destroy_project_tree
13 from rhodecode.model.repo import RepoModel
14
15
16 test_u1_id = None
17 _get_repo_perms = None
18 _get_group_perms = None
19
20
21 def permissions_setup_func(group_name='g0', perm='group.read', recursive=True):
22 """
23 Resets all permissions to perm attribute
24 """
25 repos_group = RepoGroup.get_by_group_name(group_name=group_name)
26 if not repos_group:
27 raise Exception('Cannot get group %s' % group_name)
28 perms_updates = [[test_u1_id, perm, 'user']]
29 ReposGroupModel()._update_permissions(repos_group,
30 perms_updates=perms_updates,
31 recursive=recursive)
32 Session().commit()
33
34
35 def setup_module():
36 global test_u1_id, _get_repo_perms, _get_group_perms
37 test_u1 = _create_project_tree()
38 Session().commit()
39 test_u1_id = test_u1.user_id
40 _get_repo_perms = functools.partial(_get_perms, key='repositories',
41 test_u1_id=test_u1_id)
42 _get_group_perms = functools.partial(_get_perms, key='repositories_groups',
43 test_u1_id=test_u1_id)
44
45
46 def teardown_module():
47 _destroy_project_tree(test_u1_id)
48
49
50 @with_setup(permissions_setup_func)
51 def test_user_permissions_on_group_without_recursive_mode():
52 # set permission to g0 non-recursive mode
53 recursive = False
54 group = 'g0'
55 permissions_setup_func(group, 'group.write', recursive=recursive)
56
57 items = [x for x in _get_repo_perms(group, recursive)]
58 expected = 0
59 assert len(items) == expected, ' %s != %s' % (len(items), expected)
60 for name, perm in items:
61 yield check_tree_perms, name, perm, group, 'repository.read'
62
63 items = [x for x in _get_group_perms(group, recursive)]
64 expected = 1
65 assert len(items) == expected, ' %s != %s' % (len(items), expected)
66 for name, perm in items:
67 yield check_tree_perms, name, perm, group, 'group.write'
68
69
70 @with_setup(permissions_setup_func)
71 def test_user_permissions_on_group_without_recursive_mode_subgroup():
72 # set permission to g0 non-recursive mode
73 recursive = False
74 group = 'g0/g0_1'
75 permissions_setup_func(group, 'group.write', recursive=recursive)
76
77 items = [x for x in _get_repo_perms(group, recursive)]
78 expected = 0
79 assert len(items) == expected, ' %s != %s' % (len(items), expected)
80 for name, perm in items:
81 yield check_tree_perms, name, perm, group, 'repository.read'
82
83 items = [x for x in _get_group_perms(group, recursive)]
84 expected = 1
85 assert len(items) == expected, ' %s != %s' % (len(items), expected)
86 for name, perm in items:
87 yield check_tree_perms, name, perm, group, 'group.write'
88
89
90 @with_setup(permissions_setup_func)
91 def test_user_permissions_on_group_with_recursive_mode():
92
93 # set permission to g0 recursive mode, all children including
94 # other repos and groups should have this permission now set !
95 recursive = True
96 group = 'g0'
97 permissions_setup_func(group, 'group.write', recursive=recursive)
98
99 repo_items = [x for x in _get_repo_perms(group, recursive)]
100 items = [x for x in _get_group_perms(group, recursive)]
101 _check_expected_count(items, repo_items, expected_count(group, True))
102
103 for name, perm in repo_items:
104 yield check_tree_perms, name, perm, group, 'repository.write'
105
106 for name, perm in items:
107 yield check_tree_perms, name, perm, group, 'group.write'
108
109
110 @with_setup(permissions_setup_func)
111 def test_user_permissions_on_group_with_recursive_mode_inner_group():
112 ## set permission to g0_3 group to none
113 recursive = True
114 group = 'g0/g0_3'
115 permissions_setup_func(group, 'group.none', recursive=recursive)
116
117 repo_items = [x for x in _get_repo_perms(group, recursive)]
118 items = [x for x in _get_group_perms(group, recursive)]
119 _check_expected_count(items, repo_items, expected_count(group, True))
120
121 for name, perm in repo_items:
122 yield check_tree_perms, name, perm, group, 'repository.none'
123
124 for name, perm in items:
125 yield check_tree_perms, name, perm, group, 'group.none'
126
127
128 @with_setup(permissions_setup_func)
129 def test_user_permissions_on_group_with_recursive_mode_deepest():
130 ## set permission to g0_3 group to none
131 recursive = True
132 group = 'g0/g0_1/g0_1_1'
133 permissions_setup_func(group, 'group.write', recursive=recursive)
134
135 repo_items = [x for x in _get_repo_perms(group, recursive)]
136 items = [x for x in _get_group_perms(group, recursive)]
137 _check_expected_count(items, repo_items, expected_count(group, True))
138
139 for name, perm in repo_items:
140 yield check_tree_perms, name, perm, group, 'repository.write'
141
142 for name, perm in items:
143 yield check_tree_perms, name, perm, group, 'group.write'
144
145
146 @with_setup(permissions_setup_func)
147 def test_user_permissions_on_group_with_recursive_mode_only_with_repos():
148 ## set permission to g0_3 group to none
149 recursive = True
150 group = 'g0/g0_2'
151 permissions_setup_func(group, 'group.admin', recursive=recursive)
152
153 repo_items = [x for x in _get_repo_perms(group, recursive)]
154 items = [x for x in _get_group_perms(group, recursive)]
155 _check_expected_count(items, repo_items, expected_count(group, True))
156
157 for name, perm in repo_items:
158 yield check_tree_perms, name, perm, group, 'repository.admin'
159
160 for name, perm in items:
161 yield check_tree_perms, name, perm, group, 'group.admin'
@@ -0,0 +1,170 b''
1 import os
2 import unittest
3 import functools
4 from rhodecode.tests import *
5
6 from rhodecode.model.repos_group import ReposGroupModel
7 from rhodecode.model.db import RepoGroup, Repository, User
8
9 from rhodecode.model.meta import Session
10 from nose.tools import with_setup
11 from rhodecode.tests.models.common import _create_project_tree, check_tree_perms, \
12 _get_perms, _check_expected_count, expected_count, _destroy_project_tree
13 from rhodecode.model.users_group import UsersGroupModel
14 from rhodecode.model.repo import RepoModel
15
16
17 test_u2_id = None
18 test_u2_gr_id = None
19 _get_repo_perms = None
20 _get_group_perms = None
21
22
23 def permissions_setup_func(group_name='g0', perm='group.read', recursive=True):
24 """
25 Resets all permissions to perm attribute
26 """
27 repos_group = RepoGroup.get_by_group_name(group_name=group_name)
28 if not repos_group:
29 raise Exception('Cannot get group %s' % group_name)
30 perms_updates = [[test_u2_gr_id, perm, 'users_group']]
31 ReposGroupModel()._update_permissions(repos_group,
32 perms_updates=perms_updates,
33 recursive=recursive)
34 Session().commit()
35
36
37 def setup_module():
38 global test_u2_id, test_u2_gr_id, _get_repo_perms, _get_group_perms
39 test_u2 = _create_project_tree()
40 Session().commit()
41 test_u2_id = test_u2.user_id
42
43 gr1 = UsersGroupModel().create(name='perms_group_1')
44 Session().commit()
45 test_u2_gr_id = gr1.users_group_id
46 UsersGroupModel().add_user_to_group(gr1, user=test_u2_id)
47 Session().commit()
48
49 _get_repo_perms = functools.partial(_get_perms, key='repositories',
50 test_u1_id=test_u2_id)
51 _get_group_perms = functools.partial(_get_perms, key='repositories_groups',
52 test_u1_id=test_u2_id)
53
54
55 def teardown_module():
56 _destroy_project_tree(test_u2_id)
57
58
59 @with_setup(permissions_setup_func)
60 def test_user_permissions_on_group_without_recursive_mode():
61 # set permission to g0 non-recursive mode
62 recursive = False
63 group = 'g0'
64 permissions_setup_func(group, 'group.write', recursive=recursive)
65
66 items = [x for x in _get_repo_perms(group, recursive)]
67 expected = 0
68 assert len(items) == expected, ' %s != %s' % (len(items), expected)
69 for name, perm in items:
70 yield check_tree_perms, name, perm, group, 'repository.read'
71
72 items = [x for x in _get_group_perms(group, recursive)]
73 expected = 1
74 assert len(items) == expected, ' %s != %s' % (len(items), expected)
75 for name, perm in items:
76 yield check_tree_perms, name, perm, group, 'group.write'
77
78
79 @with_setup(permissions_setup_func)
80 def test_user_permissions_on_group_without_recursive_mode_subgroup():
81 # set permission to g0 non-recursive mode
82 recursive = False
83 group = 'g0/g0_1'
84 permissions_setup_func(group, 'group.write', recursive=recursive)
85
86 items = [x for x in _get_repo_perms(group, recursive)]
87 expected = 0
88 assert len(items) == expected, ' %s != %s' % (len(items), expected)
89 for name, perm in items:
90 yield check_tree_perms, name, perm, group, 'repository.read'
91
92 items = [x for x in _get_group_perms(group, recursive)]
93 expected = 1
94 assert len(items) == expected, ' %s != %s' % (len(items), expected)
95 for name, perm in items:
96 yield check_tree_perms, name, perm, group, 'group.write'
97
98
99 @with_setup(permissions_setup_func)
100 def test_user_permissions_on_group_with_recursive_mode():
101
102 # set permission to g0 recursive mode, all children including
103 # other repos and groups should have this permission now set !
104 recursive = True
105 group = 'g0'
106 permissions_setup_func(group, 'group.write', recursive=recursive)
107
108 repo_items = [x for x in _get_repo_perms(group, recursive)]
109 items = [x for x in _get_group_perms(group, recursive)]
110 _check_expected_count(items, repo_items, expected_count(group, True))
111
112 for name, perm in repo_items:
113 yield check_tree_perms, name, perm, group, 'repository.write'
114
115 for name, perm in items:
116 yield check_tree_perms, name, perm, group, 'group.write'
117
118
119 @with_setup(permissions_setup_func)
120 def test_user_permissions_on_group_with_recursive_mode_inner_group():
121 ## set permission to g0_3 group to none
122 recursive = True
123 group = 'g0/g0_3'
124 permissions_setup_func(group, 'group.none', recursive=recursive)
125
126 repo_items = [x for x in _get_repo_perms(group, recursive)]
127 items = [x for x in _get_group_perms(group, recursive)]
128 _check_expected_count(items, repo_items, expected_count(group, True))
129
130 for name, perm in repo_items:
131 yield check_tree_perms, name, perm, group, 'repository.none'
132
133 for name, perm in items:
134 yield check_tree_perms, name, perm, group, 'group.none'
135
136
137 @with_setup(permissions_setup_func)
138 def test_user_permissions_on_group_with_recursive_mode_deepest():
139 ## set permission to g0_3 group to none
140 recursive = True
141 group = 'g0/g0_1/g0_1_1'
142 permissions_setup_func(group, 'group.write', recursive=recursive)
143
144 repo_items = [x for x in _get_repo_perms(group, recursive)]
145 items = [x for x in _get_group_perms(group, recursive)]
146 _check_expected_count(items, repo_items, expected_count(group, True))
147
148 for name, perm in repo_items:
149 yield check_tree_perms, name, perm, group, 'repository.write'
150
151 for name, perm in items:
152 yield check_tree_perms, name, perm, group, 'group.write'
153
154
155 @with_setup(permissions_setup_func)
156 def test_user_permissions_on_group_with_recursive_mode_only_with_repos():
157 ## set permission to g0_3 group to none
158 recursive = True
159 group = 'g0/g0_2'
160 permissions_setup_func(group, 'group.admin', recursive=recursive)
161
162 repo_items = [x for x in _get_repo_perms(group, recursive)]
163 items = [x for x in _get_group_perms(group, recursive)]
164 _check_expected_count(items, repo_items, expected_count(group, True))
165
166 for name, perm in repo_items:
167 yield check_tree_perms, name, perm, group, 'repository.admin'
168
169 for name, perm in items:
170 yield check_tree_perms, name, perm, group, 'group.admin'
@@ -4,6 +4,45 b''
4 Changelog
4 Changelog
5 =========
5 =========
6
6
7
8 1.4.1 (**2012-09-07**)
9 ----------------------
10
11 :status: in-progress
12 :branch: beta
13
14 news
15 ++++
16
17 - always put a comment about code-review status change even if user send
18 empty data
19 - modified_on column saves repository update and it's going to be used
20 later for light version of main page ref #500
21 - pull request notifications send much nicer emails with details about pull
22 request
23 - #551 show breadcrumbs in summary view for repositories inside a group
24
25 fixes
26 +++++
27
28 - fixed migrations of permissions that can lead to inconsistency.
29 Some users sent feedback that after upgrading from older versions issues
30 with updating default permissions occurred. RhodeCode detects that now and
31 resets default user permission to initial state if there is a need for that.
32 Also forces users to set the default value for new forking permission.
33 - #535 improved apache wsgi example configuration in docs
34 - fixes #550 mercurial repositories comparision failed when origin repo had
35 additional not-common changesets
36 - fixed status of code-review in preview windows of pull request
37 - git forks were not initialized at bare repos
38 - fixes #555 fixes issues with comparing non-related repositories
39 - fixes #557 follower counter always counts up
40 - fixed issue #560 require push ssl checkbox wasn't shown when option was
41 enabled
42 - fixed #559
43 - fixed issue #559 fixed bug in routing that mapped repo names with <name>_<num> in name as
44 if it was a request to url by repository ID
45
7 1.4.0 (**2012-09-03**)
46 1.4.0 (**2012-09-03**)
8 ----------------------
47 ----------------------
9
48
@@ -11,8 +11,8 b' clients. Minimal version of hg client kn'
11 **1.6**. If you're using older client, please upgrade.
11 **1.6**. If you're using older client, please upgrade.
12
12
13
13
14 Installing RhodeCode from Cheese Shop
14 Installing RhodeCode from PyPI (aka "Cheeseshop")
15 -------------------------------------
15 -------------------------------------------------
16
16
17 Rhodecode requires python version 2.5 or higher.
17 Rhodecode requires python version 2.5 or higher.
18
18
@@ -126,4 +126,4 b' You can now proceed to :ref:`setup`'
126 .. _python: http://www.python.org/
126 .. _python: http://www.python.org/
127 .. _mercurial: http://mercurial.selenic.com/
127 .. _mercurial: http://mercurial.selenic.com/
128 .. _celery: http://celeryproject.org/
128 .. _celery: http://celeryproject.org/
129 .. _rabbitmq: http://www.rabbitmq.com/ No newline at end of file
129 .. _rabbitmq: http://www.rabbitmq.com/
@@ -667,12 +667,21 b" that, you'll need to:"
667
667
668 Here is a sample excerpt from an Apache Virtual Host configuration file::
668 Here is a sample excerpt from an Apache Virtual Host configuration file::
669
669
670 WSGIDaemonProcess pylons user=www-data group=www-data processes=1 \
670 WSGIDaemonProcess pylons \
671 threads=4 \
671 threads=4 \
672 python-path=/home/web/rhodecode/pyenv/lib/python2.6/site-packages
672 python-path=/home/web/rhodecode/pyenv/lib/python2.6/site-packages
673 WSGIScriptAlias / /home/web/rhodecode/dispatch.wsgi
673 WSGIScriptAlias / /home/web/rhodecode/dispatch.wsgi
674 WSGIPassAuthorization On
674 WSGIPassAuthorization On
675
675
676 .. note::
677 when running apache as root please add: `user=www-data group=www-data`
678 into above configuration
679
680 .. note::
681 RhodeCode cannot be runned in multiprocess mode in apache, make sure
682 you don't specify `processes=num` directive in the config
683
684
676 Example wsgi dispatch script::
685 Example wsgi dispatch script::
677
686
678 import os
687 import os
@@ -4,14 +4,43 b''
4 Upgrade
4 Upgrade
5 =======
5 =======
6
6
7 Upgrading from Cheese Shop
7 Upgrading from PyPI (aka "Cheeseshop")
8 --------------------------
8 ---------------------------------------
9
9
10 .. note::
10 .. note::
11 Firstly, it is recommended that you **always** perform a database backup
11 Firstly, it is recommended that you **always** perform a database and
12 before doing an upgrade.
12 configuration backup before doing an upgrade.
13
14 (These directions will use '{version}' to note that this is the version of
15 Rhodecode that these files were used with. If backing up your RhodeCode
16 instance from version 1.3.6 to 1.4.0, the ``production.ini`` file would be
17 backed up to ``production.ini.1-3-6``.)
18
19
20 If using a sqlite database, stop the Rhodecode process/daemon/service, and
21 then make a copy of the database file::
22
23 service rhodecode stop
24 cp rhodecode.db rhodecode.db.{version}
25
13
26
14 The easiest way to upgrade ``rhodecode`` is to run::
27 Back up your configuration file::
28
29 cp production.ini production.ini.{version}
30
31
32 Ensure that you are using the Python Virtual Environment that you'd originally
33 installed Rhodecode in::
34
35 pip freeze
36
37 will list all packages installed in the current environment. If Rhodecode
38 isn't listed, change virtual environments to your venv location::
39
40 source /opt/rhodecode-venv/bin/activate
41
42
43 Once you have verified the environment you can upgrade ``Rhodecode`` with::
15
44
16 easy_install -U rhodecode
45 easy_install -U rhodecode
17
46
@@ -20,14 +49,13 b' Or::'
20 pip install --upgrade rhodecode
49 pip install --upgrade rhodecode
21
50
22
51
23 Then make sure you run the following command from the installation directory::
52 Then run the following command from the installation directory::
24
53
25 paster make-config RhodeCode production.ini
54 paster make-config RhodeCode production.ini
26
55
27 This will display any changes made by the new version of RhodeCode to your
56 This will display any changes made by the new version of RhodeCode to your
28 current configuration. It will try to perform an automerge. It's always better
57 current configuration. It will try to perform an automerge. It's recommended
29 to make a backup of your configuration file before hand and re check the
58 that you re-check the content after the automerge.
30 content after the automerge.
31
59
32 .. note::
60 .. note::
33 Please always make sure your .ini files are up to date. Often errors are
61 Please always make sure your .ini files are up to date. Often errors are
@@ -41,12 +69,25 b' Read the changelog to see if there were '
41
69
42 The final step is to upgrade the database. To do this simply run::
70 The final step is to upgrade the database. To do this simply run::
43
71
44 paster upgrade-db production.ini
72 paster upgrade-db production.ini
45
73
46 This will upgrade the schema and update some of the defaults in the database,
74 This will upgrade the schema and update some of the defaults in the database,
47 and will always recheck the settings of the application, if there are no new
75 and will always recheck the settings of the application, if there are no new
48 options that need to be set.
76 options that need to be set.
49
77
78 You may find it helpful to clear out your log file so that new errors are
79 readily apparent::
80
81 echo > rhodecode.log
82
83 Once that is complete, you may now start your upgraded Rhodecode Instance::
84
85 service rhodecode start
86
87 Or::
88
89 paster serve /var/www/rhodecode/production.ini
90
50 .. note::
91 .. note::
51 If you're using Celery, make sure you restart all instances of it after
92 If you're using Celery, make sure you restart all instances of it after
52 upgrade.
93 upgrade.
@@ -55,4 +96,4 b' options that need to be set.'
55 .. _python: http://www.python.org/
96 .. _python: http://www.python.org/
56 .. _mercurial: http://mercurial.selenic.com/
97 .. _mercurial: http://mercurial.selenic.com/
57 .. _celery: http://celeryproject.org/
98 .. _celery: http://celeryproject.org/
58 .. _rabbitmq: http://www.rabbitmq.com/ No newline at end of file
99 .. _rabbitmq: http://www.rabbitmq.com/
@@ -26,7 +26,7 b''
26 import sys
26 import sys
27 import platform
27 import platform
28
28
29 VERSION = (1, 4, 0)
29 VERSION = (1, 4, 1)
30
30
31 try:
31 try:
32 from rhodecode.lib import get_current_revision
32 from rhodecode.lib import get_current_revision
@@ -38,7 +38,7 b' except ImportError:'
38
38
39 __version__ = ('.'.join((str(each) for each in VERSION[:3])) +
39 __version__ = ('.'.join((str(each) for each in VERSION[:3])) +
40 '.'.join(VERSION[3:]))
40 '.'.join(VERSION[3:]))
41 __dbversion__ = 6 # defines current db version for migrations
41 __dbversion__ = 7 # defines current db version for migrations
42 __platform__ = platform.system()
42 __platform__ = platform.system()
43 __license__ = 'GPLv3'
43 __license__ = 'GPLv3'
44 __py_version__ = sys.version_info
44 __py_version__ = sys.version_info
@@ -34,7 +34,7 b' def make_map(config):'
34
34
35 try:
35 try:
36 by_id = repo_name.split('_')
36 by_id = repo_name.split('_')
37 if len(by_id) == 2 and by_id[1].isdigit():
37 if len(by_id) == 2 and by_id[1].isdigit() and by_id[0] == '':
38 repo_name = Repository.get(by_id[1]).repo_name
38 repo_name = Repository.get(by_id[1]).repo_name
39 match_dict['repo_name'] = repo_name
39 match_dict['repo_name'] = repo_name
40 except:
40 except:
@@ -45,6 +45,7 b' from rhodecode.model.forms import ReposG'
45 from rhodecode.model.meta import Session
45 from rhodecode.model.meta import Session
46 from rhodecode.model.repo import RepoModel
46 from rhodecode.model.repo import RepoModel
47 from webob.exc import HTTPInternalServerError, HTTPNotFound
47 from webob.exc import HTTPInternalServerError, HTTPNotFound
48 from rhodecode.lib.utils2 import str2bool
48
49
49 log = logging.getLogger(__name__)
50 log = logging.getLogger(__name__)
50
51
@@ -162,7 +163,7 b' class ReposGroupsController(BaseControll'
162 Session().commit()
163 Session().commit()
163 h.flash(_('updated repos group %s') \
164 h.flash(_('updated repos group %s') \
164 % form_result['group_name'], category='success')
165 % form_result['group_name'], category='success')
165 #TODO: in futureaction_logger(, '', '', '', self.sa)
166 #TODO: in future action_logger(, '', '', '', self.sa)
166 except formencode.Invalid, errors:
167 except formencode.Invalid, errors:
167
168
168 return htmlfill.render(
169 return htmlfill.render(
@@ -227,10 +228,11 b' class ReposGroupsController(BaseControll'
227
228
228 :param group_name:
229 :param group_name:
229 """
230 """
230
231 try:
231 try:
232 ReposGroupModel().revoke_user_permission(
232 recursive = str2bool(request.POST.get('recursive', False))
233 repos_group=group_name, user=request.POST['user_id']
233 ReposGroupModel().delete_permission(
234 repos_group=group_name, obj=request.POST['user_id'],
235 obj_type='user', recursive=recursive
234 )
236 )
235 Session().commit()
237 Session().commit()
236 except Exception:
238 except Exception:
@@ -248,9 +250,10 b' class ReposGroupsController(BaseControll'
248 """
250 """
249
251
250 try:
252 try:
251 ReposGroupModel().revoke_users_group_permission(
253 recursive = str2bool(request.POST.get('recursive', False))
252 repos_group=group_name,
254 ReposGroupModel().delete_permission(
253 group_name=request.POST['users_group_id']
255 repos_group=group_name, obj=request.POST['users_group_id'],
256 obj_type='users_group', recursive=recursive
254 )
257 )
255 Session().commit()
258 Session().commit()
256 except Exception:
259 except Exception:
@@ -51,6 +51,7 b' from rhodecode.model.user import UserMod'
51 from rhodecode.model.db import User
51 from rhodecode.model.db import User
52 from rhodecode.model.notification import EmailNotificationModel
52 from rhodecode.model.notification import EmailNotificationModel
53 from rhodecode.model.meta import Session
53 from rhodecode.model.meta import Session
54 from rhodecode.lib.utils2 import str2bool
54
55
55 log = logging.getLogger(__name__)
56 log = logging.getLogger(__name__)
56
57
@@ -471,6 +472,9 b' class SettingsController(BaseController)'
471 if k == '/':
472 if k == '/':
472 k = 'root_path'
473 k = 'root_path'
473
474
475 if k == 'push_ssl':
476 v = str2bool(v)
477
474 if k.find('.') != -1:
478 if k.find('.') != -1:
475 k = k.replace('.', '_')
479 k = k.replace('.', '_')
476
480
@@ -478,5 +482,4 b' class SettingsController(BaseController)'
478 v = each.ui_active
482 v = each.ui_active
479
483
480 settings[each.ui_section + '_' + k] = v
484 settings[each.ui_section + '_' + k] = v
481
482 return settings
485 return settings
@@ -376,9 +376,13 b' class ChangesetController(BaseRepoContro'
376 def comment(self, repo_name, revision):
376 def comment(self, repo_name, revision):
377 status = request.POST.get('changeset_status')
377 status = request.POST.get('changeset_status')
378 change_status = request.POST.get('change_changeset_status')
378 change_status = request.POST.get('change_changeset_status')
379 text = request.POST.get('text')
380 if status and change_status:
381 text = text or (_('Status change -> %s')
382 % ChangesetStatus.get_status_lbl(status))
379
383
380 comm = ChangesetCommentsModel().create(
384 comm = ChangesetCommentsModel().create(
381 text=request.POST.get('text'),
385 text=text,
382 repo=c.rhodecode_db_repo.repo_id,
386 repo=c.rhodecode_db_repo.repo_id,
383 user=c.rhodecode_user.user_id,
387 user=c.rhodecode_user.user_id,
384 revision=revision,
388 revision=revision,
@@ -391,7 +395,7 b' class ChangesetController(BaseRepoContro'
391 # get status if set !
395 # get status if set !
392 if status and change_status:
396 if status and change_status:
393 # if latest status was from pull request and it's closed
397 # if latest status was from pull request and it's closed
394 # disallow changing status !
398 # disallow changing status !
395 # dont_allow_on_closed_pull_request = True !
399 # dont_allow_on_closed_pull_request = True !
396
400
397 try:
401 try:
@@ -249,8 +249,7 b' class PullrequestsController(BaseRepoCon'
249 org_repo, org_ref, other_repo, other_ref
249 org_repo, org_ref, other_repo, other_ref
250 )
250 )
251
251
252 c.statuses = c.rhodecode_db_repo.statuses([x.raw_id for x in
252 c.statuses = org_repo.statuses([x.raw_id for x in c.cs_ranges])
253 c.cs_ranges])
254 # defines that we need hidden inputs with changesets
253 # defines that we need hidden inputs with changesets
255 c.as_form = request.GET.get('as_form', False)
254 c.as_form = request.GET.get('as_form', False)
256
255
@@ -277,6 +276,7 b' class PullrequestsController(BaseRepoCon'
277 c.users_array = repo_model.get_users_js()
276 c.users_array = repo_model.get_users_js()
278 c.users_groups_array = repo_model.get_users_groups_js()
277 c.users_groups_array = repo_model.get_users_groups_js()
279 c.pull_request = PullRequest.get_or_404(pull_request_id)
278 c.pull_request = PullRequest.get_or_404(pull_request_id)
279 c.target_repo = c.pull_request.org_repo.repo_name
280
280
281 cc_model = ChangesetCommentsModel()
281 cc_model = ChangesetCommentsModel()
282 cs_model = ChangesetStatusModel()
282 cs_model = ChangesetStatusModel()
@@ -322,12 +322,20 b' class PullrequestsController(BaseRepoCon'
322 c.comments = cc_model.get_comments(c.rhodecode_db_repo.repo_id,
322 c.comments = cc_model.get_comments(c.rhodecode_db_repo.repo_id,
323 pull_request=pull_request_id)
323 pull_request=pull_request_id)
324
324
325 # changeset(pull-request) status
325 try:
326 c.current_changeset_status = cs_model.calculate_status(
326 cur_status = c.statuses[c.pull_request.revisions[0]][0]
327 c.pull_request_reviewers
327 except:
328 )
328 log.error(traceback.format_exc())
329 cur_status = 'undefined'
330 if c.pull_request.is_closed() and 0:
331 c.current_changeset_status = cur_status
332 else:
333 # changeset(pull-request) status calulation based on reviewers
334 c.current_changeset_status = cs_model.calculate_status(
335 c.pull_request_reviewers,
336 )
329 c.changeset_statuses = ChangesetStatus.STATUSES
337 c.changeset_statuses = ChangesetStatus.STATUSES
330 c.target_repo = c.pull_request.org_repo.repo_name
338
331 return render('/pullrequests/pullrequest_show.html')
339 return render('/pullrequests/pullrequest_show.html')
332
340
333 @NotAnonymous()
341 @NotAnonymous()
@@ -339,9 +347,12 b' class PullrequestsController(BaseRepoCon'
339
347
340 status = request.POST.get('changeset_status')
348 status = request.POST.get('changeset_status')
341 change_status = request.POST.get('change_changeset_status')
349 change_status = request.POST.get('change_changeset_status')
342
350 text = request.POST.get('text')
351 if status and change_status:
352 text = text or (_('Status change -> %s')
353 % ChangesetStatus.get_status_lbl(status))
343 comm = ChangesetCommentsModel().create(
354 comm = ChangesetCommentsModel().create(
344 text=request.POST.get('text'),
355 text=text,
345 repo=c.rhodecode_db_repo.repo_id,
356 repo=c.rhodecode_db_repo.repo_id,
346 user=c.rhodecode_user.user_id,
357 user=c.rhodecode_user.user_id,
347 pull_request=pull_request_id,
358 pull_request=pull_request_id,
@@ -400,9 +400,19 b' def create_repo_fork(form_data, cur_user'
400 log.info('creating fork of %s as %s', source_repo_path,
400 log.info('creating fork of %s as %s', source_repo_path,
401 destination_fork_path)
401 destination_fork_path)
402 backend = get_backend(repo_type)
402 backend = get_backend(repo_type)
403 backend(safe_str(destination_fork_path), create=True,
403
404 src_url=safe_str(source_repo_path),
404 if repo_type == 'git':
405 update_after_clone=update_after_clone)
405 backend(safe_str(destination_fork_path), create=True,
406 src_url=safe_str(source_repo_path),
407 update_after_clone=update_after_clone,
408 bare=True)
409 elif repo_type == 'hg':
410 backend(safe_str(destination_fork_path), create=True,
411 src_url=safe_str(source_repo_path),
412 update_after_clone=update_after_clone)
413 else:
414 raise Exception('Unknown backend type %s' % repo_type)
415
406 log_create_repository(fork_repo.get_dict(), created_by=cur_user.username)
416 log_create_repository(fork_repo.get_dict(), created_by=cur_user.username)
407
417
408 action_logger(cur_user, 'user_forked_repo:%s' % fork_name,
418 action_logger(cur_user, 'user_forked_repo:%s' % fork_name,
@@ -589,6 +589,3 b' else:'
589 self.__cond.wait(timeout)
589 self.__cond.wait(timeout)
590 finally:
590 finally:
591 self.__cond.release()
591 self.__cond.release()
592
593
594
@@ -247,7 +247,22 b' class DbManage(object):'
247 Session().add(hggit)
247 Session().add(hggit)
248
248
249 notify('re-check default permissions')
249 notify('re-check default permissions')
250 self.klass.populate_default_permissions()
250 default_user = User.get_by_username(User.DEFAULT_USER)
251 perm = Permission.get_by_key('hg.fork.repository')
252 reg_perm = UserToPerm()
253 reg_perm.user = default_user
254 reg_perm.permission = perm
255 Session().add(reg_perm)
256
257 def step_7(self):
258 perm_fixes = self.klass.reset_permissions(User.DEFAULT_USER)
259 Session().commit()
260 if perm_fixes:
261 notify('There was an inconsistent state of permissions '
262 'detected for default user. Permissions are now '
263 'reset to the default value for default user. '
264 'Please validate and check default permissions '
265 'in admin panel')
251
266
252 upgrade_steps = [0] + range(curr_version + 1, __dbversion__ + 1)
267 upgrade_steps = [0] + range(curr_version + 1, __dbversion__ + 1)
253
268
@@ -470,6 +485,28 b' class DbManage(object):'
470 log.debug('missing default permission for group %s adding' % g)
485 log.debug('missing default permission for group %s adding' % g)
471 ReposGroupModel()._create_default_perms(g)
486 ReposGroupModel()._create_default_perms(g)
472
487
488 def reset_permissions(self, username):
489 """
490 Resets permissions to default state, usefull when old systems had
491 bad permissions, we must clean them up
492
493 :param username:
494 :type username:
495 """
496 default_user = User.get_by_username(username)
497 if not default_user:
498 return
499
500 u2p = UserToPerm.query()\
501 .filter(UserToPerm.user == default_user).all()
502 fixed = False
503 if len(u2p) != len(User.DEFAULT_PERMISSIONS):
504 for p in u2p:
505 Session().delete(p)
506 fixed = True
507 self.populate_default_permissions()
508 return fixed
509
473 def config_prompt(self, test_repo_path='', retries=3, defaults={}):
510 def config_prompt(self, test_repo_path='', retries=3, defaults={}):
474 _path = defaults.get('repos_location')
511 _path = defaults.get('repos_location')
475 if retries == 3:
512 if retries == 3:
@@ -506,7 +543,15 b' class DbManage(object):'
506 retries -= 1
543 retries -= 1
507 return self.config_prompt(test_repo_path, retries)
544 return self.config_prompt(test_repo_path, retries)
508
545
509 return path
546 real_path = os.path.realpath(path)
547
548 if real_path != path:
549 if not ask_ok(('Path looks like a symlink, Rhodecode will store '
550 'given path as %s ? [y/n]') % (real_path)):
551 log.error('Canceled by user')
552 sys.exit(-1)
553
554 return real_path
510
555
511 def create_settings(self, path):
556 def create_settings(self, path):
512
557
@@ -597,8 +642,7 b' class DbManage(object):'
597
642
598 default_user = User.get_by_username('default')
643 default_user = User.get_by_username('default')
599
644
600 for def_perm in ['hg.register.manual_activate', 'hg.create.repository',
645 for def_perm in User.DEFAULT_PERMISSIONS:
601 'hg.fork.repository', 'repository.read']:
602
646
603 perm = self.sa.query(Permission)\
647 perm = self.sa.query(Permission)\
604 .filter(Permission.permission_name == def_perm)\
648 .filter(Permission.permission_name == def_perm)\
@@ -1317,4 +1317,4 b' class PullRequest(Base, BaseModel):'
1317 org_repo_id = Column('org_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1317 org_repo_id = Column('org_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1318 org_ref = Column('org_ref', Unicode(256), nullable=False)
1318 org_ref = Column('org_ref', Unicode(256), nullable=False)
1319 other_repo_id = Column('other_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1319 other_repo_id = Column('other_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1320 other_ref = Column('other_ref', Unicode(256), nullable=False) No newline at end of file
1320 other_ref = Column('other_ref', Unicode(256), nullable=False)
@@ -49,7 +49,7 b' def upgrade(migrate_engine):'
49 tbl = ChangesetStatus.__table__
49 tbl = ChangesetStatus.__table__
50 tbl.create()
50 tbl.create()
51
51
52 ## RESET COMPLETLY THE metadata for sqlalchemy to use the 1_3_0 Base
52 ## RESET COMPLETLY THE metadata for sqlalchemy to use the 1_3_0 Base
53 Base = declarative_base()
53 Base = declarative_base()
54 Base.metadata.clear()
54 Base.metadata.clear()
55 Base.metadata = MetaData()
55 Base.metadata = MetaData()
@@ -610,7 +610,7 b' def differ(org_repo, org_ref, other_repo'
610 other_repo.ui.setconfig('hooks', k, None)
610 other_repo.ui.setconfig('hooks', k, None)
611
611
612 unbundle = other_repo.getbundle('incoming', common=common,
612 unbundle = other_repo.getbundle('incoming', common=common,
613 heads=rheads)
613 heads=None)
614
614
615 buf = BytesIO()
615 buf = BytesIO()
616 while True:
616 while True:
@@ -92,7 +92,7 b' try:'
92 return _obj_dump(obj)
92 return _obj_dump(obj)
93 except NotImplementedError:
93 except NotImplementedError:
94 pass
94 pass
95 return json.JSONEncoder.default(self, obj)
95 raise TypeError("%r is not JSON serializable" % (obj,))
96 # monkey-patch JSON encoder to use extended version
96 # monkey-patch JSON encoder to use extended version
97 json.dumps = functools.partial(json.dumps, cls=ExtendedEncoder)
97 json.dumps = functools.partial(json.dumps, cls=ExtendedEncoder)
98 json.dump = functools.partial(json.dump, cls=ExtendedEncoder)
98 json.dump = functools.partial(json.dump, cls=ExtendedEncoder)
@@ -34,10 +34,9 b' from rhodecode.lib import helpers as h'
34 from rhodecode.lib.utils import action_logger
34 from rhodecode.lib.utils import action_logger
35 from rhodecode.lib.vcs.backends.base import EmptyChangeset
35 from rhodecode.lib.vcs.backends.base import EmptyChangeset
36 from rhodecode.lib.compat import json
36 from rhodecode.lib.compat import json
37 from rhodecode.model.db import Repository, User
37 from rhodecode.lib.exceptions import HTTPLockedRC
38 from rhodecode.lib.utils2 import safe_str
38 from rhodecode.lib.utils2 import safe_str
39 from rhodecode.lib.exceptions import HTTPLockedRC
39 from rhodecode.model.db import Repository, User
40
41
40
42 def _get_scm_size(alias, root_path):
41 def _get_scm_size(alias, root_path):
43
42
@@ -330,7 +329,12 b' def handle_git_receive(repo_path, revs, '
330 # fix if it's not a bare repo
329 # fix if it's not a bare repo
331 if repo_path.endswith('.git'):
330 if repo_path.endswith('.git'):
332 repo_path = repo_path[:-4]
331 repo_path = repo_path[:-4]
332
333 repo = Repository.get_by_full_path(repo_path)
333 repo = Repository.get_by_full_path(repo_path)
334 if not repo:
335 raise OSError('Repository %s not found in database'
336 % (safe_str(repo_path)))
337
334 _hooks = dict(baseui.configitems('hooks')) or {}
338 _hooks = dict(baseui.configitems('hooks')) or {}
335
339
336 extras = json.loads(env['RHODECODE_EXTRAS'])
340 extras = json.loads(env['RHODECODE_EXTRAS'])
@@ -206,7 +206,7 b' def get_repos(path, recursive=False):'
206 def is_valid_repo(repo_name, base_path, scm=None):
206 def is_valid_repo(repo_name, base_path, scm=None):
207 """
207 """
208 Returns True if given path is a valid repository False otherwise.
208 Returns True if given path is a valid repository False otherwise.
209 If scm param is given also compare if given scm is the same as expected
209 If scm param is given also compare if given scm is the same as expected
210 from scm parameter
210 from scm parameter
211
211
212 :param repo_name:
212 :param repo_name:
@@ -413,6 +413,11 b' def repo2db_mapper(initial_repo_list, re'
413 raise Exception('Missing administrative account !')
413 raise Exception('Missing administrative account !')
414 added = []
414 added = []
415
415
416 # # clear cache keys
417 # log.debug("Clearing cache keys now...")
418 # CacheInvalidation.clear_cache()
419 # sa.commit()
420
416 for name, repo in initial_repo_list.items():
421 for name, repo in initial_repo_list.items():
417 group = map_groups(name)
422 group = map_groups(name)
418 db_repo = rm.get_by_repo_name(name)
423 db_repo = rm.get_by_repo_name(name)
@@ -438,6 +443,11 b' def repo2db_mapper(initial_repo_list, re'
438 elif install_git_hook:
443 elif install_git_hook:
439 if db_repo.repo_type == 'git':
444 if db_repo.repo_type == 'git':
440 ScmModel().install_git_hook(db_repo.scm_instance)
445 ScmModel().install_git_hook(db_repo.scm_instance)
446 # during starting install all cache keys for all repositories in the
447 # system, this will register all repos and multiple instances
448 key, _prefix, _org_key = CacheInvalidation._get_key(name)
449 log.debug("Creating cache key for %s instance_id:`%s`" % (name, _prefix))
450 CacheInvalidation._get_or_create_key(key, _prefix, _org_key, commit=False)
441 sa.commit()
451 sa.commit()
442 removed = []
452 removed = []
443 if remove_obsolete:
453 if remove_obsolete:
@@ -455,10 +465,6 b' def repo2db_mapper(initial_repo_list, re'
455 log.error(traceback.format_exc())
465 log.error(traceback.format_exc())
456 sa.rollback()
466 sa.rollback()
457
467
458 # clear cache keys
459 log.debug("Clearing cache keys now...")
460 CacheInvalidation.clear_cache()
461 sa.commit()
462 return added, removed
468 return added, removed
463
469
464
470
@@ -178,7 +178,7 b' class GitRepository(BaseRepository):'
178 raise urllib2.URLError("[%s] %s" % (url, e))
178 raise urllib2.URLError("[%s] %s" % (url, e))
179
179
180 def _get_repo(self, create, src_url=None, update_after_clone=False,
180 def _get_repo(self, create, src_url=None, update_after_clone=False,
181 bare=False):
181 bare=False):
182 if create and os.path.exists(self.path):
182 if create and os.path.exists(self.path):
183 raise RepositoryError("Location already exist")
183 raise RepositoryError("Location already exist")
184 if src_url and not create:
184 if src_url and not create:
@@ -14,4 +14,5 b' from mercurial.node import hex'
14 from mercurial.encoding import tolocal
14 from mercurial.encoding import tolocal
15 from mercurial import discovery
15 from mercurial import discovery
16 from mercurial import localrepo
16 from mercurial import localrepo
17 from mercurial import scmutil No newline at end of file
17 from mercurial import scmutil
18 from mercurial.discovery import findcommonoutgoing
@@ -64,7 +64,7 b' class ChangesetStatusModel(BaseModel):'
64
64
65 def calculate_status(self, statuses_by_reviewers):
65 def calculate_status(self, statuses_by_reviewers):
66 """
66 """
67 leading one wins, if number of occurences are equal than weaker wins
67 leading one wins, if number of occurrences are equal than weaker wins
68
68
69 :param statuses_by_reviewers:
69 :param statuses_by_reviewers:
70 """
70 """
@@ -123,18 +123,20 b' class ChangesetCommentsModel(BaseModel):'
123 recipients = ChangesetComment.get_users(revision=revision)
123 recipients = ChangesetComment.get_users(revision=revision)
124 # add changeset author if it's in rhodecode system
124 # add changeset author if it's in rhodecode system
125 recipients += [User.get_by_email(author_email)]
125 recipients += [User.get_by_email(author_email)]
126 email_kwargs = {
127 'status_change': status_change,
128 }
126 #pull request
129 #pull request
127 elif pull_request:
130 elif pull_request:
131 _url = h.url('pullrequest_show',
132 repo_name=pull_request.other_repo.repo_name,
133 pull_request_id=pull_request.pull_request_id,
134 anchor='comment-%s' % comment.comment_id,
135 qualified=True,
136 )
128 subj = safe_unicode(
137 subj = safe_unicode(
129 h.link_to('Re pull request: %(desc)s %(line)s' % \
138 h.link_to('Re pull request: %(desc)s %(line)s' % \
130 {'desc': desc, 'line': line},
139 {'desc': desc, 'line': line}, _url)
131 h.url('pullrequest_show',
132 repo_name=pull_request.other_repo.repo_name,
133 pull_request_id=pull_request.pull_request_id,
134 anchor='comment-%s' % comment.comment_id,
135 qualified=True,
136 )
137 )
138 )
140 )
139
141
140 notification_type = Notification.TYPE_PULL_REQUEST_COMMENT
142 notification_type = Notification.TYPE_PULL_REQUEST_COMMENT
@@ -144,22 +146,36 b' class ChangesetCommentsModel(BaseModel):'
144 # add pull request author
146 # add pull request author
145 recipients += [pull_request.author]
147 recipients += [pull_request.author]
146
148
149 # add the reviewers to notification
150 recipients += [x.user for x in pull_request.reviewers]
151
152 #set some variables for email notification
153 email_kwargs = {
154 'pr_id': pull_request.pull_request_id,
155 'status_change': status_change,
156 'pr_comment_url': _url,
157 'pr_comment_user': h.person(user.email),
158 'pr_target_repo': h.url('summary_home',
159 repo_name=pull_request.other_repo.repo_name,
160 qualified=True)
161 }
147 # create notification objects, and emails
162 # create notification objects, and emails
148 NotificationModel().create(
163 NotificationModel().create(
149 created_by=user, subject=subj, body=body,
164 created_by=user, subject=subj, body=body,
150 recipients=recipients, type_=notification_type,
165 recipients=recipients, type_=notification_type,
151 email_kwargs={'status_change': status_change}
166 email_kwargs=email_kwargs
152 )
167 )
153
168
154 mention_recipients = set(self._extract_mentions(body))\
169 mention_recipients = set(self._extract_mentions(body))\
155 .difference(recipients)
170 .difference(recipients)
156 if mention_recipients:
171 if mention_recipients:
172 email_kwargs.update({'pr_mention': True})
157 subj = _('[Mention]') + ' ' + subj
173 subj = _('[Mention]') + ' ' + subj
158 NotificationModel().create(
174 NotificationModel().create(
159 created_by=user, subject=subj, body=body,
175 created_by=user, subject=subj, body=body,
160 recipients=mention_recipients,
176 recipients=mention_recipients,
161 type_=notification_type,
177 type_=notification_type,
162 email_kwargs={'status_change': status_change}
178 email_kwargs=email_kwargs
163 )
179 )
164
180
165 return comment
181 return comment
@@ -278,6 +278,10 b' class RhodeCodeUi(Base, BaseModel):'
278
278
279 Session().add(new_ui)
279 Session().add(new_ui)
280
280
281 def __repr__(self):
282 return '<DB:%s[%s:%s]>' % (self.__class__.__name__, self.ui_key,
283 self.ui_value)
284
281
285
282 class User(Base, BaseModel):
286 class User(Base, BaseModel):
283 __tablename__ = 'users'
287 __tablename__ = 'users'
@@ -289,7 +293,10 b' class User(Base, BaseModel):'
289 'mysql_charset': 'utf8'}
293 'mysql_charset': 'utf8'}
290 )
294 )
291 DEFAULT_USER = 'default'
295 DEFAULT_USER = 'default'
292
296 DEFAULT_PERMISSIONS = [
297 'hg.register.manual_activate', 'hg.create.repository',
298 'hg.fork.repository', 'repository.read'
299 ]
293 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
300 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
294 username = Column("username", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
301 username = Column("username", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
295 password = Column("password", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
302 password = Column("password", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
@@ -602,6 +609,7 b' class Repository(Base, BaseModel):'
602 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
609 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
603 description = Column("description", String(10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
610 description = Column("description", String(10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
604 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
611 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
612 updated_on = Column('updated_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
605 landing_rev = Column("landing_revision", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default=None)
613 landing_rev = Column("landing_revision", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default=None)
606 enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
614 enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
607 _locked = Column("locked", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
615 _locked = Column("locked", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
@@ -742,6 +750,16 b' class Repository(Base, BaseModel):'
742 p += self.repo_name.split(Repository.url_sep())
750 p += self.repo_name.split(Repository.url_sep())
743 return os.path.join(*p)
751 return os.path.join(*p)
744
752
753 @property
754 def cache_keys(self):
755 """
756 Returns associated cache keys for that repo
757 """
758 return CacheInvalidation.query()\
759 .filter(CacheInvalidation.cache_args == self.repo_name)\
760 .order_by(CacheInvalidation.cache_key)\
761 .all()
762
745 def get_new_name(self, repo_name):
763 def get_new_name(self, repo_name):
746 """
764 """
747 returns new full repository name based on assigned group and new new
765 returns new full repository name based on assigned group and new new
@@ -1398,6 +1416,13 b' class CacheInvalidation(Base, BaseModel)'
1398 return u"<%s('%s:%s')>" % (self.__class__.__name__,
1416 return u"<%s('%s:%s')>" % (self.__class__.__name__,
1399 self.cache_id, self.cache_key)
1417 self.cache_id, self.cache_key)
1400
1418
1419 @property
1420 def prefix(self):
1421 _split = self.cache_key.split(self.cache_args, 1)
1422 if _split and len(_split) == 2:
1423 return _split[0]
1424 return ''
1425
1401 @classmethod
1426 @classmethod
1402 def clear_cache(cls):
1427 def clear_cache(cls):
1403 cls.query().delete()
1428 cls.query().delete()
@@ -1421,13 +1446,14 b' class CacheInvalidation(Base, BaseModel)'
1421 return cls.query().filter(cls.cache_key == key).scalar()
1446 return cls.query().filter(cls.cache_key == key).scalar()
1422
1447
1423 @classmethod
1448 @classmethod
1424 def _get_or_create_key(cls, key, prefix, org_key):
1449 def _get_or_create_key(cls, key, prefix, org_key, commit=True):
1425 inv_obj = Session().query(cls).filter(cls.cache_key == key).scalar()
1450 inv_obj = Session().query(cls).filter(cls.cache_key == key).scalar()
1426 if not inv_obj:
1451 if not inv_obj:
1427 try:
1452 try:
1428 inv_obj = CacheInvalidation(key, org_key)
1453 inv_obj = CacheInvalidation(key, org_key)
1429 Session().add(inv_obj)
1454 Session().add(inv_obj)
1430 Session().commit()
1455 if commit:
1456 Session().commit()
1431 except Exception:
1457 except Exception:
1432 log.error(traceback.format_exc())
1458 log.error(traceback.format_exc())
1433 Session().rollback()
1459 Session().rollback()
@@ -128,6 +128,7 b' def ReposGroupForm(edit=False, old_data='
128 testValueList=True,
128 testValueList=True,
129 if_missing=None, not_empty=False)
129 if_missing=None, not_empty=False)
130 enable_locking = v.StringBoolean(if_missing=False)
130 enable_locking = v.StringBoolean(if_missing=False)
131 recursive = v.StringBoolean(if_missing=False)
131 chained_validators = [v.ValidReposGroup(edit, old_data),
132 chained_validators = [v.ValidReposGroup(edit, old_data),
132 v.ValidPerms('group')]
133 v.ValidPerms('group')]
133
134
@@ -340,4 +341,4 b' def PullRequestForm():'
340 pullrequest_title = v.UnicodeString(strip=True, required=True, min=3)
341 pullrequest_title = v.UnicodeString(strip=True, required=True, min=3)
341 pullrequest_desc = v.UnicodeString(strip=True, required=False)
342 pullrequest_desc = v.UnicodeString(strip=True, required=False)
342
343
343 return _PullRequestForm No newline at end of file
344 return _PullRequestForm
@@ -245,6 +245,7 b' class EmailNotificationModel(BaseModel):'
245 TYPE_PASSWORD_RESET = 'passoword_link'
245 TYPE_PASSWORD_RESET = 'passoword_link'
246 TYPE_REGISTRATION = Notification.TYPE_REGISTRATION
246 TYPE_REGISTRATION = Notification.TYPE_REGISTRATION
247 TYPE_PULL_REQUEST = Notification.TYPE_PULL_REQUEST
247 TYPE_PULL_REQUEST = Notification.TYPE_PULL_REQUEST
248 TYPE_PULL_REQUEST_COMMENT = Notification.TYPE_PULL_REQUEST_COMMENT
248 TYPE_DEFAULT = 'default'
249 TYPE_DEFAULT = 'default'
249
250
250 def __init__(self):
251 def __init__(self):
@@ -255,7 +256,9 b' class EmailNotificationModel(BaseModel):'
255 self.TYPE_CHANGESET_COMMENT: 'email_templates/changeset_comment.html',
256 self.TYPE_CHANGESET_COMMENT: 'email_templates/changeset_comment.html',
256 self.TYPE_PASSWORD_RESET: 'email_templates/password_reset.html',
257 self.TYPE_PASSWORD_RESET: 'email_templates/password_reset.html',
257 self.TYPE_REGISTRATION: 'email_templates/registration.html',
258 self.TYPE_REGISTRATION: 'email_templates/registration.html',
258 self.TYPE_DEFAULT: 'email_templates/default.html'
259 self.TYPE_DEFAULT: 'email_templates/default.html',
260 self.TYPE_PULL_REQUEST: 'email_templates/pull_request.html',
261 self.TYPE_PULL_REQUEST_COMMENT: 'email_templates/pull_request_comment.html',
259 }
262 }
260
263
261 def get_email_tmpl(self, type_, **kwargs):
264 def get_email_tmpl(self, type_, **kwargs):
@@ -77,7 +77,7 b' class PermissionModel(BaseModel):'
77 form_result['perm_user_name']).scalar()
77 form_result['perm_user_name']).scalar()
78 u2p = self.sa.query(UserToPerm).filter(UserToPerm.user ==
78 u2p = self.sa.query(UserToPerm).filter(UserToPerm.user ==
79 perm_user).all()
79 perm_user).all()
80 if len(u2p) != 4:
80 if len(u2p) != len(User.DEFAULT_PERMISSIONS):
81 raise Exception('Defined: %s should be 4 permissions for default'
81 raise Exception('Defined: %s should be 4 permissions for default'
82 ' user. This should not happen please verify'
82 ' user. This should not happen please verify'
83 ' your database' % len(u2p))
83 ' your database' % len(u2p))
@@ -36,7 +36,8 b' from rhodecode.model.db import PullReque'
36 from rhodecode.model.notification import NotificationModel
36 from rhodecode.model.notification import NotificationModel
37 from rhodecode.lib.utils2 import safe_unicode
37 from rhodecode.lib.utils2 import safe_unicode
38
38
39 from rhodecode.lib.vcs.utils.hgcompat import discovery, localrepo, scmutil
39 from rhodecode.lib.vcs.utils.hgcompat import discovery, localrepo, scmutil, \
40 findcommonoutgoing
40
41
41 log = logging.getLogger(__name__)
42 log = logging.getLogger(__name__)
42
43
@@ -79,22 +80,30 b' class PullRequestModel(BaseModel):'
79 #notification to reviewers
80 #notification to reviewers
80 notif = NotificationModel()
81 notif = NotificationModel()
81
82
83 pr_url = h.url('pullrequest_show', repo_name=other_repo.repo_name,
84 pull_request_id=new.pull_request_id,
85 qualified=True,
86 )
82 subject = safe_unicode(
87 subject = safe_unicode(
83 h.link_to(
88 h.link_to(
84 _('%(user)s wants you to review pull request #%(pr_id)s') % \
89 _('%(user)s wants you to review pull request #%(pr_id)s') % \
85 {'user': created_by_user.username,
90 {'user': created_by_user.username,
86 'pr_id': new.pull_request_id},
91 'pr_id': new.pull_request_id},
87 h.url('pullrequest_show', repo_name=other_repo.repo_name,
92 pr_url
88 pull_request_id=new.pull_request_id,
89 qualified=True,
90 )
91 )
93 )
92 )
94 )
93 body = description
95 body = description
96 kwargs = {
97 'pr_title': title,
98 'pr_user_created': h.person(created_by_user.email),
99 'pr_repo_url': h.url('summary_home', repo_name=other_repo.repo_name,
100 qualified=True,),
101 'pr_url': pr_url,
102 'pr_revisions': revisions
103 }
94 notif.create(created_by=created_by_user, subject=subject, body=body,
104 notif.create(created_by=created_by_user, subject=subject, body=body,
95 recipients=reviewers,
105 recipients=reviewers,
96 type_=Notification.TYPE_PULL_REQUEST,)
106 type_=Notification.TYPE_PULL_REQUEST, email_kwargs=kwargs)
97
98 return new
107 return new
99
108
100 def update_reviewers(self, pull_request, reviewers_ids):
109 def update_reviewers(self, pull_request, reviewers_ids):
@@ -156,7 +165,10 b' class PullRequestModel(BaseModel):'
156 #case two independent repos
165 #case two independent repos
157 common, incoming, rheads = discovery_data
166 common, incoming, rheads = discovery_data
158 if org_repo != other_repo and incoming:
167 if org_repo != other_repo and incoming:
159 revs = org_repo._repo.changelog.findmissing(common, rheads)
168 obj = findcommonoutgoing(org_repo._repo,
169 localrepo.locallegacypeer(other_repo._repo.local()),
170 force=True)
171 revs = obj.missing
160
172
161 for cs in reversed(map(binascii.hexlify, revs)):
173 for cs in reversed(map(binascii.hexlify, revs)):
162 changesets.append(org_repo.get_changeset(cs))
174 changesets.append(org_repo.get_changeset(cs))
@@ -205,6 +217,7 b' class PullRequestModel(BaseModel):'
205 log.debug('Doing discovery for %s@%s vs %s@%s' % (
217 log.debug('Doing discovery for %s@%s vs %s@%s' % (
206 org_repo, org_ref, other_repo, other_ref)
218 org_repo, org_ref, other_repo, other_ref)
207 )
219 )
220
208 #log.debug('Filter heads are %s[%s]' % ('', org_ref[1]))
221 #log.debug('Filter heads are %s[%s]' % ('', org_ref[1]))
209 org_peer = localrepo.locallegacypeer(_org_repo.local())
222 org_peer = localrepo.locallegacypeer(_org_repo.local())
210 tmp = discovery.findcommonincoming(
223 tmp = discovery.findcommonincoming(
@@ -212,7 +225,7 b' class PullRequestModel(BaseModel):'
212 remote=org_peer, # org_repo source for incoming
225 remote=org_peer, # org_repo source for incoming
213 heads=[_other_repo[other_rev].node(),
226 heads=[_other_repo[other_rev].node(),
214 _org_repo[org_rev].node()],
227 _org_repo[org_rev].node()],
215 force=False
228 force=True
216 )
229 )
217 return tmp
230 return tmp
218
231
@@ -368,6 +368,7 b' class RepoModel(BaseModel):'
368 obj.user = user
368 obj.user = user
369 obj.permission = permission
369 obj.permission = permission
370 self.sa.add(obj)
370 self.sa.add(obj)
371 log.debug('Granted perm %s to %s on %s' % (perm, user, repo))
371
372
372 def revoke_user_permission(self, repo, user):
373 def revoke_user_permission(self, repo, user):
373 """
374 """
@@ -383,8 +384,10 b' class RepoModel(BaseModel):'
383 obj = self.sa.query(UserRepoToPerm)\
384 obj = self.sa.query(UserRepoToPerm)\
384 .filter(UserRepoToPerm.repository == repo)\
385 .filter(UserRepoToPerm.repository == repo)\
385 .filter(UserRepoToPerm.user == user)\
386 .filter(UserRepoToPerm.user == user)\
386 .one()
387 .scalar()
387 self.sa.delete(obj)
388 if obj:
389 self.sa.delete(obj)
390 log.debug('Revoked perm on %s on %s' % (repo, user))
388
391
389 def grant_users_group_permission(self, repo, group_name, perm):
392 def grant_users_group_permission(self, repo, group_name, perm):
390 """
393 """
@@ -414,6 +417,7 b' class RepoModel(BaseModel):'
414 obj.users_group = group_name
417 obj.users_group = group_name
415 obj.permission = permission
418 obj.permission = permission
416 self.sa.add(obj)
419 self.sa.add(obj)
420 log.debug('Granted perm %s to %s on %s' % (perm, group_name, repo))
417
421
418 def revoke_users_group_permission(self, repo, group_name):
422 def revoke_users_group_permission(self, repo, group_name):
419 """
423 """
@@ -429,8 +433,10 b' class RepoModel(BaseModel):'
429 obj = self.sa.query(UsersGroupRepoToPerm)\
433 obj = self.sa.query(UsersGroupRepoToPerm)\
430 .filter(UsersGroupRepoToPerm.repository == repo)\
434 .filter(UsersGroupRepoToPerm.repository == repo)\
431 .filter(UsersGroupRepoToPerm.users_group == group_name)\
435 .filter(UsersGroupRepoToPerm.users_group == group_name)\
432 .one()
436 .scalar()
433 self.sa.delete(obj)
437 if obj:
438 self.sa.delete(obj)
439 log.debug('Revoked perm to %s on %s' % (repo, group_name))
434
440
435 def delete_stats(self, repo_name):
441 def delete_stats(self, repo_name):
436 """
442 """
@@ -32,7 +32,7 b' from rhodecode.lib.utils2 import LazyPro'
32
32
33 from rhodecode.model import BaseModel
33 from rhodecode.model import BaseModel
34 from rhodecode.model.db import RepoGroup, RhodeCodeUi, UserRepoGroupToPerm, \
34 from rhodecode.model.db import RepoGroup, RhodeCodeUi, UserRepoGroupToPerm, \
35 User, Permission, UsersGroupRepoGroupToPerm, UsersGroup
35 User, Permission, UsersGroupRepoGroupToPerm, UsersGroup, Repository
36
36
37 log = logging.getLogger(__name__)
37 log = logging.getLogger(__name__)
38
38
@@ -115,11 +115,12 b' class ReposGroupModel(BaseModel):'
115 'existing dir %s' % new_path)
115 'existing dir %s' % new_path)
116 shutil.move(old_path, new_path)
116 shutil.move(old_path, new_path)
117
117
118 def __delete_group(self, group):
118 def __delete_group(self, group, force_delete=False):
119 """
119 """
120 Deletes a group from a filesystem
120 Deletes a group from a filesystem
121
121
122 :param group: instance of group from database
122 :param group: instance of group from database
123 :param force_delete: use shutil rmtree to remove all objects
123 """
124 """
124 paths = group.full_path.split(RepoGroup.url_sep())
125 paths = group.full_path.split(RepoGroup.url_sep())
125 paths = os.sep.join(paths)
126 paths = os.sep.join(paths)
@@ -127,7 +128,10 b' class ReposGroupModel(BaseModel):'
127 rm_path = os.path.join(self.repos_path, paths)
128 rm_path = os.path.join(self.repos_path, paths)
128 if os.path.isdir(rm_path):
129 if os.path.isdir(rm_path):
129 # delete only if that path really exists
130 # delete only if that path really exists
130 os.rmdir(rm_path)
131 if force_delete:
132 shutil.rmtree(rm_path)
133 else:
134 os.rmdir(rm_path) # this raises an exception when there are still objects inside
131
135
132 def create(self, group_name, group_description, parent=None, just_db=False):
136 def create(self, group_name, group_description, parent=None, just_db=False):
133 try:
137 try:
@@ -150,32 +154,79 b' class ReposGroupModel(BaseModel):'
150 log.error(traceback.format_exc())
154 log.error(traceback.format_exc())
151 raise
155 raise
152
156
157 def _update_permissions(self, repos_group, perms_new=None,
158 perms_updates=None, recursive=False):
159 from rhodecode.model.repo import RepoModel
160 if not perms_new:
161 perms_new = []
162 if not perms_updates:
163 perms_updates = []
164
165 def _set_perm_user(obj, user, perm):
166 if isinstance(obj, RepoGroup):
167 ReposGroupModel().grant_user_permission(
168 repos_group=obj, user=user, perm=perm
169 )
170 elif isinstance(obj, Repository):
171 # we set group permission but we have to switch to repo
172 # permission
173 perm = perm.replace('group.', 'repository.')
174 RepoModel().grant_user_permission(
175 repo=obj, user=user, perm=perm
176 )
177
178 def _set_perm_group(obj, users_group, perm):
179 if isinstance(obj, RepoGroup):
180 ReposGroupModel().grant_users_group_permission(
181 repos_group=obj, group_name=users_group, perm=perm
182 )
183 elif isinstance(obj, Repository):
184 # we set group permission but we have to switch to repo
185 # permission
186 perm = perm.replace('group.', 'repository.')
187 RepoModel().grant_users_group_permission(
188 repo=obj, group_name=users_group, perm=perm
189 )
190 updates = []
191 log.debug('Now updating permissions for %s in recursive mode:%s'
192 % (repos_group, recursive))
193
194 for obj in repos_group.recursive_groups_and_repos():
195 if not recursive:
196 obj = repos_group
197
198 # update permissions
199 for member, perm, member_type in perms_updates:
200 ## set for user
201 if member_type == 'user':
202 # this updates also current one if found
203 _set_perm_user(obj, user=member, perm=perm)
204 ## set for users group
205 else:
206 _set_perm_group(obj, users_group=member, perm=perm)
207 # set new permissions
208 for member, perm, member_type in perms_new:
209 if member_type == 'user':
210 _set_perm_user(obj, user=member, perm=perm)
211 else:
212 _set_perm_group(obj, users_group=member, perm=perm)
213 updates.append(obj)
214 #if it's not recursive call
215 # break the loop and don't proceed with other changes
216 if not recursive:
217 break
218 return updates
219
153 def update(self, repos_group_id, form_data):
220 def update(self, repos_group_id, form_data):
154
221
155 try:
222 try:
156 repos_group = RepoGroup.get(repos_group_id)
223 repos_group = RepoGroup.get(repos_group_id)
157
224 recursive = form_data['recursive']
158 # update permissions
225 # iterate over all members(if in recursive mode) of this groups and
159 for member, perm, member_type in form_data['perms_updates']:
226 # set the permissions !
160 if member_type == 'user':
227 # this can be potentially heavy operation
161 # this updates also current one if found
228 self._update_permissions(repos_group, form_data['perms_new'],
162 ReposGroupModel().grant_user_permission(
229 form_data['perms_updates'], recursive)
163 repos_group=repos_group, user=member, perm=perm
164 )
165 else:
166 ReposGroupModel().grant_users_group_permission(
167 repos_group=repos_group, group_name=member, perm=perm
168 )
169 # set new permissions
170 for member, perm, member_type in form_data['perms_new']:
171 if member_type == 'user':
172 ReposGroupModel().grant_user_permission(
173 repos_group=repos_group, user=member, perm=perm
174 )
175 else:
176 ReposGroupModel().grant_users_group_permission(
177 repos_group=repos_group, group_name=member, perm=perm
178 )
179
230
180 old_path = repos_group.full_path
231 old_path = repos_group.full_path
181
232
@@ -191,7 +242,6 b' class ReposGroupModel(BaseModel):'
191
242
192 # iterate over all members of this groups and set the locking !
243 # iterate over all members of this groups and set the locking !
193 # this can be potentially heavy operation
244 # this can be potentially heavy operation
194
195 for obj in repos_group.recursive_groups_and_repos():
245 for obj in repos_group.recursive_groups_and_repos():
196 #set the value from it's parent
246 #set the value from it's parent
197 obj.enable_locking = repos_group.enable_locking
247 obj.enable_locking = repos_group.enable_locking
@@ -210,15 +260,54 b' class ReposGroupModel(BaseModel):'
210 log.error(traceback.format_exc())
260 log.error(traceback.format_exc())
211 raise
261 raise
212
262
213 def delete(self, repos_group):
263 def delete(self, repos_group, force_delete=False):
214 repos_group = self._get_repos_group(repos_group)
264 repos_group = self._get_repos_group(repos_group)
215 try:
265 try:
216 self.sa.delete(repos_group)
266 self.sa.delete(repos_group)
217 self.__delete_group(repos_group)
267 self.__delete_group(repos_group, force_delete)
218 except:
268 except:
219 log.exception('Error removing repos_group %s' % repos_group)
269 log.exception('Error removing repos_group %s' % repos_group)
220 raise
270 raise
221
271
272 def delete_permission(self, repos_group, obj, obj_type, recursive):
273 """
274 Revokes permission for repos_group for given obj(user or users_group),
275 obj_type can be user or users group
276
277 :param repos_group:
278 :param obj: user or users group id
279 :param obj_type: user or users group type
280 :param recursive: recurse to all children of group
281 """
282 from rhodecode.model.repo import RepoModel
283 repos_group = self._get_repos_group(repos_group)
284
285 for el in repos_group.recursive_groups_and_repos():
286 if not recursive:
287 # if we don't recurse set the permission on only the top level
288 # object
289 el = repos_group
290
291 if isinstance(el, RepoGroup):
292 if obj_type == 'user':
293 ReposGroupModel().revoke_user_permission(el, user=obj)
294 elif obj_type == 'users_group':
295 ReposGroupModel().revoke_users_group_permission(el, group_name=obj)
296 else:
297 raise Exception('undefined object type %s' % obj_type)
298 elif isinstance(el, Repository):
299 if obj_type == 'user':
300 RepoModel().revoke_user_permission(el, user=obj)
301 elif obj_type == 'users_group':
302 RepoModel().revoke_users_group_permission(el, group_name=obj)
303 else:
304 raise Exception('undefined object type %s' % obj_type)
305
306 #if it's not recursive call
307 # break the loop and don't proceed with other changes
308 if not recursive:
309 break
310
222 def grant_user_permission(self, repos_group, user, perm):
311 def grant_user_permission(self, repos_group, user, perm):
223 """
312 """
224 Grant permission for user on given repositories group, or update
313 Grant permission for user on given repositories group, or update
@@ -246,6 +335,7 b' class ReposGroupModel(BaseModel):'
246 obj.user = user
335 obj.user = user
247 obj.permission = permission
336 obj.permission = permission
248 self.sa.add(obj)
337 self.sa.add(obj)
338 log.debug('Granted perm %s to %s on %s' % (perm, user, repos_group))
249
339
250 def revoke_user_permission(self, repos_group, user):
340 def revoke_user_permission(self, repos_group, user):
251 """
341 """
@@ -262,8 +352,10 b' class ReposGroupModel(BaseModel):'
262 obj = self.sa.query(UserRepoGroupToPerm)\
352 obj = self.sa.query(UserRepoGroupToPerm)\
263 .filter(UserRepoGroupToPerm.user == user)\
353 .filter(UserRepoGroupToPerm.user == user)\
264 .filter(UserRepoGroupToPerm.group == repos_group)\
354 .filter(UserRepoGroupToPerm.group == repos_group)\
265 .one()
355 .scalar()
266 self.sa.delete(obj)
356 if obj:
357 self.sa.delete(obj)
358 log.debug('Revoked perm on %s on %s' % (repos_group, user))
267
359
268 def grant_users_group_permission(self, repos_group, group_name, perm):
360 def grant_users_group_permission(self, repos_group, group_name, perm):
269 """
361 """
@@ -294,6 +386,7 b' class ReposGroupModel(BaseModel):'
294 obj.users_group = group_name
386 obj.users_group = group_name
295 obj.permission = permission
387 obj.permission = permission
296 self.sa.add(obj)
388 self.sa.add(obj)
389 log.debug('Granted perm %s to %s on %s' % (perm, group_name, repos_group))
297
390
298 def revoke_users_group_permission(self, repos_group, group_name):
391 def revoke_users_group_permission(self, repos_group, group_name):
299 """
392 """
@@ -310,5 +403,7 b' class ReposGroupModel(BaseModel):'
310 obj = self.sa.query(UsersGroupRepoGroupToPerm)\
403 obj = self.sa.query(UsersGroupRepoGroupToPerm)\
311 .filter(UsersGroupRepoGroupToPerm.group == repos_group)\
404 .filter(UsersGroupRepoGroupToPerm.group == repos_group)\
312 .filter(UsersGroupRepoGroupToPerm.users_group == group_name)\
405 .filter(UsersGroupRepoGroupToPerm.users_group == group_name)\
313 .one()
406 .scalar()
314 self.sa.delete(obj)
407 if obj:
408 self.sa.delete(obj)
409 log.debug('Revoked perm to %s on %s' % (repos_group, group_name))
@@ -564,7 +564,7 b' class UserModel(BaseModel):'
564 rg_k = perm.UserRepoGroupToPerm.group.group_name
564 rg_k = perm.UserRepoGroupToPerm.group.group_name
565 p = perm.Permission.permission_name
565 p = perm.Permission.permission_name
566 cur_perm = user.permissions[GK][rg_k]
566 cur_perm = user.permissions[GK][rg_k]
567 if PERM_WEIGHTS[p] > PERM_WEIGHTS[cur_perm]:
567 if PERM_WEIGHTS[p] > PERM_WEIGHTS[cur_perm] or 1: # disable check
568 user.permissions[GK][rg_k] = p
568 user.permissions[GK][rg_k] = p
569
569
570 # REPO GROUP + USER GROUP
570 # REPO GROUP + USER GROUP
@@ -588,7 +588,7 b' class UserModel(BaseModel):'
588 cur_perm = user.permissions[GK][g_k]
588 cur_perm = user.permissions[GK][g_k]
589 # overwrite permission only if it's greater than permission
589 # overwrite permission only if it's greater than permission
590 # given from other sources
590 # given from other sources
591 if PERM_WEIGHTS[p] > PERM_WEIGHTS[cur_perm]:
591 if PERM_WEIGHTS[p] > PERM_WEIGHTS[cur_perm] or 1: # disable check
592 user.permissions[GK][g_k] = p
592 user.permissions[GK][g_k] = p
593
593
594 return user
594 return user
@@ -499,9 +499,9 b" def ValidPerms(type_='repo'):"
499 # fill new permissions in order of how they were added
499 # fill new permissions in order of how they were added
500 for k in sorted(map(int, new_perms_group.keys())):
500 for k in sorted(map(int, new_perms_group.keys())):
501 perm_dict = new_perms_group[str(k)]
501 perm_dict = new_perms_group[str(k)]
502 new_member = perm_dict['name']
502 new_member = perm_dict.get('name')
503 new_perm = perm_dict['perm']
503 new_perm = perm_dict.get('perm')
504 new_type = perm_dict['type']
504 new_type = perm_dict.get('type')
505 if new_member and new_perm and new_type:
505 if new_member and new_perm and new_type:
506 perms_new.add((new_member, new_perm, new_type))
506 perms_new.add((new_member, new_perm, new_type))
507
507
@@ -115,7 +115,7 b''
115 ${h.checkbox('enable_locking',value="True")}
115 ${h.checkbox('enable_locking',value="True")}
116 <span class="help-block">${_('Enable lock-by-pulling on repository.')}</span>
116 <span class="help-block">${_('Enable lock-by-pulling on repository.')}</span>
117 </div>
117 </div>
118 </div>
118 </div>
119 <div class="field">
119 <div class="field">
120 <div class="label">
120 <div class="label">
121 <label for="user">${_('Owner')}:</label>
121 <label for="user">${_('Owner')}:</label>
@@ -188,6 +188,20 b''
188 <div class="form">
188 <div class="form">
189 <div class="fields">
189 <div class="fields">
190 ${h.submit('reset_cache_%s' % c.repo_info.repo_name,_('Invalidate repository cache'),class_="ui-btn",onclick="return confirm('"+_('Confirm to invalidate repository cache')+"');")}
190 ${h.submit('reset_cache_%s' % c.repo_info.repo_name,_('Invalidate repository cache'),class_="ui-btn",onclick="return confirm('"+_('Confirm to invalidate repository cache')+"');")}
191 <div class="field" style="border:none;color:#888">
192 <ul>
193 <li>${_('Manually invalidate cache for this repository. On first access repository will be cached again')}
194 </li>
195 </ul>
196 </div>
197 <div class="field" style="border:none;">
198 ${_('List of cached values')}
199 <ul>
200 %for cache in c.repo_info.cache_keys:
201 <li>INSTANCE ID:${cache.prefix or '-'} ${cache.cache_args} CACHED: ${h.bool2icon(cache.cache_active)}</li>
202 %endfor
203 </ul>
204 </div>
191 </div>
205 </div>
192 </div>
206 </div>
193 ${h.end_form()}
207 ${h.end_form()}
@@ -195,20 +209,20 b''
195 <h3>${_('Public journal')}</h3>
209 <h3>${_('Public journal')}</h3>
196 ${h.form(url('repo_public_journal', repo_name=c.repo_info.repo_name),method='put')}
210 ${h.form(url('repo_public_journal', repo_name=c.repo_info.repo_name),method='put')}
197 <div class="form">
211 <div class="form">
198 ${h.hidden('auth_token',str(h.get_token()))}
212 ${h.hidden('auth_token',str(h.get_token()))}
199 <div class="field">
213 <div class="field">
200 %if c.in_public_journal:
214 %if c.in_public_journal:
201 ${h.submit('set_public_%s' % c.repo_info.repo_name,_('Remove from public journal'),class_="ui-btn")}
215 ${h.submit('set_public_%s' % c.repo_info.repo_name,_('Remove from public journal'),class_="ui-btn")}
202 %else:
216 %else:
203 ${h.submit('set_public_%s' % c.repo_info.repo_name,_('Add to public journal'),class_="ui-btn")}
217 ${h.submit('set_public_%s' % c.repo_info.repo_name,_('Add to public journal'),class_="ui-btn")}
204 %endif
218 %endif
205 </div>
219 </div>
206 <div class="field" style="border:none;color:#888">
220 <div class="field" style="border:none;color:#888">
207 <ul>
221 <ul>
208 <li>${_('All actions made on this repository will be accessible to everyone in public journal')}
222 <li>${_('All actions made on this repository will be accessible to everyone in public journal')}
209 </li>
223 </li>
210 </ul>
224 </ul>
211 </div>
225 </div>
212 </div>
226 </div>
213 ${h.end_form()}
227 ${h.end_form()}
214
228
@@ -229,7 +243,7 b''
229 <li>${_('Force locking on repository. Works only when anonymous access is disabled')}
243 <li>${_('Force locking on repository. Works only when anonymous access is disabled')}
230 </li>
244 </li>
231 </ul>
245 </ul>
232 </div>
246 </div>
233 </div>
247 </div>
234 ${h.end_form()}
248 ${h.end_form()}
235
249
@@ -245,9 +259,9 b''
245 <li>${_('''Manually set this repository as a fork of another from the list''')}</li>
259 <li>${_('''Manually set this repository as a fork of another from the list''')}</li>
246 </ul>
260 </ul>
247 </div>
261 </div>
248 </div>
262 </div>
249 ${h.end_form()}
263 ${h.end_form()}
250
264
251 <h3>${_('Delete')}</h3>
265 <h3>${_('Delete')}</h3>
252 ${h.form(url('repo', repo_name=c.repo_info.repo_name),method='delete')}
266 ${h.form(url('repo', repo_name=c.repo_info.repo_name),method='delete')}
253 <div class="form">
267 <div class="form">
@@ -262,7 +276,7 b''
262 </ul>
276 </ul>
263 </div>
277 </div>
264 </div>
278 </div>
265 ${h.end_form()}
279 ${h.end_form()}
266 </div>
280 </div>
267
281
268 </%def>
282 </%def>
@@ -74,8 +74,8 b''
74 </div> \
74 </div> \
75 </td> \
75 </td> \
76 <td></td>'""")
76 <td></td>'""")
77 %>
77 %>
78 ## ADD HERE DYNAMICALLY NEW INPUTS FROM THE '_tmpl'
78 ## ADD HERE DYNAMICALLY NEW INPUTS FROM THE '_tmpl'
79 <tr class="new_members last_new_member" id="add_perm_input"></tr>
79 <tr class="new_members last_new_member" id="add_perm_input"></tr>
80 <tr>
80 <tr>
81 <td colspan="6">
81 <td colspan="6">
@@ -58,8 +58,8 b''
58 </div> \
58 </div> \
59 </td> \
59 </td> \
60 <td></td>'""")
60 <td></td>'""")
61 %>
61 %>
62 ## ADD HERE DYNAMICALLY NEW INPUTS FROM THE '_tmpl'
62 ## ADD HERE DYNAMICALLY NEW INPUTS FROM THE '_tmpl'
63 <tr class="new_members last_new_member" id="add_perm_input"></tr>
63 <tr class="new_members last_new_member" id="add_perm_input"></tr>
64 <tr>
64 <tr>
65 <td colspan="6">
65 <td colspan="6">
@@ -68,6 +68,12 b''
68 </span>
68 </span>
69 </td>
69 </td>
70 </tr>
70 </tr>
71 <tr>
72 <td colspan="6">
73 ${h.checkbox('recursive',value="True", label=_('apply to parents'))}
74 <span class="help-block">${_('Set or revoke permission to all children of that group, including repositories and other groups')}</span>
75 </td>
76 </tr>
71 </table>
77 </table>
72 <script type="text/javascript">
78 <script type="text/javascript">
73 function ajaxActionUser(user_id, field_id) {
79 function ajaxActionUser(user_id, field_id) {
@@ -81,7 +87,8 b' function ajaxActionUser(user_id, field_i'
81 alert("${_('Failed to remove user')}");
87 alert("${_('Failed to remove user')}");
82 },
88 },
83 };
89 };
84 var postData = '_method=delete&user_id=' + user_id;
90 var recursive = YUD.get('recursive').checked;
91 var postData = '_method=delete&recursive={0}&user_id={1}'.format(recursive,user_id);
85 var request = YAHOO.util.Connect.asyncRequest('POST', sUrl, callback, postData);
92 var request = YAHOO.util.Connect.asyncRequest('POST', sUrl, callback, postData);
86 };
93 };
87
94
@@ -96,7 +103,8 b' function ajaxActionUsersGroup(users_grou'
96 alert("${_('Failed to remove users group')}");
103 alert("${_('Failed to remove users group')}");
97 },
104 },
98 };
105 };
99 var postData = '_method=delete&users_group_id='+users_group_id;
106 var recursive = YUD.get('recursive').checked;
107 var postData = '_method=delete&recursive={0}&users_group_id={1}'.format(recursive,users_group_id);
100 var request = YAHOO.util.Connect.asyncRequest('POST', sUrl, callback, postData);
108 var request = YAHOO.util.Connect.asyncRequest('POST', sUrl, callback, postData);
101 };
109 };
102
110
@@ -5,7 +5,8 b''
5 </%def>
5 </%def>
6
6
7 <%def name="breadcrumbs()">
7 <%def name="breadcrumbs()">
8 <span class="groups_breadcrumbs"> ${_('Groups')}
8 <span class="groups_breadcrumbs">
9 ${h.link_to(_(u'Home'),h.url('/'))}
9 %if c.group.parent_group:
10 %if c.group.parent_group:
10 &raquo; ${h.link_to(c.group.parent_group.name,h.url('repos_group_home',group_name=c.group.parent_group.group_name))}
11 &raquo; ${h.link_to(c.group.parent_group.name,h.url('repos_group_home',group_name=c.group.parent_group.group_name))}
11 %endif
12 %endif
@@ -69,7 +69,7 b''
69 ${h.checkbox('enable_locking',value="True")}
69 ${h.checkbox('enable_locking',value="True")}
70 <span class="help-block">${_('Enable lock-by-pulling on group. This option will be applied to all other groups and repositories inside')}</span>
70 <span class="help-block">${_('Enable lock-by-pulling on group. This option will be applied to all other groups and repositories inside')}</span>
71 </div>
71 </div>
72 </div>
72 </div>
73 <div class="buttons">
73 <div class="buttons">
74 ${h.submit('save',_('Save'),class_="ui-btn large")}
74 ${h.submit('save',_('Save'),class_="ui-btn large")}
75 ${h.reset('reset',_('Reset'),class_="ui-btn large")}
75 ${h.reset('reset',_('Reset'),class_="ui-btn large")}
@@ -154,7 +154,7 b''
154 <li>[stale] <span class="metatag" tag="stale">stale</span></li>
154 <li>[stale] <span class="metatag" tag="stale">stale</span></li>
155 <li>[dead] <span class="metatag" tag="dead">dead</span></li>
155 <li>[dead] <span class="metatag" tag="dead">dead</span></li>
156 <li>[lang =&gt; lang] <span class="metatag" tag="lang" >lang</span></li>
156 <li>[lang =&gt; lang] <span class="metatag" tag="lang" >lang</span></li>
157 <li>[license =&gt; License] <span class="metatag" tag="license"><a href="http://www.opensource.org/licenses/License" >License</a></span></li>
157 <li>[license =&gt; License] <span class="metatag" tag="license"><a href="http://www.opensource.org/licenses/License" >License</a></span></li>
158 <li>[requires =&gt; Repo] <span class="metatag" tag="requires" >requires =&gt; <a href="#" >Repo</a></span></li>
158 <li>[requires =&gt; Repo] <span class="metatag" tag="requires" >requires =&gt; <a href="#" >Repo</a></span></li>
159 <li>[recommends =&gt; Repo] <span class="metatag" tag="recommends" >recommends =&gt; <a href="#" >Repo</a></span></li>
159 <li>[recommends =&gt; Repo] <span class="metatag" tag="recommends" >recommends =&gt; <a href="#" >Repo</a></span></li>
160 <li>[see =&gt; URI] <span class="metatag" tag="see">see =&gt; <a href="#">URI</a> </span></li>
160 <li>[see =&gt; URI] <span class="metatag" tag="see">see =&gt; <a href="#">URI</a> </span></li>
@@ -186,7 +186,7 b''
186 </div>
186 </div>
187 <div class="checkboxes">
187 <div class="checkboxes">
188 <div class="checkbox">
188 <div class="checkbox">
189 ${h.checkbox('web_push_ssl','true')}
189 ${h.checkbox('web_push_ssl', 'True')}
190 <label for="web_push_ssl">${_('require ssl for vcs operations')}</label>
190 <label for="web_push_ssl">${_('require ssl for vcs operations')}</label>
191 </div>
191 </div>
192 <span class="help-block">${_('RhodeCode will require SSL for pushing or pulling. If SSL is missing it will return HTTP Error 406: Not Acceptable')}</span>
192 <span class="help-block">${_('RhodeCode will require SSL for pushing or pulling. If SSL is missing it will return HTTP Error 406: Not Acceptable')}</span>
@@ -237,9 +237,9 b''
237 ## ${h.checkbox('extensions_hggit','True')}
237 ## ${h.checkbox('extensions_hggit','True')}
238 ## <label for="extensions_hggit">${_('hg-git extensions')}</label>
238 ## <label for="extensions_hggit">${_('hg-git extensions')}</label>
239 ##</div>
239 ##</div>
240 ##<span class="help-block">${_('Requires hg-git library installed. Allows clonning from git remote locations')}</span>
240 ##<span class="help-block">${_('Requires hg-git library installed. Allows clonning from git remote locations')}</span>
241 </div>
241 </div>
242 </div>
242 </div>
243 <div class="field">
243 <div class="field">
244 <div class="label">
244 <div class="label">
245 <label for="paths_root_path">${_('Repositories location')}:</label>
245 <label for="paths_root_path">${_('Repositories location')}:</label>
@@ -152,7 +152,7 b''
152 <span class="help-block">${h.literal(_('Select to inherit permissions from %s settings. '
152 <span class="help-block">${h.literal(_('Select to inherit permissions from %s settings. '
153 'With this selected below options does not have any action') % h.link_to('default', url('edit_permission', id='default')))}</span>
153 'With this selected below options does not have any action') % h.link_to('default', url('edit_permission', id='default')))}</span>
154 </div>
154 </div>
155 <div id="inherit_overlay" style="${'opacity:0.3' if c.user.inherit_default_permissions else ''}" >
155 <div id="inherit_overlay" style="${'opacity:0.3' if c.user.inherit_default_permissions else ''}" >
156 <div class="field">
156 <div class="field">
157 <div class="label label-checkbox">
157 <div class="label label-checkbox">
158 <label for="create_repo_perm">${_('Create repositories')}:</label>
158 <label for="create_repo_perm">${_('Create repositories')}:</label>
@@ -169,7 +169,7 b''
169 ${h.checkbox('fork_repo_perm',value=True)}
169 ${h.checkbox('fork_repo_perm',value=True)}
170 </div>
170 </div>
171 </div>
171 </div>
172 </div>
172 </div>
173 <div class="buttons">
173 <div class="buttons">
174 ${h.submit('save',_('Save'),class_="ui-btn large")}
174 ${h.submit('save',_('Save'),class_="ui-btn large")}
175 ${h.reset('reset',_('Reset'),class_="ui-btn large")}
175 ${h.reset('reset',_('Reset'),class_="ui-btn large")}
@@ -112,7 +112,7 b''
112 </div>
112 </div>
113 <span class="help-block">${h.literal(_('Select to inherit permissions from %s settings. '
113 <span class="help-block">${h.literal(_('Select to inherit permissions from %s settings. '
114 'With this selected below options does not have any action') % h.link_to('default', url('edit_permission', id='default')))}</span>
114 'With this selected below options does not have any action') % h.link_to('default', url('edit_permission', id='default')))}</span>
115 </div>
115 </div>
116 <div id="inherit_overlay" style="${'opacity:0.3' if c.users_group.inherit_default_permissions else ''}" >
116 <div id="inherit_overlay" style="${'opacity:0.3' if c.users_group.inherit_default_permissions else ''}" >
117 <div class="field">
117 <div class="field">
118 <div class="label label-checkbox">
118 <div class="label label-checkbox">
@@ -130,7 +130,7 b''
130 ${h.checkbox('fork_repo_perm',value=True)}
130 ${h.checkbox('fork_repo_perm',value=True)}
131 </div>
131 </div>
132 </div>
132 </div>
133 </div>
133 </div>
134 <div class="buttons">
134 <div class="buttons">
135 ${h.submit('save',_('Save'),class_="ui-btn large")}
135 ${h.submit('save',_('Save'),class_="ui-btn large")}
136 ${h.reset('reset',_('Reset'),class_="ui-btn large")}
136 ${h.reset('reset',_('Reset'),class_="ui-btn large")}
@@ -90,7 +90,7 b''
90 f.setAttribute('class','follow');
90 f.setAttribute('class','follow');
91 f.setAttribute('title',_TM['Start following this repository']);
91 f.setAttribute('title',_TM['Start following this repository']);
92 if(f_cnt){
92 if(f_cnt){
93 var cnt = Number(f_cnt.innerHTML)+1;
93 var cnt = Number(f_cnt.innerHTML)-1;
94 f_cnt.innerHTML = cnt;
94 f_cnt.innerHTML = cnt;
95 }
95 }
96 }
96 }
@@ -47,38 +47,38 b' var url_base = \'${h.url("files_nodelist_'
47
47
48 var ypjax_links = function(){
48 var ypjax_links = function(){
49 YUE.on(YUQ('.ypjax-link'), 'click',function(e){
49 YUE.on(YUQ('.ypjax-link'), 'click',function(e){
50
50
51 //don't do ypjax on middle click
51 //don't do ypjax on middle click
52 if(e.which == 2 || !History.enabled){
52 if(e.which == 2 || !History.enabled){
53 return true;
53 return true;
54 }
54 }
55
55
56 var el = e.currentTarget;
56 var el = e.currentTarget;
57 var url = el.href;
57 var url = el.href;
58
58
59 var _base_url = '${h.url("files_home",repo_name=c.repo_name,revision='',f_path='')}';
59 var _base_url = '${h.url("files_home",repo_name=c.repo_name,revision='',f_path='')}';
60 _base_url = _base_url.replace('//','/')
60 _base_url = _base_url.replace('//','/')
61
61
62 //extract rev and the f_path from url.
62 //extract rev and the f_path from url.
63 parts = url.split(_base_url)
63 parts = url.split(_base_url)
64 if(parts.length != 2){
64 if(parts.length != 2){
65 return false;
65 return false;
66 }
66 }
67
67
68 var parts2 = parts[1].split('/');
68 var parts2 = parts[1].split('/');
69 var rev = parts2.shift(); // pop the first element which is the revision
69 var rev = parts2.shift(); // pop the first element which is the revision
70 var f_path = parts2.join('/');
70 var f_path = parts2.join('/');
71
71
72 var title = "${_('%s files') % c.repo_name}" + " - " + f_path;
72 var title = "${_('%s files') % c.repo_name}" + " - " + f_path;
73
73
74 var _node_list_url = node_list_url.replace('__REV__',rev);
74 var _node_list_url = node_list_url.replace('__REV__',rev);
75 var _url_base = url_base.replace('__REV__',rev).replace('__FPATH__', f_path);
75 var _url_base = url_base.replace('__REV__',rev).replace('__FPATH__', f_path);
76
76
77 // Change our States and save some data for handling events
77 // Change our States and save some data for handling events
78 var data = {url:url,title:title, url_base:_url_base,
78 var data = {url:url,title:title, url_base:_url_base,
79 node_list_url:_node_list_url};
79 node_list_url:_node_list_url};
80 History.pushState(data, title, url);
80 History.pushState(data, title, url);
81
81
82 //now we're sure that we can do ypjax things
82 //now we're sure that we can do ypjax things
83 YUE.preventDefault(e)
83 YUE.preventDefault(e)
84 return false;
84 return false;
@@ -92,10 +92,10 b' var callbacks = function(State){'
92 // Inform Google Analytics of the change
92 // Inform Google Analytics of the change
93 if ( typeof window.pageTracker !== 'undefined' ) {
93 if ( typeof window.pageTracker !== 'undefined' ) {
94 window.pageTracker._trackPageview(State.url);
94 window.pageTracker._trackPageview(State.url);
95 }
95 }
96 }
96 }
97
97
98 YUE.onDOMReady(function(){
98 YUE.onDOMReady(function(){
99 ypjax_links();
99 ypjax_links();
100 var container = 'files_data';
100 var container = 'files_data';
101 //Bind to StateChange Event
101 //Bind to StateChange Event
@@ -124,8 +124,8 b' YUE.onDOMReady(function(){'
124 }
124 }
125 });
125 });
126 }
126 }
127 });
127 });
128
128
129 // init the search filter
129 // init the search filter
130 var _State = {
130 var _State = {
131 url: "${h.url.current()}",
131 url: "${h.url.current()}",
@@ -9,7 +9,7 b''
9 <%include file='files_browser.html'/>
9 <%include file='files_browser.html'/>
10 %else:
10 %else:
11 <%include file='files_source.html'/>
11 <%include file='files_source.html'/>
12 %endif
12 %endif
13 %else:
13 %else:
14 <h2>
14 <h2>
15 <a href="#" onClick="javascript:parent.history.back();" target="main">${_('Go back')}</a>
15 <a href="#" onClick="javascript:parent.history.back();" target="main">${_('Go back')}</a>
@@ -20,10 +20,10 b''
20 ${self.breadcrumbs()}
20 ${self.breadcrumbs()}
21 </div>
21 </div>
22 %if c.pull_request.is_closed():
22 %if c.pull_request.is_closed():
23 <div style="padding:10px; font-size:22px;width:100%;text-align: center; color:#88D882">${_('Closed %s') % (h.age(c.pull_request.updated_on))}</div>
23 <div style="padding:10px; font-size:22px;width:100%;text-align: center; color:#88D882">${_('Closed %s') % (h.age(c.pull_request.updated_on))} ${_('with status %s') % h.changeset_status_lbl(c.current_changeset_status)}</div>
24 %endif
24 %endif
25 <h3>${_('Title')}: ${c.pull_request.title}</h3>
25 <h3>${_('Title')}: ${c.pull_request.title}</h3>
26
26
27 <div class="form">
27 <div class="form">
28 <div id="summary" class="fields">
28 <div id="summary" class="fields">
29 <div class="field">
29 <div class="field">
@@ -46,9 +46,9 b''
46 <div class="input">
46 <div class="input">
47 <div class="tooltip" title="${h.tooltip(','.join([x.username for x in c.pull_request_pending_reviewers]))}">${ungettext('%d reviewer', '%d reviewers',len(c.pull_request_pending_reviewers)) % len(c.pull_request_pending_reviewers)}</div>
47 <div class="tooltip" title="${h.tooltip(','.join([x.username for x in c.pull_request_pending_reviewers]))}">${ungettext('%d reviewer', '%d reviewers',len(c.pull_request_pending_reviewers)) % len(c.pull_request_pending_reviewers)}</div>
48 </div>
48 </div>
49 </div>
49 </div>
50 </div>
50 </div>
51 </div>
51 </div>
52 <div style="white-space:pre-wrap;padding:3px 3px 5px 20px">${h.literal(c.pull_request.description)}</div>
52 <div style="white-space:pre-wrap;padding:3px 3px 5px 20px">${h.literal(c.pull_request.description)}</div>
53 <div style="padding:4px 4px 10px 20px">
53 <div style="padding:4px 4px 10px 20px">
54 <div>${_('Created on')}: ${h.fmt_date(c.pull_request.created_on)}</div>
54 <div>${_('Created on')}: ${h.fmt_date(c.pull_request.created_on)}</div>
@@ -7,7 +7,7 b''
7 <%def name="breadcrumbs_links()">
7 <%def name="breadcrumbs_links()">
8 ${h.link_to(_(u'Home'),h.url('/'))}
8 ${h.link_to(_(u'Home'),h.url('/'))}
9 &raquo;
9 &raquo;
10 ${h.link_to(c.dbrepo.just_name,h.url('summary_home',repo_name=c.repo_name))}
10 ${h.repo_link(c.dbrepo.groups_and_repo)}
11 &raquo;
11 &raquo;
12 ${_('summary')}
12 ${_('summary')}
13 </%def>
13 </%def>
@@ -108,7 +108,7 b''
108 <div class="input ${summary(c.show_stats)} desc">${h.urlify_text(h.desc_stylize(c.dbrepo.description))}</div>
108 <div class="input ${summary(c.show_stats)} desc">${h.urlify_text(h.desc_stylize(c.dbrepo.description))}</div>
109 %else:
109 %else:
110 <div class="input ${summary(c.show_stats)} desc">${h.urlify_text(c.dbrepo.description)}</div>
110 <div class="input ${summary(c.show_stats)} desc">${h.urlify_text(c.dbrepo.description)}</div>
111 %endif
111 %endif
112 </div>
112 </div>
113
113
114 <div class="field">
114 <div class="field">
@@ -30,17 +30,17 b' class TestCompareController(TestControll'
30 response.mustcontain('''<a href="/%s/changeset/c5ddebc06eaaba3010c2d66ea6ec9d074eb0f678">r112:c5ddebc06eaa</a>''' % HG_REPO)
30 response.mustcontain('''<a href="/%s/changeset/c5ddebc06eaaba3010c2d66ea6ec9d074eb0f678">r112:c5ddebc06eaa</a>''' % HG_REPO)
31
31
32 ## files diff
32 ## files diff
33 response.mustcontain('''<div class="node"><a href="/%s/compare/tag@%s...tag@%s#C--1c5cf9e91c12">docs/api/utils/index.rst</a></div>''' % (HG_REPO, tag1, tag2))
33 response.mustcontain('''<div class="node"><a href="/%s/compare/tag@%s...tag@%s#C--1c5cf9e91c12">docs/api/utils/index.rst</a></div>''' % (HG_REPO, tag1, tag2))
34 response.mustcontain('''<div class="node"><a href="/%s/compare/tag@%s...tag@%s#C--e3305437df55">test_and_report.sh</a></div>''' % (HG_REPO, tag1, tag2))
34 response.mustcontain('''<div class="node"><a href="/%s/compare/tag@%s...tag@%s#C--e3305437df55">test_and_report.sh</a></div>''' % (HG_REPO, tag1, tag2))
35 response.mustcontain('''<div class="node"><a href="/%s/compare/tag@%s...tag@%s#C--c8e92ef85cd1">.hgignore</a></div>''' % (HG_REPO, tag1, tag2))
35 response.mustcontain('''<div class="node"><a href="/%s/compare/tag@%s...tag@%s#C--c8e92ef85cd1">.hgignore</a></div>''' % (HG_REPO, tag1, tag2))
36 response.mustcontain('''<div class="node"><a href="/%s/compare/tag@%s...tag@%s#C--6e08b694d687">.hgtags</a></div>''' % (HG_REPO, tag1, tag2))
36 response.mustcontain('''<div class="node"><a href="/%s/compare/tag@%s...tag@%s#C--6e08b694d687">.hgtags</a></div>''' % (HG_REPO, tag1, tag2))
37 response.mustcontain('''<div class="node"><a href="/%s/compare/tag@%s...tag@%s#C--2c14b00f3393">docs/api/index.rst</a></div>''' % (HG_REPO, tag1, tag2))
37 response.mustcontain('''<div class="node"><a href="/%s/compare/tag@%s...tag@%s#C--2c14b00f3393">docs/api/index.rst</a></div>''' % (HG_REPO, tag1, tag2))
38 response.mustcontain('''<div class="node"><a href="/%s/compare/tag@%s...tag@%s#C--430ccbc82bdf">vcs/__init__.py</a></div>''' % (HG_REPO, tag1, tag2))
38 response.mustcontain('''<div class="node"><a href="/%s/compare/tag@%s...tag@%s#C--430ccbc82bdf">vcs/__init__.py</a></div>''' % (HG_REPO, tag1, tag2))
39 response.mustcontain('''<div class="node"><a href="/%s/compare/tag@%s...tag@%s#C--9c390eb52cd6">vcs/backends/hg.py</a></div>''' % (HG_REPO, tag1, tag2))
39 response.mustcontain('''<div class="node"><a href="/%s/compare/tag@%s...tag@%s#C--9c390eb52cd6">vcs/backends/hg.py</a></div>''' % (HG_REPO, tag1, tag2))
40 response.mustcontain('''<div class="node"><a href="/%s/compare/tag@%s...tag@%s#C--ebb592c595c0">vcs/utils/__init__.py</a></div>''' % (HG_REPO, tag1, tag2))
40 response.mustcontain('''<div class="node"><a href="/%s/compare/tag@%s...tag@%s#C--ebb592c595c0">vcs/utils/__init__.py</a></div>''' % (HG_REPO, tag1, tag2))
41 response.mustcontain('''<div class="node"><a href="/%s/compare/tag@%s...tag@%s#C--7abc741b5052">vcs/utils/annotate.py</a></div>''' % (HG_REPO, tag1, tag2))
41 response.mustcontain('''<div class="node"><a href="/%s/compare/tag@%s...tag@%s#C--7abc741b5052">vcs/utils/annotate.py</a></div>''' % (HG_REPO, tag1, tag2))
42 response.mustcontain('''<div class="node"><a href="/%s/compare/tag@%s...tag@%s#C--2ef0ef106c56">vcs/utils/diffs.py</a></div>''' % (HG_REPO, tag1, tag2))
42 response.mustcontain('''<div class="node"><a href="/%s/compare/tag@%s...tag@%s#C--2ef0ef106c56">vcs/utils/diffs.py</a></div>''' % (HG_REPO, tag1, tag2))
43 response.mustcontain('''<div class="node"><a href="/%s/compare/tag@%s...tag@%s#C--3150cb87d4b7">vcs/utils/lazy.py</a></div>''' % (HG_REPO, tag1, tag2))
43 response.mustcontain('''<div class="node"><a href="/%s/compare/tag@%s...tag@%s#C--3150cb87d4b7">vcs/utils/lazy.py</a></div>''' % (HG_REPO, tag1, tag2))
44
44
45 def test_index_branch(self):
45 def test_index_branch(self):
46 self.log_user()
46 self.log_user()
@@ -183,7 +183,111 b' class TestCompareController(TestControll'
183 ## files
183 ## files
184 response.mustcontain("""<a href="/%s/compare/branch@%s...branch@%s#C--826e8142e6ba">file1</a>""" % (r2_name, rev1, rev2))
184 response.mustcontain("""<a href="/%s/compare/branch@%s...branch@%s#C--826e8142e6ba">file1</a>""" % (r2_name, rev1, rev2))
185
185
186
187 finally:
186 finally:
188 RepoModel().delete(r1_id)
187 RepoModel().delete(r1_id)
189 RepoModel().delete(r2_id)
188 RepoModel().delete(r2_id)
189
190 def test_org_repo_new_commits_after_forking(self):
191 self.log_user()
192
193 repo1 = RepoModel().create_repo(repo_name='one', repo_type='hg',
194 description='diff-test',
195 owner=TEST_USER_ADMIN_LOGIN)
196
197 Session().commit()
198 r1_id = repo1.repo_id
199 r1_name = repo1.repo_name
200
201 #commit something initially !
202 cs0 = ScmModel().create_node(
203 repo=repo1.scm_instance, repo_name=r1_name,
204 cs=EmptyChangeset(alias='hg'), user=TEST_USER_ADMIN_LOGIN,
205 author=TEST_USER_ADMIN_LOGIN,
206 message='commit1',
207 content='line1',
208 f_path='file1'
209 )
210 Session().commit()
211 self.assertEqual(repo1.scm_instance.revisions, [cs0.raw_id])
212 #fork the repo1
213 repo2 = RepoModel().create_repo(repo_name='one-fork', repo_type='hg',
214 description='compare-test',
215 clone_uri=repo1.repo_full_path,
216 owner=TEST_USER_ADMIN_LOGIN, fork_of='one')
217 Session().commit()
218 self.assertEqual(repo2.scm_instance.revisions, [cs0.raw_id])
219 r2_id = repo2.repo_id
220 r2_name = repo2.repo_name
221
222 #make 3 new commits in fork
223 cs1 = ScmModel().create_node(
224 repo=repo2.scm_instance, repo_name=r2_name,
225 cs=repo2.scm_instance[-1], user=TEST_USER_ADMIN_LOGIN,
226 author=TEST_USER_ADMIN_LOGIN,
227 message='commit1-fork',
228 content='file1-line1-from-fork',
229 f_path='file1-fork'
230 )
231 cs2 = ScmModel().create_node(
232 repo=repo2.scm_instance, repo_name=r2_name,
233 cs=cs1, user=TEST_USER_ADMIN_LOGIN,
234 author=TEST_USER_ADMIN_LOGIN,
235 message='commit2-fork',
236 content='file2-line1-from-fork',
237 f_path='file2-fork'
238 )
239 cs3 = ScmModel().create_node(
240 repo=repo2.scm_instance, repo_name=r2_name,
241 cs=cs2, user=TEST_USER_ADMIN_LOGIN,
242 author=TEST_USER_ADMIN_LOGIN,
243 message='commit3-fork',
244 content='file3-line1-from-fork',
245 f_path='file3-fork'
246 )
247
248 #compare !
249 rev1 = 'default'
250 rev2 = 'default'
251 response = self.app.get(url(controller='compare', action='index',
252 repo_name=r2_name,
253 org_ref_type="branch",
254 org_ref=rev1,
255 other_ref_type="branch",
256 other_ref=rev2,
257 repo=r1_name
258 ))
259
260 try:
261 response.mustcontain('%s@%s -> %s@%s' % (r2_name, rev1, r1_name, rev2))
262 response.mustcontain("""file1-line1-from-fork""")
263 response.mustcontain("""file2-line1-from-fork""")
264 response.mustcontain("""file3-line1-from-fork""")
265
266 #add new commit into parent !
267 cs0 = ScmModel().create_node(
268 repo=repo1.scm_instance, repo_name=r1_name,
269 cs=EmptyChangeset(alias='hg'), user=TEST_USER_ADMIN_LOGIN,
270 author=TEST_USER_ADMIN_LOGIN,
271 message='commit2',
272 content='line1',
273 f_path='file2'
274 )
275 #compare !
276 rev1 = 'default'
277 rev2 = 'default'
278 response = self.app.get(url(controller='compare', action='index',
279 repo_name=r2_name,
280 org_ref_type="branch",
281 org_ref=rev1,
282 other_ref_type="branch",
283 other_ref=rev2,
284 repo=r1_name
285 ))
286
287 response.mustcontain('%s@%s -> %s@%s' % (r2_name, rev1, r1_name, rev2))
288 response.mustcontain("""file1-line1-from-fork""")
289 response.mustcontain("""file2-line1-from-fork""")
290 response.mustcontain("""file3-line1-from-fork""")
291 finally:
292 RepoModel().delete(r2_id)
293 RepoModel().delete(r1_id)
@@ -1,6 +1,9 b''
1 from rhodecode.tests import *
1 from rhodecode.tests import *
2 from rhodecode.model.db import Repository
2 from rhodecode.model.db import Repository
3 from rhodecode.lib.utils import invalidate_cache
3 from rhodecode.lib.utils import invalidate_cache
4 from rhodecode.model.repo import RepoModel
5 from rhodecode.tests.models.common import _make_repo
6 from rhodecode.model.meta import Session
4
7
5
8
6 class TestSummaryController(TestController):
9 class TestSummaryController(TestController):
@@ -82,6 +85,20 b' class TestSummaryController(TestControll'
82 """title="public repository" alt="public """
85 """title="public repository" alt="public """
83 """repository" src="/images/icons/lock_open.png"/>""")
86 """repository" src="/images/icons/lock_open.png"/>""")
84
87
88 def test_index_by_repo_having_id_path_in_name_hg(self):
89 self.log_user()
90 _make_repo(name='repo_1')
91 Session().commit()
92 response = self.app.get(url(controller='summary',
93 action='index',
94 repo_name='repo_1'))
95
96 try:
97 response.mustcontain("""repo_1""")
98 finally:
99 RepoModel().delete(Repository.get_by_repo_name('repo_1'))
100 Session().commit()
101
85 def test_index_by_id_git(self):
102 def test_index_by_id_git(self):
86 self.log_user()
103 self.log_user()
87 ID = Repository.get_by_repo_name(GIT_REPO).repo_id
104 ID = Repository.get_by_repo_name(GIT_REPO).repo_id
@@ -1,7 +1,7 b''
1 import os
1 import os
2 import unittest
2 import unittest
3 from rhodecode.tests import *
3 from rhodecode.tests import *
4
4 from rhodecode.tests.models.common import _make_group
5 from rhodecode.model.repos_group import ReposGroupModel
5 from rhodecode.model.repos_group import ReposGroupModel
6 from rhodecode.model.repo import RepoModel
6 from rhodecode.model.repo import RepoModel
7 from rhodecode.model.db import RepoGroup, User, UsersGroupRepoGroupToPerm
7 from rhodecode.model.db import RepoGroup, User, UsersGroupRepoGroupToPerm
@@ -12,16 +12,6 b' from rhodecode.model.users_group import '
12 from rhodecode.lib.auth import AuthUser
12 from rhodecode.lib.auth import AuthUser
13
13
14
14
15 def _make_group(path, desc='desc', parent_id=None,
16 skip_if_exists=False):
17
18 gr = RepoGroup.get_by_group_name(path)
19 if gr and skip_if_exists:
20 return gr
21
22 gr = ReposGroupModel().create(path, desc, parent_id)
23 return gr
24
25
15
26 class TestPermissions(unittest.TestCase):
16 class TestPermissions(unittest.TestCase):
27 def __init__(self, methodName='runTest'):
17 def __init__(self, methodName='runTest'):
@@ -435,4 +425,3 b' class TestPermissions(unittest.TestCase)'
435 set(['hg.create.repository', 'hg.fork.repository',
425 set(['hg.create.repository', 'hg.fork.repository',
436 'hg.register.manual_activate',
426 'hg.register.manual_activate',
437 'repository.read']))
427 'repository.read']))
438
@@ -4,7 +4,7 b' from rhodecode.tests import *'
4
4
5 from rhodecode.model.repos_group import ReposGroupModel
5 from rhodecode.model.repos_group import ReposGroupModel
6 from rhodecode.model.repo import RepoModel
6 from rhodecode.model.repo import RepoModel
7 from rhodecode.model.db import RepoGroup, User
7 from rhodecode.model.db import RepoGroup, User, Repository
8 from rhodecode.model.meta import Session
8 from rhodecode.model.meta import Session
9 from sqlalchemy.exc import IntegrityError
9 from sqlalchemy.exc import IntegrityError
10
10
@@ -15,7 +15,8 b" def _make_group(path, desc='desc', paren"
15 gr = RepoGroup.get_by_group_name(path)
15 gr = RepoGroup.get_by_group_name(path)
16 if gr and skip_if_exists:
16 if gr and skip_if_exists:
17 return gr
17 return gr
18
18 if isinstance(parent_id, RepoGroup):
19 parent_id = parent_id.group_id
19 gr = ReposGroupModel().create(path, desc, parent_id)
20 gr = ReposGroupModel().create(path, desc, parent_id)
20 return gr
21 return gr
21
22
@@ -54,7 +55,8 b' class TestReposGroups(unittest.TestCase)'
54 group_parent_id=parent_id,
55 group_parent_id=parent_id,
55 perms_updates=[],
56 perms_updates=[],
56 perms_new=[],
57 perms_new=[],
57 enable_locking=False
58 enable_locking=False,
59 recursive=False
58 )
60 )
59 gr = ReposGroupModel().update(id_, form_data)
61 gr = ReposGroupModel().update(id_, form_data)
60 return gr
62 return gr
@@ -132,7 +134,8 b' class TestReposGroups(unittest.TestCase)'
132 repo_type='hg',
134 repo_type='hg',
133 clone_uri=None,
135 clone_uri=None,
134 landing_rev='tip',
136 landing_rev='tip',
135 enable_locking=False)
137 enable_locking=False,
138 recursive=False)
136 cur_user = User.get_by_username(TEST_USER_ADMIN_LOGIN)
139 cur_user = User.get_by_username(TEST_USER_ADMIN_LOGIN)
137 r = RepoModel().create(form_data, cur_user)
140 r = RepoModel().create(form_data, cur_user)
138
141
@@ -3,10 +3,14 b" psql -U postgres -h localhost -c 'create"
3 paster setup-rhodecode rc.ini -q --user=marcink --password=qweqwe --email=marcin@python-blog.com --repos=/home/marcink/repos
3 paster setup-rhodecode rc.ini -q --user=marcink --password=qweqwe --email=marcin@python-blog.com --repos=/home/marcink/repos
4 API_KEY=`psql -R " " -A -U postgres -h localhost -c "select api_key from users where admin=TRUE" -d rhodecode | awk '{print $2}'`
4 API_KEY=`psql -R " " -A -U postgres -h localhost -c "select api_key from users where admin=TRUE" -d rhodecode | awk '{print $2}'`
5 echo "run those after running server"
5 echo "run those after running server"
6 echo "rhodecode-api --apikey=$API_KEY --apihost=http://127.0.0.1:5001 create_user username:demo1 password:qweqwe email:demo1@rhodecode.org"
6 paster serve rc.ini --pid-file=rc.pid --daemon
7 echo "rhodecode-api --apikey=$API_KEY --apihost=http://127.0.0.1:5001 create_user username:demo2 password:qweqwe email:demo2@rhodecode.org"
7 sleep 3
8 echo "rhodecode-api --apikey=$API_KEY --apihost=http://127.0.0.1:5001 create_user username:demo3 password:qweqwe email:demo3@rhodecode.org"
8 rhodecode-api --apikey=$API_KEY --apihost=http://127.0.0.1:5001 create_user username:demo1 password:qweqwe email:demo1@rhodecode.org
9 echo "rhodecode-api --apikey=$API_KEY --apihost=http://127.0.0.1:5001 create_users_group group_name:demo12"
9 rhodecode-api --apikey=$API_KEY --apihost=http://127.0.0.1:5001 create_user username:demo2 password:qweqwe email:demo2@rhodecode.org
10 echo "rhodecode-api --apikey=$API_KEY --apihost=http://127.0.0.1:5001 add_user_to_users_group usersgroupid:demo12 userid:demo1"
10 rhodecode-api --apikey=$API_KEY --apihost=http://127.0.0.1:5001 create_user username:demo3 password:qweqwe email:demo3@rhodecode.org
11 echo "rhodecode-api --apikey=$API_KEY --apihost=http://127.0.0.1:5001 add_user_to_users_group usersgroupid:demo12 userid:demo2"
11 rhodecode-api --apikey=$API_KEY --apihost=http://127.0.0.1:5001 create_users_group group_name:demo12
12
12 rhodecode-api --apikey=$API_KEY --apihost=http://127.0.0.1:5001 add_user_to_users_group usersgroupid:demo12 userid:demo1
13 rhodecode-api --apikey=$API_KEY --apihost=http://127.0.0.1:5001 add_user_to_users_group usersgroupid:demo12 userid:demo2
14 echo "killing server"
15 kill `cat rc.pid`
16 rm rc.pid
General Comments 0
You need to be logged in to leave comments. Login now