##// END OF EJS Templates
extended repo creation by repo type. fixed fork creation to maintain repo type.
marcink -
r659:758f64f3 beta
parent child Browse files
Show More
@@ -1,175 +1,175 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 # settings controller for pylons
3 # settings controller for pylons
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 #
5 #
6 # This program is free software; you can redistribute it and/or
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; version 2
8 # as published by the Free Software Foundation; version 2
9 # of the License or (at your opinion) any later version of the license.
9 # of the License or (at your opinion) any later version of the license.
10 #
10 #
11 # This program is distributed in the hope that it will be useful,
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
14 # GNU General Public License for more details.
15 #
15 #
16 # You should have received a copy of the GNU General Public License
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 # MA 02110-1301, USA.
19 # MA 02110-1301, USA.
20 """
20 """
21 Created on June 30, 2010
21 Created on June 30, 2010
22 settings controller for pylons
22 settings controller for pylons
23 @author: marcink
23 @author: marcink
24 """
24 """
25 from formencode import htmlfill
25 from formencode import htmlfill
26 from pylons import tmpl_context as c, request, url
26 from pylons import tmpl_context as c, request, url
27 from pylons.controllers.util import redirect
27 from pylons.controllers.util import redirect
28 from pylons.i18n.translation import _
28 from pylons.i18n.translation import _
29 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAllDecorator
29 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAllDecorator
30 from rhodecode.lib.base import BaseController, render
30 from rhodecode.lib.base import BaseController, render
31 from rhodecode.lib.utils import invalidate_cache, action_logger
31 from rhodecode.lib.utils import invalidate_cache, action_logger
32 from rhodecode.model.forms import RepoSettingsForm, RepoForkForm
32 from rhodecode.model.forms import RepoSettingsForm, RepoForkForm
33 from rhodecode.model.repo import RepoModel
33 from rhodecode.model.repo import RepoModel
34 import formencode
34 import formencode
35 import logging
35 import logging
36 import rhodecode.lib.helpers as h
36 import rhodecode.lib.helpers as h
37 import traceback
37 import traceback
38
38
39 log = logging.getLogger(__name__)
39 log = logging.getLogger(__name__)
40
40
41 class SettingsController(BaseController):
41 class SettingsController(BaseController):
42
42
43 @LoginRequired()
43 @LoginRequired()
44 @HasRepoPermissionAllDecorator('repository.admin')
44 @HasRepoPermissionAllDecorator('repository.admin')
45 def __before__(self):
45 def __before__(self):
46 super(SettingsController, self).__before__()
46 super(SettingsController, self).__before__()
47
47
48 def index(self, repo_name):
48 def index(self, repo_name):
49 repo_model = RepoModel()
49 repo_model = RepoModel()
50 c.repo_info = repo = repo_model.get(repo_name)
50 c.repo_info = repo = repo_model.get(repo_name)
51 if not repo:
51 if not repo:
52 h.flash(_('%s repository is not mapped to db perhaps'
52 h.flash(_('%s repository is not mapped to db perhaps'
53 ' it was created or renamed from the filesystem'
53 ' it was created or renamed from the filesystem'
54 ' please run the application again'
54 ' please run the application again'
55 ' in order to rescan repositories') % repo_name,
55 ' in order to rescan repositories') % repo_name,
56 category='error')
56 category='error')
57
57
58 return redirect(url('home'))
58 return redirect(url('home'))
59 defaults = c.repo_info.__dict__
59 defaults = c.repo_info.__dict__
60 defaults.update({'user':c.repo_info.user.username})
60 defaults.update({'user':c.repo_info.user.username})
61 c.users_array = repo_model.get_users_js()
61 c.users_array = repo_model.get_users_js()
62
62
63 for p in c.repo_info.repo_to_perm:
63 for p in c.repo_info.repo_to_perm:
64 defaults.update({'perm_%s' % p.user.username:
64 defaults.update({'perm_%s' % p.user.username:
65 p.permission.permission_name})
65 p.permission.permission_name})
66
66
67 return htmlfill.render(
67 return htmlfill.render(
68 render('settings/repo_settings.html'),
68 render('settings/repo_settings.html'),
69 defaults=defaults,
69 defaults=defaults,
70 encoding="UTF-8",
70 encoding="UTF-8",
71 force_defaults=False
71 force_defaults=False
72 )
72 )
73
73
74 def update(self, repo_name):
74 def update(self, repo_name):
75 repo_model = RepoModel()
75 repo_model = RepoModel()
76 changed_name = repo_name
76 changed_name = repo_name
77 _form = RepoSettingsForm(edit=True, old_data={'repo_name':repo_name})()
77 _form = RepoSettingsForm(edit=True, old_data={'repo_name':repo_name})()
78 try:
78 try:
79 form_result = _form.to_python(dict(request.POST))
79 form_result = _form.to_python(dict(request.POST))
80 repo_model.update(repo_name, form_result)
80 repo_model.update(repo_name, form_result)
81 invalidate_cache('cached_repo_list')
81 invalidate_cache('cached_repo_list')
82 h.flash(_('Repository %s updated successfully' % repo_name),
82 h.flash(_('Repository %s updated successfully' % repo_name),
83 category='success')
83 category='success')
84 changed_name = form_result['repo_name']
84 changed_name = form_result['repo_name']
85 except formencode.Invalid, errors:
85 except formencode.Invalid, errors:
86 c.repo_info = repo_model.get(repo_name)
86 c.repo_info = repo_model.get(repo_name)
87 c.users_array = repo_model.get_users_js()
87 c.users_array = repo_model.get_users_js()
88 errors.value.update({'user':c.repo_info.user.username})
88 errors.value.update({'user':c.repo_info.user.username})
89 return htmlfill.render(
89 return htmlfill.render(
90 render('settings/repo_settings.html'),
90 render('settings/repo_settings.html'),
91 defaults=errors.value,
91 defaults=errors.value,
92 errors=errors.error_dict or {},
92 errors=errors.error_dict or {},
93 prefix_error=False,
93 prefix_error=False,
94 encoding="UTF-8")
94 encoding="UTF-8")
95 except Exception:
95 except Exception:
96 log.error(traceback.format_exc())
96 log.error(traceback.format_exc())
97 h.flash(_('error occured during update of repository %s') \
97 h.flash(_('error occured during update of repository %s') \
98 % repo_name, category='error')
98 % repo_name, category='error')
99
99
100 return redirect(url('repo_settings_home', repo_name=changed_name))
100 return redirect(url('repo_settings_home', repo_name=changed_name))
101
101
102
102
103
103
104 def delete(self, repo_name):
104 def delete(self, repo_name):
105 """DELETE /repos/repo_name: Delete an existing item"""
105 """DELETE /repos/repo_name: Delete an existing item"""
106 # Forms posted to this method should contain a hidden field:
106 # Forms posted to this method should contain a hidden field:
107 # <input type="hidden" name="_method" value="DELETE" />
107 # <input type="hidden" name="_method" value="DELETE" />
108 # Or using helpers:
108 # Or using helpers:
109 # h.form(url('repo_settings_delete', repo_name=ID),
109 # h.form(url('repo_settings_delete', repo_name=ID),
110 # method='delete')
110 # method='delete')
111 # url('repo_settings_delete', repo_name=ID)
111 # url('repo_settings_delete', repo_name=ID)
112
112
113 repo_model = RepoModel()
113 repo_model = RepoModel()
114 repo = repo_model.get(repo_name)
114 repo = repo_model.get(repo_name)
115 if not repo:
115 if not repo:
116 h.flash(_('%s repository is not mapped to db perhaps'
116 h.flash(_('%s repository is not mapped to db perhaps'
117 ' it was moved or renamed from the filesystem'
117 ' it was moved or renamed from the filesystem'
118 ' please run the application again'
118 ' please run the application again'
119 ' in order to rescan repositories') % repo_name,
119 ' in order to rescan repositories') % repo_name,
120 category='error')
120 category='error')
121
121
122 return redirect(url('home'))
122 return redirect(url('home'))
123 try:
123 try:
124 action_logger(self.rhodecode_user, 'user_deleted_repo',
124 action_logger(self.rhodecode_user, 'user_deleted_repo',
125 repo_name, '', self.sa)
125 repo_name, '', self.sa)
126 repo_model.delete(repo)
126 repo_model.delete(repo)
127 invalidate_cache('cached_repo_list')
127 invalidate_cache('cached_repo_list')
128 h.flash(_('deleted repository %s') % repo_name, category='success')
128 h.flash(_('deleted repository %s') % repo_name, category='success')
129 except Exception:
129 except Exception:
130 h.flash(_('An error occurred during deletion of %s') % repo_name,
130 h.flash(_('An error occurred during deletion of %s') % repo_name,
131 category='error')
131 category='error')
132
132
133 return redirect(url('home'))
133 return redirect(url('home'))
134
134
135 def fork(self, repo_name):
135 def fork(self, repo_name):
136 repo_model = RepoModel()
136 repo_model = RepoModel()
137 c.repo_info = repo = repo_model.get(repo_name)
137 c.repo_info = repo = repo_model.get(repo_name)
138 if not repo:
138 if not repo:
139 h.flash(_('%s repository is not mapped to db perhaps'
139 h.flash(_('%s repository is not mapped to db perhaps'
140 ' it was created or renamed from the filesystem'
140 ' it was created or renamed from the filesystem'
141 ' please run the application again'
141 ' please run the application again'
142 ' in order to rescan repositories') % repo_name,
142 ' in order to rescan repositories') % repo_name,
143 category='error')
143 category='error')
144
144
145 return redirect(url('home'))
145 return redirect(url('home'))
146
146
147 return render('settings/repo_fork.html')
147 return render('settings/repo_fork.html')
148
148
149
149
150
150
151 def fork_create(self, repo_name):
151 def fork_create(self, repo_name):
152 repo_model = RepoModel()
152 repo_model = RepoModel()
153 c.repo_info = repo_model.get(repo_name)
153 c.repo_info = repo_model.get(repo_name)
154 _form = RepoForkForm()()
154 _form = RepoForkForm(old_data={'repo_type':c.repo_info.repo_type})()
155 form_result = {}
155 form_result = {}
156 try:
156 try:
157 form_result = _form.to_python(dict(request.POST))
157 form_result = _form.to_python(dict(request.POST))
158 form_result.update({'repo_name':repo_name})
158 form_result.update({'repo_name':repo_name})
159 repo_model.create_fork(form_result, c.rhodecode_user)
159 repo_model.create_fork(form_result, c.rhodecode_user)
160 h.flash(_('fork %s repository as %s task added') \
160 h.flash(_('fork %s repository as %s task added') \
161 % (repo_name, form_result['fork_name']),
161 % (repo_name, form_result['fork_name']),
162 category='success')
162 category='success')
163 action_logger(self.rhodecode_user, 'user_forked_repo',
163 action_logger(self.rhodecode_user, 'user_forked_repo',
164 repo_name, '', self.sa)
164 repo_name, '', self.sa)
165 except formencode.Invalid, errors:
165 except formencode.Invalid, errors:
166 c.new_repo = errors.value['fork_name']
166 c.new_repo = errors.value['fork_name']
167 r = render('settings/repo_fork.html')
167 r = render('settings/repo_fork.html')
168
168
169 return htmlfill.render(
169 return htmlfill.render(
170 r,
170 r,
171 defaults=errors.value,
171 defaults=errors.value,
172 errors=errors.error_dict or {},
172 errors=errors.error_dict or {},
173 prefix_error=False,
173 prefix_error=False,
174 encoding="UTF-8")
174 encoding="UTF-8")
175 return redirect(url('home'))
175 return redirect(url('home'))
@@ -1,46 +1,46 b''
1 """The base Controller API
1 """The base Controller API
2
2
3 Provides the BaseController class for subclassing.
3 Provides the BaseController class for subclassing.
4 """
4 """
5 from pylons import config, tmpl_context as c, request, session
5 from pylons import config, tmpl_context as c, request, session
6 from pylons.controllers import WSGIController
6 from pylons.controllers import WSGIController
7 from pylons.templating import render_mako as render
7 from pylons.templating import render_mako as render
8 from rhodecode import __version__
8 from rhodecode import __version__
9 from rhodecode.lib import auth
9 from rhodecode.lib import auth
10 from rhodecode.lib.utils import get_repo_slug
10 from rhodecode.lib.utils import get_repo_slug
11 from rhodecode.model import meta
11 from rhodecode.model import meta
12 from rhodecode.model.hg import _get_repos_cached, \
12 from rhodecode.model.hg import _get_repos_cached, \
13 _get_repos_switcher_cached
13 _get_repos_switcher_cached
14
14 from vcs import BACKENDS
15 class BaseController(WSGIController):
15 class BaseController(WSGIController):
16
16
17 def __before__(self):
17 def __before__(self):
18 c.rhodecode_version = __version__
18 c.rhodecode_version = __version__
19 c.rhodecode_name = config['rhodecode_title']
19 c.rhodecode_name = config['rhodecode_title']
20 c.repo_name = get_repo_slug(request)
20 c.repo_name = get_repo_slug(request)
21 c.cached_repo_list = _get_repos_cached()
21 c.cached_repo_list = _get_repos_cached()
22 c.repo_switcher_list = _get_repos_switcher_cached(c.cached_repo_list)
22 c.repo_switcher_list = _get_repos_switcher_cached(c.cached_repo_list)
23
23 c.backends = BACKENDS.keys()
24 if c.repo_name:
24 if c.repo_name:
25 cached_repo = c.cached_repo_list.get(c.repo_name)
25 cached_repo = c.cached_repo_list.get(c.repo_name)
26
26
27 if cached_repo:
27 if cached_repo:
28 c.repository_tags = cached_repo.tags
28 c.repository_tags = cached_repo.tags
29 c.repository_branches = cached_repo.branches
29 c.repository_branches = cached_repo.branches
30 else:
30 else:
31 c.repository_tags = {}
31 c.repository_tags = {}
32 c.repository_branches = {}
32 c.repository_branches = {}
33
33
34 self.sa = meta.Session()
34 self.sa = meta.Session()
35
35
36 def __call__(self, environ, start_response):
36 def __call__(self, environ, start_response):
37 """Invoke the Controller"""
37 """Invoke the Controller"""
38 # WSGIController.__call__ dispatches to the Controller method
38 # WSGIController.__call__ dispatches to the Controller method
39 # the request is routed to. This routing information is
39 # the request is routed to. This routing information is
40 # available in environ['pylons.routes_dict']
40 # available in environ['pylons.routes_dict']
41 try:
41 try:
42 #putting this here makes sure that we update permissions every time
42 #putting this here makes sure that we update permissions every time
43 self.rhodecode_user = c.rhodecode_user = auth.get_user(session)
43 self.rhodecode_user = c.rhodecode_user = auth.get_user(session)
44 return WSGIController.__call__(self, environ, start_response)
44 return WSGIController.__call__(self, environ, start_response)
45 finally:
45 finally:
46 meta.Session.remove()
46 meta.Session.remove()
@@ -1,334 +1,336 b''
1 from celery.decorators import task
1 from celery.decorators import task
2
2
3 from operator import itemgetter
3 from operator import itemgetter
4 from pylons.i18n.translation import _
4 from pylons.i18n.translation import _
5 from rhodecode.lib.celerylib import run_task, locked_task
5 from rhodecode.lib.celerylib import run_task, locked_task
6 from rhodecode.lib.helpers import person
6 from rhodecode.lib.helpers import person
7 from rhodecode.lib.smtp_mailer import SmtpMailer
7 from rhodecode.lib.smtp_mailer import SmtpMailer
8 from rhodecode.lib.utils import OrderedDict
8 from rhodecode.lib.utils import OrderedDict
9 from time import mktime
9 from time import mktime
10 from vcs.backends.hg import MercurialRepository
10 from vcs.backends.hg import MercurialRepository
11 from vcs.backends.git import GitRepository
11 from vcs.backends.git import GitRepository
12 import os
12 import os
13 import traceback
13 import traceback
14 from vcs.backends import get_repo
14 from vcs.backends import get_repo
15 from vcs.utils.helpers import get_scm
15 from vcs.utils.helpers import get_scm
16
16
17 try:
17 try:
18 import json
18 import json
19 except ImportError:
19 except ImportError:
20 #python 2.5 compatibility
20 #python 2.5 compatibility
21 import simplejson as json
21 import simplejson as json
22
22
23 try:
23 try:
24 from celeryconfig import PYLONS_CONFIG as config
24 from celeryconfig import PYLONS_CONFIG as config
25 celery_on = True
25 celery_on = True
26 except ImportError:
26 except ImportError:
27 #if celeryconfig is not present let's just load our pylons
27 #if celeryconfig is not present let's just load our pylons
28 #config instead
28 #config instead
29 from pylons import config
29 from pylons import config
30 celery_on = False
30 celery_on = False
31
31
32
32
33 __all__ = ['whoosh_index', 'get_commits_stats',
33 __all__ = ['whoosh_index', 'get_commits_stats',
34 'reset_user_password', 'send_email']
34 'reset_user_password', 'send_email']
35
35
36 def get_session():
36 def get_session():
37 if celery_on:
37 if celery_on:
38 from sqlalchemy import engine_from_config
38 from sqlalchemy import engine_from_config
39 from sqlalchemy.orm import sessionmaker, scoped_session
39 from sqlalchemy.orm import sessionmaker, scoped_session
40 engine = engine_from_config(dict(config.items('app:main')), 'sqlalchemy.db1.')
40 engine = engine_from_config(dict(config.items('app:main')), 'sqlalchemy.db1.')
41 sa = scoped_session(sessionmaker(bind=engine))
41 sa = scoped_session(sessionmaker(bind=engine))
42 else:
42 else:
43 #If we don't use celery reuse our current application Session
43 #If we don't use celery reuse our current application Session
44 from rhodecode.model.meta import Session
44 from rhodecode.model.meta import Session
45 sa = Session()
45 sa = Session()
46
46
47 return sa
47 return sa
48
48
49 def get_hg_settings():
49 def get_hg_settings():
50 from rhodecode.model.db import RhodeCodeSettings
50 from rhodecode.model.db import RhodeCodeSettings
51 sa = get_session()
51 sa = get_session()
52 ret = sa.query(RhodeCodeSettings).all()
52 ret = sa.query(RhodeCodeSettings).all()
53
53
54 if not ret:
54 if not ret:
55 raise Exception('Could not get application settings !')
55 raise Exception('Could not get application settings !')
56 settings = {}
56 settings = {}
57 for each in ret:
57 for each in ret:
58 settings['rhodecode_' + each.app_settings_name] = each.app_settings_value
58 settings['rhodecode_' + each.app_settings_name] = each.app_settings_value
59
59
60 return settings
60 return settings
61
61
62 def get_hg_ui_settings():
62 def get_hg_ui_settings():
63 from rhodecode.model.db import RhodeCodeUi
63 from rhodecode.model.db import RhodeCodeUi
64 sa = get_session()
64 sa = get_session()
65 ret = sa.query(RhodeCodeUi).all()
65 ret = sa.query(RhodeCodeUi).all()
66
66
67 if not ret:
67 if not ret:
68 raise Exception('Could not get application ui settings !')
68 raise Exception('Could not get application ui settings !')
69 settings = {}
69 settings = {}
70 for each in ret:
70 for each in ret:
71 k = each.ui_key
71 k = each.ui_key
72 v = each.ui_value
72 v = each.ui_value
73 if k == '/':
73 if k == '/':
74 k = 'root_path'
74 k = 'root_path'
75
75
76 if k.find('.') != -1:
76 if k.find('.') != -1:
77 k = k.replace('.', '_')
77 k = k.replace('.', '_')
78
78
79 if each.ui_section == 'hooks':
79 if each.ui_section == 'hooks':
80 v = each.ui_active
80 v = each.ui_active
81
81
82 settings[each.ui_section + '_' + k] = v
82 settings[each.ui_section + '_' + k] = v
83
83
84 return settings
84 return settings
85
85
86 @task
86 @task
87 @locked_task
87 @locked_task
88 def whoosh_index(repo_location, full_index):
88 def whoosh_index(repo_location, full_index):
89 log = whoosh_index.get_logger()
89 log = whoosh_index.get_logger()
90 from rhodecode.lib.indexers.daemon import WhooshIndexingDaemon
90 from rhodecode.lib.indexers.daemon import WhooshIndexingDaemon
91 WhooshIndexingDaemon(repo_location=repo_location).run(full_index=full_index)
91 WhooshIndexingDaemon(repo_location=repo_location).run(full_index=full_index)
92
92
93 @task
93 @task
94 @locked_task
94 @locked_task
95 def get_commits_stats(repo_name, ts_min_y, ts_max_y):
95 def get_commits_stats(repo_name, ts_min_y, ts_max_y):
96 from rhodecode.model.db import Statistics, Repository
96 from rhodecode.model.db import Statistics, Repository
97 log = get_commits_stats.get_logger()
97 log = get_commits_stats.get_logger()
98 author_key_cleaner = lambda k: person(k).replace('"', "") #for js data compatibilty
98 author_key_cleaner = lambda k: person(k).replace('"', "") #for js data compatibilty
99
99
100 commits_by_day_author_aggregate = {}
100 commits_by_day_author_aggregate = {}
101 commits_by_day_aggregate = {}
101 commits_by_day_aggregate = {}
102 repos_path = get_hg_ui_settings()['paths_root_path']
102 repos_path = get_hg_ui_settings()['paths_root_path']
103 p = os.path.join(repos_path, repo_name)
103 p = os.path.join(repos_path, repo_name)
104 repo = get_repo(p)
104 repo = get_repo(p)
105
105
106 skip_date_limit = True
106 skip_date_limit = True
107 parse_limit = 250 #limit for single task changeset parsing optimal for
107 parse_limit = 250 #limit for single task changeset parsing optimal for
108 last_rev = 0
108 last_rev = 0
109 last_cs = None
109 last_cs = None
110 timegetter = itemgetter('time')
110 timegetter = itemgetter('time')
111
111
112 sa = get_session()
112 sa = get_session()
113
113
114 dbrepo = sa.query(Repository)\
114 dbrepo = sa.query(Repository)\
115 .filter(Repository.repo_name == repo_name).scalar()
115 .filter(Repository.repo_name == repo_name).scalar()
116 cur_stats = sa.query(Statistics)\
116 cur_stats = sa.query(Statistics)\
117 .filter(Statistics.repository == dbrepo).scalar()
117 .filter(Statistics.repository == dbrepo).scalar()
118 if cur_stats:
118 if cur_stats:
119 last_rev = cur_stats.stat_on_revision
119 last_rev = cur_stats.stat_on_revision
120 if not repo.revisions:
120 if not repo.revisions:
121 return True
121 return True
122
122
123 if last_rev == repo.revisions[-1] and len(repo.revisions) > 1:
123 if last_rev == repo.revisions[-1] and len(repo.revisions) > 1:
124 #pass silently without any work if we're not on first revision or current
124 #pass silently without any work if we're not on first revision or current
125 #state of parsing revision(from db marker) is the last revision
125 #state of parsing revision(from db marker) is the last revision
126 return True
126 return True
127
127
128 if cur_stats:
128 if cur_stats:
129 commits_by_day_aggregate = OrderedDict(
129 commits_by_day_aggregate = OrderedDict(
130 json.loads(
130 json.loads(
131 cur_stats.commit_activity_combined))
131 cur_stats.commit_activity_combined))
132 commits_by_day_author_aggregate = json.loads(cur_stats.commit_activity)
132 commits_by_day_author_aggregate = json.loads(cur_stats.commit_activity)
133
133
134 log.debug('starting parsing %s', parse_limit)
134 log.debug('starting parsing %s', parse_limit)
135 lmktime = mktime
135 lmktime = mktime
136
136
137 for cnt, rev in enumerate(repo.revisions[last_rev:]):
137 for cnt, rev in enumerate(repo.revisions[last_rev:]):
138 last_cs = cs = repo.get_changeset(rev)
138 last_cs = cs = repo.get_changeset(rev)
139 k = '%s-%s-%s' % (cs.date.timetuple()[0], cs.date.timetuple()[1],
139 k = '%s-%s-%s' % (cs.date.timetuple()[0], cs.date.timetuple()[1],
140 cs.date.timetuple()[2])
140 cs.date.timetuple()[2])
141 timetupple = [int(x) for x in k.split('-')]
141 timetupple = [int(x) for x in k.split('-')]
142 timetupple.extend([0 for _ in xrange(6)])
142 timetupple.extend([0 for _ in xrange(6)])
143 k = lmktime(timetupple)
143 k = lmktime(timetupple)
144 if commits_by_day_author_aggregate.has_key(author_key_cleaner(cs.author)):
144 if commits_by_day_author_aggregate.has_key(author_key_cleaner(cs.author)):
145 try:
145 try:
146 l = [timegetter(x) for x in commits_by_day_author_aggregate\
146 l = [timegetter(x) for x in commits_by_day_author_aggregate\
147 [author_key_cleaner(cs.author)]['data']]
147 [author_key_cleaner(cs.author)]['data']]
148 time_pos = l.index(k)
148 time_pos = l.index(k)
149 except ValueError:
149 except ValueError:
150 time_pos = False
150 time_pos = False
151
151
152 if time_pos >= 0 and time_pos is not False:
152 if time_pos >= 0 and time_pos is not False:
153
153
154 datadict = commits_by_day_author_aggregate\
154 datadict = commits_by_day_author_aggregate\
155 [author_key_cleaner(cs.author)]['data'][time_pos]
155 [author_key_cleaner(cs.author)]['data'][time_pos]
156
156
157 datadict["commits"] += 1
157 datadict["commits"] += 1
158 datadict["added"] += len(cs.added)
158 datadict["added"] += len(cs.added)
159 datadict["changed"] += len(cs.changed)
159 datadict["changed"] += len(cs.changed)
160 datadict["removed"] += len(cs.removed)
160 datadict["removed"] += len(cs.removed)
161
161
162 else:
162 else:
163 if k >= ts_min_y and k <= ts_max_y or skip_date_limit:
163 if k >= ts_min_y and k <= ts_max_y or skip_date_limit:
164
164
165 datadict = {"time":k,
165 datadict = {"time":k,
166 "commits":1,
166 "commits":1,
167 "added":len(cs.added),
167 "added":len(cs.added),
168 "changed":len(cs.changed),
168 "changed":len(cs.changed),
169 "removed":len(cs.removed),
169 "removed":len(cs.removed),
170 }
170 }
171 commits_by_day_author_aggregate\
171 commits_by_day_author_aggregate\
172 [author_key_cleaner(cs.author)]['data'].append(datadict)
172 [author_key_cleaner(cs.author)]['data'].append(datadict)
173
173
174 else:
174 else:
175 if k >= ts_min_y and k <= ts_max_y or skip_date_limit:
175 if k >= ts_min_y and k <= ts_max_y or skip_date_limit:
176 commits_by_day_author_aggregate[author_key_cleaner(cs.author)] = {
176 commits_by_day_author_aggregate[author_key_cleaner(cs.author)] = {
177 "label":author_key_cleaner(cs.author),
177 "label":author_key_cleaner(cs.author),
178 "data":[{"time":k,
178 "data":[{"time":k,
179 "commits":1,
179 "commits":1,
180 "added":len(cs.added),
180 "added":len(cs.added),
181 "changed":len(cs.changed),
181 "changed":len(cs.changed),
182 "removed":len(cs.removed),
182 "removed":len(cs.removed),
183 }],
183 }],
184 "schema":["commits"],
184 "schema":["commits"],
185 }
185 }
186
186
187 #gather all data by day
187 #gather all data by day
188 if commits_by_day_aggregate.has_key(k):
188 if commits_by_day_aggregate.has_key(k):
189 commits_by_day_aggregate[k] += 1
189 commits_by_day_aggregate[k] += 1
190 else:
190 else:
191 commits_by_day_aggregate[k] = 1
191 commits_by_day_aggregate[k] = 1
192
192
193 if cnt >= parse_limit:
193 if cnt >= parse_limit:
194 #don't fetch to much data since we can freeze application
194 #don't fetch to much data since we can freeze application
195 break
195 break
196 overview_data = []
196 overview_data = []
197 for k, v in commits_by_day_aggregate.items():
197 for k, v in commits_by_day_aggregate.items():
198 overview_data.append([k, v])
198 overview_data.append([k, v])
199 overview_data = sorted(overview_data, key=itemgetter(0))
199 overview_data = sorted(overview_data, key=itemgetter(0))
200 if not commits_by_day_author_aggregate:
200 if not commits_by_day_author_aggregate:
201 commits_by_day_author_aggregate[author_key_cleaner(repo.contact)] = {
201 commits_by_day_author_aggregate[author_key_cleaner(repo.contact)] = {
202 "label":author_key_cleaner(repo.contact),
202 "label":author_key_cleaner(repo.contact),
203 "data":[0, 1],
203 "data":[0, 1],
204 "schema":["commits"],
204 "schema":["commits"],
205 }
205 }
206
206
207 stats = cur_stats if cur_stats else Statistics()
207 stats = cur_stats if cur_stats else Statistics()
208 stats.commit_activity = json.dumps(commits_by_day_author_aggregate)
208 stats.commit_activity = json.dumps(commits_by_day_author_aggregate)
209 stats.commit_activity_combined = json.dumps(overview_data)
209 stats.commit_activity_combined = json.dumps(overview_data)
210
210
211 log.debug('last revison %s', last_rev)
211 log.debug('last revison %s', last_rev)
212 leftovers = len(repo.revisions[last_rev:])
212 leftovers = len(repo.revisions[last_rev:])
213 log.debug('revisions to parse %s', leftovers)
213 log.debug('revisions to parse %s', leftovers)
214
214
215 if last_rev == 0 or leftovers < parse_limit:
215 if last_rev == 0 or leftovers < parse_limit:
216 stats.languages = json.dumps(__get_codes_stats(repo_name))
216 stats.languages = json.dumps(__get_codes_stats(repo_name))
217
217
218 stats.repository = dbrepo
218 stats.repository = dbrepo
219 stats.stat_on_revision = last_cs.revision
219 stats.stat_on_revision = last_cs.revision
220
220
221 try:
221 try:
222 sa.add(stats)
222 sa.add(stats)
223 sa.commit()
223 sa.commit()
224 except:
224 except:
225 log.error(traceback.format_exc())
225 log.error(traceback.format_exc())
226 sa.rollback()
226 sa.rollback()
227 return False
227 return False
228 if len(repo.revisions) > 1:
228 if len(repo.revisions) > 1:
229 run_task(get_commits_stats, repo_name, ts_min_y, ts_max_y)
229 run_task(get_commits_stats, repo_name, ts_min_y, ts_max_y)
230
230
231 return True
231 return True
232
232
233 @task
233 @task
234 def reset_user_password(user_email):
234 def reset_user_password(user_email):
235 log = reset_user_password.get_logger()
235 log = reset_user_password.get_logger()
236 from rhodecode.lib import auth
236 from rhodecode.lib import auth
237 from rhodecode.model.db import User
237 from rhodecode.model.db import User
238
238
239 try:
239 try:
240 try:
240 try:
241 sa = get_session()
241 sa = get_session()
242 user = sa.query(User).filter(User.email == user_email).scalar()
242 user = sa.query(User).filter(User.email == user_email).scalar()
243 new_passwd = auth.PasswordGenerator().gen_password(8,
243 new_passwd = auth.PasswordGenerator().gen_password(8,
244 auth.PasswordGenerator.ALPHABETS_BIG_SMALL)
244 auth.PasswordGenerator.ALPHABETS_BIG_SMALL)
245 if user:
245 if user:
246 user.password = auth.get_crypt_password(new_passwd)
246 user.password = auth.get_crypt_password(new_passwd)
247 sa.add(user)
247 sa.add(user)
248 sa.commit()
248 sa.commit()
249 log.info('change password for %s', user_email)
249 log.info('change password for %s', user_email)
250 if new_passwd is None:
250 if new_passwd is None:
251 raise Exception('unable to generate new password')
251 raise Exception('unable to generate new password')
252
252
253 except:
253 except:
254 log.error(traceback.format_exc())
254 log.error(traceback.format_exc())
255 sa.rollback()
255 sa.rollback()
256
256
257 run_task(send_email, user_email,
257 run_task(send_email, user_email,
258 "Your new rhodecode password",
258 "Your new rhodecode password",
259 'Your new rhodecode password:%s' % (new_passwd))
259 'Your new rhodecode password:%s' % (new_passwd))
260 log.info('send new password mail to %s', user_email)
260 log.info('send new password mail to %s', user_email)
261
261
262
262
263 except:
263 except:
264 log.error('Failed to update user password')
264 log.error('Failed to update user password')
265 log.error(traceback.format_exc())
265 log.error(traceback.format_exc())
266 return True
266 return True
267
267
268 @task
268 @task
269 def send_email(recipients, subject, body):
269 def send_email(recipients, subject, body):
270 log = send_email.get_logger()
270 log = send_email.get_logger()
271 email_config = dict(config.items('DEFAULT'))
271 email_config = dict(config.items('DEFAULT'))
272 mail_from = email_config.get('app_email_from')
272 mail_from = email_config.get('app_email_from')
273 user = email_config.get('smtp_username')
273 user = email_config.get('smtp_username')
274 passwd = email_config.get('smtp_password')
274 passwd = email_config.get('smtp_password')
275 mail_server = email_config.get('smtp_server')
275 mail_server = email_config.get('smtp_server')
276 mail_port = email_config.get('smtp_port')
276 mail_port = email_config.get('smtp_port')
277 tls = email_config.get('smtp_use_tls')
277 tls = email_config.get('smtp_use_tls')
278 ssl = False
278 ssl = False
279
279
280 try:
280 try:
281 m = SmtpMailer(mail_from, user, passwd, mail_server,
281 m = SmtpMailer(mail_from, user, passwd, mail_server,
282 mail_port, ssl, tls)
282 mail_port, ssl, tls)
283 m.send(recipients, subject, body)
283 m.send(recipients, subject, body)
284 except:
284 except:
285 log.error('Mail sending failed')
285 log.error('Mail sending failed')
286 log.error(traceback.format_exc())
286 log.error(traceback.format_exc())
287 return False
287 return False
288 return True
288 return True
289
289
290 @task
290 @task
291 def create_repo_fork(form_data, cur_user):
291 def create_repo_fork(form_data, cur_user):
292 import os
293 from rhodecode.model.repo import RepoModel
292 from rhodecode.model.repo import RepoModel
294
293 from vcs import get_backend
294 log = create_repo_fork.get_logger()
295 repo_model = RepoModel(get_session())
295 repo_model = RepoModel(get_session())
296 repo_model.create(form_data, cur_user, just_db=True, fork=True)
296 repo_model.create(form_data, cur_user, just_db=True, fork=True)
297
297 repo_name = form_data['repo_name']
298 repos_path = get_hg_ui_settings()['paths_root_path'].replace('*', '')
298 repos_path = get_hg_ui_settings()['paths_root_path']
299 repo_path = os.path.join(repos_path, form_data['repo_name'])
299 repo_path = os.path.join(repos_path, repo_name)
300 repo_fork_path = os.path.join(repos_path, form_data['fork_name'])
300 repo_fork_path = os.path.join(repos_path, form_data['fork_name'])
301 alias = form_data['repo_type']
301
302
302 MercurialRepository(str(repo_fork_path), True, clone_url=str(repo_path))
303 log.info('creating repo fork %s as %s', repo_name, repo_path)
303
304 backend = get_backend(alias)
305 backend(str(repo_fork_path), create=True, src_url=str(repo_path))
304
306
305 def __get_codes_stats(repo_name):
307 def __get_codes_stats(repo_name):
306 LANGUAGES_EXTENSIONS = ['action', 'adp', 'ashx', 'asmx',
308 LANGUAGES_EXTENSIONS = ['action', 'adp', 'ashx', 'asmx',
307 'aspx', 'asx', 'axd', 'c', 'cfg', 'cfm', 'cpp', 'cs', 'diff', 'do', 'el',
309 'aspx', 'asx', 'axd', 'c', 'cfg', 'cfm', 'cpp', 'cs', 'diff', 'do', 'el',
308 'erl', 'h', 'java', 'js', 'jsp', 'jspx', 'lisp', 'lua', 'm', 'mako', 'ml',
310 'erl', 'h', 'java', 'js', 'jsp', 'jspx', 'lisp', 'lua', 'm', 'mako', 'ml',
309 'pas', 'patch', 'php', 'php3', 'php4', 'phtml', 'pm', 'py', 'rb', 'rst',
311 'pas', 'patch', 'php', 'php3', 'php4', 'phtml', 'pm', 'py', 'rb', 'rst',
310 's', 'sh', 'tpl', 'txt', 'vim', 'wss', 'xhtml', 'xml', 'xsl', 'xslt', 'yaws']
312 's', 'sh', 'tpl', 'txt', 'vim', 'wss', 'xhtml', 'xml', 'xsl', 'xslt', 'yaws']
311
313
312
314
313 repos_path = get_hg_ui_settings()['paths_root_path']
315 repos_path = get_hg_ui_settings()['paths_root_path']
314 p = os.path.join(repos_path, repo_name)
316 p = os.path.join(repos_path, repo_name)
315 repo = get_repo(p)
317 repo = get_repo(p)
316 tip = repo.get_changeset()
318 tip = repo.get_changeset()
317 code_stats = {}
319 code_stats = {}
318
320
319 def aggregate(cs):
321 def aggregate(cs):
320 for f in cs[2]:
322 for f in cs[2]:
321 k = f.mimetype
323 k = f.mimetype
322 if f.extension in LANGUAGES_EXTENSIONS:
324 if f.extension in LANGUAGES_EXTENSIONS:
323 if code_stats.has_key(k):
325 if code_stats.has_key(k):
324 code_stats[k] += 1
326 code_stats[k] += 1
325 else:
327 else:
326 code_stats[k] = 1
328 code_stats[k] = 1
327
329
328 map(aggregate, tip.walk('/'))
330 map(aggregate, tip.walk('/'))
329
331
330 return code_stats or {}
332 return code_stats or {}
331
333
332
334
333
335
334
336
@@ -1,537 +1,538 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 # Utilities for RhodeCode
3 # Utilities for RhodeCode
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 # This program is free software; you can redistribute it and/or
5 # This program is free software; you can redistribute it and/or
6 # modify it under the terms of the GNU General Public License
6 # modify it under the terms of the GNU General Public License
7 # as published by the Free Software Foundation; version 2
7 # as published by the Free Software Foundation; version 2
8 # of the License or (at your opinion) any later version of the license.
8 # of the License or (at your opinion) any later version of the license.
9 #
9 #
10 # This program is distributed in the hope that it will be useful,
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
13 # GNU General Public License for more details.
14 #
14 #
15 # You should have received a copy of the GNU General Public License
15 # You should have received a copy of the GNU General Public License
16 # along with this program; if not, write to the Free Software
16 # along with this program; if not, write to the Free Software
17 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
17 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18 # MA 02110-1301, USA.
18 # MA 02110-1301, USA.
19 """
19 """
20 Created on April 18, 2010
20 Created on April 18, 2010
21 Utilities for RhodeCode
21 Utilities for RhodeCode
22 @author: marcink
22 @author: marcink
23 """
23 """
24
24
25 from UserDict import DictMixin
25 from UserDict import DictMixin
26 from mercurial import ui, config, hg
26 from mercurial import ui, config, hg
27 from mercurial.error import RepoError
27 from mercurial.error import RepoError
28 from rhodecode.model import meta
28 from rhodecode.model import meta
29 from rhodecode.model.caching_query import FromCache
29 from rhodecode.model.caching_query import FromCache
30 from rhodecode.model.db import Repository, User, RhodeCodeUi, RhodeCodeSettings, \
30 from rhodecode.model.db import Repository, User, RhodeCodeUi, RhodeCodeSettings, \
31 UserLog
31 UserLog
32 from rhodecode.model.repo import RepoModel
32 from rhodecode.model.repo import RepoModel
33 from rhodecode.model.user import UserModel
33 from rhodecode.model.user import UserModel
34 from vcs.backends.base import BaseChangeset
34 from vcs.backends.base import BaseChangeset
35 from vcs.backends.git import GitRepository
35 from vcs.backends.git import GitRepository
36 from vcs.backends.hg import MercurialRepository
36 from vcs.backends.hg import MercurialRepository
37 from vcs.utils.lazy import LazyProperty
37 from vcs.utils.lazy import LazyProperty
38 import traceback
38 import traceback
39 import datetime
39 import datetime
40 import logging
40 import logging
41 import os
41 import os
42
42
43 log = logging.getLogger(__name__)
43 log = logging.getLogger(__name__)
44
44
45
45
46 def get_repo_slug(request):
46 def get_repo_slug(request):
47 return request.environ['pylons.routes_dict'].get('repo_name')
47 return request.environ['pylons.routes_dict'].get('repo_name')
48
48
49 def is_mercurial(environ):
49 def is_mercurial(environ):
50 """
50 """
51 Returns True if request's target is mercurial server - header
51 Returns True if request's target is mercurial server - header
52 ``HTTP_ACCEPT`` of such request would start with ``application/mercurial``.
52 ``HTTP_ACCEPT`` of such request would start with ``application/mercurial``.
53 """
53 """
54 http_accept = environ.get('HTTP_ACCEPT')
54 http_accept = environ.get('HTTP_ACCEPT')
55 if http_accept and http_accept.startswith('application/mercurial'):
55 if http_accept and http_accept.startswith('application/mercurial'):
56 return True
56 return True
57 return False
57 return False
58
58
59 def is_git(environ):
59 def is_git(environ):
60 """
60 """
61 Returns True if request's target is git server. ``HTTP_USER_AGENT`` would
61 Returns True if request's target is git server. ``HTTP_USER_AGENT`` would
62 then have git client version given.
62 then have git client version given.
63
63
64 :param environ:
64 :param environ:
65 """
65 """
66 http_user_agent = environ.get('HTTP_USER_AGENT')
66 http_user_agent = environ.get('HTTP_USER_AGENT')
67 if http_user_agent.startswith('git'):
67 if http_user_agent.startswith('git'):
68 return True
68 return True
69 return False
69 return False
70
70
71 def action_logger(user, action, repo, ipaddr, sa=None):
71 def action_logger(user, action, repo, ipaddr, sa=None):
72 """
72 """
73 Action logger for various action made by users
73 Action logger for various action made by users
74 """
74 """
75
75
76 if not sa:
76 if not sa:
77 sa = meta.Session()
77 sa = meta.Session()
78
78
79 try:
79 try:
80 if hasattr(user, 'user_id'):
80 if hasattr(user, 'user_id'):
81 user_obj = user
81 user_obj = user
82 elif isinstance(user, basestring):
82 elif isinstance(user, basestring):
83 user_obj = UserModel(sa).get_by_username(user, cache=False)
83 user_obj = UserModel(sa).get_by_username(user, cache=False)
84 else:
84 else:
85 raise Exception('You have to provide user object or username')
85 raise Exception('You have to provide user object or username')
86
86
87 repo_name = repo.lstrip('/')
87 repo_name = repo.lstrip('/')
88 user_log = UserLog()
88 user_log = UserLog()
89 user_log.user_id = user_obj.user_id
89 user_log.user_id = user_obj.user_id
90 user_log.action = action
90 user_log.action = action
91 user_log.repository_name = repo_name
91 user_log.repository_name = repo_name
92 user_log.repository = RepoModel(sa).get(repo_name, cache=False)
92 user_log.repository = RepoModel(sa).get(repo_name, cache=False)
93 user_log.action_date = datetime.datetime.now()
93 user_log.action_date = datetime.datetime.now()
94 user_log.user_ip = ipaddr
94 user_log.user_ip = ipaddr
95 sa.add(user_log)
95 sa.add(user_log)
96 sa.commit()
96 sa.commit()
97
97
98 log.info('Adding user %s, action %s on %s',
98 log.info('Adding user %s, action %s on %s',
99 user_obj.username, action, repo)
99 user_obj.username, action, repo)
100 except:
100 except:
101 log.error(traceback.format_exc())
101 log.error(traceback.format_exc())
102 sa.rollback()
102 sa.rollback()
103
103
104 def get_repos(path, recursive=False, initial=False):
104 def get_repos(path, recursive=False, initial=False):
105 """
105 """
106 Scans given path for repos and return (name,(type,path)) tuple
106 Scans given path for repos and return (name,(type,path)) tuple
107 :param prefix:
107 :param prefix:
108 :param path:
108 :param path:
109 :param recursive:
109 :param recursive:
110 :param initial:
110 :param initial:
111 """
111 """
112 from vcs.utils.helpers import get_scm
112 from vcs.utils.helpers import get_scm
113 from vcs.exceptions import VCSError
113 from vcs.exceptions import VCSError
114
114
115 try:
115 try:
116 scm = get_scm(path)
116 scm = get_scm(path)
117 except:
117 except:
118 pass
118 pass
119 else:
119 else:
120 raise Exception('The given path %s should not be a repository got %s',
120 raise Exception('The given path %s should not be a repository got %s',
121 path, scm)
121 path, scm)
122
122
123 for dirpath in os.listdir(path):
123 for dirpath in os.listdir(path):
124 try:
124 try:
125 yield dirpath, get_scm(os.path.join(path, dirpath))
125 yield dirpath, get_scm(os.path.join(path, dirpath))
126 except VCSError:
126 except VCSError:
127 pass
127 pass
128
128
129 if __name__ == '__main__':
129 if __name__ == '__main__':
130 get_repos('', '/home/marcink/workspace-python')
130 get_repos('', '/home/marcink/workspace-python')
131
131
132
132
133 def check_repo_fast(repo_name, base_path):
133 def check_repo_fast(repo_name, base_path):
134 if os.path.isdir(os.path.join(base_path, repo_name)):return False
134 if os.path.isdir(os.path.join(base_path, repo_name)):return False
135 return True
135 return True
136
136
137 def check_repo(repo_name, base_path, verify=True):
137 def check_repo(repo_name, base_path, verify=True):
138
138
139 repo_path = os.path.join(base_path, repo_name)
139 repo_path = os.path.join(base_path, repo_name)
140
140
141 try:
141 try:
142 if not check_repo_fast(repo_name, base_path):
142 if not check_repo_fast(repo_name, base_path):
143 return False
143 return False
144 r = hg.repository(ui.ui(), repo_path)
144 r = hg.repository(ui.ui(), repo_path)
145 if verify:
145 if verify:
146 hg.verify(r)
146 hg.verify(r)
147 #here we hnow that repo exists it was verified
147 #here we hnow that repo exists it was verified
148 log.info('%s repo is already created', repo_name)
148 log.info('%s repo is already created', repo_name)
149 return False
149 return False
150 except RepoError:
150 except RepoError:
151 #it means that there is no valid repo there...
151 #it means that there is no valid repo there...
152 log.info('%s repo is free for creation', repo_name)
152 log.info('%s repo is free for creation', repo_name)
153 return True
153 return True
154
154
155 def ask_ok(prompt, retries=4, complaint='Yes or no, please!'):
155 def ask_ok(prompt, retries=4, complaint='Yes or no, please!'):
156 while True:
156 while True:
157 ok = raw_input(prompt)
157 ok = raw_input(prompt)
158 if ok in ('y', 'ye', 'yes'): return True
158 if ok in ('y', 'ye', 'yes'): return True
159 if ok in ('n', 'no', 'nop', 'nope'): return False
159 if ok in ('n', 'no', 'nop', 'nope'): return False
160 retries = retries - 1
160 retries = retries - 1
161 if retries < 0: raise IOError
161 if retries < 0: raise IOError
162 print complaint
162 print complaint
163
163
164 def get_hg_ui_cached():
164 def get_hg_ui_cached():
165 try:
165 try:
166 sa = meta.Session
166 sa = meta.Session
167 ret = sa.query(RhodeCodeUi)\
167 ret = sa.query(RhodeCodeUi)\
168 .options(FromCache("sql_cache_short", "get_hg_ui_settings"))\
168 .options(FromCache("sql_cache_short", "get_hg_ui_settings"))\
169 .all()
169 .all()
170 except:
170 except:
171 pass
171 pass
172 finally:
172 finally:
173 meta.Session.remove()
173 meta.Session.remove()
174 return ret
174 return ret
175
175
176
176
177 def get_hg_settings():
177 def get_hg_settings():
178 try:
178 try:
179 sa = meta.Session()
179 sa = meta.Session()
180 ret = sa.query(RhodeCodeSettings)\
180 ret = sa.query(RhodeCodeSettings)\
181 .options(FromCache("sql_cache_short", "get_hg_settings"))\
181 .options(FromCache("sql_cache_short", "get_hg_settings"))\
182 .all()
182 .all()
183 except:
183 except:
184 pass
184 pass
185 finally:
185 finally:
186 meta.Session.remove()
186 meta.Session.remove()
187
187
188 if not ret:
188 if not ret:
189 raise Exception('Could not get application settings !')
189 raise Exception('Could not get application settings !')
190 settings = {}
190 settings = {}
191 for each in ret:
191 for each in ret:
192 settings['rhodecode_' + each.app_settings_name] = each.app_settings_value
192 settings['rhodecode_' + each.app_settings_name] = each.app_settings_value
193
193
194 return settings
194 return settings
195
195
196 def get_hg_ui_settings():
196 def get_hg_ui_settings():
197 try:
197 try:
198 sa = meta.Session()
198 sa = meta.Session()
199 ret = sa.query(RhodeCodeUi).all()
199 ret = sa.query(RhodeCodeUi).all()
200 except:
200 except:
201 pass
201 pass
202 finally:
202 finally:
203 meta.Session.remove()
203 meta.Session.remove()
204
204
205 if not ret:
205 if not ret:
206 raise Exception('Could not get application ui settings !')
206 raise Exception('Could not get application ui settings !')
207 settings = {}
207 settings = {}
208 for each in ret:
208 for each in ret:
209 k = each.ui_key
209 k = each.ui_key
210 v = each.ui_value
210 v = each.ui_value
211 if k == '/':
211 if k == '/':
212 k = 'root_path'
212 k = 'root_path'
213
213
214 if k.find('.') != -1:
214 if k.find('.') != -1:
215 k = k.replace('.', '_')
215 k = k.replace('.', '_')
216
216
217 if each.ui_section == 'hooks':
217 if each.ui_section == 'hooks':
218 v = each.ui_active
218 v = each.ui_active
219
219
220 settings[each.ui_section + '_' + k] = v
220 settings[each.ui_section + '_' + k] = v
221
221
222 return settings
222 return settings
223
223
224 #propagated from mercurial documentation
224 #propagated from mercurial documentation
225 ui_sections = ['alias', 'auth',
225 ui_sections = ['alias', 'auth',
226 'decode/encode', 'defaults',
226 'decode/encode', 'defaults',
227 'diff', 'email',
227 'diff', 'email',
228 'extensions', 'format',
228 'extensions', 'format',
229 'merge-patterns', 'merge-tools',
229 'merge-patterns', 'merge-tools',
230 'hooks', 'http_proxy',
230 'hooks', 'http_proxy',
231 'smtp', 'patch',
231 'smtp', 'patch',
232 'paths', 'profiling',
232 'paths', 'profiling',
233 'server', 'trusted',
233 'server', 'trusted',
234 'ui', 'web', ]
234 'ui', 'web', ]
235
235
236 def make_ui(read_from='file', path=None, checkpaths=True):
236 def make_ui(read_from='file', path=None, checkpaths=True):
237 """
237 """
238 A function that will read python rc files or database
238 A function that will read python rc files or database
239 and make an mercurial ui object from read options
239 and make an mercurial ui object from read options
240
240
241 :param path: path to mercurial config file
241 :param path: path to mercurial config file
242 :param checkpaths: check the path
242 :param checkpaths: check the path
243 :param read_from: read from 'file' or 'db'
243 :param read_from: read from 'file' or 'db'
244 """
244 """
245
245
246 baseui = ui.ui()
246 baseui = ui.ui()
247
247
248 if read_from == 'file':
248 if read_from == 'file':
249 if not os.path.isfile(path):
249 if not os.path.isfile(path):
250 log.warning('Unable to read config file %s' % path)
250 log.warning('Unable to read config file %s' % path)
251 return False
251 return False
252 log.debug('reading hgrc from %s', path)
252 log.debug('reading hgrc from %s', path)
253 cfg = config.config()
253 cfg = config.config()
254 cfg.read(path)
254 cfg.read(path)
255 for section in ui_sections:
255 for section in ui_sections:
256 for k, v in cfg.items(section):
256 for k, v in cfg.items(section):
257 baseui.setconfig(section, k, v)
257 baseui.setconfig(section, k, v)
258 log.debug('settings ui from file[%s]%s:%s', section, k, v)
258 log.debug('settings ui from file[%s]%s:%s', section, k, v)
259
259
260 elif read_from == 'db':
260 elif read_from == 'db':
261 hg_ui = get_hg_ui_cached()
261 hg_ui = get_hg_ui_cached()
262 for ui_ in hg_ui:
262 for ui_ in hg_ui:
263 if ui_.ui_active:
263 if ui_.ui_active:
264 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section, ui_.ui_key, ui_.ui_value)
264 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section, ui_.ui_key, ui_.ui_value)
265 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
265 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
266
266
267
267
268 return baseui
268 return baseui
269
269
270
270
271 def set_rhodecode_config(config):
271 def set_rhodecode_config(config):
272 hgsettings = get_hg_settings()
272 hgsettings = get_hg_settings()
273
273
274 for k, v in hgsettings.items():
274 for k, v in hgsettings.items():
275 config[k] = v
275 config[k] = v
276
276
277 def invalidate_cache(name, *args):
277 def invalidate_cache(name, *args):
278 """Invalidates given name cache"""
278 """Invalidates given name cache"""
279
279
280 from beaker.cache import region_invalidate
280 from beaker.cache import region_invalidate
281 log.info('INVALIDATING CACHE FOR %s', name)
281 log.info('INVALIDATING CACHE FOR %s', name)
282
282
283 """propagate our arguments to make sure invalidation works. First
283 """propagate our arguments to make sure invalidation works. First
284 argument has to be the name of cached func name give to cache decorator
284 argument has to be the name of cached func name give to cache decorator
285 without that the invalidation would not work"""
285 without that the invalidation would not work"""
286 tmp = [name]
286 tmp = [name]
287 tmp.extend(args)
287 tmp.extend(args)
288 args = tuple(tmp)
288 args = tuple(tmp)
289
289
290 if name == 'cached_repo_list':
290 if name == 'cached_repo_list':
291 from rhodecode.model.hg import _get_repos_cached
291 from rhodecode.model.hg import _get_repos_cached
292 region_invalidate(_get_repos_cached, None, *args)
292 region_invalidate(_get_repos_cached, None, *args)
293
293
294 if name == 'full_changelog':
294 if name == 'full_changelog':
295 from rhodecode.model.hg import _full_changelog_cached
295 from rhodecode.model.hg import _full_changelog_cached
296 region_invalidate(_full_changelog_cached, None, *args)
296 region_invalidate(_full_changelog_cached, None, *args)
297
297
298 class EmptyChangeset(BaseChangeset):
298 class EmptyChangeset(BaseChangeset):
299 """
299 """
300 An dummy empty changeset. It's possible to pass hash when creating
300 An dummy empty changeset. It's possible to pass hash when creating
301 an EmptyChangeset
301 an EmptyChangeset
302 """
302 """
303
303
304 def __init__(self, cs='0' * 40):
304 def __init__(self, cs='0' * 40):
305 self._empty_cs = cs
305 self._empty_cs = cs
306 self.revision = -1
306 self.revision = -1
307 self.message = ''
307 self.message = ''
308 self.author = ''
308 self.author = ''
309 self.date = ''
309 self.date = ''
310
310
311 @LazyProperty
311 @LazyProperty
312 def raw_id(self):
312 def raw_id(self):
313 """
313 """
314 Returns raw string identifying this changeset, useful for web
314 Returns raw string identifying this changeset, useful for web
315 representation.
315 representation.
316 """
316 """
317 return self._empty_cs
317 return self._empty_cs
318
318
319 @LazyProperty
319 @LazyProperty
320 def short_id(self):
320 def short_id(self):
321 return self.raw_id[:12]
321 return self.raw_id[:12]
322
322
323 def get_file_changeset(self, path):
323 def get_file_changeset(self, path):
324 return self
324 return self
325
325
326 def get_file_content(self, path):
326 def get_file_content(self, path):
327 return u''
327 return u''
328
328
329 def get_file_size(self, path):
329 def get_file_size(self, path):
330 return 0
330 return 0
331
331
332 def repo2db_mapper(initial_repo_list, remove_obsolete=False):
332 def repo2db_mapper(initial_repo_list, remove_obsolete=False):
333 """
333 """
334 maps all found repositories into db
334 maps all found repositories into db
335 """
335 """
336
336
337 sa = meta.Session()
337 sa = meta.Session()
338 rm = RepoModel(sa)
338 rm = RepoModel(sa)
339 user = sa.query(User).filter(User.admin == True).first()
339 user = sa.query(User).filter(User.admin == True).first()
340
340
341 for name, repo in initial_repo_list.items():
341 for name, repo in initial_repo_list.items():
342 if not rm.get(name, cache=False):
342 if not rm.get(name, cache=False):
343 log.info('repository %s not found creating default', name)
343 log.info('repository %s not found creating default', name)
344
344
345 form_data = {
345 form_data = {
346 'repo_name':name,
346 'repo_name':name,
347 'repo_type':repo.alias,
347 'repo_type':repo.alias,
348 'description':repo.description if repo.description != 'unknown' else \
348 'description':repo.description \
349 'auto description for %s' % name,
349 if repo.description != 'unknown' else \
350 '%s repository' % name,
350 'private':False
351 'private':False
351 }
352 }
352 rm.create(form_data, user, just_db=True)
353 rm.create(form_data, user, just_db=True)
353
354
354
355
355 if remove_obsolete:
356 if remove_obsolete:
356 #remove from database those repositories that are not in the filesystem
357 #remove from database those repositories that are not in the filesystem
357 for repo in sa.query(Repository).all():
358 for repo in sa.query(Repository).all():
358 if repo.repo_name not in initial_repo_list.keys():
359 if repo.repo_name not in initial_repo_list.keys():
359 sa.delete(repo)
360 sa.delete(repo)
360 sa.commit()
361 sa.commit()
361
362
362
363
363 meta.Session.remove()
364 meta.Session.remove()
364
365
365
366
366 class OrderedDict(dict, DictMixin):
367 class OrderedDict(dict, DictMixin):
367
368
368 def __init__(self, *args, **kwds):
369 def __init__(self, *args, **kwds):
369 if len(args) > 1:
370 if len(args) > 1:
370 raise TypeError('expected at most 1 arguments, got %d' % len(args))
371 raise TypeError('expected at most 1 arguments, got %d' % len(args))
371 try:
372 try:
372 self.__end
373 self.__end
373 except AttributeError:
374 except AttributeError:
374 self.clear()
375 self.clear()
375 self.update(*args, **kwds)
376 self.update(*args, **kwds)
376
377
377 def clear(self):
378 def clear(self):
378 self.__end = end = []
379 self.__end = end = []
379 end += [None, end, end] # sentinel node for doubly linked list
380 end += [None, end, end] # sentinel node for doubly linked list
380 self.__map = {} # key --> [key, prev, next]
381 self.__map = {} # key --> [key, prev, next]
381 dict.clear(self)
382 dict.clear(self)
382
383
383 def __setitem__(self, key, value):
384 def __setitem__(self, key, value):
384 if key not in self:
385 if key not in self:
385 end = self.__end
386 end = self.__end
386 curr = end[1]
387 curr = end[1]
387 curr[2] = end[1] = self.__map[key] = [key, curr, end]
388 curr[2] = end[1] = self.__map[key] = [key, curr, end]
388 dict.__setitem__(self, key, value)
389 dict.__setitem__(self, key, value)
389
390
390 def __delitem__(self, key):
391 def __delitem__(self, key):
391 dict.__delitem__(self, key)
392 dict.__delitem__(self, key)
392 key, prev, next = self.__map.pop(key)
393 key, prev, next = self.__map.pop(key)
393 prev[2] = next
394 prev[2] = next
394 next[1] = prev
395 next[1] = prev
395
396
396 def __iter__(self):
397 def __iter__(self):
397 end = self.__end
398 end = self.__end
398 curr = end[2]
399 curr = end[2]
399 while curr is not end:
400 while curr is not end:
400 yield curr[0]
401 yield curr[0]
401 curr = curr[2]
402 curr = curr[2]
402
403
403 def __reversed__(self):
404 def __reversed__(self):
404 end = self.__end
405 end = self.__end
405 curr = end[1]
406 curr = end[1]
406 while curr is not end:
407 while curr is not end:
407 yield curr[0]
408 yield curr[0]
408 curr = curr[1]
409 curr = curr[1]
409
410
410 def popitem(self, last=True):
411 def popitem(self, last=True):
411 if not self:
412 if not self:
412 raise KeyError('dictionary is empty')
413 raise KeyError('dictionary is empty')
413 if last:
414 if last:
414 key = reversed(self).next()
415 key = reversed(self).next()
415 else:
416 else:
416 key = iter(self).next()
417 key = iter(self).next()
417 value = self.pop(key)
418 value = self.pop(key)
418 return key, value
419 return key, value
419
420
420 def __reduce__(self):
421 def __reduce__(self):
421 items = [[k, self[k]] for k in self]
422 items = [[k, self[k]] for k in self]
422 tmp = self.__map, self.__end
423 tmp = self.__map, self.__end
423 del self.__map, self.__end
424 del self.__map, self.__end
424 inst_dict = vars(self).copy()
425 inst_dict = vars(self).copy()
425 self.__map, self.__end = tmp
426 self.__map, self.__end = tmp
426 if inst_dict:
427 if inst_dict:
427 return (self.__class__, (items,), inst_dict)
428 return (self.__class__, (items,), inst_dict)
428 return self.__class__, (items,)
429 return self.__class__, (items,)
429
430
430 def keys(self):
431 def keys(self):
431 return list(self)
432 return list(self)
432
433
433 setdefault = DictMixin.setdefault
434 setdefault = DictMixin.setdefault
434 update = DictMixin.update
435 update = DictMixin.update
435 pop = DictMixin.pop
436 pop = DictMixin.pop
436 values = DictMixin.values
437 values = DictMixin.values
437 items = DictMixin.items
438 items = DictMixin.items
438 iterkeys = DictMixin.iterkeys
439 iterkeys = DictMixin.iterkeys
439 itervalues = DictMixin.itervalues
440 itervalues = DictMixin.itervalues
440 iteritems = DictMixin.iteritems
441 iteritems = DictMixin.iteritems
441
442
442 def __repr__(self):
443 def __repr__(self):
443 if not self:
444 if not self:
444 return '%s()' % (self.__class__.__name__,)
445 return '%s()' % (self.__class__.__name__,)
445 return '%s(%r)' % (self.__class__.__name__, self.items())
446 return '%s(%r)' % (self.__class__.__name__, self.items())
446
447
447 def copy(self):
448 def copy(self):
448 return self.__class__(self)
449 return self.__class__(self)
449
450
450 @classmethod
451 @classmethod
451 def fromkeys(cls, iterable, value=None):
452 def fromkeys(cls, iterable, value=None):
452 d = cls()
453 d = cls()
453 for key in iterable:
454 for key in iterable:
454 d[key] = value
455 d[key] = value
455 return d
456 return d
456
457
457 def __eq__(self, other):
458 def __eq__(self, other):
458 if isinstance(other, OrderedDict):
459 if isinstance(other, OrderedDict):
459 return len(self) == len(other) and self.items() == other.items()
460 return len(self) == len(other) and self.items() == other.items()
460 return dict.__eq__(self, other)
461 return dict.__eq__(self, other)
461
462
462 def __ne__(self, other):
463 def __ne__(self, other):
463 return not self == other
464 return not self == other
464
465
465
466
466 #===============================================================================
467 #===============================================================================
467 # TEST FUNCTIONS AND CREATORS
468 # TEST FUNCTIONS AND CREATORS
468 #===============================================================================
469 #===============================================================================
469 def create_test_index(repo_location, full_index):
470 def create_test_index(repo_location, full_index):
470 """Makes default test index
471 """Makes default test index
471 :param repo_location:
472 :param repo_location:
472 :param full_index:
473 :param full_index:
473 """
474 """
474 from rhodecode.lib.indexers.daemon import WhooshIndexingDaemon
475 from rhodecode.lib.indexers.daemon import WhooshIndexingDaemon
475 from rhodecode.lib.pidlock import DaemonLock, LockHeld
476 from rhodecode.lib.pidlock import DaemonLock, LockHeld
476 from rhodecode.lib.indexers import IDX_LOCATION
477 from rhodecode.lib.indexers import IDX_LOCATION
477 import shutil
478 import shutil
478
479
479 if os.path.exists(IDX_LOCATION):
480 if os.path.exists(IDX_LOCATION):
480 shutil.rmtree(IDX_LOCATION)
481 shutil.rmtree(IDX_LOCATION)
481
482
482 try:
483 try:
483 l = DaemonLock()
484 l = DaemonLock()
484 WhooshIndexingDaemon(repo_location=repo_location)\
485 WhooshIndexingDaemon(repo_location=repo_location)\
485 .run(full_index=full_index)
486 .run(full_index=full_index)
486 l.release()
487 l.release()
487 except LockHeld:
488 except LockHeld:
488 pass
489 pass
489
490
490 def create_test_env(repos_test_path, config):
491 def create_test_env(repos_test_path, config):
491 """Makes a fresh database and
492 """Makes a fresh database and
492 install test repository into tmp dir
493 install test repository into tmp dir
493 """
494 """
494 from rhodecode.lib.db_manage import DbManage
495 from rhodecode.lib.db_manage import DbManage
495 import tarfile
496 import tarfile
496 import shutil
497 import shutil
497 from os.path import dirname as dn, join as jn, abspath
498 from os.path import dirname as dn, join as jn, abspath
498
499
499 log = logging.getLogger('TestEnvCreator')
500 log = logging.getLogger('TestEnvCreator')
500 # create logger
501 # create logger
501 log.setLevel(logging.DEBUG)
502 log.setLevel(logging.DEBUG)
502 log.propagate = True
503 log.propagate = True
503 # create console handler and set level to debug
504 # create console handler and set level to debug
504 ch = logging.StreamHandler()
505 ch = logging.StreamHandler()
505 ch.setLevel(logging.DEBUG)
506 ch.setLevel(logging.DEBUG)
506
507
507 # create formatter
508 # create formatter
508 formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
509 formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
509
510
510 # add formatter to ch
511 # add formatter to ch
511 ch.setFormatter(formatter)
512 ch.setFormatter(formatter)
512
513
513 # add ch to logger
514 # add ch to logger
514 log.addHandler(ch)
515 log.addHandler(ch)
515
516
516 #PART ONE create db
517 #PART ONE create db
517 dbname = config['sqlalchemy.db1.url'].split('/')[-1]
518 dbname = config['sqlalchemy.db1.url'].split('/')[-1]
518 log.debug('making test db %s', dbname)
519 log.debug('making test db %s', dbname)
519
520
520 dbmanage = DbManage(log_sql=True, dbname=dbname, root=config['here'],
521 dbmanage = DbManage(log_sql=True, dbname=dbname, root=config['here'],
521 tests=True)
522 tests=True)
522 dbmanage.create_tables(override=True)
523 dbmanage.create_tables(override=True)
523 dbmanage.config_prompt(repos_test_path)
524 dbmanage.config_prompt(repos_test_path)
524 dbmanage.create_default_user()
525 dbmanage.create_default_user()
525 dbmanage.admin_prompt()
526 dbmanage.admin_prompt()
526 dbmanage.create_permissions()
527 dbmanage.create_permissions()
527 dbmanage.populate_default_permissions()
528 dbmanage.populate_default_permissions()
528
529
529 #PART TWO make test repo
530 #PART TWO make test repo
530 log.debug('making test vcs repo')
531 log.debug('making test vcs repo')
531 if os.path.isdir('/tmp/vcs_test'):
532 if os.path.isdir('/tmp/vcs_test'):
532 shutil.rmtree('/tmp/vcs_test')
533 shutil.rmtree('/tmp/vcs_test')
533
534
534 cur_dir = dn(dn(abspath(__file__)))
535 cur_dir = dn(dn(abspath(__file__)))
535 tar = tarfile.open(jn(cur_dir, 'tests', "vcs_test.tar.gz"))
536 tar = tarfile.open(jn(cur_dir, 'tests', "vcs_test.tar.gz"))
536 tar.extractall('/tmp')
537 tar.extractall('/tmp')
537 tar.close()
538 tar.close()
@@ -1,353 +1,363 b''
1 """ this is forms validation classes
1 """ this is forms validation classes
2 http://formencode.org/module-formencode.validators.html
2 http://formencode.org/module-formencode.validators.html
3 for list off all availible validators
3 for list off all availible validators
4
4
5 we can create our own validators
5 we can create our own validators
6
6
7 The table below outlines the options which can be used in a schema in addition to the validators themselves
7 The table below outlines the options which can be used in a schema in addition to the validators themselves
8 pre_validators [] These validators will be applied before the schema
8 pre_validators [] These validators will be applied before the schema
9 chained_validators [] These validators will be applied after the schema
9 chained_validators [] These validators will be applied after the schema
10 allow_extra_fields False If True, then it is not an error when keys that aren't associated with a validator are present
10 allow_extra_fields False If True, then it is not an error when keys that aren't associated with a validator are present
11 filter_extra_fields False If True, then keys that aren't associated with a validator are removed
11 filter_extra_fields False If True, then keys that aren't associated with a validator are removed
12 if_key_missing NoDefault If this is given, then any keys that aren't available but are expected will be replaced with this value (and then validated). This does not override a present .if_missing attribute on validators. NoDefault is a special FormEncode class to mean that no default values has been specified and therefore missing keys shouldn't take a default value.
12 if_key_missing NoDefault If this is given, then any keys that aren't available but are expected will be replaced with this value (and then validated). This does not override a present .if_missing attribute on validators. NoDefault is a special FormEncode class to mean that no default values has been specified and therefore missing keys shouldn't take a default value.
13 ignore_key_missing False If True, then missing keys will be missing in the result, if the validator doesn't have .if_missing on it already
13 ignore_key_missing False If True, then missing keys will be missing in the result, if the validator doesn't have .if_missing on it already
14
14
15
15
16 <name> = formencode.validators.<name of validator>
16 <name> = formencode.validators.<name of validator>
17 <name> must equal form name
17 <name> must equal form name
18 list=[1,2,3,4,5]
18 list=[1,2,3,4,5]
19 for SELECT use formencode.All(OneOf(list), Int())
19 for SELECT use formencode.All(OneOf(list), Int())
20
20
21 """
21 """
22 from formencode import All
22 from formencode import All
23 from formencode.validators import UnicodeString, OneOf, Int, Number, Regex, \
23 from formencode.validators import UnicodeString, OneOf, Int, Number, Regex, \
24 Email, Bool, StringBoolean
24 Email, Bool, StringBoolean
25 from pylons import session
25 from pylons import session
26 from pylons.i18n.translation import _
26 from pylons.i18n.translation import _
27 from rhodecode.lib.auth import check_password, get_crypt_password
27 from rhodecode.lib.auth import check_password, get_crypt_password
28 from rhodecode.model import meta
28 from rhodecode.model import meta
29 from rhodecode.model.user import UserModel
29 from rhodecode.model.user import UserModel
30 from rhodecode.model.repo import RepoModel
30 from rhodecode.model.repo import RepoModel
31 from rhodecode.model.db import User
31 from rhodecode.model.db import User
32 from webhelpers.pylonslib.secure_form import authentication_token
32 from webhelpers.pylonslib.secure_form import authentication_token
33 from vcs import BACKENDS
33 import formencode
34 import formencode
34 import logging
35 import logging
35 import os
36 import os
36 import rhodecode.lib.helpers as h
37 import rhodecode.lib.helpers as h
37
38
38 log = logging.getLogger(__name__)
39 log = logging.getLogger(__name__)
39
40
40 #this is needed to translate the messages using _() in validators
41 #this is needed to translate the messages using _() in validators
41 class State_obj(object):
42 class State_obj(object):
42 _ = staticmethod(_)
43 _ = staticmethod(_)
43
44
44 #===============================================================================
45 #===============================================================================
45 # VALIDATORS
46 # VALIDATORS
46 #===============================================================================
47 #===============================================================================
47 class ValidAuthToken(formencode.validators.FancyValidator):
48 class ValidAuthToken(formencode.validators.FancyValidator):
48 messages = {'invalid_token':_('Token mismatch')}
49 messages = {'invalid_token':_('Token mismatch')}
49
50
50 def validate_python(self, value, state):
51 def validate_python(self, value, state):
51
52
52 if value != authentication_token():
53 if value != authentication_token():
53 raise formencode.Invalid(self.message('invalid_token', state,
54 raise formencode.Invalid(self.message('invalid_token', state,
54 search_number=value), value, state)
55 search_number=value), value, state)
55
56
56 def ValidUsername(edit, old_data):
57 def ValidUsername(edit, old_data):
57 class _ValidUsername(formencode.validators.FancyValidator):
58 class _ValidUsername(formencode.validators.FancyValidator):
58
59
59 def validate_python(self, value, state):
60 def validate_python(self, value, state):
60 if value in ['default', 'new_user']:
61 if value in ['default', 'new_user']:
61 raise formencode.Invalid(_('Invalid username'), value, state)
62 raise formencode.Invalid(_('Invalid username'), value, state)
62 #check if user is unique
63 #check if user is unique
63 old_un = None
64 old_un = None
64 if edit:
65 if edit:
65 old_un = UserModel().get(old_data.get('user_id')).username
66 old_un = UserModel().get(old_data.get('user_id')).username
66
67
67 if old_un != value or not edit:
68 if old_un != value or not edit:
68 if UserModel().get_by_username(value, cache=False):
69 if UserModel().get_by_username(value, cache=False):
69 raise formencode.Invalid(_('This username already exists') ,
70 raise formencode.Invalid(_('This username already exists') ,
70 value, state)
71 value, state)
71
72
72 return _ValidUsername
73 return _ValidUsername
73
74
74 class ValidPassword(formencode.validators.FancyValidator):
75 class ValidPassword(formencode.validators.FancyValidator):
75
76
76 def to_python(self, value, state):
77 def to_python(self, value, state):
77 if value:
78 if value:
78 return get_crypt_password(value)
79 return get_crypt_password(value)
79
80
80 class ValidAuth(formencode.validators.FancyValidator):
81 class ValidAuth(formencode.validators.FancyValidator):
81 messages = {
82 messages = {
82 'invalid_password':_('invalid password'),
83 'invalid_password':_('invalid password'),
83 'invalid_login':_('invalid user name'),
84 'invalid_login':_('invalid user name'),
84 'disabled_account':_('Your acccount is disabled')
85 'disabled_account':_('Your acccount is disabled')
85
86
86 }
87 }
87 #error mapping
88 #error mapping
88 e_dict = {'username':messages['invalid_login'],
89 e_dict = {'username':messages['invalid_login'],
89 'password':messages['invalid_password']}
90 'password':messages['invalid_password']}
90 e_dict_disable = {'username':messages['disabled_account']}
91 e_dict_disable = {'username':messages['disabled_account']}
91
92
92 def validate_python(self, value, state):
93 def validate_python(self, value, state):
93 password = value['password']
94 password = value['password']
94 username = value['username']
95 username = value['username']
95 user = UserModel().get_by_username(username)
96 user = UserModel().get_by_username(username)
96 if user is None:
97 if user is None:
97 raise formencode.Invalid(self.message('invalid_password',
98 raise formencode.Invalid(self.message('invalid_password',
98 state=State_obj), value, state,
99 state=State_obj), value, state,
99 error_dict=self.e_dict)
100 error_dict=self.e_dict)
100 if user:
101 if user:
101 if user.active:
102 if user.active:
102 if user.username == username and check_password(password,
103 if user.username == username and check_password(password,
103 user.password):
104 user.password):
104 return value
105 return value
105 else:
106 else:
106 log.warning('user %s not authenticated', username)
107 log.warning('user %s not authenticated', username)
107 raise formencode.Invalid(self.message('invalid_password',
108 raise formencode.Invalid(self.message('invalid_password',
108 state=State_obj), value, state,
109 state=State_obj), value, state,
109 error_dict=self.e_dict)
110 error_dict=self.e_dict)
110 else:
111 else:
111 log.warning('user %s is disabled', username)
112 log.warning('user %s is disabled', username)
112 raise formencode.Invalid(self.message('disabled_account',
113 raise formencode.Invalid(self.message('disabled_account',
113 state=State_obj),
114 state=State_obj),
114 value, state,
115 value, state,
115 error_dict=self.e_dict_disable)
116 error_dict=self.e_dict_disable)
116
117
117 class ValidRepoUser(formencode.validators.FancyValidator):
118 class ValidRepoUser(formencode.validators.FancyValidator):
118
119
119 def to_python(self, value, state):
120 def to_python(self, value, state):
120 sa = meta.Session()
121 sa = meta.Session()
121 try:
122 try:
122 self.user_db = sa.query(User)\
123 self.user_db = sa.query(User)\
123 .filter(User.active == True)\
124 .filter(User.active == True)\
124 .filter(User.username == value).one()
125 .filter(User.username == value).one()
125 except Exception:
126 except Exception:
126 raise formencode.Invalid(_('This username is not valid'),
127 raise formencode.Invalid(_('This username is not valid'),
127 value, state)
128 value, state)
128 finally:
129 finally:
129 meta.Session.remove()
130 meta.Session.remove()
130
131
131 return self.user_db.user_id
132 return self.user_db.user_id
132
133
133 def ValidRepoName(edit, old_data):
134 def ValidRepoName(edit, old_data):
134 class _ValidRepoName(formencode.validators.FancyValidator):
135 class _ValidRepoName(formencode.validators.FancyValidator):
135
136
136 def to_python(self, value, state):
137 def to_python(self, value, state):
137 slug = h.repo_name_slug(value)
138 slug = h.repo_name_slug(value)
138 if slug in ['_admin']:
139 if slug in ['_admin']:
139 raise formencode.Invalid(_('This repository name is disallowed'),
140 raise formencode.Invalid(_('This repository name is disallowed'),
140 value, state)
141 value, state)
141 if old_data.get('repo_name') != value or not edit:
142 if old_data.get('repo_name') != value or not edit:
142 if RepoModel().get(slug, cache=False):
143 if RepoModel().get(slug, cache=False):
143 raise formencode.Invalid(_('This repository already exists') ,
144 raise formencode.Invalid(_('This repository already exists') ,
144 value, state)
145 value, state)
145 return slug
146 return slug
146
147
147
148
148 return _ValidRepoName
149 return _ValidRepoName
149
150
151 def ValidForkType(old_data):
152 class _ValidForkType(formencode.validators.FancyValidator):
153
154 def to_python(self, value, state):
155 if old_data['repo_type'] != value:
156 raise formencode.Invalid(_('Fork have to be the same type as original'), value, state)
157 return value
158 return _ValidForkType
159
150 class ValidPerms(formencode.validators.FancyValidator):
160 class ValidPerms(formencode.validators.FancyValidator):
151 messages = {'perm_new_user_name':_('This username is not valid')}
161 messages = {'perm_new_user_name':_('This username is not valid')}
152
162
153 def to_python(self, value, state):
163 def to_python(self, value, state):
154 perms_update = []
164 perms_update = []
155 perms_new = []
165 perms_new = []
156 #build a list of permission to update and new permission to create
166 #build a list of permission to update and new permission to create
157 for k, v in value.items():
167 for k, v in value.items():
158 if k.startswith('perm_'):
168 if k.startswith('perm_'):
159 if k.startswith('perm_new_user'):
169 if k.startswith('perm_new_user'):
160 new_perm = value.get('perm_new_user', False)
170 new_perm = value.get('perm_new_user', False)
161 new_user = value.get('perm_new_user_name', False)
171 new_user = value.get('perm_new_user_name', False)
162 if new_user and new_perm:
172 if new_user and new_perm:
163 if (new_user, new_perm) not in perms_new:
173 if (new_user, new_perm) not in perms_new:
164 perms_new.append((new_user, new_perm))
174 perms_new.append((new_user, new_perm))
165 else:
175 else:
166 usr = k[5:]
176 usr = k[5:]
167 if usr == 'default':
177 if usr == 'default':
168 if value['private']:
178 if value['private']:
169 #set none for default when updating to private repo
179 #set none for default when updating to private repo
170 v = 'repository.none'
180 v = 'repository.none'
171 perms_update.append((usr, v))
181 perms_update.append((usr, v))
172 value['perms_updates'] = perms_update
182 value['perms_updates'] = perms_update
173 value['perms_new'] = perms_new
183 value['perms_new'] = perms_new
174 sa = meta.Session
184 sa = meta.Session
175 for k, v in perms_new:
185 for k, v in perms_new:
176 try:
186 try:
177 self.user_db = sa.query(User)\
187 self.user_db = sa.query(User)\
178 .filter(User.active == True)\
188 .filter(User.active == True)\
179 .filter(User.username == k).one()
189 .filter(User.username == k).one()
180 except Exception:
190 except Exception:
181 msg = self.message('perm_new_user_name',
191 msg = self.message('perm_new_user_name',
182 state=State_obj)
192 state=State_obj)
183 raise formencode.Invalid(msg, value, state, error_dict={'perm_new_user_name':msg})
193 raise formencode.Invalid(msg, value, state, error_dict={'perm_new_user_name':msg})
184 return value
194 return value
185
195
186 class ValidSettings(formencode.validators.FancyValidator):
196 class ValidSettings(formencode.validators.FancyValidator):
187
197
188 def to_python(self, value, state):
198 def to_python(self, value, state):
189 #settings form can't edit user
199 #settings form can't edit user
190 if value.has_key('user'):
200 if value.has_key('user'):
191 del['value']['user']
201 del['value']['user']
192
202
193 return value
203 return value
194
204
195 class ValidPath(formencode.validators.FancyValidator):
205 class ValidPath(formencode.validators.FancyValidator):
196 def to_python(self, value, state):
206 def to_python(self, value, state):
197
207
198 if not os.path.isdir(value):
208 if not os.path.isdir(value):
199 msg = _('This is not a valid path')
209 msg = _('This is not a valid path')
200 raise formencode.Invalid(msg, value, state,
210 raise formencode.Invalid(msg, value, state,
201 error_dict={'paths_root_path':msg})
211 error_dict={'paths_root_path':msg})
202 return value
212 return value
203
213
204 def UniqSystemEmail(old_data):
214 def UniqSystemEmail(old_data):
205 class _UniqSystemEmail(formencode.validators.FancyValidator):
215 class _UniqSystemEmail(formencode.validators.FancyValidator):
206 def to_python(self, value, state):
216 def to_python(self, value, state):
207 if old_data.get('email') != value:
217 if old_data.get('email') != value:
208 sa = meta.Session()
218 sa = meta.Session()
209 try:
219 try:
210 user = sa.query(User).filter(User.email == value).scalar()
220 user = sa.query(User).filter(User.email == value).scalar()
211 if user:
221 if user:
212 raise formencode.Invalid(_("That e-mail address is already taken") ,
222 raise formencode.Invalid(_("That e-mail address is already taken") ,
213 value, state)
223 value, state)
214 finally:
224 finally:
215 meta.Session.remove()
225 meta.Session.remove()
216
226
217 return value
227 return value
218
228
219 return _UniqSystemEmail
229 return _UniqSystemEmail
220
230
221 class ValidSystemEmail(formencode.validators.FancyValidator):
231 class ValidSystemEmail(formencode.validators.FancyValidator):
222 def to_python(self, value, state):
232 def to_python(self, value, state):
223 sa = meta.Session
233 sa = meta.Session
224 try:
234 try:
225 user = sa.query(User).filter(User.email == value).scalar()
235 user = sa.query(User).filter(User.email == value).scalar()
226 if user is None:
236 if user is None:
227 raise formencode.Invalid(_("That e-mail address doesn't exist.") ,
237 raise formencode.Invalid(_("That e-mail address doesn't exist.") ,
228 value, state)
238 value, state)
229 finally:
239 finally:
230 meta.Session.remove()
240 meta.Session.remove()
231
241
232 return value
242 return value
233
243
234 #===============================================================================
244 #===============================================================================
235 # FORMS
245 # FORMS
236 #===============================================================================
246 #===============================================================================
237 class LoginForm(formencode.Schema):
247 class LoginForm(formencode.Schema):
238 allow_extra_fields = True
248 allow_extra_fields = True
239 filter_extra_fields = True
249 filter_extra_fields = True
240 username = UnicodeString(
250 username = UnicodeString(
241 strip=True,
251 strip=True,
242 min=1,
252 min=1,
243 not_empty=True,
253 not_empty=True,
244 messages={
254 messages={
245 'empty':_('Please enter a login'),
255 'empty':_('Please enter a login'),
246 'tooShort':_('Enter a value %(min)i characters long or more')}
256 'tooShort':_('Enter a value %(min)i characters long or more')}
247 )
257 )
248
258
249 password = UnicodeString(
259 password = UnicodeString(
250 strip=True,
260 strip=True,
251 min=6,
261 min=6,
252 not_empty=True,
262 not_empty=True,
253 messages={
263 messages={
254 'empty':_('Please enter a password'),
264 'empty':_('Please enter a password'),
255 'tooShort':_('Enter %(min)i characters or more')}
265 'tooShort':_('Enter %(min)i characters or more')}
256 )
266 )
257
267
258
268
259 #chained validators have access to all data
269 #chained validators have access to all data
260 chained_validators = [ValidAuth]
270 chained_validators = [ValidAuth]
261
271
262 def UserForm(edit=False, old_data={}):
272 def UserForm(edit=False, old_data={}):
263 class _UserForm(formencode.Schema):
273 class _UserForm(formencode.Schema):
264 allow_extra_fields = True
274 allow_extra_fields = True
265 filter_extra_fields = True
275 filter_extra_fields = True
266 username = All(UnicodeString(strip=True, min=1, not_empty=True), ValidUsername(edit, old_data))
276 username = All(UnicodeString(strip=True, min=1, not_empty=True), ValidUsername(edit, old_data))
267 if edit:
277 if edit:
268 new_password = All(UnicodeString(strip=True, min=6, not_empty=False), ValidPassword)
278 new_password = All(UnicodeString(strip=True, min=6, not_empty=False), ValidPassword)
269 admin = StringBoolean(if_missing=False)
279 admin = StringBoolean(if_missing=False)
270 else:
280 else:
271 password = All(UnicodeString(strip=True, min=6, not_empty=True), ValidPassword)
281 password = All(UnicodeString(strip=True, min=6, not_empty=True), ValidPassword)
272 active = StringBoolean(if_missing=False)
282 active = StringBoolean(if_missing=False)
273 name = UnicodeString(strip=True, min=1, not_empty=True)
283 name = UnicodeString(strip=True, min=1, not_empty=True)
274 lastname = UnicodeString(strip=True, min=1, not_empty=True)
284 lastname = UnicodeString(strip=True, min=1, not_empty=True)
275 email = All(Email(not_empty=True), UniqSystemEmail(old_data))
285 email = All(Email(not_empty=True), UniqSystemEmail(old_data))
276
286
277 return _UserForm
287 return _UserForm
278
288
279 RegisterForm = UserForm
289 RegisterForm = UserForm
280
290
281 def PasswordResetForm():
291 def PasswordResetForm():
282 class _PasswordResetForm(formencode.Schema):
292 class _PasswordResetForm(formencode.Schema):
283 allow_extra_fields = True
293 allow_extra_fields = True
284 filter_extra_fields = True
294 filter_extra_fields = True
285 email = All(ValidSystemEmail(), Email(not_empty=True))
295 email = All(ValidSystemEmail(), Email(not_empty=True))
286 return _PasswordResetForm
296 return _PasswordResetForm
287
297
288 def RepoForm(edit=False, old_data={}):
298 def RepoForm(edit=False, old_data={}):
289 class _RepoForm(formencode.Schema):
299 class _RepoForm(formencode.Schema):
290 allow_extra_fields = True
300 allow_extra_fields = True
291 filter_extra_fields = False
301 filter_extra_fields = False
292 repo_name = All(UnicodeString(strip=True, min=1, not_empty=True), ValidRepoName(edit, old_data))
302 repo_name = All(UnicodeString(strip=True, min=1, not_empty=True), ValidRepoName(edit, old_data))
293 description = UnicodeString(strip=True, min=1, not_empty=True)
303 description = UnicodeString(strip=True, min=1, not_empty=True)
294 private = StringBoolean(if_missing=False)
304 private = StringBoolean(if_missing=False)
295
305 repo_type = OneOf(BACKENDS.keys())
296 if edit:
306 if edit:
297 user = All(Int(not_empty=True), ValidRepoUser)
307 user = All(Int(not_empty=True), ValidRepoUser)
298
308
299 chained_validators = [ValidPerms]
309 chained_validators = [ValidPerms]
300 return _RepoForm
310 return _RepoForm
301
311
302 def RepoForkForm(edit=False, old_data={}):
312 def RepoForkForm(edit=False, old_data={}):
303 class _RepoForkForm(formencode.Schema):
313 class _RepoForkForm(formencode.Schema):
304 allow_extra_fields = True
314 allow_extra_fields = True
305 filter_extra_fields = False
315 filter_extra_fields = False
306 fork_name = All(UnicodeString(strip=True, min=1, not_empty=True), ValidRepoName(edit, old_data))
316 fork_name = All(UnicodeString(strip=True, min=1, not_empty=True), ValidRepoName(edit, old_data))
307 description = UnicodeString(strip=True, min=1, not_empty=True)
317 description = UnicodeString(strip=True, min=1, not_empty=True)
308 private = StringBoolean(if_missing=False)
318 private = StringBoolean(if_missing=False)
309
319 repo_type = All(ValidForkType(old_data), OneOf(BACKENDS.keys()))
310 return _RepoForkForm
320 return _RepoForkForm
311
321
312 def RepoSettingsForm(edit=False, old_data={}):
322 def RepoSettingsForm(edit=False, old_data={}):
313 class _RepoForm(formencode.Schema):
323 class _RepoForm(formencode.Schema):
314 allow_extra_fields = True
324 allow_extra_fields = True
315 filter_extra_fields = False
325 filter_extra_fields = False
316 repo_name = All(UnicodeString(strip=True, min=1, not_empty=True), ValidRepoName(edit, old_data))
326 repo_name = All(UnicodeString(strip=True, min=1, not_empty=True), ValidRepoName(edit, old_data))
317 description = UnicodeString(strip=True, min=1, not_empty=True)
327 description = UnicodeString(strip=True, min=1, not_empty=True)
318 private = StringBoolean(if_missing=False)
328 private = StringBoolean(if_missing=False)
319
329
320 chained_validators = [ValidPerms, ValidSettings]
330 chained_validators = [ValidPerms, ValidSettings]
321 return _RepoForm
331 return _RepoForm
322
332
323
333
324 def ApplicationSettingsForm():
334 def ApplicationSettingsForm():
325 class _ApplicationSettingsForm(formencode.Schema):
335 class _ApplicationSettingsForm(formencode.Schema):
326 allow_extra_fields = True
336 allow_extra_fields = True
327 filter_extra_fields = False
337 filter_extra_fields = False
328 rhodecode_title = UnicodeString(strip=True, min=1, not_empty=True)
338 rhodecode_title = UnicodeString(strip=True, min=1, not_empty=True)
329 rhodecode_realm = UnicodeString(strip=True, min=1, not_empty=True)
339 rhodecode_realm = UnicodeString(strip=True, min=1, not_empty=True)
330
340
331 return _ApplicationSettingsForm
341 return _ApplicationSettingsForm
332
342
333 def ApplicationUiSettingsForm():
343 def ApplicationUiSettingsForm():
334 class _ApplicationUiSettingsForm(formencode.Schema):
344 class _ApplicationUiSettingsForm(formencode.Schema):
335 allow_extra_fields = True
345 allow_extra_fields = True
336 filter_extra_fields = False
346 filter_extra_fields = False
337 web_push_ssl = OneOf(['true', 'false'], if_missing='false')
347 web_push_ssl = OneOf(['true', 'false'], if_missing='false')
338 paths_root_path = All(ValidPath(), UnicodeString(strip=True, min=1, not_empty=True))
348 paths_root_path = All(ValidPath(), UnicodeString(strip=True, min=1, not_empty=True))
339 hooks_changegroup_update = OneOf(['True', 'False'], if_missing=False)
349 hooks_changegroup_update = OneOf(['True', 'False'], if_missing=False)
340 hooks_changegroup_repo_size = OneOf(['True', 'False'], if_missing=False)
350 hooks_changegroup_repo_size = OneOf(['True', 'False'], if_missing=False)
341
351
342 return _ApplicationUiSettingsForm
352 return _ApplicationUiSettingsForm
343
353
344 def DefaultPermissionsForm(perms_choices, register_choices, create_choices):
354 def DefaultPermissionsForm(perms_choices, register_choices, create_choices):
345 class _DefaultPermissionsForm(formencode.Schema):
355 class _DefaultPermissionsForm(formencode.Schema):
346 allow_extra_fields = True
356 allow_extra_fields = True
347 filter_extra_fields = True
357 filter_extra_fields = True
348 overwrite_default = OneOf(['true', 'false'], if_missing='false')
358 overwrite_default = OneOf(['true', 'false'], if_missing='false')
349 default_perm = OneOf(perms_choices)
359 default_perm = OneOf(perms_choices)
350 default_register = OneOf(register_choices)
360 default_register = OneOf(register_choices)
351 default_create = OneOf(create_choices)
361 default_create = OneOf(create_choices)
352
362
353 return _DefaultPermissionsForm
363 return _DefaultPermissionsForm
@@ -1,210 +1,210 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 # model for handling repositories actions
3 # model for handling repositories actions
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 # This program is free software; you can redistribute it and/or
5 # This program is free software; you can redistribute it and/or
6 # modify it under the terms of the GNU General Public License
6 # modify it under the terms of the GNU General Public License
7 # as published by the Free Software Foundation; version 2
7 # as published by the Free Software Foundation; version 2
8 # of the License or (at your opinion) any later version of the license.
8 # of the License or (at your opinion) any later version of the license.
9 #
9 #
10 # This program is distributed in the hope that it will be useful,
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
13 # GNU General Public License for more details.
14 #
14 #
15 # You should have received a copy of the GNU General Public License
15 # You should have received a copy of the GNU General Public License
16 # along with this program; if not, write to the Free Software
16 # along with this program; if not, write to the Free Software
17 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
17 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18 # MA 02110-1301, USA.
18 # MA 02110-1301, USA.
19 """
19 """
20 Created on Jun 5, 2010
20 Created on Jun 5, 2010
21 model for handling repositories actions
21 model for handling repositories actions
22 :author: marcink
22 :author: marcink
23 """
23 """
24
24 from vcs.backends import get_repo, get_backend
25 from datetime import datetime
25 from datetime import datetime
26 from pylons import app_globals as g
26 from pylons import app_globals as g
27 from rhodecode.model.db import Repository, RepoToPerm, User, Permission
27 from rhodecode.model.db import Repository, RepoToPerm, User, Permission
28 from rhodecode.model.meta import Session
28 from rhodecode.model.meta import Session
29 from rhodecode.model.user import UserModel
29 from rhodecode.model.user import UserModel
30 from rhodecode.model.caching_query import FromCache
30 from rhodecode.model.caching_query import FromCache
31 import logging
31 import logging
32 import os
32 import os
33 import shutil
33 import shutil
34 import traceback
34 import traceback
35 log = logging.getLogger(__name__)
35 log = logging.getLogger(__name__)
36
36
37 class RepoModel(object):
37 class RepoModel(object):
38
38
39 def __init__(self, sa=None):
39 def __init__(self, sa=None):
40 if not sa:
40 if not sa:
41 self.sa = Session()
41 self.sa = Session()
42 else:
42 else:
43 self.sa = sa
43 self.sa = sa
44
44
45 def get(self, repo_id, cache=False):
45 def get(self, repo_id, cache=False):
46 repo = self.sa.query(Repository)\
46 repo = self.sa.query(Repository)\
47 .filter(Repository.repo_name == repo_id)
47 .filter(Repository.repo_name == repo_id)
48
48
49 if cache:
49 if cache:
50 repo = repo.options(FromCache("sql_cache_short",
50 repo = repo.options(FromCache("sql_cache_short",
51 "get_repo_%s" % repo))
51 "get_repo_%s" % repo))
52 return repo.scalar()
52 return repo.scalar()
53
53
54 def get_users_js(self):
54 def get_users_js(self):
55
55
56 users = self.sa.query(User).filter(User.active == True).all()
56 users = self.sa.query(User).filter(User.active == True).all()
57 u_tmpl = '''{id:%s, fname:"%s", lname:"%s", nname:"%s"},'''
57 u_tmpl = '''{id:%s, fname:"%s", lname:"%s", nname:"%s"},'''
58 users_array = '[%s];' % '\n'.join([u_tmpl % (u.user_id, u.name,
58 users_array = '[%s];' % '\n'.join([u_tmpl % (u.user_id, u.name,
59 u.lastname, u.username)
59 u.lastname, u.username)
60 for u in users])
60 for u in users])
61 return users_array
61 return users_array
62
62
63
63
64 def update(self, repo_name, form_data):
64 def update(self, repo_name, form_data):
65 try:
65 try:
66
66
67 #update permissions
67 #update permissions
68 for username, perm in form_data['perms_updates']:
68 for username, perm in form_data['perms_updates']:
69 r2p = self.sa.query(RepoToPerm)\
69 r2p = self.sa.query(RepoToPerm)\
70 .filter(RepoToPerm.user == UserModel(self.sa).get_by_username(username, cache=False))\
70 .filter(RepoToPerm.user == UserModel(self.sa).get_by_username(username, cache=False))\
71 .filter(RepoToPerm.repository == self.get(repo_name))\
71 .filter(RepoToPerm.repository == self.get(repo_name))\
72 .one()
72 .one()
73
73
74 r2p.permission_id = self.sa.query(Permission).filter(
74 r2p.permission_id = self.sa.query(Permission).filter(
75 Permission.permission_name ==
75 Permission.permission_name ==
76 perm).one().permission_id
76 perm).one().permission_id
77 self.sa.add(r2p)
77 self.sa.add(r2p)
78
78
79 #set new permissions
79 #set new permissions
80 for username, perm in form_data['perms_new']:
80 for username, perm in form_data['perms_new']:
81 r2p = RepoToPerm()
81 r2p = RepoToPerm()
82 r2p.repository = self.get(repo_name)
82 r2p.repository = self.get(repo_name)
83 r2p.user = UserModel(self.sa).get_by_username(username, cache=False)
83 r2p.user = UserModel(self.sa).get_by_username(username, cache=False)
84
84
85 r2p.permission_id = self.sa.query(Permission).filter(
85 r2p.permission_id = self.sa.query(Permission).filter(
86 Permission.permission_name == perm)\
86 Permission.permission_name == perm)\
87 .one().permission_id
87 .one().permission_id
88 self.sa.add(r2p)
88 self.sa.add(r2p)
89
89
90 #update current repo
90 #update current repo
91 cur_repo = self.get(repo_name, cache=False)
91 cur_repo = self.get(repo_name, cache=False)
92
92
93 for k, v in form_data.items():
93 for k, v in form_data.items():
94 if k == 'user':
94 if k == 'user':
95 cur_repo.user_id = v
95 cur_repo.user_id = v
96 else:
96 else:
97 setattr(cur_repo, k, v)
97 setattr(cur_repo, k, v)
98
98
99 self.sa.add(cur_repo)
99 self.sa.add(cur_repo)
100
100
101 if repo_name != form_data['repo_name']:
101 if repo_name != form_data['repo_name']:
102 #rename our data
102 #rename our data
103 self.__rename_repo(repo_name, form_data['repo_name'])
103 self.__rename_repo(repo_name, form_data['repo_name'])
104
104
105 self.sa.commit()
105 self.sa.commit()
106 except:
106 except:
107 log.error(traceback.format_exc())
107 log.error(traceback.format_exc())
108 self.sa.rollback()
108 self.sa.rollback()
109 raise
109 raise
110
110
111 def create(self, form_data, cur_user, just_db=False, fork=False):
111 def create(self, form_data, cur_user, just_db=False, fork=False):
112 try:
112 try:
113 if fork:
113 if fork:
114 #force str since hg doesn't go with unicode
114 #force str since hg doesn't go with unicode
115 repo_name = str(form_data['fork_name'])
115 repo_name = str(form_data['fork_name'])
116 org_name = str(form_data['repo_name'])
116 org_name = str(form_data['repo_name'])
117
117
118 else:
118 else:
119 org_name = repo_name = str(form_data['repo_name'])
119 org_name = repo_name = str(form_data['repo_name'])
120 new_repo = Repository()
120 new_repo = Repository()
121 for k, v in form_data.items():
121 for k, v in form_data.items():
122 if k == 'repo_name':
122 if k == 'repo_name':
123 v = repo_name
123 v = repo_name
124 setattr(new_repo, k, v)
124 setattr(new_repo, k, v)
125
125
126 if fork:
126 if fork:
127 parent_repo = self.sa.query(Repository)\
127 parent_repo = self.sa.query(Repository)\
128 .filter(Repository.repo_name == org_name).scalar()
128 .filter(Repository.repo_name == org_name).scalar()
129 new_repo.fork = parent_repo
129 new_repo.fork = parent_repo
130
130
131 new_repo.user_id = cur_user.user_id
131 new_repo.user_id = cur_user.user_id
132 self.sa.add(new_repo)
132 self.sa.add(new_repo)
133
133
134 #create default permission
134 #create default permission
135 repo_to_perm = RepoToPerm()
135 repo_to_perm = RepoToPerm()
136 default = 'repository.read'
136 default = 'repository.read'
137 for p in UserModel(self.sa).get_by_username('default', cache=False).user_perms:
137 for p in UserModel(self.sa).get_by_username('default', cache=False).user_perms:
138 if p.permission.permission_name.startswith('repository.'):
138 if p.permission.permission_name.startswith('repository.'):
139 default = p.permission.permission_name
139 default = p.permission.permission_name
140 break
140 break
141
141
142 default_perm = 'repository.none' if form_data['private'] else default
142 default_perm = 'repository.none' if form_data['private'] else default
143
143
144 repo_to_perm.permission_id = self.sa.query(Permission)\
144 repo_to_perm.permission_id = self.sa.query(Permission)\
145 .filter(Permission.permission_name == default_perm)\
145 .filter(Permission.permission_name == default_perm)\
146 .one().permission_id
146 .one().permission_id
147
147
148 repo_to_perm.repository_id = new_repo.repo_id
148 repo_to_perm.repository_id = new_repo.repo_id
149 repo_to_perm.user_id = UserModel(self.sa).get_by_username('default', cache=False).user_id
149 repo_to_perm.user_id = UserModel(self.sa).get_by_username('default', cache=False).user_id
150
150
151 self.sa.add(repo_to_perm)
151 self.sa.add(repo_to_perm)
152 self.sa.commit()
152 self.sa.commit()
153 if not just_db:
153 if not just_db:
154 self.__create_repo(repo_name)
154 self.__create_repo(repo_name, form_data['repo_type'])
155 except:
155 except:
156 log.error(traceback.format_exc())
156 log.error(traceback.format_exc())
157 self.sa.rollback()
157 self.sa.rollback()
158 raise
158 raise
159
159
160 def create_fork(self, form_data, cur_user):
160 def create_fork(self, form_data, cur_user):
161 from rhodecode.lib.celerylib import tasks, run_task
161 from rhodecode.lib.celerylib import tasks, run_task
162 run_task(tasks.create_repo_fork, form_data, cur_user)
162 run_task(tasks.create_repo_fork, form_data, cur_user)
163
163
164 def delete(self, repo):
164 def delete(self, repo):
165 try:
165 try:
166 self.sa.delete(repo)
166 self.sa.delete(repo)
167 self.sa.commit()
167 self.sa.commit()
168 self.__delete_repo(repo.repo_name)
168 self.__delete_repo(repo.repo_name)
169 except:
169 except:
170 log.error(traceback.format_exc())
170 log.error(traceback.format_exc())
171 self.sa.rollback()
171 self.sa.rollback()
172 raise
172 raise
173
173
174 def delete_perm_user(self, form_data, repo_name):
174 def delete_perm_user(self, form_data, repo_name):
175 try:
175 try:
176 self.sa.query(RepoToPerm)\
176 self.sa.query(RepoToPerm)\
177 .filter(RepoToPerm.repository == self.get(repo_name))\
177 .filter(RepoToPerm.repository == self.get(repo_name))\
178 .filter(RepoToPerm.user_id == form_data['user_id']).delete()
178 .filter(RepoToPerm.user_id == form_data['user_id']).delete()
179 self.sa.commit()
179 self.sa.commit()
180 except:
180 except:
181 log.error(traceback.format_exc())
181 log.error(traceback.format_exc())
182 self.sa.rollback()
182 self.sa.rollback()
183 raise
183 raise
184
184
185 def __create_repo(self, repo_name):
185 def __create_repo(self, repo_name, alias):
186 from rhodecode.lib.utils import check_repo
186 from rhodecode.lib.utils import check_repo
187 repo_path = os.path.join(g.base_path, repo_name)
187 repo_path = os.path.join(g.base_path, repo_name)
188 if check_repo(repo_name, g.base_path):
188 if check_repo(repo_name, g.base_path):
189 log.info('creating repo %s in %s', repo_name, repo_path)
189 log.info('creating repo %s in %s', repo_name, repo_path)
190 from vcs.backends.hg import MercurialRepository
190 backend = get_backend(alias)
191 MercurialRepository(repo_path, create=True)
191 backend(repo_path, create=True)
192
192
193 def __rename_repo(self, old, new):
193 def __rename_repo(self, old, new):
194 log.info('renaming repo from %s to %s', old, new)
194 log.info('renaming repo from %s to %s', old, new)
195
195
196 old_path = os.path.join(g.base_path, old)
196 old_path = os.path.join(g.base_path, old)
197 new_path = os.path.join(g.base_path, new)
197 new_path = os.path.join(g.base_path, new)
198 if os.path.isdir(new_path):
198 if os.path.isdir(new_path):
199 raise Exception('Was trying to rename to already existing dir %s',
199 raise Exception('Was trying to rename to already existing dir %s',
200 new_path)
200 new_path)
201 shutil.move(old_path, new_path)
201 shutil.move(old_path, new_path)
202
202
203 def __delete_repo(self, name):
203 def __delete_repo(self, name):
204 rm_path = os.path.join(g.base_path, name)
204 rm_path = os.path.join(g.base_path, name)
205 log.info("Removing %s", rm_path)
205 log.info("Removing %s", rm_path)
206 #disable hg
206 #disable hg
207 shutil.move(os.path.join(rm_path, '.hg'), os.path.join(rm_path, 'rm__.hg'))
207 shutil.move(os.path.join(rm_path, '.hg'), os.path.join(rm_path, 'rm__.hg'))
208 #disable repo
208 #disable repo
209 shutil.move(rm_path, os.path.join(g.base_path, 'rm__%s__%s' \
209 shutil.move(rm_path, os.path.join(g.base_path, 'rm__%s__%s' \
210 % (datetime.today(), name)))
210 % (datetime.today(), name)))
@@ -1,60 +1,68 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <%inherit file="/base/base.html"/>
2 <%inherit file="/base/base.html"/>
3
3
4 <%def name="title()">
4 <%def name="title()">
5 ${_('Add repository')} - ${c.rhodecode_name}
5 ${_('Add repository')} - ${c.rhodecode_name}
6 </%def>
6 </%def>
7
7
8 <%def name="breadcrumbs_links()">
8 <%def name="breadcrumbs_links()">
9 ${h.link_to(_('Admin'),h.url('admin_home'))}
9 ${h.link_to(_('Admin'),h.url('admin_home'))}
10 &raquo;
10 &raquo;
11 ${h.link_to(_('Repositories'),h.url('repos'))}
11 ${h.link_to(_('Repositories'),h.url('repos'))}
12 &raquo;
12 &raquo;
13 ${_('add new')}
13 ${_('add new')}
14 </%def>
14 </%def>
15
15
16 <%def name="page_nav()">
16 <%def name="page_nav()">
17 ${self.menu('admin')}
17 ${self.menu('admin')}
18 </%def>
18 </%def>
19 <%def name="main()">
19 <%def name="main()">
20 <div class="box">
20 <div class="box">
21 <!-- box / title -->
21 <!-- box / title -->
22 <div class="title">
22 <div class="title">
23 ${self.breadcrumbs()}
23 ${self.breadcrumbs()}
24 </div>
24 </div>
25 ${h.form(url('repos'))}
25 ${h.form(url('repos'))}
26 <div class="form">
26 <div class="form">
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">${_('Name')}:</label>
31 <label for="repo_name">${_('Name')}:</label>
32 </div>
32 </div>
33 <div class="input">
33 <div class="input">
34 ${h.text('repo_name',c.new_repo,class_="small")}
34 ${h.text('repo_name',c.new_repo,class_="small")}
35 </div>
35 </div>
36 </div>
36 </div>
37 <div class="field">
37 <div class="field">
38 <div class="label">
39 <label for="repo_type">${_('Type')}:</label>
40 </div>
41 <div class="input">
42 ${h.select('repo_type','hg',c.backends,class_="small")}
43 </div>
44 </div>
45 <div class="field">
38 <div class="label label-textarea">
46 <div class="label label-textarea">
39 <label for="description">${_('Description')}:</label>
47 <label for="description">${_('Description')}:</label>
40 </div>
48 </div>
41 <div class="textarea text-area editor">
49 <div class="textarea text-area editor">
42 ${h.textarea('description',cols=23,rows=5)}
50 ${h.textarea('description',cols=23,rows=5)}
43 </div>
51 </div>
44 </div>
52 </div>
45 <div class="field">
53 <div class="field">
46 <div class="label label-checkbox">
54 <div class="label label-checkbox">
47 <label for="private">${_('Private')}:</label>
55 <label for="private">${_('Private')}:</label>
48 </div>
56 </div>
49 <div class="checkboxes">
57 <div class="checkboxes">
50 ${h.checkbox('private',value="True")}
58 ${h.checkbox('private',value="True")}
51 </div>
59 </div>
52 </div>
60 </div>
53 <div class="buttons">
61 <div class="buttons">
54 ${h.submit('add','add',class_="ui-button ui-widget ui-state-default ui-corner-all")}
62 ${h.submit('add','add',class_="ui-button ui-widget ui-state-default ui-corner-all")}
55 </div>
63 </div>
56 </div>
64 </div>
57 </div>
65 </div>
58 ${h.end_form()}
66 ${h.end_form()}
59 </div>
67 </div>
60 </%def>
68 </%def>
@@ -1,57 +1,65 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <%inherit file="/base/base.html"/>
2 <%inherit file="/base/base.html"/>
3
3
4 <%def name="title()">
4 <%def name="title()">
5 ${_('Add repository')} - ${c.rhodecode_name}
5 ${_('Add repository')} - ${c.rhodecode_name}
6 </%def>
6 </%def>
7
7
8 <%def name="breadcrumbs_links()">
8 <%def name="breadcrumbs_links()">
9 ${_('add new repository')}
9 ${_('add new repository')}
10 </%def>
10 </%def>
11
11
12 <%def name="page_nav()">
12 <%def name="page_nav()">
13 ${self.menu('admin')}
13 ${self.menu('admin')}
14 </%def>
14 </%def>
15 <%def name="main()">
15 <%def name="main()">
16 <div class="box">
16 <div class="box">
17 <!-- box / title -->
17 <!-- box / title -->
18 <div class="title">
18 <div class="title">
19 ${self.breadcrumbs()}
19 ${self.breadcrumbs()}
20 </div>
20 </div>
21 ${h.form(url('repos'))}
21 ${h.form(url('repos'))}
22 <div class="form">
22 <div class="form">
23 <!-- fields -->
23 <!-- fields -->
24 <div class="fields">
24 <div class="fields">
25 <div class="field">
25 <div class="field">
26 <div class="label">
26 <div class="label">
27 <label for="repo_name">${_('Name')}:</label>
27 <label for="repo_name">${_('Name')}:</label>
28 </div>
28 </div>
29 <div class="input">
29 <div class="input">
30 ${h.text('repo_name',c.new_repo,class_="small")}
30 ${h.text('repo_name',c.new_repo,class_="small")}
31 ${h.hidden('user_created','True')}
31 ${h.hidden('user_created','True')}
32 </div>
32 </div>
33 </div>
33 </div>
34 <div class="field">
34 <div class="field">
35 <div class="label">
36 <label for="repo_type">${_('Type')}:</label>
37 </div>
38 <div class="input">
39 ${h.select('repo_type','hg',c.backends,class_="small")}
40 </div>
41 </div>
42 <div class="field">
35 <div class="label label-textarea">
43 <div class="label label-textarea">
36 <label for="description">${_('Description')}:</label>
44 <label for="description">${_('Description')}:</label>
37 </div>
45 </div>
38 <div class="textarea text-area editor">
46 <div class="textarea text-area editor">
39 ${h.textarea('description',cols=23,rows=5)}
47 ${h.textarea('description',cols=23,rows=5)}
40 </div>
48 </div>
41 </div>
49 </div>
42 <div class="field">
50 <div class="field">
43 <div class="label label-checkbox">
51 <div class="label label-checkbox">
44 <label for="private">${_('Private')}:</label>
52 <label for="private">${_('Private')}:</label>
45 </div>
53 </div>
46 <div class="checkboxes">
54 <div class="checkboxes">
47 ${h.checkbox('private',value="True")}
55 ${h.checkbox('private',value="True")}
48 </div>
56 </div>
49 </div>
57 </div>
50 <div class="buttons">
58 <div class="buttons">
51 ${h.submit('add','add',class_="ui-button ui-widget ui-state-default ui-corner-all")}
59 ${h.submit('add','add',class_="ui-button ui-widget ui-state-default ui-corner-all")}
52 </div>
60 </div>
53 </div>
61 </div>
54 </div>
62 </div>
55 ${h.end_form()}
63 ${h.end_form()}
56 </div>
64 </div>
57 </%def>
65 </%def>
@@ -1,275 +1,282 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <%inherit file="/base/base.html"/>
2 <%inherit file="/base/base.html"/>
3
3
4 <%def name="title()">
4 <%def name="title()">
5 ${_('Edit repository')} ${c.repo_info.repo_name} - ${c.rhodecode_name}
5 ${_('Edit repository')} ${c.repo_info.repo_name} - ${c.rhodecode_name}
6 </%def>
6 </%def>
7
7
8 <%def name="breadcrumbs_links()">
8 <%def name="breadcrumbs_links()">
9 ${h.link_to(_('Admin'),h.url('admin_home'))}
9 ${h.link_to(_('Admin'),h.url('admin_home'))}
10 &raquo;
10 &raquo;
11 ${h.link_to(_('Repositories'),h.url('repos'))}
11 ${h.link_to(_('Repositories'),h.url('repos'))}
12 &raquo;
12 &raquo;
13 ${_('edit')} "${c.repo_name}"
13 ${_('edit')} "${c.repo_name}"
14 </%def>
14 </%def>
15
15
16 <%def name="page_nav()">
16 <%def name="page_nav()">
17 ${self.menu('admin')}
17 ${self.menu('admin')}
18 </%def>
18 </%def>
19
19
20 <%def name="main()">
20 <%def name="main()">
21 <div class="box">
21 <div class="box">
22 <!-- box / title -->
22 <!-- box / title -->
23 <div class="title">
23 <div class="title">
24 ${self.breadcrumbs()}
24 ${self.breadcrumbs()}
25 </div>
25 </div>
26 ${h.form(url('repo', repo_name=c.repo_info.repo_name),method='put')}
26 ${h.form(url('repo', repo_name=c.repo_info.repo_name),method='put')}
27 <div class="form">
27 <div class="form">
28 <!-- fields -->
28 <!-- fields -->
29 <div class="fields">
29 <div class="fields">
30 <div class="field">
30 <div class="field">
31 <div class="label">
31 <div class="label">
32 <label for="repo_name">${_('Name')}:</label>
32 <label for="repo_name">${_('Name')}:</label>
33 </div>
33 </div>
34 <div class="input input-medium">
34 <div class="input input-medium">
35 ${h.text('repo_name',class_="small")}
35 ${h.text('repo_name',class_="small")}
36 </div>
36 </div>
37 </div>
37 </div>
38
38 <div class="field">
39 <div class="label">
40 <label for="repo_type">${_('Type')}:</label>
41 </div>
42 <div class="input">
43 ${h.select('repo_type','hg',c.backends,class_="small")}
44 </div>
45 </div>
39 <div class="field">
46 <div class="field">
40 <div class="label label-textarea">
47 <div class="label label-textarea">
41 <label for="description">${_('Description')}:</label>
48 <label for="description">${_('Description')}:</label>
42 </div>
49 </div>
43 <div class="textarea text-area editor">
50 <div class="textarea text-area editor">
44 ${h.textarea('description',cols=23,rows=5)}
51 ${h.textarea('description',cols=23,rows=5)}
45 </div>
52 </div>
46 </div>
53 </div>
47
54
48 <div class="field">
55 <div class="field">
49 <div class="label label-checkbox">
56 <div class="label label-checkbox">
50 <label for="private">${_('Private')}:</label>
57 <label for="private">${_('Private')}:</label>
51 </div>
58 </div>
52 <div class="checkboxes">
59 <div class="checkboxes">
53 ${h.checkbox('private',value="True")}
60 ${h.checkbox('private',value="True")}
54 </div>
61 </div>
55 </div>
62 </div>
56
63
57 <div class="field">
64 <div class="field">
58 <div class="label">
65 <div class="label">
59 <label for="user">${_('Owner')}:</label>
66 <label for="user">${_('Owner')}:</label>
60 </div>
67 </div>
61 <div class="input input-small ac">
68 <div class="input input-small ac">
62 <div class="perm_ac">
69 <div class="perm_ac">
63 ${h.text('user',class_='yui-ac-input')}
70 ${h.text('user',class_='yui-ac-input')}
64 <div id="owner_container"></div>
71 <div id="owner_container"></div>
65 </div>
72 </div>
66 </div>
73 </div>
67 </div>
74 </div>
68
75
69 <div class="field">
76 <div class="field">
70 <div class="label">
77 <div class="label">
71 <label for="input">${_('Permissions')}:</label>
78 <label for="input">${_('Permissions')}:</label>
72 </div>
79 </div>
73 <div class="input">
80 <div class="input">
74 <table id="permissions_manage">
81 <table id="permissions_manage">
75 <tr>
82 <tr>
76 <td>${_('none')}</td>
83 <td>${_('none')}</td>
77 <td>${_('read')}</td>
84 <td>${_('read')}</td>
78 <td>${_('write')}</td>
85 <td>${_('write')}</td>
79 <td>${_('admin')}</td>
86 <td>${_('admin')}</td>
80 <td>${_('user')}</td>
87 <td>${_('user')}</td>
81 <td></td>
88 <td></td>
82 </tr>
89 </tr>
83
90
84 %for r2p in c.repo_info.repo_to_perm:
91 %for r2p in c.repo_info.repo_to_perm:
85 %if r2p.user.username =='default' and c.repo_info.private:
92 %if r2p.user.username =='default' and c.repo_info.private:
86 <tr>
93 <tr>
87 <td colspan="4">
94 <td colspan="4">
88 <span class="private_repo_msg">
95 <span class="private_repo_msg">
89 ${_('private repository')}
96 ${_('private repository')}
90 </span>
97 </span>
91 </td>
98 </td>
92 <td class="private_repo_msg">${r2p.user.username}</td>
99 <td class="private_repo_msg">${r2p.user.username}</td>
93 </tr>
100 </tr>
94 %else:
101 %else:
95 <tr id="id${id(r2p.user.username)}">
102 <tr id="id${id(r2p.user.username)}">
96 <td>${h.radio('perm_%s' % r2p.user.username,'repository.none')}</td>
103 <td>${h.radio('perm_%s' % r2p.user.username,'repository.none')}</td>
97 <td>${h.radio('perm_%s' % r2p.user.username,'repository.read')}</td>
104 <td>${h.radio('perm_%s' % r2p.user.username,'repository.read')}</td>
98 <td>${h.radio('perm_%s' % r2p.user.username,'repository.write')}</td>
105 <td>${h.radio('perm_%s' % r2p.user.username,'repository.write')}</td>
99 <td>${h.radio('perm_%s' % r2p.user.username,'repository.admin')}</td>
106 <td>${h.radio('perm_%s' % r2p.user.username,'repository.admin')}</td>
100 <td>${r2p.user.username}</td>
107 <td>${r2p.user.username}</td>
101 <td>
108 <td>
102 %if r2p.user.username !='default':
109 %if r2p.user.username !='default':
103 <span class="delete_icon action_button" onclick="ajaxAction(${r2p.user.user_id},'${'id%s'%id(r2p.user.username)}')">
110 <span class="delete_icon action_button" onclick="ajaxAction(${r2p.user.user_id},'${'id%s'%id(r2p.user.username)}')">
104 <script type="text/javascript">
111 <script type="text/javascript">
105 function ajaxAction(user_id,field_id){
112 function ajaxAction(user_id,field_id){
106 var sUrl = "${h.url('delete_repo_user',repo_name=c.repo_name)}";
113 var sUrl = "${h.url('delete_repo_user',repo_name=c.repo_name)}";
107 var callback = { success:function(o){
114 var callback = { success:function(o){
108 var tr = YAHOO.util.Dom.get(String(field_id));
115 var tr = YAHOO.util.Dom.get(String(field_id));
109 tr.parentNode.removeChild(tr);},failure:function(o){
116 tr.parentNode.removeChild(tr);},failure:function(o){
110 alert("${_('Failed to remove user')}");},};
117 alert("${_('Failed to remove user')}");},};
111 var postData = '_method=delete&user_id='+user_id;
118 var postData = '_method=delete&user_id='+user_id;
112 var request = YAHOO.util.Connect.asyncRequest('POST', sUrl, callback, postData);};
119 var request = YAHOO.util.Connect.asyncRequest('POST', sUrl, callback, postData);};
113 </script>
120 </script>
114 </span>
121 </span>
115 %endif
122 %endif
116 </td>
123 </td>
117 </tr>
124 </tr>
118 %endif
125 %endif
119 %endfor
126 %endfor
120
127
121 <tr id="add_perm_input">
128 <tr id="add_perm_input">
122 <td>${h.radio('perm_new_user','repository.none')}</td>
129 <td>${h.radio('perm_new_user','repository.none')}</td>
123 <td>${h.radio('perm_new_user','repository.read')}</td>
130 <td>${h.radio('perm_new_user','repository.read')}</td>
124 <td>${h.radio('perm_new_user','repository.write')}</td>
131 <td>${h.radio('perm_new_user','repository.write')}</td>
125 <td>${h.radio('perm_new_user','repository.admin')}</td>
132 <td>${h.radio('perm_new_user','repository.admin')}</td>
126 <td class='ac'>
133 <td class='ac'>
127 <div class="perm_ac" id="perm_ac">
134 <div class="perm_ac" id="perm_ac">
128 ${h.text('perm_new_user_name',class_='yui-ac-input')}
135 ${h.text('perm_new_user_name',class_='yui-ac-input')}
129 <div id="perm_container"></div>
136 <div id="perm_container"></div>
130 </div>
137 </div>
131 </td>
138 </td>
132 <td></td>
139 <td></td>
133 </tr>
140 </tr>
134 <tr>
141 <tr>
135 <td colspan="6">
142 <td colspan="6">
136 <span id="add_perm" class="add_icon" style="cursor: pointer;">
143 <span id="add_perm" class="add_icon" style="cursor: pointer;">
137 ${_('Add another user')}
144 ${_('Add another user')}
138 </span>
145 </span>
139 </td>
146 </td>
140 </tr>
147 </tr>
141 </table>
148 </table>
142 </div>
149 </div>
143
150
144 <div class="buttons">
151 <div class="buttons">
145 ${h.submit('save','save',class_="ui-button ui-widget ui-state-default ui-corner-all")}
152 ${h.submit('save','save',class_="ui-button ui-widget ui-state-default ui-corner-all")}
146 </div>
153 </div>
147 </div>
154 </div>
148 </div>
155 </div>
149 </div>
156 </div>
150 ${h.end_form()}
157 ${h.end_form()}
151 <script type="text/javascript">
158 <script type="text/javascript">
152 YAHOO.util.Event.onDOMReady(function(){
159 YAHOO.util.Event.onDOMReady(function(){
153 var D = YAHOO.util.Dom;
160 var D = YAHOO.util.Dom;
154 if(!D.hasClass('perm_new_user_name','error')){
161 if(!D.hasClass('perm_new_user_name','error')){
155 D.setStyle('add_perm_input','display','none');
162 D.setStyle('add_perm_input','display','none');
156 }
163 }
157 YAHOO.util.Event.addListener('add_perm','click',function(){
164 YAHOO.util.Event.addListener('add_perm','click',function(){
158 D.setStyle('add_perm_input','display','');
165 D.setStyle('add_perm_input','display','');
159 D.setStyle('add_perm','opacity','0.6');
166 D.setStyle('add_perm','opacity','0.6');
160 D.setStyle('add_perm','cursor','default');
167 D.setStyle('add_perm','cursor','default');
161 });
168 });
162 });
169 });
163 </script>
170 </script>
164 <script type="text/javascript">
171 <script type="text/javascript">
165 YAHOO.example.FnMultipleFields = function(){
172 YAHOO.example.FnMultipleFields = function(){
166 var myContacts = ${c.users_array|n}
173 var myContacts = ${c.users_array|n}
167
174
168 // Define a custom search function for the DataSource
175 // Define a custom search function for the DataSource
169 var matchNames = function(sQuery) {
176 var matchNames = function(sQuery) {
170 // Case insensitive matching
177 // Case insensitive matching
171 var query = sQuery.toLowerCase(),
178 var query = sQuery.toLowerCase(),
172 contact,
179 contact,
173 i=0,
180 i=0,
174 l=myContacts.length,
181 l=myContacts.length,
175 matches = [];
182 matches = [];
176
183
177 // Match against each name of each contact
184 // Match against each name of each contact
178 for(; i<l; i++) {
185 for(; i<l; i++) {
179 contact = myContacts[i];
186 contact = myContacts[i];
180 if((contact.fname.toLowerCase().indexOf(query) > -1) ||
187 if((contact.fname.toLowerCase().indexOf(query) > -1) ||
181 (contact.lname.toLowerCase().indexOf(query) > -1) ||
188 (contact.lname.toLowerCase().indexOf(query) > -1) ||
182 (contact.nname && (contact.nname.toLowerCase().indexOf(query) > -1))) {
189 (contact.nname && (contact.nname.toLowerCase().indexOf(query) > -1))) {
183 matches[matches.length] = contact;
190 matches[matches.length] = contact;
184 }
191 }
185 }
192 }
186
193
187 return matches;
194 return matches;
188 };
195 };
189
196
190 // Use a FunctionDataSource
197 // Use a FunctionDataSource
191 var oDS = new YAHOO.util.FunctionDataSource(matchNames);
198 var oDS = new YAHOO.util.FunctionDataSource(matchNames);
192 oDS.responseSchema = {
199 oDS.responseSchema = {
193 fields: ["id", "fname", "lname", "nname"]
200 fields: ["id", "fname", "lname", "nname"]
194 }
201 }
195
202
196 // Instantiate AutoComplete for perms
203 // Instantiate AutoComplete for perms
197 var oAC_perms = new YAHOO.widget.AutoComplete("perm_new_user_name", "perm_container", oDS);
204 var oAC_perms = new YAHOO.widget.AutoComplete("perm_new_user_name", "perm_container", oDS);
198 oAC_perms.useShadow = false;
205 oAC_perms.useShadow = false;
199 oAC_perms.resultTypeList = false;
206 oAC_perms.resultTypeList = false;
200
207
201 // Instantiate AutoComplete for owner
208 // Instantiate AutoComplete for owner
202 var oAC_owner = new YAHOO.widget.AutoComplete("user", "owner_container", oDS);
209 var oAC_owner = new YAHOO.widget.AutoComplete("user", "owner_container", oDS);
203 oAC_owner.useShadow = false;
210 oAC_owner.useShadow = false;
204 oAC_owner.resultTypeList = false;
211 oAC_owner.resultTypeList = false;
205
212
206
213
207 // Custom formatter to highlight the matching letters
214 // Custom formatter to highlight the matching letters
208 var custom_formatter = function(oResultData, sQuery, sResultMatch) {
215 var custom_formatter = function(oResultData, sQuery, sResultMatch) {
209 var query = sQuery.toLowerCase(),
216 var query = sQuery.toLowerCase(),
210 fname = oResultData.fname,
217 fname = oResultData.fname,
211 lname = oResultData.lname,
218 lname = oResultData.lname,
212 nname = oResultData.nname || "", // Guard against null value
219 nname = oResultData.nname || "", // Guard against null value
213 query = sQuery.toLowerCase(),
220 query = sQuery.toLowerCase(),
214 fnameMatchIndex = fname.toLowerCase().indexOf(query),
221 fnameMatchIndex = fname.toLowerCase().indexOf(query),
215 lnameMatchIndex = lname.toLowerCase().indexOf(query),
222 lnameMatchIndex = lname.toLowerCase().indexOf(query),
216 nnameMatchIndex = nname.toLowerCase().indexOf(query),
223 nnameMatchIndex = nname.toLowerCase().indexOf(query),
217 displayfname, displaylname, displaynname;
224 displayfname, displaylname, displaynname;
218
225
219 if(fnameMatchIndex > -1) {
226 if(fnameMatchIndex > -1) {
220 displayfname = highlightMatch(fname, query, fnameMatchIndex);
227 displayfname = highlightMatch(fname, query, fnameMatchIndex);
221 }
228 }
222 else {
229 else {
223 displayfname = fname;
230 displayfname = fname;
224 }
231 }
225
232
226 if(lnameMatchIndex > -1) {
233 if(lnameMatchIndex > -1) {
227 displaylname = highlightMatch(lname, query, lnameMatchIndex);
234 displaylname = highlightMatch(lname, query, lnameMatchIndex);
228 }
235 }
229 else {
236 else {
230 displaylname = lname;
237 displaylname = lname;
231 }
238 }
232
239
233 if(nnameMatchIndex > -1) {
240 if(nnameMatchIndex > -1) {
234 displaynname = "(" + highlightMatch(nname, query, nnameMatchIndex) + ")";
241 displaynname = "(" + highlightMatch(nname, query, nnameMatchIndex) + ")";
235 }
242 }
236 else {
243 else {
237 displaynname = nname ? "(" + nname + ")" : "";
244 displaynname = nname ? "(" + nname + ")" : "";
238 }
245 }
239
246
240 return displayfname + " " + displaylname + " " + displaynname;
247 return displayfname + " " + displaylname + " " + displaynname;
241
248
242 };
249 };
243 oAC_perms.formatResult = custom_formatter;
250 oAC_perms.formatResult = custom_formatter;
244 oAC_owner.formatResult = custom_formatter;
251 oAC_owner.formatResult = custom_formatter;
245
252
246 // Helper function for the formatter
253 // Helper function for the formatter
247 var highlightMatch = function(full, snippet, matchindex) {
254 var highlightMatch = function(full, snippet, matchindex) {
248 return full.substring(0, matchindex) +
255 return full.substring(0, matchindex) +
249 "<span class='match'>" +
256 "<span class='match'>" +
250 full.substr(matchindex, snippet.length) +
257 full.substr(matchindex, snippet.length) +
251 "</span>" +
258 "</span>" +
252 full.substring(matchindex + snippet.length);
259 full.substring(matchindex + snippet.length);
253 };
260 };
254
261
255 var myHandler = function(sType, aArgs) {
262 var myHandler = function(sType, aArgs) {
256 var myAC = aArgs[0]; // reference back to the AC instance
263 var myAC = aArgs[0]; // reference back to the AC instance
257 var elLI = aArgs[1]; // reference to the selected LI element
264 var elLI = aArgs[1]; // reference to the selected LI element
258 var oData = aArgs[2]; // object literal of selected item's result data
265 var oData = aArgs[2]; // object literal of selected item's result data
259 myAC.getInputEl().value = oData.nname;
266 myAC.getInputEl().value = oData.nname;
260 };
267 };
261
268
262 oAC_perms.itemSelectEvent.subscribe(myHandler);
269 oAC_perms.itemSelectEvent.subscribe(myHandler);
263 oAC_owner.itemSelectEvent.subscribe(myHandler);
270 oAC_owner.itemSelectEvent.subscribe(myHandler);
264
271
265 return {
272 return {
266 oDS: oDS,
273 oDS: oDS,
267 oAC_perms: oAC_perms,
274 oAC_perms: oAC_perms,
268 oAC_owner: oAC_owner,
275 oAC_owner: oAC_owner,
269 };
276 };
270 }();
277 }();
271
278
272 </script>
279 </script>
273
280
274 </div>
281 </div>
275 </%def> No newline at end of file
282 </%def>
@@ -1,58 +1,59 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <%inherit file="/base/base.html"/>
2 <%inherit file="/base/base.html"/>
3
3
4 <%def name="title()">
4 <%def name="title()">
5 ${c.repo_name} ${_('Fork')} - ${c.rhodecode_name}
5 ${c.repo_name} ${_('Fork')} - ${c.rhodecode_name}
6 </%def>
6 </%def>
7
7
8 <%def name="breadcrumbs_links()">
8 <%def name="breadcrumbs_links()">
9 ${h.link_to(c.repo_info.repo_name,h.url('summary_home',repo_name=c.repo_info.repo_name))}
9 ${h.link_to(c.repo_info.repo_name,h.url('summary_home',repo_name=c.repo_info.repo_name))}
10 &raquo;
10 &raquo;
11 ${_('fork')}
11 ${_('fork')}
12 </%def>
12 </%def>
13
13
14 <%def name="page_nav()">
14 <%def name="page_nav()">
15 ${self.menu('')}
15 ${self.menu('')}
16 </%def>
16 </%def>
17 <%def name="main()">
17 <%def name="main()">
18 <div class="box">
18 <div class="box">
19 <!-- box / title -->
19 <!-- box / title -->
20 <div class="title">
20 <div class="title">
21 ${self.breadcrumbs()}
21 ${self.breadcrumbs()}
22 </div>
22 </div>
23 ${h.form(url('repo_fork_create_home',repo_name=c.repo_info.repo_name))}
23 ${h.form(url('repo_fork_create_home',repo_name=c.repo_info.repo_name))}
24 <div class="form">
24 <div class="form">
25 <!-- fields -->
25 <!-- fields -->
26 <div class="fields">
26 <div class="fields">
27 <div class="field">
27 <div class="field">
28 <div class="label">
28 <div class="label">
29 <label for="repo_name">${_('Fork name')}:</label>
29 <label for="repo_name">${_('Fork name')}:</label>
30 </div>
30 </div>
31 <div class="input">
31 <div class="input">
32 ${h.text('fork_name',class_="small")}
32 ${h.text('fork_name',class_="small")}
33 ${h.hidden('repo_type',c.repo_info.repo_type)}
33 </div>
34 </div>
34 </div>
35 </div>
35 <div class="field">
36 <div class="field">
36 <div class="label label-textarea">
37 <div class="label label-textarea">
37 <label for="description">${_('Description')}:</label>
38 <label for="description">${_('Description')}:</label>
38 </div>
39 </div>
39 <div class="textarea text-area editor">
40 <div class="textarea text-area editor">
40 ${h.textarea('description',cols=23,rows=5)}
41 ${h.textarea('description',cols=23,rows=5)}
41 </div>
42 </div>
42 </div>
43 </div>
43 <div class="field">
44 <div class="field">
44 <div class="label label-checkbox">
45 <div class="label label-checkbox">
45 <label for="private">${_('Private')}:</label>
46 <label for="private">${_('Private')}:</label>
46 </div>
47 </div>
47 <div class="checkboxes">
48 <div class="checkboxes">
48 ${h.checkbox('private',value="True")}
49 ${h.checkbox('private',value="True")}
49 </div>
50 </div>
50 </div>
51 </div>
51 <div class="buttons">
52 <div class="buttons">
52 ${h.submit('','fork this repository',class_="ui-button ui-widget ui-state-default ui-corner-all")}
53 ${h.submit('','fork this repository',class_="ui-button ui-widget ui-state-default ui-corner-all")}
53 </div>
54 </div>
54 </div>
55 </div>
55 </div>
56 </div>
56 ${h.end_form()}
57 ${h.end_form()}
57 </div>
58 </div>
58 </%def>
59 </%def>
@@ -1,147 +1,147 b''
1 from rhodecode.tests import *
1 from rhodecode.tests import *
2 from rhodecode.model.db import User
2 from rhodecode.model.db import User
3 from rhodecode.lib.auth import check_password
3 from rhodecode.lib.auth import check_password
4
4
5
5
6 class TestLoginController(TestController):
6 class TestLoginController(TestController):
7
7
8 def test_index(self):
8 def test_index(self):
9 response = self.app.get(url(controller='login', action='index'))
9 response = self.app.get(url(controller='login', action='index'))
10 assert response.status == '200 OK', 'Wrong response from login page got %s' % response.status
10 assert response.status == '200 OK', 'Wrong response from login page got %s' % response.status
11 # Test response...
11 # Test response...
12
12
13 def test_login_admin_ok(self):
13 def test_login_admin_ok(self):
14 response = self.app.post(url(controller='login', action='index'),
14 response = self.app.post(url(controller='login', action='index'),
15 {'username':'test_admin',
15 {'username':'test_admin',
16 'password':'test12'})
16 'password':'test12'})
17 assert response.status == '302 Found', 'Wrong response code from login got %s' % response.status
17 assert response.status == '302 Found', 'Wrong response code from login got %s' % response.status
18 assert response.session['rhodecode_user'].username == 'test_admin', 'wrong logged in user'
18 assert response.session['rhodecode_user'].username == 'test_admin', 'wrong logged in user'
19 response = response.follow()
19 response = response.follow()
20 assert 'auto description for vcs_test' in response.body
20 assert 'vcs_test repository' in response.body
21
21
22 def test_login_regular_ok(self):
22 def test_login_regular_ok(self):
23 response = self.app.post(url(controller='login', action='index'),
23 response = self.app.post(url(controller='login', action='index'),
24 {'username':'test_regular',
24 {'username':'test_regular',
25 'password':'test12'})
25 'password':'test12'})
26 print response
26 print response
27 assert response.status == '302 Found', 'Wrong response code from login got %s' % response.status
27 assert response.status == '302 Found', 'Wrong response code from login got %s' % response.status
28 assert response.session['rhodecode_user'].username == 'test_regular', 'wrong logged in user'
28 assert response.session['rhodecode_user'].username == 'test_regular', 'wrong logged in user'
29 response = response.follow()
29 response = response.follow()
30 assert 'auto description for vcs_test' in response.body
30 assert 'vcs_test repository' in response.body
31 assert '<a title="Admin" href="/_admin">' not in response.body
31 assert '<a title="Admin" href="/_admin">' not in response.body
32
32
33 def test_login_ok_came_from(self):
33 def test_login_ok_came_from(self):
34 test_came_from = '/_admin/users'
34 test_came_from = '/_admin/users'
35 response = self.app.post(url(controller='login', action='index', came_from=test_came_from),
35 response = self.app.post(url(controller='login', action='index', came_from=test_came_from),
36 {'username':'test_admin',
36 {'username':'test_admin',
37 'password':'test12'})
37 'password':'test12'})
38 assert response.status == '302 Found', 'Wrong response code from came from redirection'
38 assert response.status == '302 Found', 'Wrong response code from came from redirection'
39 response = response.follow()
39 response = response.follow()
40
40
41 assert response.status == '200 OK', 'Wrong response from login page got %s' % response.status
41 assert response.status == '200 OK', 'Wrong response from login page got %s' % response.status
42 assert 'Users administration' in response.body, 'No proper title in response'
42 assert 'Users administration' in response.body, 'No proper title in response'
43
43
44
44
45 def test_login_short_password(self):
45 def test_login_short_password(self):
46 response = self.app.post(url(controller='login', action='index'),
46 response = self.app.post(url(controller='login', action='index'),
47 {'username':'error',
47 {'username':'error',
48 'password':'test'})
48 'password':'test'})
49 assert response.status == '200 OK', 'Wrong response from login page'
49 assert response.status == '200 OK', 'Wrong response from login page'
50 print response.body
50 print response.body
51 assert 'Enter 6 characters or more' in response.body, 'No error password message in response'
51 assert 'Enter 6 characters or more' in response.body, 'No error password message in response'
52
52
53 def test_login_wrong_username_password(self):
53 def test_login_wrong_username_password(self):
54 response = self.app.post(url(controller='login', action='index'),
54 response = self.app.post(url(controller='login', action='index'),
55 {'username':'error',
55 {'username':'error',
56 'password':'test12'})
56 'password':'test12'})
57 assert response.status == '200 OK', 'Wrong response from login page'
57 assert response.status == '200 OK', 'Wrong response from login page'
58
58
59 assert 'invalid user name' in response.body, 'No error username message in response'
59 assert 'invalid user name' in response.body, 'No error username message in response'
60 assert 'invalid password' in response.body, 'No error password message in response'
60 assert 'invalid password' in response.body, 'No error password message in response'
61
61
62
62
63 def test_register(self):
63 def test_register(self):
64 response = self.app.get(url(controller='login', action='register'))
64 response = self.app.get(url(controller='login', action='register'))
65 assert 'Sign Up to rhodecode' in response.body, 'wrong page for user registration'
65 assert 'Sign Up to rhodecode' in response.body, 'wrong page for user registration'
66
66
67 def test_register_err_same_username(self):
67 def test_register_err_same_username(self):
68 response = self.app.post(url(controller='login', action='register'),
68 response = self.app.post(url(controller='login', action='register'),
69 {'username':'test_admin',
69 {'username':'test_admin',
70 'password':'test',
70 'password':'test',
71 'email':'goodmail@domain.com',
71 'email':'goodmail@domain.com',
72 'name':'test',
72 'name':'test',
73 'lastname':'test'})
73 'lastname':'test'})
74
74
75 assert response.status == '200 OK', 'Wrong response from register page got %s' % response.status
75 assert response.status == '200 OK', 'Wrong response from register page got %s' % response.status
76 assert 'This username already exists' in response.body
76 assert 'This username already exists' in response.body
77
77
78 def test_register_err_wrong_data(self):
78 def test_register_err_wrong_data(self):
79 response = self.app.post(url(controller='login', action='register'),
79 response = self.app.post(url(controller='login', action='register'),
80 {'username':'xs',
80 {'username':'xs',
81 'password':'',
81 'password':'',
82 'email':'goodmailm',
82 'email':'goodmailm',
83 'name':'test',
83 'name':'test',
84 'lastname':'test'})
84 'lastname':'test'})
85
85
86 assert response.status == '200 OK', 'Wrong response from register page got %s' % response.status
86 assert response.status == '200 OK', 'Wrong response from register page got %s' % response.status
87 assert 'An email address must contain a single @' in response.body
87 assert 'An email address must contain a single @' in response.body
88 assert 'Please enter a value' in response.body
88 assert 'Please enter a value' in response.body
89
89
90
90
91
91
92 def test_register_ok(self):
92 def test_register_ok(self):
93 username = 'test_regular4'
93 username = 'test_regular4'
94 password = 'qweqwe'
94 password = 'qweqwe'
95 email = 'marcin@test.com'
95 email = 'marcin@test.com'
96 name = 'testname'
96 name = 'testname'
97 lastname = 'testlastname'
97 lastname = 'testlastname'
98
98
99 response = self.app.post(url(controller='login', action='register'),
99 response = self.app.post(url(controller='login', action='register'),
100 {'username':username,
100 {'username':username,
101 'password':password,
101 'password':password,
102 'email':email,
102 'email':email,
103 'name':name,
103 'name':name,
104 'lastname':lastname})
104 'lastname':lastname})
105 print response.body
105 print response.body
106 assert response.status == '302 Found', 'Wrong response from register page got %s' % response.status
106 assert response.status == '302 Found', 'Wrong response from register page got %s' % response.status
107 assert 'You have successfully registered into rhodecode' in response.session['flash'][0], 'No flash message about user registration'
107 assert 'You have successfully registered into rhodecode' in response.session['flash'][0], 'No flash message about user registration'
108
108
109 ret = self.sa.query(User).filter(User.username == 'test_regular4').one()
109 ret = self.sa.query(User).filter(User.username == 'test_regular4').one()
110 assert ret.username == username , 'field mismatch %s %s' % (ret.username, username)
110 assert ret.username == username , 'field mismatch %s %s' % (ret.username, username)
111 assert check_password(password, ret.password) == True , 'password mismatch'
111 assert check_password(password, ret.password) == True , 'password mismatch'
112 assert ret.email == email , 'field mismatch %s %s' % (ret.email, email)
112 assert ret.email == email , 'field mismatch %s %s' % (ret.email, email)
113 assert ret.name == name , 'field mismatch %s %s' % (ret.name, name)
113 assert ret.name == name , 'field mismatch %s %s' % (ret.name, name)
114 assert ret.lastname == lastname , 'field mismatch %s %s' % (ret.lastname, lastname)
114 assert ret.lastname == lastname , 'field mismatch %s %s' % (ret.lastname, lastname)
115
115
116
116
117 def test_forgot_password_wrong_mail(self):
117 def test_forgot_password_wrong_mail(self):
118 response = self.app.post(url(controller='login', action='password_reset'),
118 response = self.app.post(url(controller='login', action='password_reset'),
119 {'email':'marcin@wrongmail.org', })
119 {'email':'marcin@wrongmail.org', })
120
120
121 assert "That e-mail address doesn't exist" in response.body, 'Missing error message about wrong email'
121 assert "That e-mail address doesn't exist" in response.body, 'Missing error message about wrong email'
122
122
123 def test_forgot_password(self):
123 def test_forgot_password(self):
124 response = self.app.get(url(controller='login', action='password_reset'))
124 response = self.app.get(url(controller='login', action='password_reset'))
125 assert response.status == '200 OK', 'Wrong response from login page got %s' % response.status
125 assert response.status == '200 OK', 'Wrong response from login page got %s' % response.status
126
126
127 username = 'test_password_reset_1'
127 username = 'test_password_reset_1'
128 password = 'qweqwe'
128 password = 'qweqwe'
129 email = 'marcin@python-works.com'
129 email = 'marcin@python-works.com'
130 name = 'passwd'
130 name = 'passwd'
131 lastname = 'reset'
131 lastname = 'reset'
132
132
133 response = self.app.post(url(controller='login', action='register'),
133 response = self.app.post(url(controller='login', action='register'),
134 {'username':username,
134 {'username':username,
135 'password':password,
135 'password':password,
136 'email':email,
136 'email':email,
137 'name':name,
137 'name':name,
138 'lastname':lastname})
138 'lastname':lastname})
139 #register new user for email test
139 #register new user for email test
140 response = self.app.post(url(controller='login', action='password_reset'),
140 response = self.app.post(url(controller='login', action='password_reset'),
141 {'email':email, })
141 {'email':email, })
142 print response.session['flash']
142 print response.session['flash']
143 assert 'You have successfully registered into rhodecode' in response.session['flash'][0], 'No flash message about user registration'
143 assert 'You have successfully registered into rhodecode' in response.session['flash'][0], 'No flash message about user registration'
144 assert 'Your new password was sent' in response.session['flash'][1], 'No flash message about password reset'
144 assert 'Your new password was sent' in response.session['flash'][1], 'No flash message about password reset'
145
145
146
146
147
147
General Comments 0
You need to be logged in to leave comments. Login now