##// END OF EJS Templates
#235 forking page repo group selection...
marcink -
r1722:e7eef7a1 beta
parent child Browse files
Show More
@@ -465,19 +465,20 b' def make_map(config):'
465 conditions=dict(function=check_repo))
465 conditions=dict(function=check_repo))
466
466
467 rmap.connect('repo_fork_create_home', '/{repo_name:.*}/fork',
467 rmap.connect('repo_fork_create_home', '/{repo_name:.*}/fork',
468 controller='settings', action='fork_create',
468 controller='forks', action='fork_create',
469 conditions=dict(function=check_repo, method=["POST"]))
469 conditions=dict(function=check_repo, method=["POST"]))
470
470
471 rmap.connect('repo_fork_home', '/{repo_name:.*}/fork',
471 rmap.connect('repo_fork_home', '/{repo_name:.*}/fork',
472 controller='settings', action='fork',
472 controller='forks', action='fork',
473 conditions=dict(function=check_repo))
473 conditions=dict(function=check_repo))
474
474
475 rmap.connect('repo_forks_home', '/{repo_name:.*}/forks',
476 controller='forks', action='forks',
477 conditions=dict(function=check_repo))
478
475 rmap.connect('repo_followers_home', '/{repo_name:.*}/followers',
479 rmap.connect('repo_followers_home', '/{repo_name:.*}/followers',
476 controller='followers', action='followers',
480 controller='followers', action='followers',
477 conditions=dict(function=check_repo))
481 conditions=dict(function=check_repo))
478
482
479 rmap.connect('repo_forks_home', '/{repo_name:.*}/forks',
480 controller='forks', action='forks',
481 conditions=dict(function=check_repo))
482
483
483 return rmap
484 return rmap
@@ -29,9 +29,10 b' import formencode'
29 from formencode import htmlfill
29 from formencode import htmlfill
30
30
31 from paste.httpexceptions import HTTPInternalServerError
31 from paste.httpexceptions import HTTPInternalServerError
32 from pylons import request, response, session, tmpl_context as c, url
32 from pylons import request, session, tmpl_context as c, url
33 from pylons.controllers.util import abort, redirect
33 from pylons.controllers.util import redirect
34 from pylons.i18n.translation import _
34 from pylons.i18n.translation import _
35 from sqlalchemy.exc import IntegrityError
35
36
36 from rhodecode.lib import helpers as h
37 from rhodecode.lib import helpers as h
37 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator, \
38 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator, \
@@ -39,11 +40,11 b' from rhodecode.lib.auth import LoginRequ'
39 from rhodecode.lib.base import BaseController, render
40 from rhodecode.lib.base import BaseController, render
40 from rhodecode.lib.utils import invalidate_cache, action_logger, repo_name_slug
41 from rhodecode.lib.utils import invalidate_cache, action_logger, repo_name_slug
41 from rhodecode.lib.helpers import get_token
42 from rhodecode.lib.helpers import get_token
43 from rhodecode.model.meta import Session
42 from rhodecode.model.db import User, Repository, UserFollowing, RepoGroup
44 from rhodecode.model.db import User, Repository, UserFollowing, RepoGroup
43 from rhodecode.model.forms import RepoForm
45 from rhodecode.model.forms import RepoForm
44 from rhodecode.model.scm import ScmModel
46 from rhodecode.model.scm import ScmModel
45 from rhodecode.model.repo import RepoModel
47 from rhodecode.model.repo import RepoModel
46 from sqlalchemy.exc import IntegrityError
47
48
48 log = logging.getLogger(__name__)
49 log = logging.getLogger(__name__)
49
50
@@ -65,7 +66,7 b' class ReposController(BaseController):'
65 def __load_defaults(self):
66 def __load_defaults(self):
66 c.repo_groups = RepoGroup.groups_choices()
67 c.repo_groups = RepoGroup.groups_choices()
67 c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
68 c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
68
69
69 repo_model = RepoModel()
70 repo_model = RepoModel()
70 c.users_array = repo_model.get_users_js()
71 c.users_array = repo_model.get_users_js()
71 c.users_groups_array = repo_model.get_users_groups_js()
72 c.users_groups_array = repo_model.get_users_groups_js()
@@ -127,13 +128,13 b' class ReposController(BaseController):'
127 """
128 """
128 POST /repos: Create a new item"""
129 POST /repos: Create a new item"""
129 # url('repos')
130 # url('repos')
130 repo_model = RepoModel()
131
131 self.__load_defaults()
132 self.__load_defaults()
132 form_result = {}
133 form_result = {}
133 try:
134 try:
134 form_result = RepoForm(repo_groups=c.repo_groups_choices)()\
135 form_result = RepoForm(repo_groups=c.repo_groups_choices)()\
135 .to_python(dict(request.POST))
136 .to_python(dict(request.POST))
136 repo_model.create(form_result, self.rhodecode_user)
137 RepoModel().create(form_result, self.rhodecode_user)
137 if form_result['clone_uri']:
138 if form_result['clone_uri']:
138 h.flash(_('created repository %s from %s') \
139 h.flash(_('created repository %s from %s') \
139 % (form_result['repo_name'], form_result['clone_uri']),
140 % (form_result['repo_name'], form_result['clone_uri']),
@@ -143,13 +144,13 b' class ReposController(BaseController):'
143 category='success')
144 category='success')
144
145
145 if request.POST.get('user_created'):
146 if request.POST.get('user_created'):
146 #created by regular non admin user
147 # created by regular non admin user
147 action_logger(self.rhodecode_user, 'user_created_repo',
148 action_logger(self.rhodecode_user, 'user_created_repo',
148 form_result['repo_name_full'], '', self.sa)
149 form_result['repo_name_full'], '', self.sa)
149 else:
150 else:
150 action_logger(self.rhodecode_user, 'admin_created_repo',
151 action_logger(self.rhodecode_user, 'admin_created_repo',
151 form_result['repo_name_full'], '', self.sa)
152 form_result['repo_name_full'], '', self.sa)
152
153 Session().commit()
153 except formencode.Invalid, errors:
154 except formencode.Invalid, errors:
154
155
155 c.new_repo = errors.value['repo_name']
156 c.new_repo = errors.value['repo_name']
@@ -207,7 +208,7 b' class ReposController(BaseController):'
207 changed_name = repo.repo_name
208 changed_name = repo.repo_name
208 action_logger(self.rhodecode_user, 'admin_updated_repo',
209 action_logger(self.rhodecode_user, 'admin_updated_repo',
209 changed_name, '', self.sa)
210 changed_name, '', self.sa)
210
211 Session().commit()
211 except formencode.Invalid, errors:
212 except formencode.Invalid, errors:
212 defaults = self.__load_data(repo_name)
213 defaults = self.__load_data(repo_name)
213 defaults.update(errors.value)
214 defaults.update(errors.value)
@@ -251,7 +252,7 b' class ReposController(BaseController):'
251 repo_model.delete(repo)
252 repo_model.delete(repo)
252 invalidate_cache('get_repo_cached_%s' % repo_name)
253 invalidate_cache('get_repo_cached_%s' % repo_name)
253 h.flash(_('deleted repository %s') % repo_name, category='success')
254 h.flash(_('deleted repository %s') % repo_name, category='success')
254
255 Session().commit()
255 except IntegrityError, e:
256 except IntegrityError, e:
256 if e.message.find('repositories_fork_id_fkey'):
257 if e.message.find('repositories_fork_id_fkey'):
257 log.error(traceback.format_exc())
258 log.error(traceback.format_exc())
@@ -23,13 +23,23 b''
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 import logging
25 import logging
26 import formencode
27 import traceback
28 from formencode import htmlfill
26
29
27 from pylons import tmpl_context as c, request
30 from pylons import tmpl_context as c, request, url
31 from pylons.controllers.util import redirect
32 from pylons.i18n.translation import _
33
34 import rhodecode.lib.helpers as h
28
35
29 from rhodecode.lib.helpers import Page
36 from rhodecode.lib.helpers import Page
30 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
37 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator, \
38 NotAnonymous
31 from rhodecode.lib.base import BaseRepoController, render
39 from rhodecode.lib.base import BaseRepoController, render
32 from rhodecode.model.db import Repository, User, UserFollowing
40 from rhodecode.model.db import Repository, RepoGroup, UserFollowing, User
41 from rhodecode.model.repo import RepoModel
42 from rhodecode.model.forms import RepoForkForm
33
43
34 log = logging.getLogger(__name__)
44 log = logging.getLogger(__name__)
35
45
@@ -37,11 +47,59 b' log = logging.getLogger(__name__)'
37 class ForksController(BaseRepoController):
47 class ForksController(BaseRepoController):
38
48
39 @LoginRequired()
49 @LoginRequired()
40 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
41 'repository.admin')
42 def __before__(self):
50 def __before__(self):
43 super(ForksController, self).__before__()
51 super(ForksController, self).__before__()
44
52
53 def __load_defaults(self):
54 c.repo_groups = RepoGroup.groups_choices()
55 c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
56
57 def __load_data(self, repo_name=None):
58 """
59 Load defaults settings for edit, and update
60
61 :param repo_name:
62 """
63 self.__load_defaults()
64
65 c.repo_info = db_repo = Repository.get_by_repo_name(repo_name)
66 repo = db_repo.scm_instance
67
68 if c.repo_info is None:
69 h.flash(_('%s repository is not mapped to db perhaps'
70 ' it was created or renamed from the filesystem'
71 ' please run the application again'
72 ' in order to rescan repositories') % repo_name,
73 category='error')
74
75 return redirect(url('repos'))
76
77 c.default_user_id = User.get_by_username('default').user_id
78 c.in_public_journal = UserFollowing.query()\
79 .filter(UserFollowing.user_id == c.default_user_id)\
80 .filter(UserFollowing.follows_repository == c.repo_info).scalar()
81
82 if c.repo_info.stats:
83 last_rev = c.repo_info.stats.stat_on_revision
84 else:
85 last_rev = 0
86 c.stats_revision = last_rev
87
88 c.repo_last_rev = repo.count() - 1 if repo.revisions else 0
89
90 if last_rev == 0 or c.repo_last_rev == 0:
91 c.stats_percentage = 0
92 else:
93 c.stats_percentage = '%.2f' % ((float((last_rev)) /
94 c.repo_last_rev) * 100)
95
96 defaults = RepoModel()._get_defaults(repo_name)
97 # add prefix to fork
98 defaults['repo_name'] = 'fork-' + defaults['repo_name']
99 return defaults
100
101 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
102 'repository.admin')
45 def forks(self, repo_name):
103 def forks(self, repo_name):
46 p = int(request.params.get('page', 1))
104 p = int(request.params.get('page', 1))
47 repo_id = c.rhodecode_db_repo.repo_id
105 repo_id = c.rhodecode_db_repo.repo_id
@@ -54,3 +112,63 b' class ForksController(BaseRepoController'
54 return c.forks_data
112 return c.forks_data
55
113
56 return render('/forks/forks.html')
114 return render('/forks/forks.html')
115
116 @NotAnonymous()
117 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
118 'repository.admin')
119 def fork(self, repo_name):
120 c.repo_info = Repository.get_by_repo_name(repo_name)
121 if not c.repo_info:
122 h.flash(_('%s repository is not mapped to db perhaps'
123 ' it was created or renamed from the file system'
124 ' please run the application again'
125 ' in order to rescan repositories') % repo_name,
126 category='error')
127
128 return redirect(url('home'))
129
130 defaults = self.__load_data(repo_name)
131
132 return htmlfill.render(
133 render('forks/fork.html'),
134 defaults=defaults,
135 encoding="UTF-8",
136 force_defaults=False
137 )
138
139
140 @NotAnonymous()
141 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
142 'repository.admin')
143 def fork_create(self, repo_name):
144 self.__load_defaults()
145 c.repo_info = Repository.get_by_repo_name(repo_name)
146 _form = RepoForkForm(old_data={'repo_type': c.repo_info.repo_type},
147 repo_groups=c.repo_groups_choices,)()
148 form_result = {}
149 try:
150 form_result = _form.to_python(dict(request.POST))
151 # add org_path of repo so we can do a clone from it later
152 form_result['org_path'] = c.repo_info.repo_name
153
154 # create fork is done sometimes async on celery, db transaction
155 # management is handled there.
156 RepoModel().create_fork(form_result, self.rhodecode_user)
157 h.flash(_('forked %s repository as %s') \
158 % (repo_name, form_result['repo_name']),
159 category='success')
160 except formencode.Invalid, errors:
161 c.new_repo = errors.value['repo_name']
162
163 return htmlfill.render(
164 render('forks/fork.html'),
165 defaults=errors.value,
166 errors=errors.error_dict or {},
167 prefix_error=False,
168 encoding="UTF-8")
169 except Exception:
170 log.error(traceback.format_exc())
171 h.flash(_('An error occurred during repository forking %s') %
172 repo_name, category='error')
173
174 return redirect(url('home'))
@@ -23,21 +23,22 b''
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 import logging
25 import logging
26 from itertools import groupby
26
27
27 from sqlalchemy import or_
28 from sqlalchemy import or_
28 from sqlalchemy.orm import joinedload, make_transient
29 from sqlalchemy.orm import joinedload
29 from webhelpers.paginate import Page
30 from webhelpers.paginate import Page
30 from itertools import groupby
31 from webhelpers.feedgenerator import Atom1Feed, Rss201rev2Feed
31
32
32 from paste.httpexceptions import HTTPBadRequest
33 from paste.httpexceptions import HTTPBadRequest
33 from pylons import request, tmpl_context as c, response, url
34 from pylons import request, tmpl_context as c, response, url
34 from pylons.i18n.translation import _
35 from pylons.i18n.translation import _
35 from webhelpers.feedgenerator import Atom1Feed, Rss201rev2Feed
36
36
37 import rhodecode.lib.helpers as h
37 import rhodecode.lib.helpers as h
38 from rhodecode.lib.auth import LoginRequired, NotAnonymous
38 from rhodecode.lib.auth import LoginRequired, NotAnonymous
39 from rhodecode.lib.base import BaseController, render
39 from rhodecode.lib.base import BaseController, render
40 from rhodecode.model.db import UserLog, UserFollowing
40 from rhodecode.model.db import UserLog, UserFollowing
41 from rhodecode.model.meta import Session
41
42
42 log = logging.getLogger(__name__)
43 log = logging.getLogger(__name__)
43
44
@@ -124,6 +125,7 b' class JournalController(BaseController):'
124 try:
125 try:
125 self.scm_model.toggle_following_user(user_id,
126 self.scm_model.toggle_following_user(user_id,
126 self.rhodecode_user.user_id)
127 self.rhodecode_user.user_id)
128 Session().commit()
127 return 'ok'
129 return 'ok'
128 except:
130 except:
129 raise HTTPBadRequest()
131 raise HTTPBadRequest()
@@ -133,6 +135,7 b' class JournalController(BaseController):'
133 try:
135 try:
134 self.scm_model.toggle_following_repo(repo_id,
136 self.scm_model.toggle_following_repo(repo_id,
135 self.rhodecode_user.user_id)
137 self.rhodecode_user.user_id)
138 Session().commit()
136 return 'ok'
139 return 'ok'
137 except:
140 except:
138 raise HTTPBadRequest()
141 raise HTTPBadRequest()
@@ -35,14 +35,14 b' from pylons.i18n.translation import _'
35
35
36 import rhodecode.lib.helpers as h
36 import rhodecode.lib.helpers as h
37
37
38 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAllDecorator, \
38 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAllDecorator
39 HasRepoPermissionAnyDecorator, NotAnonymous
40 from rhodecode.lib.base import BaseRepoController, render
39 from rhodecode.lib.base import BaseRepoController, render
41 from rhodecode.lib.utils import invalidate_cache, action_logger
40 from rhodecode.lib.utils import invalidate_cache, action_logger
42
41
43 from rhodecode.model.forms import RepoSettingsForm, RepoForkForm
42 from rhodecode.model.forms import RepoSettingsForm
44 from rhodecode.model.repo import RepoModel
43 from rhodecode.model.repo import RepoModel
45 from rhodecode.model.db import RepoGroup
44 from rhodecode.model.db import RepoGroup
45 from rhodecode.model.meta import Session
46
46
47 log = logging.getLogger(__name__)
47 log = logging.getLogger(__name__)
48
48
@@ -52,15 +52,15 b' class SettingsController(BaseRepoControl'
52 @LoginRequired()
52 @LoginRequired()
53 def __before__(self):
53 def __before__(self):
54 super(SettingsController, self).__before__()
54 super(SettingsController, self).__before__()
55
55
56 def __load_defaults(self):
56 def __load_defaults(self):
57 c.repo_groups = RepoGroup.groups_choices()
57 c.repo_groups = RepoGroup.groups_choices()
58 c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
58 c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
59
59
60 repo_model = RepoModel()
60 repo_model = RepoModel()
61 c.users_array = repo_model.get_users_js()
61 c.users_array = repo_model.get_users_js()
62 c.users_groups_array = repo_model.get_users_groups_js()
62 c.users_groups_array = repo_model.get_users_groups_js()
63
63
64 @HasRepoPermissionAllDecorator('repository.admin')
64 @HasRepoPermissionAllDecorator('repository.admin')
65 def index(self, repo_name):
65 def index(self, repo_name):
66 repo_model = RepoModel()
66 repo_model = RepoModel()
@@ -89,15 +89,15 b' class SettingsController(BaseRepoControl'
89 def update(self, repo_name):
89 def update(self, repo_name):
90 repo_model = RepoModel()
90 repo_model = RepoModel()
91 changed_name = repo_name
91 changed_name = repo_name
92
92
93 self.__load_defaults()
93 self.__load_defaults()
94
94
95 _form = RepoSettingsForm(edit=True,
95 _form = RepoSettingsForm(edit=True,
96 old_data={'repo_name': repo_name},
96 old_data={'repo_name': repo_name},
97 repo_groups=c.repo_groups_choices)()
97 repo_groups=c.repo_groups_choices)()
98 try:
98 try:
99 form_result = _form.to_python(dict(request.POST))
99 form_result = _form.to_python(dict(request.POST))
100
100
101 repo_model.update(repo_name, form_result)
101 repo_model.update(repo_name, form_result)
102 invalidate_cache('get_repo_cached_%s' % repo_name)
102 invalidate_cache('get_repo_cached_%s' % repo_name)
103 h.flash(_('Repository %s updated successfully' % repo_name),
103 h.flash(_('Repository %s updated successfully' % repo_name),
@@ -105,6 +105,7 b' class SettingsController(BaseRepoControl'
105 changed_name = form_result['repo_name_full']
105 changed_name = form_result['repo_name_full']
106 action_logger(self.rhodecode_user, 'user_updated_repo',
106 action_logger(self.rhodecode_user, 'user_updated_repo',
107 changed_name, '', self.sa)
107 changed_name, '', self.sa)
108 Session().commit()
108 except formencode.Invalid, errors:
109 except formencode.Invalid, errors:
109 c.repo_info = repo_model.get_by_repo_name(repo_name)
110 c.repo_info = repo_model.get_by_repo_name(repo_name)
110 c.users_array = repo_model.get_users_js()
111 c.users_array = repo_model.get_users_js()
@@ -148,61 +149,10 b' class SettingsController(BaseRepoControl'
148 repo_model.delete(repo)
149 repo_model.delete(repo)
149 invalidate_cache('get_repo_cached_%s' % repo_name)
150 invalidate_cache('get_repo_cached_%s' % repo_name)
150 h.flash(_('deleted repository %s') % repo_name, category='success')
151 h.flash(_('deleted repository %s') % repo_name, category='success')
152 Session().commit()
151 except Exception:
153 except Exception:
152 log.error(traceback.format_exc())
154 log.error(traceback.format_exc())
153 h.flash(_('An error occurred during deletion of %s') % repo_name,
155 h.flash(_('An error occurred during deletion of %s') % repo_name,
154 category='error')
156 category='error')
155
157
156 return redirect(url('home'))
158 return redirect(url('home'))
157
158 @NotAnonymous()
159 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
160 'repository.admin')
161 def fork(self, repo_name):
162 repo_model = RepoModel()
163 c.repo_info = repo = repo_model.get_by_repo_name(repo_name)
164 if not repo:
165 h.flash(_('%s repository is not mapped to db perhaps'
166 ' it was created or renamed from the file system'
167 ' please run the application again'
168 ' in order to rescan repositories') % repo_name,
169 category='error')
170
171 return redirect(url('home'))
172
173 return render('settings/repo_fork.html')
174
175 @NotAnonymous()
176 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
177 'repository.admin')
178 def fork_create(self, repo_name):
179 repo_model = RepoModel()
180 c.repo_info = repo_model.get_by_repo_name(repo_name)
181 _form = RepoForkForm(old_data={'repo_type': c.repo_info.repo_type})()
182 form_result = {}
183 try:
184 form_result = _form.to_python(dict(request.POST))
185 form_result.update({'repo_name': repo_name})
186 repo_model.create_fork(form_result, self.rhodecode_user)
187 h.flash(_('forked %s repository as %s') \
188 % (repo_name, form_result['fork_name']),
189 category='success')
190 action_logger(self.rhodecode_user,
191 'user_forked_repo:%s' % form_result['fork_name'],
192 repo_name, '', self.sa)
193 except formencode.Invalid, errors:
194 c.new_repo = errors.value['fork_name']
195 r = render('settings/repo_fork.html')
196
197 return htmlfill.render(
198 r,
199 defaults=errors.value,
200 errors=errors.error_dict or {},
201 prefix_error=False,
202 encoding="UTF-8")
203 except Exception:
204 log.error(traceback.format_exc())
205 h.flash(_('An error occurred during repository forking %s') %
206 repo_name, category='error')
207
208 return redirect(url('home'))
@@ -37,29 +37,28 b' from string import lower'
37 from pylons import config, url
37 from pylons import config, url
38 from pylons.i18n.translation import _
38 from pylons.i18n.translation import _
39
39
40 from vcs import get_backend
40
41
41 from rhodecode.lib import LANGUAGES_EXTENSIONS_MAP, safe_str
42 from rhodecode.lib import LANGUAGES_EXTENSIONS_MAP, safe_str
42 from rhodecode.lib.celerylib import run_task, locked_task, str2bool, \
43 from rhodecode.lib.celerylib import run_task, locked_task, str2bool, \
43 __get_lockkey, LockHeld, DaemonLock
44 __get_lockkey, LockHeld, DaemonLock
44 from rhodecode.lib.helpers import person
45 from rhodecode.lib.helpers import person
45 from rhodecode.lib.rcmail.smtp_mailer import SmtpMailer
46 from rhodecode.lib.rcmail.smtp_mailer import SmtpMailer
46 from rhodecode.lib.utils import add_cache
47 from rhodecode.lib.utils import add_cache, action_logger
47 from rhodecode.lib.compat import json, OrderedDict
48 from rhodecode.lib.compat import json, OrderedDict
48
49
49 from rhodecode.model import init_model
50 from rhodecode.model import init_model
50 from rhodecode.model import meta
51 from rhodecode.model import meta
51 from rhodecode.model.db import RhodeCodeUi, Statistics, Repository, User
52 from rhodecode.model.db import Statistics, Repository, User
52
53 from vcs.backends import get_repo
54
53
55 from sqlalchemy import engine_from_config
54 from sqlalchemy import engine_from_config
56
55
57
58 add_cache(config)
56 add_cache(config)
59
57
60 __all__ = ['whoosh_index', 'get_commits_stats',
58 __all__ = ['whoosh_index', 'get_commits_stats',
61 'reset_user_password', 'send_email']
59 'reset_user_password', 'send_email']
62
60
61
63 CELERY_ON = str2bool(config['app_conf'].get('use_celery'))
62 CELERY_ON = str2bool(config['app_conf'].get('use_celery'))
64
63
65
64
@@ -81,17 +80,13 b' def get_logger(cls):'
81
80
82 return log
81 return log
83
82
84 def get_repos_path():
85 sa = get_session()
86 q = sa.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/').one()
87 return q.ui_value
88
89
90 @task(ignore_result=True)
83 @task(ignore_result=True)
91 @locked_task
84 @locked_task
92 def whoosh_index(repo_location, full_index):
85 def whoosh_index(repo_location, full_index):
86 from rhodecode.lib.indexers.daemon import WhooshIndexingDaemon
87
93 #log = whoosh_index.get_logger()
88 #log = whoosh_index.get_logger()
94 from rhodecode.lib.indexers.daemon import WhooshIndexingDaemon
89
95 index_location = config['index_dir']
90 index_location = config['index_dir']
96 WhooshIndexingDaemon(index_location=index_location,
91 WhooshIndexingDaemon(index_location=index_location,
97 repo_location=repo_location, sa=get_session())\
92 repo_location=repo_location, sa=get_session())\
@@ -111,13 +106,12 b' def get_commits_stats(repo_name, ts_min_'
111 sa = get_session()
106 sa = get_session()
112 lock = l = DaemonLock(file_=jn(lockkey_path, lockkey))
107 lock = l = DaemonLock(file_=jn(lockkey_path, lockkey))
113
108
114 #for js data compatibilty cleans the key for person from '
109 # for js data compatibilty cleans the key for person from '
115 akc = lambda k: person(k).replace('"', "")
110 akc = lambda k: person(k).replace('"', "")
116
111
117 co_day_auth_aggr = {}
112 co_day_auth_aggr = {}
118 commits_by_day_aggregate = {}
113 commits_by_day_aggregate = {}
119 repos_path = get_repos_path()
114 repo = Repository.get_by_repo_name(repo_name).scm_instance
120 repo = get_repo(safe_str(os.path.join(repos_path, repo_name)))
121 repo_size = len(repo.revisions)
115 repo_size = len(repo.revisions)
122 #return if repo have no revisions
116 #return if repo have no revisions
123 if repo_size < 1:
117 if repo_size < 1:
@@ -139,9 +133,9 b' def get_commits_stats(repo_name, ts_min_'
139 last_rev = cur_stats.stat_on_revision
133 last_rev = cur_stats.stat_on_revision
140
134
141 if last_rev == repo.get_changeset().revision and repo_size > 1:
135 if last_rev == repo.get_changeset().revision and repo_size > 1:
142 #pass silently without any work if we're not on first revision or
136 # pass silently without any work if we're not on first revision or
143 #current state of parsing revision(from db marker) is the
137 # current state of parsing revision(from db marker) is the
144 #last revision
138 # last revision
145 lock.release()
139 lock.release()
146 return True
140 return True
147
141
@@ -255,10 +249,11 b' def get_commits_stats(repo_name, ts_min_'
255
249
256 @task(ignore_result=True)
250 @task(ignore_result=True)
257 def send_password_link(user_email):
251 def send_password_link(user_email):
252 from rhodecode.model.notification import EmailNotificationModel
253
258 log = get_logger(send_password_link)
254 log = get_logger(send_password_link)
259
255
260 try:
256 try:
261 from rhodecode.model.notification import EmailNotificationModel
262 sa = get_session()
257 sa = get_session()
263 user = User.get_by_email(user_email)
258 user = User.get_by_email(user_email)
264 if user:
259 if user:
@@ -283,9 +278,9 b' def send_password_link(user_email):'
283
278
284 @task(ignore_result=True)
279 @task(ignore_result=True)
285 def reset_user_password(user_email):
280 def reset_user_password(user_email):
286 log = get_logger(reset_user_password)
281 from rhodecode.lib import auth
287
282
288 from rhodecode.lib import auth
283 log = get_logger(reset_user_password)
289
284
290 try:
285 try:
291 try:
286 try:
@@ -361,27 +356,39 b' def send_email(recipients, subject, body'
361
356
362 @task(ignore_result=True)
357 @task(ignore_result=True)
363 def create_repo_fork(form_data, cur_user):
358 def create_repo_fork(form_data, cur_user):
359 """
360 Creates a fork of repository using interval VCS methods
361
362 :param form_data:
363 :param cur_user:
364 """
365 from rhodecode.model.repo import RepoModel
366
364 log = get_logger(create_repo_fork)
367 log = get_logger(create_repo_fork)
365
368
366 from rhodecode.model.repo import RepoModel
369 Session = get_session()
367 from vcs import get_backend
370 base_path = Repository.base_path()
371
372 RepoModel(Session).create(form_data, cur_user, just_db=True, fork=True)
373
374 alias = form_data['repo_type']
375 org_repo_name = form_data['org_path']
376 source_repo_path = os.path.join(base_path, org_repo_name)
377 destination_fork_path = os.path.join(base_path, form_data['repo_name_full'])
368
378
369 repo_model = RepoModel(get_session())
379 log.info('creating fork of %s as %s', source_repo_path,
370 repo_model.create(form_data, cur_user, just_db=True, fork=True)
380 destination_fork_path)
371 repo_name = form_data['repo_name']
372 repos_path = get_repos_path()
373 repo_path = os.path.join(repos_path, repo_name)
374 repo_fork_path = os.path.join(repos_path, form_data['fork_name'])
375 alias = form_data['repo_type']
376
377 log.info('creating repo fork %s as %s', repo_name, repo_path)
378 backend = get_backend(alias)
381 backend = get_backend(alias)
379 backend(str(repo_fork_path), create=True, src_url=str(repo_path))
382 backend(safe_str(destination_fork_path), create=True,
380
383 src_url=safe_str(source_repo_path))
384 action_logger(cur_user, 'user_forked_repo:%s' % org_repo_name,
385 org_repo_name, '', Session)
386 # finally commit at latest possible stage
387 Session.commit()
381
388
382 def __get_codes_stats(repo_name):
389 def __get_codes_stats(repo_name):
383 repos_path = get_repos_path()
390 repo = Repository.get_by_repo_name(repo_name).scm_instance
384 repo = get_repo(safe_str(os.path.join(repos_path, repo_name)))
391
385 tip = repo.get_changeset()
392 tip = repo.get_changeset()
386 code_stats = {}
393 code_stats = {}
387
394
@@ -33,7 +33,8 b' from rhodecode.lib.utils import action_l'
33
33
34
34
35 def repo_size(ui, repo, hooktype=None, **kwargs):
35 def repo_size(ui, repo, hooktype=None, **kwargs):
36 """Presents size of repository after push
36 """
37 Presents size of repository after push
37
38
38 :param ui:
39 :param ui:
39 :param repo:
40 :param repo:
@@ -65,7 +66,8 b' def repo_size(ui, repo, hooktype=None, *'
65
66
66
67
67 def log_pull_action(ui, repo, **kwargs):
68 def log_pull_action(ui, repo, **kwargs):
68 """Logs user last pull action
69 """
70 Logs user last pull action
69
71
70 :param ui:
72 :param ui:
71 :param repo:
73 :param repo:
@@ -76,13 +78,15 b' def log_pull_action(ui, repo, **kwargs):'
76 repository = extra_params['repository']
78 repository = extra_params['repository']
77 action = 'pull'
79 action = 'pull'
78
80
79 action_logger(username, action, repository, extra_params['ip'])
81 action_logger(username, action, repository, extra_params['ip'],
82 commit=True)
80
83
81 return 0
84 return 0
82
85
83
86
84 def log_push_action(ui, repo, **kwargs):
87 def log_push_action(ui, repo, **kwargs):
85 """Maps user last push action to new changeset id, from mercurial
88 """
89 Maps user last push action to new changeset id, from mercurial
86
90
87 :param ui:
91 :param ui:
88 :param repo:
92 :param repo:
@@ -110,6 +114,7 b' def log_push_action(ui, repo, **kwargs):'
110
114
111 action = action % ','.join(revs)
115 action = action % ','.join(revs)
112
116
113 action_logger(username, action, repository, extra_params['ip'])
117 action_logger(username, action, repository, extra_params['ip'],
118 commit=True)
114
119
115 return 0
120 return 0
@@ -93,7 +93,7 b' def get_repo_slug(request):'
93 return request.environ['pylons.routes_dict'].get('repo_name')
93 return request.environ['pylons.routes_dict'].get('repo_name')
94
94
95
95
96 def action_logger(user, action, repo, ipaddr='', sa=None):
96 def action_logger(user, action, repo, ipaddr='', sa=None, commit=False):
97 """
97 """
98 Action logger for various actions made by users
98 Action logger for various actions made by users
99
99
@@ -138,12 +138,13 b' def action_logger(user, action, repo, ip'
138 user_log.action_date = datetime.datetime.now()
138 user_log.action_date = datetime.datetime.now()
139 user_log.user_ip = ipaddr
139 user_log.user_ip = ipaddr
140 sa.add(user_log)
140 sa.add(user_log)
141 sa.commit()
142
141
143 log.info('Adding user %s, action %s on %s', user_obj, action, repo)
142 log.info('Adding user %s, action %s on %s', user_obj, action, repo)
143 if commit:
144 sa.commit()
144 except:
145 except:
145 log.error(traceback.format_exc())
146 log.error(traceback.format_exc())
146 sa.rollback()
147 raise
147
148
148
149
149 def get_repos(path, recursive=False):
150 def get_repos(path, recursive=False):
@@ -185,7 +185,7 b' class ValidPassword(formencode.validator'
185 class ValidPasswordsMatch(formencode.validators.FancyValidator):
185 class ValidPasswordsMatch(formencode.validators.FancyValidator):
186
186
187 def validate_python(self, value, state):
187 def validate_python(self, value, state):
188
188
189 pass_val = value.get('password') or value.get('new_password')
189 pass_val = value.get('password') or value.get('new_password')
190 if pass_val != value['password_confirmation']:
190 if pass_val != value['password_confirmation']:
191 e_dict = {'password_confirmation':
191 e_dict = {'password_confirmation':
@@ -198,7 +198,7 b' class ValidAuth(formencode.validators.Fa'
198 'invalid_login':_('invalid user name'),
198 'invalid_login':_('invalid user name'),
199 'disabled_account':_('Your account is disabled')
199 'disabled_account':_('Your account is disabled')
200 }
200 }
201
201
202 # error mapping
202 # error mapping
203 e_dict = {'username':messages['invalid_login'],
203 e_dict = {'username':messages['invalid_login'],
204 'password':messages['invalid_password']}
204 'password':messages['invalid_password']}
@@ -208,7 +208,7 b' class ValidAuth(formencode.validators.Fa'
208 password = value['password']
208 password = value['password']
209 username = value['username']
209 username = value['username']
210 user = User.get_by_username(username)
210 user = User.get_by_username(username)
211
211
212 if authenticate(username, password):
212 if authenticate(username, password):
213 return value
213 return value
214 else:
214 else:
@@ -254,7 +254,7 b' def ValidRepoName(edit, old_data):'
254 # db key This is an actual just the name to store in the
254 # db key This is an actual just the name to store in the
255 # database
255 # database
256 repo_name_full = group_path + RepoGroup.url_sep() + repo_name
256 repo_name_full = group_path + RepoGroup.url_sep() + repo_name
257
257
258 else:
258 else:
259 group_path = ''
259 group_path = ''
260 repo_name_full = repo_name
260 repo_name_full = repo_name
@@ -289,24 +289,8 b' def ValidRepoName(edit, old_data):'
289
289
290 return _ValidRepoName
290 return _ValidRepoName
291
291
292 def ValidForkName():
292 def ValidForkName(*args, **kwargs):
293 class _ValidForkName(formencode.validators.FancyValidator):
293 return ValidRepoName(*args, **kwargs)
294 def to_python(self, value, state):
295
296 repo_name = value.get('fork_name')
297
298 slug = repo_name_slug(repo_name)
299 if slug in [ADMIN_PREFIX, '']:
300 e_dict = {'repo_name': _('This repository name is disallowed')}
301 raise formencode.Invalid('', value, state, error_dict=e_dict)
302
303 if RepoModel().get_by_repo_name(repo_name):
304 e_dict = {'fork_name':_('This repository '
305 'already exists')}
306 raise formencode.Invalid('', value, state,
307 error_dict=e_dict)
308 return value
309 return _ValidForkName
310
294
311
295
312 def SlugifyName():
296 def SlugifyName():
@@ -513,7 +497,7 b' def UserForm(edit=False, old_data={}):'
513 else:
497 else:
514 password = All(UnicodeString(strip=True, min=6, not_empty=True))
498 password = All(UnicodeString(strip=True, min=6, not_empty=True))
515 password_confirmation = All(UnicodeString(strip=True, min=6, not_empty=False))
499 password_confirmation = All(UnicodeString(strip=True, min=6, not_empty=False))
516
500
517 active = StringBoolean(if_missing=False)
501 active = StringBoolean(if_missing=False)
518 name = UnicodeString(strip=True, min=1, not_empty=True)
502 name = UnicodeString(strip=True, min=1, not_empty=True)
519 lastname = UnicodeString(strip=True, min=1, not_empty=True)
503 lastname = UnicodeString(strip=True, min=1, not_empty=True)
@@ -605,17 +589,20 b' def RepoForm(edit=False, old_data={}, su'
605 chained_validators = [ValidRepoName(edit, old_data), ValidPerms]
589 chained_validators = [ValidRepoName(edit, old_data), ValidPerms]
606 return _RepoForm
590 return _RepoForm
607
591
608 def RepoForkForm(edit=False, old_data={}, supported_backends=BACKENDS.keys()):
592 def RepoForkForm(edit=False, old_data={}, supported_backends=BACKENDS.keys(),
593 repo_groups=[]):
609 class _RepoForkForm(formencode.Schema):
594 class _RepoForkForm(formencode.Schema):
610 allow_extra_fields = True
595 allow_extra_fields = True
611 filter_extra_fields = False
596 filter_extra_fields = False
612 fork_name = All(UnicodeString(strip=True, min=1, not_empty=True),
597 repo_name = All(UnicodeString(strip=True, min=1, not_empty=True),
613 SlugifyName())
598 SlugifyName())
599 repo_group = OneOf(repo_groups, hideList=True)
600 repo_type = All(ValidForkType(old_data), OneOf(supported_backends))
614 description = UnicodeString(strip=True, min=1, not_empty=True)
601 description = UnicodeString(strip=True, min=1, not_empty=True)
615 private = StringBoolean(if_missing=False)
602 private = StringBoolean(if_missing=False)
616 repo_type = All(ValidForkType(old_data), OneOf(supported_backends))
603 copy_permissions = StringBoolean(if_missing=False)
617
604 fork_parent_id = UnicodeString()
618 chained_validators = [ValidForkName()]
605 chained_validators = [ValidForkName(edit, old_data)]
619
606
620 return _RepoForkForm
607 return _RepoForkForm
621
608
@@ -630,7 +617,7 b' def RepoSettingsForm(edit=False, old_dat'
630 repo_group = OneOf(repo_groups, hideList=True)
617 repo_group = OneOf(repo_groups, hideList=True)
631 private = StringBoolean(if_missing=False)
618 private = StringBoolean(if_missing=False)
632
619
633 chained_validators = [ValidRepoName(edit, old_data), ValidPerms,
620 chained_validators = [ValidRepoName(edit, old_data), ValidPerms,
634 ValidSettings]
621 ValidSettings]
635 return _RepoForm
622 return _RepoForm
636
623
@@ -35,8 +35,6 b' from pylons.i18n.translation import _'
35 from rhodecode.lib import helpers as h
35 from rhodecode.lib import helpers as h
36 from rhodecode.model import BaseModel
36 from rhodecode.model import BaseModel
37 from rhodecode.model.db import Notification, User, UserNotification
37 from rhodecode.model.db import Notification, User, UserNotification
38 from rhodecode.lib.celerylib import run_task
39 from rhodecode.lib.celerylib.tasks import send_email
40
38
41 log = logging.getLogger(__name__)
39 log = logging.getLogger(__name__)
42
40
@@ -74,6 +72,7 b' class NotificationModel(BaseModel):'
74 :param recipients: list of int, str or User objects
72 :param recipients: list of int, str or User objects
75 :param type_: type of notification
73 :param type_: type of notification
76 """
74 """
75 from rhodecode.lib.celerylib import tasks, run_task
77
76
78 if not getattr(recipients, '__iter__', False):
77 if not getattr(recipients, '__iter__', False):
79 raise Exception('recipients must be a list of iterable')
78 raise Exception('recipients must be a list of iterable')
@@ -100,7 +99,7 b' class NotificationModel(BaseModel):'
100 email_body_html = EmailNotificationModel()\
99 email_body_html = EmailNotificationModel()\
101 .get_email_tmpl(type_, **{'subject':subject,
100 .get_email_tmpl(type_, **{'subject':subject,
102 'body':h.rst(body)})
101 'body':h.rst(body)})
103 run_task(send_email, rec.email, email_subject, email_body,
102 run_task(tasks.send_email, rec.email, email_subject, email_body,
104 email_body_html)
103 email_body_html)
105
104
106 return notif
105 return notif
@@ -212,36 +212,33 b' class RepoModel(BaseModel):'
212 raise
212 raise
213
213
214 def create(self, form_data, cur_user, just_db=False, fork=False):
214 def create(self, form_data, cur_user, just_db=False, fork=False):
215 from rhodecode.model.scm import ScmModel
215
216
216 try:
217 try:
217 if fork:
218 if fork:
218 repo_name = form_data['fork_name']
219 fork_parent_id = form_data['fork_parent_id']
219 org_name = form_data['repo_name']
220 org_full_name = org_name
221
220
222 else:
221 # repo name is just a name of repository
223 org_name = repo_name = form_data['repo_name']
222 # while repo_name_full is a full qualified name that is combined
224 repo_name_full = form_data['repo_name_full']
223 # with name and path of group
224 repo_name = form_data['repo_name']
225 repo_name_full = form_data['repo_name_full']
225
226
226 new_repo = Repository()
227 new_repo = Repository()
227 new_repo.enable_statistics = False
228 new_repo.enable_statistics = False
229
228 for k, v in form_data.items():
230 for k, v in form_data.items():
229 if k == 'repo_name':
231 if k == 'repo_name':
230 if fork:
232 v = repo_name_full
231 v = repo_name
232 else:
233 v = repo_name_full
234 if k == 'repo_group':
233 if k == 'repo_group':
235 k = 'group_id'
234 k = 'group_id'
236
237 if k == 'description':
235 if k == 'description':
238 v = v or repo_name
236 v = v or repo_name
239
237
240 setattr(new_repo, k, v)
238 setattr(new_repo, k, v)
241
239
242 if fork:
240 if fork:
243 parent_repo = self.sa.query(Repository)\
241 parent_repo = Repository.get(fork_parent_id)
244 .filter(Repository.repo_name == org_full_name).one()
245 new_repo.fork = parent_repo
242 new_repo.fork = parent_repo
246
243
247 new_repo.user_id = cur_user.user_id
244 new_repo.user_id = cur_user.user_id
@@ -271,19 +268,21 b' class RepoModel(BaseModel):'
271 form_data['repo_group'],
268 form_data['repo_group'],
272 form_data['clone_uri'])
269 form_data['clone_uri'])
273
270
274 self.sa.commit()
271 # now automatically start following this repository as owner
275
276 #now automatically start following this repository as owner
277 from rhodecode.model.scm import ScmModel
278 ScmModel(self.sa).toggle_following_repo(new_repo.repo_id,
272 ScmModel(self.sa).toggle_following_repo(new_repo.repo_id,
279 cur_user.user_id)
273 cur_user.user_id)
280 return new_repo
274 return new_repo
281 except:
275 except:
282 log.error(traceback.format_exc())
276 log.error(traceback.format_exc())
283 self.sa.rollback()
284 raise
277 raise
285
278
286 def create_fork(self, form_data, cur_user):
279 def create_fork(self, form_data, cur_user):
280 """
281 Simple wrapper into executing celery task for fork creation
282
283 :param form_data:
284 :param cur_user:
285 """
287 from rhodecode.lib.celerylib import tasks, run_task
286 from rhodecode.lib.celerylib import tasks, run_task
288 run_task(tasks.create_repo_fork, form_data, cur_user)
287 run_task(tasks.create_repo_fork, form_data, cur_user)
289
288
@@ -325,6 +324,11 b' class RepoModel(BaseModel):'
325 raise
324 raise
326
325
327 def delete_stats(self, repo_name):
326 def delete_stats(self, repo_name):
327 """
328 removes stats for given repo
329
330 :param repo_name:
331 """
328 try:
332 try:
329 obj = self.sa.query(Statistics)\
333 obj = self.sa.query(Statistics)\
330 .filter(Statistics.repository == \
334 .filter(Statistics.repository == \
@@ -208,17 +208,14 b' class ScmModel(BaseModel):'
208 .filter(UserFollowing.user_id == user_id).scalar()
208 .filter(UserFollowing.user_id == user_id).scalar()
209
209
210 if f is not None:
210 if f is not None:
211
212 try:
211 try:
213 self.sa.delete(f)
212 self.sa.delete(f)
214 self.sa.commit()
215 action_logger(UserTemp(user_id),
213 action_logger(UserTemp(user_id),
216 'stopped_following_repo',
214 'stopped_following_repo',
217 RepoTemp(follow_repo_id))
215 RepoTemp(follow_repo_id))
218 return
216 return
219 except:
217 except:
220 log.error(traceback.format_exc())
218 log.error(traceback.format_exc())
221 self.sa.rollback()
222 raise
219 raise
223
220
224 try:
221 try:
@@ -226,13 +223,12 b' class ScmModel(BaseModel):'
226 f.user_id = user_id
223 f.user_id = user_id
227 f.follows_repo_id = follow_repo_id
224 f.follows_repo_id = follow_repo_id
228 self.sa.add(f)
225 self.sa.add(f)
229 self.sa.commit()
226
230 action_logger(UserTemp(user_id),
227 action_logger(UserTemp(user_id),
231 'started_following_repo',
228 'started_following_repo',
232 RepoTemp(follow_repo_id))
229 RepoTemp(follow_repo_id))
233 except:
230 except:
234 log.error(traceback.format_exc())
231 log.error(traceback.format_exc())
235 self.sa.rollback()
236 raise
232 raise
237
233
238 def toggle_following_user(self, follow_user_id, user_id):
234 def toggle_following_user(self, follow_user_id, user_id):
@@ -243,11 +239,9 b' class ScmModel(BaseModel):'
243 if f is not None:
239 if f is not None:
244 try:
240 try:
245 self.sa.delete(f)
241 self.sa.delete(f)
246 self.sa.commit()
247 return
242 return
248 except:
243 except:
249 log.error(traceback.format_exc())
244 log.error(traceback.format_exc())
250 self.sa.rollback()
251 raise
245 raise
252
246
253 try:
247 try:
@@ -255,10 +249,8 b' class ScmModel(BaseModel):'
255 f.user_id = user_id
249 f.user_id = user_id
256 f.follows_user_id = follow_user_id
250 f.follows_user_id = follow_user_id
257 self.sa.add(f)
251 self.sa.add(f)
258 self.sa.commit()
259 except:
252 except:
260 log.error(traceback.format_exc())
253 log.error(traceback.format_exc())
261 self.sa.rollback()
262 raise
254 raise
263
255
264 def is_following_repo(self, repo_name, user_id, cache=False):
256 def is_following_repo(self, repo_name, user_id, cache=False):
@@ -317,8 +309,8 b' class ScmModel(BaseModel):'
317 log.error(traceback.format_exc())
309 log.error(traceback.format_exc())
318 raise
310 raise
319
311
320 def commit_change(self, repo, repo_name, cs, user, author, message, content,
312 def commit_change(self, repo, repo_name, cs, user, author, message,
321 f_path):
313 content, f_path):
322
314
323 if repo.alias == 'hg':
315 if repo.alias == 'hg':
324 from vcs.backends.hg import MercurialInMemoryChangeset as IMC
316 from vcs.backends.hg import MercurialInMemoryChangeset as IMC
@@ -1790,6 +1790,10 b' div.form div.fields div.field div.button'
1790 padding: 0 !important;
1790 padding: 0 !important;
1791 }
1791 }
1792
1792
1793 .trending_language_tbl,.trending_language_tbl tr {
1794 border-spacing: 1px;
1795 }
1796
1793 .trending_language {
1797 .trending_language {
1794 background-color: #003367;
1798 background-color: #003367;
1795 color: #FFF;
1799 color: #FFF;
@@ -1797,7 +1801,7 b' div.form div.fields div.field div.button'
1797 min-width: 20px;
1801 min-width: 20px;
1798 text-decoration: none;
1802 text-decoration: none;
1799 height: 12px;
1803 height: 12px;
1800 margin-bottom: 4px;
1804 margin-bottom: 0px;
1801 margin-left: 5px;
1805 margin-left: 5px;
1802 white-space: pre;
1806 white-space: pre;
1803 padding: 3px;
1807 padding: 3px;
@@ -27,14 +27,23 b''
27 <!-- fields -->
27 <!-- fields -->
28 <div class="fields">
28 <div class="fields">
29 <div class="field">
29 <div class="field">
30 <div class="label">
30 <div class="label">
31 <label for="repo_name">${_('Fork name')}:</label>
31 <label for="repo_name">${_('Fork name')}:</label>
32 </div>
32 </div>
33 <div class="input">
33 <div class="input">
34 ${h.text('fork_name',class_="small")}
34 ${h.text('repo_name',class_="small")}
35 ${h.hidden('repo_type',c.repo_info.repo_type)}
35 ${h.hidden('repo_type',c.repo_info.repo_type)}
36 </div>
36 ${h.hidden('fork_parent_id',c.repo_info.repo_id)}
37 </div>
37 </div>
38 </div>
39 <div class="field">
40 <div class="label">
41 <label for="repo_group">${_('Repository group')}:</label>
42 </div>
43 <div class="input">
44 ${h.select('repo_group','',c.repo_groups,class_="medium")}
45 </div>
46 </div>
38 <div class="field">
47 <div class="field">
39 <div class="label label-textarea">
48 <div class="label label-textarea">
40 <label for="description">${_('Description')}:</label>
49 <label for="description">${_('Description')}:</label>
@@ -50,7 +59,15 b''
50 <div class="checkboxes">
59 <div class="checkboxes">
51 ${h.checkbox('private',value="True")}
60 ${h.checkbox('private',value="True")}
52 </div>
61 </div>
53 </div>
62 </div>
63 <div class="field">
64 <div class="label label-checkbox">
65 <label for="private">${_('Copy permissions')}:</label>
66 </div>
67 <div class="checkboxes">
68 ${h.checkbox('copy_permissions',value="True")}
69 </div>
70 </div>
54 <div class="buttons">
71 <div class="buttons">
55 ${h.submit('',_('fork this repository'),class_="ui-button")}
72 ${h.submit('',_('fork this repository'),class_="ui-button")}
56 </div>
73 </div>
General Comments 0
You need to be logged in to leave comments. Login now