##// END OF EJS Templates
implemented #84 downloads can be enabled/disabled per each repository from now.
marcink -
r962:72f008ed beta
parent child Browse files
Show More
@@ -1,267 +1,271 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.files
3 rhodecode.controllers.files
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 Files controller for RhodeCode
6 Files controller for RhodeCode
7
7
8 :created_on: Apr 21, 2010
8 :created_on: Apr 21, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software; you can redistribute it and/or
13 # This program is free software; you can redistribute it and/or
14 # modify it under the terms of the GNU General Public License
14 # modify it under the terms of the GNU General Public License
15 # as published by the Free Software Foundation; version 2
15 # as published by the Free Software Foundation; version 2
16 # of the License or (at your opinion) any later version of the license.
16 # of the License or (at your opinion) any later version of the license.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program; if not, write to the Free Software
24 # along with this program; if not, write to the Free Software
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
26 # MA 02110-1301, USA.
26 # MA 02110-1301, USA.
27 import tempfile
27 import tempfile
28 import logging
28 import logging
29 import rhodecode.lib.helpers as h
29 import rhodecode.lib.helpers as h
30
30
31 from mercurial import archival
31 from mercurial import archival
32
32
33 from pylons import request, response, session, tmpl_context as c, url
33 from pylons import request, response, session, tmpl_context as c, url
34 from pylons.i18n.translation import _
34 from pylons.i18n.translation import _
35 from pylons.controllers.util import redirect
35 from pylons.controllers.util import redirect
36
36
37 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
37 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
38 from rhodecode.lib.base import BaseController, render
38 from rhodecode.lib.base import BaseController, render
39 from rhodecode.lib.utils import EmptyChangeset
39 from rhodecode.lib.utils import EmptyChangeset
40 from rhodecode.model.scm import ScmModel
40 from rhodecode.model.scm import ScmModel
41
41
42 from vcs.backends import ARCHIVE_SPECS
42 from vcs.backends import ARCHIVE_SPECS
43 from vcs.exceptions import RepositoryError, ChangesetError, \
43 from vcs.exceptions import RepositoryError, ChangesetError, \
44 ChangesetDoesNotExistError, EmptyRepositoryError, ImproperArchiveTypeError
44 ChangesetDoesNotExistError, EmptyRepositoryError, ImproperArchiveTypeError
45 from vcs.nodes import FileNode
45 from vcs.nodes import FileNode
46 from vcs.utils import diffs as differ
46 from vcs.utils import diffs as differ
47
47
48 log = logging.getLogger(__name__)
48 log = logging.getLogger(__name__)
49
49
50 class FilesController(BaseController):
50 class FilesController(BaseController):
51
51
52 @LoginRequired()
52 @LoginRequired()
53 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
53 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
54 'repository.admin')
54 'repository.admin')
55 def __before__(self):
55 def __before__(self):
56 super(FilesController, self).__before__()
56 super(FilesController, self).__before__()
57 c.cut_off_limit = self.cut_off_limit
57 c.cut_off_limit = self.cut_off_limit
58
58
59 def index(self, repo_name, revision, f_path):
59 def index(self, repo_name, revision, f_path):
60 hg_model = ScmModel()
60 hg_model = ScmModel()
61 c.repo = hg_model.get_repo(c.repo_name)
61 c.repo = hg_model.get_repo(c.repo_name)
62
62
63 try:
63 try:
64 #reditect to given revision from form
64 #reditect to given revision from form
65 post_revision = request.POST.get('at_rev', None)
65 post_revision = request.POST.get('at_rev', None)
66 if post_revision:
66 if post_revision:
67 post_revision = c.repo.get_changeset(post_revision).raw_id
67 post_revision = c.repo.get_changeset(post_revision).raw_id
68 redirect(url('files_home', repo_name=c.repo_name,
68 redirect(url('files_home', repo_name=c.repo_name,
69 revision=post_revision, f_path=f_path))
69 revision=post_revision, f_path=f_path))
70
70
71 c.branch = request.GET.get('branch', None)
71 c.branch = request.GET.get('branch', None)
72
72
73 c.f_path = f_path
73 c.f_path = f_path
74
74
75 c.changeset = c.repo.get_changeset(revision)
75 c.changeset = c.repo.get_changeset(revision)
76 cur_rev = c.changeset.revision
76 cur_rev = c.changeset.revision
77
77
78 #prev link
78 #prev link
79 try:
79 try:
80 prev_rev = c.repo.get_changeset(cur_rev).prev(c.branch).raw_id
80 prev_rev = c.repo.get_changeset(cur_rev).prev(c.branch).raw_id
81 c.url_prev = url('files_home', repo_name=c.repo_name,
81 c.url_prev = url('files_home', repo_name=c.repo_name,
82 revision=prev_rev, f_path=f_path)
82 revision=prev_rev, f_path=f_path)
83 if c.branch:
83 if c.branch:
84 c.url_prev += '?branch=%s' % c.branch
84 c.url_prev += '?branch=%s' % c.branch
85 except ChangesetDoesNotExistError:
85 except ChangesetDoesNotExistError:
86 c.url_prev = '#'
86 c.url_prev = '#'
87
87
88 #next link
88 #next link
89 try:
89 try:
90 next_rev = c.repo.get_changeset(cur_rev).next(c.branch).raw_id
90 next_rev = c.repo.get_changeset(cur_rev).next(c.branch).raw_id
91 c.url_next = url('files_home', repo_name=c.repo_name,
91 c.url_next = url('files_home', repo_name=c.repo_name,
92 revision=next_rev, f_path=f_path)
92 revision=next_rev, f_path=f_path)
93 if c.branch:
93 if c.branch:
94 c.url_next += '?branch=%s' % c.branch
94 c.url_next += '?branch=%s' % c.branch
95 except ChangesetDoesNotExistError:
95 except ChangesetDoesNotExistError:
96 c.url_next = '#'
96 c.url_next = '#'
97
97
98 #files
98 #files
99 try:
99 try:
100 c.files_list = c.changeset.get_node(f_path)
100 c.files_list = c.changeset.get_node(f_path)
101 c.file_history = self._get_history(c.repo, c.files_list, f_path)
101 c.file_history = self._get_history(c.repo, c.files_list, f_path)
102 except RepositoryError, e:
102 except RepositoryError, e:
103 h.flash(str(e), category='warning')
103 h.flash(str(e), category='warning')
104 redirect(h.url('files_home', repo_name=repo_name, revision=revision))
104 redirect(h.url('files_home', repo_name=repo_name, revision=revision))
105
105
106 except EmptyRepositoryError, e:
106 except EmptyRepositoryError, e:
107 h.flash(_('There are no files yet'), category='warning')
107 h.flash(_('There are no files yet'), category='warning')
108 redirect(h.url('summary_home', repo_name=repo_name))
108 redirect(h.url('summary_home', repo_name=repo_name))
109
109
110 except RepositoryError, e:
110 except RepositoryError, e:
111 h.flash(str(e), category='warning')
111 h.flash(str(e), category='warning')
112 redirect(h.url('files_home', repo_name=repo_name, revision='tip'))
112 redirect(h.url('files_home', repo_name=repo_name, revision='tip'))
113
113
114
114
115
115
116 return render('files/files.html')
116 return render('files/files.html')
117
117
118 def rawfile(self, repo_name, revision, f_path):
118 def rawfile(self, repo_name, revision, f_path):
119 hg_model = ScmModel()
119 hg_model = ScmModel()
120 c.repo = hg_model.get_repo(c.repo_name)
120 c.repo = hg_model.get_repo(c.repo_name)
121 file_node = c.repo.get_changeset(revision).get_node(f_path)
121 file_node = c.repo.get_changeset(revision).get_node(f_path)
122 response.content_type = file_node.mimetype
122 response.content_type = file_node.mimetype
123 response.content_disposition = 'attachment; filename=%s' \
123 response.content_disposition = 'attachment; filename=%s' \
124 % f_path.split('/')[-1]
124 % f_path.split('/')[-1]
125 return file_node.content
125 return file_node.content
126
126
127 def raw(self, repo_name, revision, f_path):
127 def raw(self, repo_name, revision, f_path):
128 hg_model = ScmModel()
128 hg_model = ScmModel()
129 c.repo = hg_model.get_repo(c.repo_name)
129 c.repo = hg_model.get_repo(c.repo_name)
130 file_node = c.repo.get_changeset(revision).get_node(f_path)
130 file_node = c.repo.get_changeset(revision).get_node(f_path)
131 response.content_type = 'text/plain'
131 response.content_type = 'text/plain'
132
132
133 return file_node.content
133 return file_node.content
134
134
135 def annotate(self, repo_name, revision, f_path):
135 def annotate(self, repo_name, revision, f_path):
136 hg_model = ScmModel()
136 hg_model = ScmModel()
137 c.repo = hg_model.get_repo(c.repo_name)
137 c.repo = hg_model.get_repo(c.repo_name)
138
138
139 try:
139 try:
140 c.cs = c.repo.get_changeset(revision)
140 c.cs = c.repo.get_changeset(revision)
141 c.file = c.cs.get_node(f_path)
141 c.file = c.cs.get_node(f_path)
142 except RepositoryError, e:
142 except RepositoryError, e:
143 h.flash(str(e), category='warning')
143 h.flash(str(e), category='warning')
144 redirect(h.url('files_home', repo_name=repo_name, revision=revision))
144 redirect(h.url('files_home', repo_name=repo_name, revision=revision))
145
145
146 c.file_history = self._get_history(c.repo, c.file, f_path)
146 c.file_history = self._get_history(c.repo, c.file, f_path)
147
147
148 c.f_path = f_path
148 c.f_path = f_path
149
149
150 return render('files/files_annotate.html')
150 return render('files/files_annotate.html')
151
151
152 def archivefile(self, repo_name, fname):
152 def archivefile(self, repo_name, fname):
153
153
154 fileformat = None
154 fileformat = None
155 revision = None
155 revision = None
156 ext = None
156 ext = None
157
157
158 for a_type, ext_data in ARCHIVE_SPECS.items():
158 for a_type, ext_data in ARCHIVE_SPECS.items():
159 archive_spec = fname.split(ext_data[1])
159 archive_spec = fname.split(ext_data[1])
160 if len(archive_spec) == 2 and archive_spec[1] == '':
160 if len(archive_spec) == 2 and archive_spec[1] == '':
161 fileformat = a_type or ext_data[1]
161 fileformat = a_type or ext_data[1]
162 revision = archive_spec[0]
162 revision = archive_spec[0]
163 ext = ext_data[1]
163 ext = ext_data[1]
164
164
165 try:
165 try:
166 repo = ScmModel().get_repo(repo_name)
166 repo = ScmModel().get_repo(repo_name)
167
168 if repo.dbrepo.enable_downloads is False:
169 return _('downloads disabled')
170
167 cs = repo.get_changeset(revision)
171 cs = repo.get_changeset(revision)
168 content_type = ARCHIVE_SPECS[fileformat][0]
172 content_type = ARCHIVE_SPECS[fileformat][0]
169 except ChangesetDoesNotExistError:
173 except ChangesetDoesNotExistError:
170 return _('Unknown revision %s') % revision
174 return _('Unknown revision %s') % revision
171 except EmptyRepositoryError:
175 except EmptyRepositoryError:
172 return _('Empty repository')
176 return _('Empty repository')
173 except (ImproperArchiveTypeError, KeyError):
177 except (ImproperArchiveTypeError, KeyError):
174 return _('Unknown archive type')
178 return _('Unknown archive type')
175
179
176 response.content_type = content_type
180 response.content_type = content_type
177 response.content_disposition = 'attachment; filename=%s-%s%s' \
181 response.content_disposition = 'attachment; filename=%s-%s%s' \
178 % (repo_name, revision, ext)
182 % (repo_name, revision, ext)
179
183
180 return cs.get_chunked_archive(kind=fileformat)
184 return cs.get_chunked_archive(kind=fileformat)
181
185
182
186
183 def diff(self, repo_name, f_path):
187 def diff(self, repo_name, f_path):
184 hg_model = ScmModel()
188 hg_model = ScmModel()
185 diff1 = request.GET.get('diff1')
189 diff1 = request.GET.get('diff1')
186 diff2 = request.GET.get('diff2')
190 diff2 = request.GET.get('diff2')
187 c.action = request.GET.get('diff')
191 c.action = request.GET.get('diff')
188 c.no_changes = diff1 == diff2
192 c.no_changes = diff1 == diff2
189 c.f_path = f_path
193 c.f_path = f_path
190 c.repo = hg_model.get_repo(c.repo_name)
194 c.repo = hg_model.get_repo(c.repo_name)
191
195
192 try:
196 try:
193 if diff1 not in ['', None, 'None', '0' * 12, '0' * 40]:
197 if diff1 not in ['', None, 'None', '0' * 12, '0' * 40]:
194 c.changeset_1 = c.repo.get_changeset(diff1)
198 c.changeset_1 = c.repo.get_changeset(diff1)
195 node1 = c.changeset_1.get_node(f_path)
199 node1 = c.changeset_1.get_node(f_path)
196 else:
200 else:
197 c.changeset_1 = EmptyChangeset()
201 c.changeset_1 = EmptyChangeset()
198 node1 = FileNode('.', '', changeset=c.changeset_1)
202 node1 = FileNode('.', '', changeset=c.changeset_1)
199
203
200 if diff2 not in ['', None, 'None', '0' * 12, '0' * 40]:
204 if diff2 not in ['', None, 'None', '0' * 12, '0' * 40]:
201 c.changeset_2 = c.repo.get_changeset(diff2)
205 c.changeset_2 = c.repo.get_changeset(diff2)
202 node2 = c.changeset_2.get_node(f_path)
206 node2 = c.changeset_2.get_node(f_path)
203 else:
207 else:
204 c.changeset_2 = EmptyChangeset()
208 c.changeset_2 = EmptyChangeset()
205 node2 = FileNode('.', '', changeset=c.changeset_2)
209 node2 = FileNode('.', '', changeset=c.changeset_2)
206 except RepositoryError:
210 except RepositoryError:
207 return redirect(url('files_home',
211 return redirect(url('files_home',
208 repo_name=c.repo_name, f_path=f_path))
212 repo_name=c.repo_name, f_path=f_path))
209
213
210 f_udiff = differ.get_udiff(node1, node2)
214 f_udiff = differ.get_udiff(node1, node2)
211 diff = differ.DiffProcessor(f_udiff)
215 diff = differ.DiffProcessor(f_udiff)
212
216
213 if c.action == 'download':
217 if c.action == 'download':
214 diff_name = '%s_vs_%s.diff' % (diff1, diff2)
218 diff_name = '%s_vs_%s.diff' % (diff1, diff2)
215 response.content_type = 'text/plain'
219 response.content_type = 'text/plain'
216 response.content_disposition = 'attachment; filename=%s' \
220 response.content_disposition = 'attachment; filename=%s' \
217 % diff_name
221 % diff_name
218 return diff.raw_diff()
222 return diff.raw_diff()
219
223
220 elif c.action == 'raw':
224 elif c.action == 'raw':
221 response.content_type = 'text/plain'
225 response.content_type = 'text/plain'
222 return diff.raw_diff()
226 return diff.raw_diff()
223
227
224 elif c.action == 'diff':
228 elif c.action == 'diff':
225 if node1.size > self.cut_off_limit or node2.size > self.cut_off_limit:
229 if node1.size > self.cut_off_limit or node2.size > self.cut_off_limit:
226 c.cur_diff = _('Diff is to big to display')
230 c.cur_diff = _('Diff is to big to display')
227 else:
231 else:
228 c.cur_diff = diff.as_html()
232 c.cur_diff = diff.as_html()
229 else:
233 else:
230 #default option
234 #default option
231 if node1.size > self.cut_off_limit or node2.size > self.cut_off_limit:
235 if node1.size > self.cut_off_limit or node2.size > self.cut_off_limit:
232 c.cur_diff = _('Diff is to big to display')
236 c.cur_diff = _('Diff is to big to display')
233 else:
237 else:
234 c.cur_diff = diff.as_html()
238 c.cur_diff = diff.as_html()
235
239
236 if not c.cur_diff: c.no_changes = True
240 if not c.cur_diff: c.no_changes = True
237 return render('files/file_diff.html')
241 return render('files/file_diff.html')
238
242
239 def _get_history(self, repo, node, f_path):
243 def _get_history(self, repo, node, f_path):
240 from vcs.nodes import NodeKind
244 from vcs.nodes import NodeKind
241 if not node.kind is NodeKind.FILE:
245 if not node.kind is NodeKind.FILE:
242 return []
246 return []
243 changesets = node.history
247 changesets = node.history
244 hist_l = []
248 hist_l = []
245
249
246 changesets_group = ([], _("Changesets"))
250 changesets_group = ([], _("Changesets"))
247 branches_group = ([], _("Branches"))
251 branches_group = ([], _("Branches"))
248 tags_group = ([], _("Tags"))
252 tags_group = ([], _("Tags"))
249
253
250 for chs in changesets:
254 for chs in changesets:
251 n_desc = 'r%s:%s' % (chs.revision, chs.short_id)
255 n_desc = 'r%s:%s' % (chs.revision, chs.short_id)
252 changesets_group[0].append((chs.raw_id, n_desc,))
256 changesets_group[0].append((chs.raw_id, n_desc,))
253
257
254 hist_l.append(changesets_group)
258 hist_l.append(changesets_group)
255
259
256 for name, chs in c.repository_branches.items():
260 for name, chs in c.repository_branches.items():
257 #chs = chs.split(':')[-1]
261 #chs = chs.split(':')[-1]
258 branches_group[0].append((chs, name),)
262 branches_group[0].append((chs, name),)
259 hist_l.append(branches_group)
263 hist_l.append(branches_group)
260
264
261 for name, chs in c.repository_tags.items():
265 for name, chs in c.repository_tags.items():
262 #chs = chs.split(':')[-1]
266 #chs = chs.split(':')[-1]
263 tags_group[0].append((chs, name),)
267 tags_group[0].append((chs, name),)
264 hist_l.append(tags_group)
268 hist_l.append(tags_group)
265
269
266 return hist_l
270 return hist_l
267
271
@@ -1,168 +1,170 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.summary
3 rhodecode.controllers.summary
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 Summary controller for Rhodecode
6 Summary controller for Rhodecode
7
7
8 :created_on: Apr 18, 2010
8 :created_on: Apr 18, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software; you can redistribute it and/or
13 # This program is free software; you can redistribute it and/or
14 # modify it under the terms of the GNU General Public License
14 # modify it under the terms of the GNU General Public License
15 # as published by the Free Software Foundation; version 2
15 # as published by the Free Software Foundation; version 2
16 # of the License or (at your opinion) any later version of the license.
16 # of the License or (at your opinion) any later version of the license.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program; if not, write to the Free Software
24 # along with this program; if not, write to the Free Software
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
26 # MA 02110-1301, USA.
26 # MA 02110-1301, USA.
27
27
28 import calendar
28 import calendar
29 import logging
29 import logging
30 from time import mktime
30 from time import mktime
31 from datetime import datetime, timedelta, date
31 from datetime import datetime, timedelta, date
32
32
33 from vcs.exceptions import ChangesetError
33 from vcs.exceptions import ChangesetError
34
34
35 from pylons import tmpl_context as c, request, url
35 from pylons import tmpl_context as c, request, url
36 from pylons.i18n.translation import _
36 from pylons.i18n.translation import _
37
37
38 from rhodecode.model.scm import ScmModel
38 from rhodecode.model.scm import ScmModel
39 from rhodecode.model.db import Statistics
39 from rhodecode.model.db import Statistics
40
40
41 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
41 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
42 from rhodecode.lib.base import BaseController, render
42 from rhodecode.lib.base import BaseController, render
43 from rhodecode.lib.utils import OrderedDict, EmptyChangeset
43 from rhodecode.lib.utils import OrderedDict, EmptyChangeset
44
44
45 from rhodecode.lib.celerylib import run_task
45 from rhodecode.lib.celerylib import run_task
46 from rhodecode.lib.celerylib.tasks import get_commits_stats
46 from rhodecode.lib.celerylib.tasks import get_commits_stats
47
47
48 from webhelpers.paginate import Page
48 from webhelpers.paginate import Page
49
49
50 try:
50 try:
51 import json
51 import json
52 except ImportError:
52 except ImportError:
53 #python 2.5 compatibility
53 #python 2.5 compatibility
54 import simplejson as json
54 import simplejson as json
55 log = logging.getLogger(__name__)
55 log = logging.getLogger(__name__)
56
56
57 class SummaryController(BaseController):
57 class SummaryController(BaseController):
58
58
59 @LoginRequired()
59 @LoginRequired()
60 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
60 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
61 'repository.admin')
61 'repository.admin')
62 def __before__(self):
62 def __before__(self):
63 super(SummaryController, self).__before__()
63 super(SummaryController, self).__before__()
64
64
65 def index(self):
65 def index(self):
66 scm_model = ScmModel()
66 scm_model = ScmModel()
67 c.repo_info = scm_model.get_repo(c.repo_name)
67 c.repo_info = scm_model.get_repo(c.repo_name)
68 c.following = scm_model.is_following_repo(c.repo_name,
68 c.following = scm_model.is_following_repo(c.repo_name,
69 c.rhodecode_user.user_id)
69 c.rhodecode_user.user_id)
70 def url_generator(**kw):
70 def url_generator(**kw):
71 return url('shortlog_home', repo_name=c.repo_name, **kw)
71 return url('shortlog_home', repo_name=c.repo_name, **kw)
72
72
73 c.repo_changesets = Page(c.repo_info, page=1, items_per_page=10,
73 c.repo_changesets = Page(c.repo_info, page=1, items_per_page=10,
74 url=url_generator)
74 url=url_generator)
75
75
76 e = request.environ
76 e = request.environ
77
77
78 if self.rhodecode_user.username == 'default':
78 if self.rhodecode_user.username == 'default':
79 #for default(anonymous) user we don't need to pass credentials
79 #for default(anonymous) user we don't need to pass credentials
80 username = ''
80 username = ''
81 password = ''
81 password = ''
82 else:
82 else:
83 username = str(c.rhodecode_user.username)
83 username = str(c.rhodecode_user.username)
84 password = '@'
84 password = '@'
85
85
86 uri = u'%(protocol)s://%(user)s%(password)s%(host)s%(prefix)s/%(repo_name)s' % {
86 uri = u'%(protocol)s://%(user)s%(password)s%(host)s%(prefix)s/%(repo_name)s' % {
87 'protocol': e.get('wsgi.url_scheme'),
87 'protocol': e.get('wsgi.url_scheme'),
88 'user':username,
88 'user':username,
89 'password':password,
89 'password':password,
90 'host':e.get('HTTP_HOST'),
90 'host':e.get('HTTP_HOST'),
91 'prefix':e.get('SCRIPT_NAME'),
91 'prefix':e.get('SCRIPT_NAME'),
92 'repo_name':c.repo_name, }
92 'repo_name':c.repo_name, }
93 c.clone_repo_url = uri
93 c.clone_repo_url = uri
94 c.repo_tags = OrderedDict()
94 c.repo_tags = OrderedDict()
95 for name, hash in c.repo_info.tags.items()[:10]:
95 for name, hash in c.repo_info.tags.items()[:10]:
96 try:
96 try:
97 c.repo_tags[name] = c.repo_info.get_changeset(hash)
97 c.repo_tags[name] = c.repo_info.get_changeset(hash)
98 except ChangesetError:
98 except ChangesetError:
99 c.repo_tags[name] = EmptyChangeset(hash)
99 c.repo_tags[name] = EmptyChangeset(hash)
100
100
101 c.repo_branches = OrderedDict()
101 c.repo_branches = OrderedDict()
102 for name, hash in c.repo_info.branches.items()[:10]:
102 for name, hash in c.repo_info.branches.items()[:10]:
103 try:
103 try:
104 c.repo_branches[name] = c.repo_info.get_changeset(hash)
104 c.repo_branches[name] = c.repo_info.get_changeset(hash)
105 except ChangesetError:
105 except ChangesetError:
106 c.repo_branches[name] = EmptyChangeset(hash)
106 c.repo_branches[name] = EmptyChangeset(hash)
107
107
108 td = date.today() + timedelta(days=1)
108 td = date.today() + timedelta(days=1)
109 td_1m = td - timedelta(days=calendar.mdays[td.month])
109 td_1m = td - timedelta(days=calendar.mdays[td.month])
110 td_1y = td - timedelta(days=365)
110 td_1y = td - timedelta(days=365)
111
111
112 ts_min_m = mktime(td_1m.timetuple())
112 ts_min_m = mktime(td_1m.timetuple())
113 ts_min_y = mktime(td_1y.timetuple())
113 ts_min_y = mktime(td_1y.timetuple())
114 ts_max_y = mktime(td.timetuple())
114 ts_max_y = mktime(td.timetuple())
115
115
116 if c.repo_info.dbrepo.enable_statistics:
116 if c.repo_info.dbrepo.enable_statistics:
117 c.no_data_msg = _('No data loaded yet')
117 c.no_data_msg = _('No data loaded yet')
118 run_task(get_commits_stats, c.repo_info.name, ts_min_y, ts_max_y)
118 run_task(get_commits_stats, c.repo_info.name, ts_min_y, ts_max_y)
119 else:
119 else:
120 c.no_data_msg = _('Statistics update are disabled for this repository')
120 c.no_data_msg = _('Statistics update are disabled for this repository')
121 c.ts_min = ts_min_m
121 c.ts_min = ts_min_m
122 c.ts_max = ts_max_y
122 c.ts_max = ts_max_y
123
123
124 stats = self.sa.query(Statistics)\
124 stats = self.sa.query(Statistics)\
125 .filter(Statistics.repository == c.repo_info.dbrepo)\
125 .filter(Statistics.repository == c.repo_info.dbrepo)\
126 .scalar()
126 .scalar()
127
127
128
128
129 if stats and stats.languages:
129 if stats and stats.languages:
130 c.no_data = False is c.repo_info.dbrepo.enable_statistics
130 c.no_data = False is c.repo_info.dbrepo.enable_statistics
131 lang_stats = json.loads(stats.languages)
131 lang_stats = json.loads(stats.languages)
132 c.commit_data = stats.commit_activity
132 c.commit_data = stats.commit_activity
133 c.overview_data = stats.commit_activity_combined
133 c.overview_data = stats.commit_activity_combined
134 c.trending_languages = json.dumps(OrderedDict(
134 c.trending_languages = json.dumps(OrderedDict(
135 sorted(lang_stats.items(), reverse=True,
135 sorted(lang_stats.items(), reverse=True,
136 key=lambda k: k[1])[:10]
136 key=lambda k: k[1])[:10]
137 )
137 )
138 )
138 )
139 else:
139 else:
140 c.commit_data = json.dumps({})
140 c.commit_data = json.dumps({})
141 c.overview_data = json.dumps([[ts_min_y, 0], [ts_max_y, 10] ])
141 c.overview_data = json.dumps([[ts_min_y, 0], [ts_max_y, 10] ])
142 c.trending_languages = json.dumps({})
142 c.trending_languages = json.dumps({})
143 c.no_data = True
143 c.no_data = True
144
144
145 c.enable_downloads = c.repo_info.dbrepo.enable_downloads
146 if c.enable_downloads:
145 c.download_options = self._get_download_links(c.repo_info)
147 c.download_options = self._get_download_links(c.repo_info)
146
148
147 return render('summary/summary.html')
149 return render('summary/summary.html')
148
150
149
151
150
152
151 def _get_download_links(self, repo):
153 def _get_download_links(self, repo):
152
154
153 download_l = []
155 download_l = []
154
156
155 branches_group = ([], _("Branches"))
157 branches_group = ([], _("Branches"))
156 tags_group = ([], _("Tags"))
158 tags_group = ([], _("Tags"))
157
159
158 for name, chs in c.repository_branches.items():
160 for name, chs in c.repository_branches.items():
159 #chs = chs.split(':')[-1]
161 #chs = chs.split(':')[-1]
160 branches_group[0].append((chs, name),)
162 branches_group[0].append((chs, name),)
161 download_l.append(branches_group)
163 download_l.append(branches_group)
162
164
163 for name, chs in c.repository_tags.items():
165 for name, chs in c.repository_tags.items():
164 #chs = chs.split(':')[-1]
166 #chs = chs.split(':')[-1]
165 tags_group[0].append((chs, name),)
167 tags_group[0].append((chs, name),)
166 download_l.append(tags_group)
168 download_l.append(tags_group)
167
169
168 return download_l
170 return download_l
@@ -1,330 +1,331 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.model.db
3 rhodecode.model.db
4 ~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~
5
5
6 Database Models for RhodeCode
6 Database Models for RhodeCode
7
7
8 :created_on: Apr 08, 2010
8 :created_on: Apr 08, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software; you can redistribute it and/or
13 # This program is free software; you can redistribute it and/or
14 # modify it under the terms of the GNU General Public License
14 # modify it under the terms of the GNU General Public License
15 # as published by the Free Software Foundation; version 2
15 # as published by the Free Software Foundation; version 2
16 # of the License or (at your opinion) any later version of the license.
16 # of the License or (at your opinion) any later version of the license.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program; if not, write to the Free Software
24 # along with this program; if not, write to the Free Software
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
26 # MA 02110-1301, USA.
26 # MA 02110-1301, USA.
27 import logging
27 import logging
28 import datetime
28 import datetime
29
29
30 from sqlalchemy import *
30 from sqlalchemy import *
31 from sqlalchemy.exc import DatabaseError
31 from sqlalchemy.exc import DatabaseError
32 from sqlalchemy.orm import relation, backref, class_mapper
32 from sqlalchemy.orm import relation, backref, class_mapper
33 from sqlalchemy.orm.session import Session
33 from sqlalchemy.orm.session import Session
34
34
35 from rhodecode.model.meta import Base
35 from rhodecode.model.meta import Base
36
36
37 log = logging.getLogger(__name__)
37 log = logging.getLogger(__name__)
38
38
39 class BaseModel(object):
39 class BaseModel(object):
40
40
41 @classmethod
41 @classmethod
42 def _get_keys(cls):
42 def _get_keys(cls):
43 """return column names for this model """
43 """return column names for this model """
44 return class_mapper(cls).c.keys()
44 return class_mapper(cls).c.keys()
45
45
46 def get_dict(self):
46 def get_dict(self):
47 """return dict with keys and values corresponding
47 """return dict with keys and values corresponding
48 to this model data """
48 to this model data """
49
49
50 d = {}
50 d = {}
51 for k in self._get_keys():
51 for k in self._get_keys():
52 d[k] = getattr(self, k)
52 d[k] = getattr(self, k)
53 return d
53 return d
54
54
55 def get_appstruct(self):
55 def get_appstruct(self):
56 """return list with keys and values tupples corresponding
56 """return list with keys and values tupples corresponding
57 to this model data """
57 to this model data """
58
58
59 l = []
59 l = []
60 for k in self._get_keys():
60 for k in self._get_keys():
61 l.append((k, getattr(self, k),))
61 l.append((k, getattr(self, k),))
62 return l
62 return l
63
63
64 def populate_obj(self, populate_dict):
64 def populate_obj(self, populate_dict):
65 """populate model with data from given populate_dict"""
65 """populate model with data from given populate_dict"""
66
66
67 for k in self._get_keys():
67 for k in self._get_keys():
68 if k in populate_dict:
68 if k in populate_dict:
69 setattr(self, k, populate_dict[k])
69 setattr(self, k, populate_dict[k])
70
70
71 class RhodeCodeSettings(Base, BaseModel):
71 class RhodeCodeSettings(Base, BaseModel):
72 __tablename__ = 'rhodecode_settings'
72 __tablename__ = 'rhodecode_settings'
73 __table_args__ = (UniqueConstraint('app_settings_name'), {'useexisting':True})
73 __table_args__ = (UniqueConstraint('app_settings_name'), {'useexisting':True})
74 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
74 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
75 app_settings_name = Column("app_settings_name", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
75 app_settings_name = Column("app_settings_name", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
76 app_settings_value = Column("app_settings_value", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
76 app_settings_value = Column("app_settings_value", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
77
77
78 def __init__(self, k='', v=''):
78 def __init__(self, k='', v=''):
79 self.app_settings_name = k
79 self.app_settings_name = k
80 self.app_settings_value = v
80 self.app_settings_value = v
81
81
82 def __repr__(self):
82 def __repr__(self):
83 return "<%s('%s:%s')>" % (self.__class__.__name__,
83 return "<%s('%s:%s')>" % (self.__class__.__name__,
84 self.app_settings_name, self.app_settings_value)
84 self.app_settings_name, self.app_settings_value)
85
85
86 class RhodeCodeUi(Base, BaseModel):
86 class RhodeCodeUi(Base, BaseModel):
87 __tablename__ = 'rhodecode_ui'
87 __tablename__ = 'rhodecode_ui'
88 __table_args__ = {'useexisting':True}
88 __table_args__ = {'useexisting':True}
89 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
89 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
90 ui_section = Column("ui_section", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
90 ui_section = Column("ui_section", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
91 ui_key = Column("ui_key", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
91 ui_key = Column("ui_key", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
92 ui_value = Column("ui_value", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
92 ui_value = Column("ui_value", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
93 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
93 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
94
94
95
95
96 class User(Base, BaseModel):
96 class User(Base, BaseModel):
97 __tablename__ = 'users'
97 __tablename__ = 'users'
98 __table_args__ = (UniqueConstraint('username'), UniqueConstraint('email'), {'useexisting':True})
98 __table_args__ = (UniqueConstraint('username'), UniqueConstraint('email'), {'useexisting':True})
99 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
99 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
100 username = Column("username", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
100 username = Column("username", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
101 password = Column("password", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
101 password = Column("password", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
102 active = Column("active", Boolean(), nullable=True, unique=None, default=None)
102 active = Column("active", Boolean(), nullable=True, unique=None, default=None)
103 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
103 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
104 name = Column("name", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
104 name = Column("name", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
105 lastname = Column("lastname", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
105 lastname = Column("lastname", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
106 email = Column("email", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
106 email = Column("email", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
107 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
107 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
108 is_ldap = Column("is_ldap", Boolean(), nullable=False, unique=None, default=False)
108 is_ldap = Column("is_ldap", Boolean(), nullable=False, unique=None, default=False)
109
109
110 user_log = relation('UserLog', cascade='all')
110 user_log = relation('UserLog', cascade='all')
111 user_perms = relation('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
111 user_perms = relation('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
112
112
113 repositories = relation('Repository')
113 repositories = relation('Repository')
114 user_followers = relation('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
114 user_followers = relation('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
115
115
116 @property
116 @property
117 def full_contact(self):
117 def full_contact(self):
118 return '%s %s <%s>' % (self.name, self.lastname, self.email)
118 return '%s %s <%s>' % (self.name, self.lastname, self.email)
119
119
120
120
121 @property
121 @property
122 def is_admin(self):
122 def is_admin(self):
123 return self.admin
123 return self.admin
124
124
125 def __repr__(self):
125 def __repr__(self):
126 return "<%s('id:%s:%s')>" % (self.__class__.__name__,
126 return "<%s('id:%s:%s')>" % (self.__class__.__name__,
127 self.user_id, self.username)
127 self.user_id, self.username)
128
128
129 def update_lastlogin(self):
129 def update_lastlogin(self):
130 """Update user lastlogin"""
130 """Update user lastlogin"""
131
131
132 try:
132 try:
133 session = Session.object_session(self)
133 session = Session.object_session(self)
134 self.last_login = datetime.datetime.now()
134 self.last_login = datetime.datetime.now()
135 session.add(self)
135 session.add(self)
136 session.commit()
136 session.commit()
137 log.debug('updated user %s lastlogin', self.username)
137 log.debug('updated user %s lastlogin', self.username)
138 except (DatabaseError,):
138 except (DatabaseError,):
139 session.rollback()
139 session.rollback()
140
140
141
141
142 class UserLog(Base, BaseModel):
142 class UserLog(Base, BaseModel):
143 __tablename__ = 'user_logs'
143 __tablename__ = 'user_logs'
144 __table_args__ = {'useexisting':True}
144 __table_args__ = {'useexisting':True}
145 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
145 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
146 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
146 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
147 repository_id = Column("repository_id", Integer(length=None, convert_unicode=False, assert_unicode=None), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
147 repository_id = Column("repository_id", Integer(length=None, convert_unicode=False, assert_unicode=None), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
148 repository_name = Column("repository_name", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
148 repository_name = Column("repository_name", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
149 user_ip = Column("user_ip", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
149 user_ip = Column("user_ip", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
150 action = Column("action", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
150 action = Column("action", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
151 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
151 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
152
152
153 user = relation('User')
153 user = relation('User')
154 repository = relation('Repository')
154 repository = relation('Repository')
155
155
156
156
157 class UsersGroup(Base, BaseModel):
157 class UsersGroup(Base, BaseModel):
158 __tablename__ = 'users_groups'
158 __tablename__ = 'users_groups'
159 __table_args__ = {'useexisting':True}
159 __table_args__ = {'useexisting':True}
160
160
161 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
161 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
162 users_group_name = Column("users_group_name", String(length=None, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
162 users_group_name = Column("users_group_name", String(length=None, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
163 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
163 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
164
164
165 members = relation('UsersGroupMember')
165 members = relation('UsersGroupMember')
166
166
167 class UsersGroupMember(Base, BaseModel):
167 class UsersGroupMember(Base, BaseModel):
168 __tablename__ = 'users_groups_members'
168 __tablename__ = 'users_groups_members'
169 __table_args__ = {'useexisting':True}
169 __table_args__ = {'useexisting':True}
170
170
171 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
171 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
172 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
172 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
173 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
173 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
174
174
175 user = relation('User')
175 user = relation('User')
176 users_group = relation('UsersGroup')
176 users_group = relation('UsersGroup')
177
177
178 class Repository(Base, BaseModel):
178 class Repository(Base, BaseModel):
179 __tablename__ = 'repositories'
179 __tablename__ = 'repositories'
180 __table_args__ = (UniqueConstraint('repo_name'), {'useexisting':True},)
180 __table_args__ = (UniqueConstraint('repo_name'), {'useexisting':True},)
181 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
181 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
182 repo_name = Column("repo_name", String(length=None, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
182 repo_name = Column("repo_name", String(length=None, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
183 repo_type = Column("repo_type", String(length=None, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default='hg')
183 repo_type = Column("repo_type", String(length=None, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default='hg')
184 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
184 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
185 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
185 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
186 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
186 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
187 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
187 description = Column("description", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
188 description = Column("description", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
188 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
189 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
189 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
190 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
190
191
191 user = relation('User')
192 user = relation('User')
192 fork = relation('Repository', remote_side=repo_id)
193 fork = relation('Repository', remote_side=repo_id)
193 group = relation('Group')
194 group = relation('Group')
194 repo_to_perm = relation('RepoToPerm', cascade='all')
195 repo_to_perm = relation('RepoToPerm', cascade='all')
195 stats = relation('Statistics', cascade='all', uselist=False)
196 stats = relation('Statistics', cascade='all', uselist=False)
196
197
197 repo_followers = relation('UserFollowing', primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id', cascade='all')
198 repo_followers = relation('UserFollowing', primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id', cascade='all')
198
199
199 def __repr__(self):
200 def __repr__(self):
200 return "<%s('%s:%s')>" % (self.__class__.__name__,
201 return "<%s('%s:%s')>" % (self.__class__.__name__,
201 self.repo_id, self.repo_name)
202 self.repo_id, self.repo_name)
202
203
203 class Group(Base, BaseModel):
204 class Group(Base, BaseModel):
204 __tablename__ = 'groups'
205 __tablename__ = 'groups'
205 __table_args__ = (UniqueConstraint('group_name'), {'useexisting':True},)
206 __table_args__ = (UniqueConstraint('group_name'), {'useexisting':True},)
206
207
207 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
208 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
208 group_name = Column("group_name", String(length=None, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
209 group_name = Column("group_name", String(length=None, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
209 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
210 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
210
211
211 parent_group = relation('Group', remote_side=group_id)
212 parent_group = relation('Group', remote_side=group_id)
212
213
213
214
214 def __init__(self, group_name='', parent_group=None):
215 def __init__(self, group_name='', parent_group=None):
215 self.group_name = group_name
216 self.group_name = group_name
216 self.parent_group = parent_group
217 self.parent_group = parent_group
217
218
218 def __repr__(self):
219 def __repr__(self):
219 return "<%s('%s:%s')>" % (self.__class__.__name__, self.group_id,
220 return "<%s('%s:%s')>" % (self.__class__.__name__, self.group_id,
220 self.group_name)
221 self.group_name)
221
222
222 class Permission(Base, BaseModel):
223 class Permission(Base, BaseModel):
223 __tablename__ = 'permissions'
224 __tablename__ = 'permissions'
224 __table_args__ = {'useexisting':True}
225 __table_args__ = {'useexisting':True}
225 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
226 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
226 permission_name = Column("permission_name", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
227 permission_name = Column("permission_name", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
227 permission_longname = Column("permission_longname", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
228 permission_longname = Column("permission_longname", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
228
229
229 def __repr__(self):
230 def __repr__(self):
230 return "<%s('%s:%s')>" % (self.__class__.__name__,
231 return "<%s('%s:%s')>" % (self.__class__.__name__,
231 self.permission_id, self.permission_name)
232 self.permission_id, self.permission_name)
232
233
233 class RepoToPerm(Base, BaseModel):
234 class RepoToPerm(Base, BaseModel):
234 __tablename__ = 'repo_to_perm'
235 __tablename__ = 'repo_to_perm'
235 __table_args__ = (UniqueConstraint('user_id', 'repository_id'), {'useexisting':True})
236 __table_args__ = (UniqueConstraint('user_id', 'repository_id'), {'useexisting':True})
236 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
237 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
237 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
238 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
238 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
239 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
239 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
240 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
240
241
241 user = relation('User')
242 user = relation('User')
242 permission = relation('Permission')
243 permission = relation('Permission')
243 repository = relation('Repository')
244 repository = relation('Repository')
244
245
245 class UserToPerm(Base, BaseModel):
246 class UserToPerm(Base, BaseModel):
246 __tablename__ = 'user_to_perm'
247 __tablename__ = 'user_to_perm'
247 __table_args__ = (UniqueConstraint('user_id', 'permission_id'), {'useexisting':True})
248 __table_args__ = (UniqueConstraint('user_id', 'permission_id'), {'useexisting':True})
248 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
249 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
249 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
250 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
250 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
251 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
251
252
252 user = relation('User')
253 user = relation('User')
253 permission = relation('Permission')
254 permission = relation('Permission')
254
255
255 class UsersGroupToPerm(Base, BaseModel):
256 class UsersGroupToPerm(Base, BaseModel):
256 __tablename__ = 'users_group_to_perm'
257 __tablename__ = 'users_group_to_perm'
257 __table_args__ = (UniqueConstraint('users_group_id', 'permission_id'), {'useexisting':True})
258 __table_args__ = (UniqueConstraint('users_group_id', 'permission_id'), {'useexisting':True})
258 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
259 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
259 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
260 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
260 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
261 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
261
262
262 users_group = relation('UsersGroup')
263 users_group = relation('UsersGroup')
263 permission = relation('Permission')
264 permission = relation('Permission')
264
265
265 class GroupToPerm(Base, BaseModel):
266 class GroupToPerm(Base, BaseModel):
266 __tablename__ = 'group_to_perm'
267 __tablename__ = 'group_to_perm'
267 __table_args__ = (UniqueConstraint('group_id', 'permission_id'), {'useexisting':True})
268 __table_args__ = (UniqueConstraint('group_id', 'permission_id'), {'useexisting':True})
268
269
269 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
270 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
270 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
271 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
271 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
272 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
272 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
273 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
273
274
274 user = relation('User')
275 user = relation('User')
275 permission = relation('Permission')
276 permission = relation('Permission')
276 group = relation('Group')
277 group = relation('Group')
277
278
278 class Statistics(Base, BaseModel):
279 class Statistics(Base, BaseModel):
279 __tablename__ = 'statistics'
280 __tablename__ = 'statistics'
280 __table_args__ = (UniqueConstraint('repository_id'), {'useexisting':True})
281 __table_args__ = (UniqueConstraint('repository_id'), {'useexisting':True})
281 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
282 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
282 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
283 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
283 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
284 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
284 commit_activity = Column("commit_activity", LargeBinary(), nullable=False)#JSON data
285 commit_activity = Column("commit_activity", LargeBinary(), nullable=False)#JSON data
285 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
286 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
286 languages = Column("languages", LargeBinary(), nullable=False)#JSON data
287 languages = Column("languages", LargeBinary(), nullable=False)#JSON data
287
288
288 repository = relation('Repository', single_parent=True)
289 repository = relation('Repository', single_parent=True)
289
290
290 class UserFollowing(Base, BaseModel):
291 class UserFollowing(Base, BaseModel):
291 __tablename__ = 'user_followings'
292 __tablename__ = 'user_followings'
292 __table_args__ = (UniqueConstraint('user_id', 'follows_repository_id'),
293 __table_args__ = (UniqueConstraint('user_id', 'follows_repository_id'),
293 UniqueConstraint('user_id', 'follows_user_id')
294 UniqueConstraint('user_id', 'follows_user_id')
294 , {'useexisting':True})
295 , {'useexisting':True})
295
296
296 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
297 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
297 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
298 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
298 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
299 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
299 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
300 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
300
301
301 user = relation('User', primaryjoin='User.user_id==UserFollowing.user_id')
302 user = relation('User', primaryjoin='User.user_id==UserFollowing.user_id')
302
303
303 follows_user = relation('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
304 follows_user = relation('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
304 follows_repository = relation('Repository')
305 follows_repository = relation('Repository')
305
306
306 class CacheInvalidation(Base, BaseModel):
307 class CacheInvalidation(Base, BaseModel):
307 __tablename__ = 'cache_invalidation'
308 __tablename__ = 'cache_invalidation'
308 __table_args__ = (UniqueConstraint('cache_key'), {'useexisting':True})
309 __table_args__ = (UniqueConstraint('cache_key'), {'useexisting':True})
309 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
310 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
310 cache_key = Column("cache_key", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
311 cache_key = Column("cache_key", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
311 cache_args = Column("cache_args", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
312 cache_args = Column("cache_args", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
312 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
313 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
313
314
314
315
315 def __init__(self, cache_key, cache_args=''):
316 def __init__(self, cache_key, cache_args=''):
316 self.cache_key = cache_key
317 self.cache_key = cache_key
317 self.cache_args = cache_args
318 self.cache_args = cache_args
318 self.cache_active = False
319 self.cache_active = False
319
320
320 def __repr__(self):
321 def __repr__(self):
321 return "<%s('%s:%s')>" % (self.__class__.__name__,
322 return "<%s('%s:%s')>" % (self.__class__.__name__,
322 self.cache_id, self.cache_key)
323 self.cache_id, self.cache_key)
323
324
324 class DbMigrateVersion(Base, BaseModel):
325 class DbMigrateVersion(Base, BaseModel):
325 __tablename__ = 'db_migrate_version'
326 __tablename__ = 'db_migrate_version'
326 __table_args__ = {'useexisting':True}
327 __table_args__ = {'useexisting':True}
327 repository_id = Column('repository_id', String(250), primary_key=True)
328 repository_id = Column('repository_id', String(250), primary_key=True)
328 repository_path = Column('repository_path', Text)
329 repository_path = Column('repository_path', Text)
329 version = Column('version', Integer)
330 version = Column('version', Integer)
330
331
@@ -1,530 +1,531 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 import os
22 import os
23 import re
23 import re
24 import logging
24 import logging
25
25
26 import formencode
26 import formencode
27 from formencode import All
27 from formencode import All
28 from formencode.validators import UnicodeString, OneOf, Int, Number, Regex, \
28 from formencode.validators import UnicodeString, OneOf, Int, Number, Regex, \
29 Email, Bool, StringBoolean
29 Email, Bool, StringBoolean
30
30
31 from pylons.i18n.translation import _
31 from pylons.i18n.translation import _
32
32
33 import rhodecode.lib.helpers as h
33 import rhodecode.lib.helpers as h
34 from rhodecode.lib.auth import authenticate, get_crypt_password
34 from rhodecode.lib.auth import authenticate, get_crypt_password
35 from rhodecode.lib.exceptions import LdapImportError
35 from rhodecode.lib.exceptions import LdapImportError
36 from rhodecode.model import meta
36 from rhodecode.model import meta
37 from rhodecode.model.user import UserModel
37 from rhodecode.model.user import UserModel
38 from rhodecode.model.repo import RepoModel
38 from rhodecode.model.repo import RepoModel
39 from rhodecode.model.users_group import UsersGroupModel
39 from rhodecode.model.users_group import UsersGroupModel
40 from rhodecode.model.db import User
40 from rhodecode.model.db import User
41 from rhodecode import BACKENDS
41 from rhodecode import BACKENDS
42
42
43 from webhelpers.pylonslib.secure_form import authentication_token
43 from webhelpers.pylonslib.secure_form import authentication_token
44
44
45 log = logging.getLogger(__name__)
45 log = logging.getLogger(__name__)
46
46
47 #this is needed to translate the messages using _() in validators
47 #this is needed to translate the messages using _() in validators
48 class State_obj(object):
48 class State_obj(object):
49 _ = staticmethod(_)
49 _ = staticmethod(_)
50
50
51 #===============================================================================
51 #===============================================================================
52 # VALIDATORS
52 # VALIDATORS
53 #===============================================================================
53 #===============================================================================
54 class ValidAuthToken(formencode.validators.FancyValidator):
54 class ValidAuthToken(formencode.validators.FancyValidator):
55 messages = {'invalid_token':_('Token mismatch')}
55 messages = {'invalid_token':_('Token mismatch')}
56
56
57 def validate_python(self, value, state):
57 def validate_python(self, value, state):
58
58
59 if value != authentication_token():
59 if value != authentication_token():
60 raise formencode.Invalid(self.message('invalid_token', state,
60 raise formencode.Invalid(self.message('invalid_token', state,
61 search_number=value), value, state)
61 search_number=value), value, state)
62
62
63 def ValidUsername(edit, old_data):
63 def ValidUsername(edit, old_data):
64 class _ValidUsername(formencode.validators.FancyValidator):
64 class _ValidUsername(formencode.validators.FancyValidator):
65
65
66 def validate_python(self, value, state):
66 def validate_python(self, value, state):
67 if value in ['default', 'new_user']:
67 if value in ['default', 'new_user']:
68 raise formencode.Invalid(_('Invalid username'), value, state)
68 raise formencode.Invalid(_('Invalid username'), value, state)
69 #check if user is unique
69 #check if user is unique
70 old_un = None
70 old_un = None
71 if edit:
71 if edit:
72 old_un = UserModel().get(old_data.get('user_id')).username
72 old_un = UserModel().get(old_data.get('user_id')).username
73
73
74 if old_un != value or not edit:
74 if old_un != value or not edit:
75 if UserModel().get_by_username(value, cache=False,
75 if UserModel().get_by_username(value, cache=False,
76 case_insensitive=True):
76 case_insensitive=True):
77 raise formencode.Invalid(_('This username already exists') ,
77 raise formencode.Invalid(_('This username already exists') ,
78 value, state)
78 value, state)
79
79
80
80
81 if re.match(r'^[a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]+$', value) is None:
81 if re.match(r'^[a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]+$', value) is None:
82 raise formencode.Invalid(_('Username may only contain '
82 raise formencode.Invalid(_('Username may only contain '
83 'alphanumeric characters underscores, '
83 'alphanumeric characters underscores, '
84 'periods or dashes and must begin with '
84 'periods or dashes and must begin with '
85 'alphanumeric character'),
85 'alphanumeric character'),
86 value, state)
86 value, state)
87
87
88
88
89
89
90 return _ValidUsername
90 return _ValidUsername
91
91
92
92
93
93
94 def ValidUsersGroup(edit, old_data):
94 def ValidUsersGroup(edit, old_data):
95
95
96 class _ValidUsersGroup(formencode.validators.FancyValidator):
96 class _ValidUsersGroup(formencode.validators.FancyValidator):
97
97
98 def validate_python(self, value, state):
98 def validate_python(self, value, state):
99 if value in ['default']:
99 if value in ['default']:
100 raise formencode.Invalid(_('Invalid group name'), value, state)
100 raise formencode.Invalid(_('Invalid group name'), value, state)
101 #check if group is unique
101 #check if group is unique
102 old_un = None
102 old_un = None
103 if edit:
103 if edit:
104 old_un = UserModel().get(old_data.get('users_group_id')).username
104 old_un = UserModel().get(old_data.get('users_group_id')).username
105
105
106 if old_un != value or not edit:
106 if old_un != value or not edit:
107 if UsersGroupModel().get_by_groupname(value, cache=False,
107 if UsersGroupModel().get_by_groupname(value, cache=False,
108 case_insensitive=True):
108 case_insensitive=True):
109 raise formencode.Invalid(_('This users group already exists') ,
109 raise formencode.Invalid(_('This users group already exists') ,
110 value, state)
110 value, state)
111
111
112
112
113 if re.match(r'^[a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]+$', value) is None:
113 if re.match(r'^[a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]+$', value) is None:
114 raise formencode.Invalid(_('Group name may only contain '
114 raise formencode.Invalid(_('Group name may only contain '
115 'alphanumeric characters underscores, '
115 'alphanumeric characters underscores, '
116 'periods or dashes and must begin with '
116 'periods or dashes and must begin with '
117 'alphanumeric character'),
117 'alphanumeric character'),
118 value, state)
118 value, state)
119
119
120 return _ValidUsersGroup
120 return _ValidUsersGroup
121
121
122
122
123
123
124 class ValidPassword(formencode.validators.FancyValidator):
124 class ValidPassword(formencode.validators.FancyValidator):
125
125
126 def to_python(self, value, state):
126 def to_python(self, value, state):
127
127
128 if value:
128 if value:
129
129
130 if value.get('password'):
130 if value.get('password'):
131 try:
131 try:
132 value['password'] = get_crypt_password(value['password'])
132 value['password'] = get_crypt_password(value['password'])
133 except UnicodeEncodeError:
133 except UnicodeEncodeError:
134 e_dict = {'password':_('Invalid characters in password')}
134 e_dict = {'password':_('Invalid characters in password')}
135 raise formencode.Invalid('', value, state, error_dict=e_dict)
135 raise formencode.Invalid('', value, state, error_dict=e_dict)
136
136
137 if value.get('password_confirmation'):
137 if value.get('password_confirmation'):
138 try:
138 try:
139 value['password_confirmation'] = \
139 value['password_confirmation'] = \
140 get_crypt_password(value['password_confirmation'])
140 get_crypt_password(value['password_confirmation'])
141 except UnicodeEncodeError:
141 except UnicodeEncodeError:
142 e_dict = {'password_confirmation':_('Invalid characters in password')}
142 e_dict = {'password_confirmation':_('Invalid characters in password')}
143 raise formencode.Invalid('', value, state, error_dict=e_dict)
143 raise formencode.Invalid('', value, state, error_dict=e_dict)
144
144
145 if value.get('new_password'):
145 if value.get('new_password'):
146 try:
146 try:
147 value['new_password'] = \
147 value['new_password'] = \
148 get_crypt_password(value['new_password'])
148 get_crypt_password(value['new_password'])
149 except UnicodeEncodeError:
149 except UnicodeEncodeError:
150 e_dict = {'new_password':_('Invalid characters in password')}
150 e_dict = {'new_password':_('Invalid characters in password')}
151 raise formencode.Invalid('', value, state, error_dict=e_dict)
151 raise formencode.Invalid('', value, state, error_dict=e_dict)
152
152
153 return value
153 return value
154
154
155 class ValidPasswordsMatch(formencode.validators.FancyValidator):
155 class ValidPasswordsMatch(formencode.validators.FancyValidator):
156
156
157 def validate_python(self, value, state):
157 def validate_python(self, value, state):
158
158
159 if value['password'] != value['password_confirmation']:
159 if value['password'] != value['password_confirmation']:
160 e_dict = {'password_confirmation':
160 e_dict = {'password_confirmation':
161 _('Password do not match')}
161 _('Password do not match')}
162 raise formencode.Invalid('', value, state, error_dict=e_dict)
162 raise formencode.Invalid('', value, state, error_dict=e_dict)
163
163
164 class ValidAuth(formencode.validators.FancyValidator):
164 class ValidAuth(formencode.validators.FancyValidator):
165 messages = {
165 messages = {
166 'invalid_password':_('invalid password'),
166 'invalid_password':_('invalid password'),
167 'invalid_login':_('invalid user name'),
167 'invalid_login':_('invalid user name'),
168 'disabled_account':_('Your account is disabled')
168 'disabled_account':_('Your account is disabled')
169
169
170 }
170 }
171 #error mapping
171 #error mapping
172 e_dict = {'username':messages['invalid_login'],
172 e_dict = {'username':messages['invalid_login'],
173 'password':messages['invalid_password']}
173 'password':messages['invalid_password']}
174 e_dict_disable = {'username':messages['disabled_account']}
174 e_dict_disable = {'username':messages['disabled_account']}
175
175
176 def validate_python(self, value, state):
176 def validate_python(self, value, state):
177 password = value['password']
177 password = value['password']
178 username = value['username']
178 username = value['username']
179 user = UserModel().get_by_username(username)
179 user = UserModel().get_by_username(username)
180
180
181 if authenticate(username, password):
181 if authenticate(username, password):
182 return value
182 return value
183 else:
183 else:
184 if user and user.active is False:
184 if user and user.active is False:
185 log.warning('user %s is disabled', username)
185 log.warning('user %s is disabled', username)
186 raise formencode.Invalid(self.message('disabled_account',
186 raise formencode.Invalid(self.message('disabled_account',
187 state=State_obj),
187 state=State_obj),
188 value, state,
188 value, state,
189 error_dict=self.e_dict_disable)
189 error_dict=self.e_dict_disable)
190 else:
190 else:
191 log.warning('user %s not authenticated', username)
191 log.warning('user %s not authenticated', username)
192 raise formencode.Invalid(self.message('invalid_password',
192 raise formencode.Invalid(self.message('invalid_password',
193 state=State_obj), value, state,
193 state=State_obj), value, state,
194 error_dict=self.e_dict)
194 error_dict=self.e_dict)
195
195
196 class ValidRepoUser(formencode.validators.FancyValidator):
196 class ValidRepoUser(formencode.validators.FancyValidator):
197
197
198 def to_python(self, value, state):
198 def to_python(self, value, state):
199 sa = meta.Session()
199 sa = meta.Session()
200 try:
200 try:
201 self.user_db = sa.query(User)\
201 self.user_db = sa.query(User)\
202 .filter(User.active == True)\
202 .filter(User.active == True)\
203 .filter(User.username == value).one()
203 .filter(User.username == value).one()
204 except Exception:
204 except Exception:
205 raise formencode.Invalid(_('This username is not valid'),
205 raise formencode.Invalid(_('This username is not valid'),
206 value, state)
206 value, state)
207 finally:
207 finally:
208 meta.Session.remove()
208 meta.Session.remove()
209
209
210 return self.user_db.user_id
210 return self.user_db.user_id
211
211
212 def ValidRepoName(edit, old_data):
212 def ValidRepoName(edit, old_data):
213 class _ValidRepoName(formencode.validators.FancyValidator):
213 class _ValidRepoName(formencode.validators.FancyValidator):
214
214
215 def to_python(self, value, state):
215 def to_python(self, value, state):
216 slug = h.repo_name_slug(value)
216 slug = h.repo_name_slug(value)
217 if slug in ['_admin']:
217 if slug in ['_admin']:
218 raise formencode.Invalid(_('This repository name is disallowed'),
218 raise formencode.Invalid(_('This repository name is disallowed'),
219 value, state)
219 value, state)
220 if old_data.get('repo_name') != value or not edit:
220 if old_data.get('repo_name') != value or not edit:
221 if RepoModel().get_by_repo_name(slug, cache=False):
221 if RepoModel().get_by_repo_name(slug, cache=False):
222 raise formencode.Invalid(_('This repository already exists') ,
222 raise formencode.Invalid(_('This repository already exists') ,
223 value, state)
223 value, state)
224 return slug
224 return slug
225
225
226
226
227 return _ValidRepoName
227 return _ValidRepoName
228
228
229 def ValidForkType(old_data):
229 def ValidForkType(old_data):
230 class _ValidForkType(formencode.validators.FancyValidator):
230 class _ValidForkType(formencode.validators.FancyValidator):
231
231
232 def to_python(self, value, state):
232 def to_python(self, value, state):
233 if old_data['repo_type'] != value:
233 if old_data['repo_type'] != value:
234 raise formencode.Invalid(_('Fork have to be the same type as original'),
234 raise formencode.Invalid(_('Fork have to be the same type as original'),
235 value, state)
235 value, state)
236 return value
236 return value
237 return _ValidForkType
237 return _ValidForkType
238
238
239 class ValidPerms(formencode.validators.FancyValidator):
239 class ValidPerms(formencode.validators.FancyValidator):
240 messages = {'perm_new_user_name':_('This username is not valid')}
240 messages = {'perm_new_user_name':_('This username is not valid')}
241
241
242 def to_python(self, value, state):
242 def to_python(self, value, state):
243 perms_update = []
243 perms_update = []
244 perms_new = []
244 perms_new = []
245 #build a list of permission to update and new permission to create
245 #build a list of permission to update and new permission to create
246 for k, v in value.items():
246 for k, v in value.items():
247 if k.startswith('perm_'):
247 if k.startswith('perm_'):
248 if k.startswith('perm_new_user'):
248 if k.startswith('perm_new_user'):
249 new_perm = value.get('perm_new_user', False)
249 new_perm = value.get('perm_new_user', False)
250 new_user = value.get('perm_new_user_name', False)
250 new_user = value.get('perm_new_user_name', False)
251 if new_user and new_perm:
251 if new_user and new_perm:
252 if (new_user, new_perm) not in perms_new:
252 if (new_user, new_perm) not in perms_new:
253 perms_new.append((new_user, new_perm))
253 perms_new.append((new_user, new_perm))
254 else:
254 else:
255 usr = k[5:]
255 usr = k[5:]
256 if usr == 'default':
256 if usr == 'default':
257 if value['private']:
257 if value['private']:
258 #set none for default when updating to private repo
258 #set none for default when updating to private repo
259 v = 'repository.none'
259 v = 'repository.none'
260 perms_update.append((usr, v))
260 perms_update.append((usr, v))
261 value['perms_updates'] = perms_update
261 value['perms_updates'] = perms_update
262 value['perms_new'] = perms_new
262 value['perms_new'] = perms_new
263 sa = meta.Session
263 sa = meta.Session
264 for k, v in perms_new:
264 for k, v in perms_new:
265 try:
265 try:
266 self.user_db = sa.query(User)\
266 self.user_db = sa.query(User)\
267 .filter(User.active == True)\
267 .filter(User.active == True)\
268 .filter(User.username == k).one()
268 .filter(User.username == k).one()
269 except Exception:
269 except Exception:
270 msg = self.message('perm_new_user_name',
270 msg = self.message('perm_new_user_name',
271 state=State_obj)
271 state=State_obj)
272 raise formencode.Invalid(msg, value, state,
272 raise formencode.Invalid(msg, value, state,
273 error_dict={'perm_new_user_name':msg})
273 error_dict={'perm_new_user_name':msg})
274 return value
274 return value
275
275
276 class ValidSettings(formencode.validators.FancyValidator):
276 class ValidSettings(formencode.validators.FancyValidator):
277
277
278 def to_python(self, value, state):
278 def to_python(self, value, state):
279 #settings form can't edit user
279 #settings form can't edit user
280 if value.has_key('user'):
280 if value.has_key('user'):
281 del['value']['user']
281 del['value']['user']
282
282
283 return value
283 return value
284
284
285 class ValidPath(formencode.validators.FancyValidator):
285 class ValidPath(formencode.validators.FancyValidator):
286 def to_python(self, value, state):
286 def to_python(self, value, state):
287
287
288 if not os.path.isdir(value):
288 if not os.path.isdir(value):
289 msg = _('This is not a valid path')
289 msg = _('This is not a valid path')
290 raise formencode.Invalid(msg, value, state,
290 raise formencode.Invalid(msg, value, state,
291 error_dict={'paths_root_path':msg})
291 error_dict={'paths_root_path':msg})
292 return value
292 return value
293
293
294 def UniqSystemEmail(old_data):
294 def UniqSystemEmail(old_data):
295 class _UniqSystemEmail(formencode.validators.FancyValidator):
295 class _UniqSystemEmail(formencode.validators.FancyValidator):
296 def to_python(self, value, state):
296 def to_python(self, value, state):
297 value = value.lower()
297 value = value.lower()
298 if old_data.get('email') != value:
298 if old_data.get('email') != value:
299 sa = meta.Session()
299 sa = meta.Session()
300 try:
300 try:
301 user = sa.query(User).filter(User.email == value).scalar()
301 user = sa.query(User).filter(User.email == value).scalar()
302 if user:
302 if user:
303 raise formencode.Invalid(_("This e-mail address is already taken") ,
303 raise formencode.Invalid(_("This e-mail address is already taken") ,
304 value, state)
304 value, state)
305 finally:
305 finally:
306 meta.Session.remove()
306 meta.Session.remove()
307
307
308 return value
308 return value
309
309
310 return _UniqSystemEmail
310 return _UniqSystemEmail
311
311
312 class ValidSystemEmail(formencode.validators.FancyValidator):
312 class ValidSystemEmail(formencode.validators.FancyValidator):
313 def to_python(self, value, state):
313 def to_python(self, value, state):
314 value = value.lower()
314 value = value.lower()
315 sa = meta.Session
315 sa = meta.Session
316 try:
316 try:
317 user = sa.query(User).filter(User.email == value).scalar()
317 user = sa.query(User).filter(User.email == value).scalar()
318 if user is None:
318 if user is None:
319 raise formencode.Invalid(_("This e-mail address doesn't exist.") ,
319 raise formencode.Invalid(_("This e-mail address doesn't exist.") ,
320 value, state)
320 value, state)
321 finally:
321 finally:
322 meta.Session.remove()
322 meta.Session.remove()
323
323
324 return value
324 return value
325
325
326 class LdapLibValidator(formencode.validators.FancyValidator):
326 class LdapLibValidator(formencode.validators.FancyValidator):
327
327
328 def to_python(self, value, state):
328 def to_python(self, value, state):
329
329
330 try:
330 try:
331 import ldap
331 import ldap
332 except ImportError:
332 except ImportError:
333 raise LdapImportError
333 raise LdapImportError
334 return value
334 return value
335
335
336 class BaseDnValidator(formencode.validators.FancyValidator):
336 class BaseDnValidator(formencode.validators.FancyValidator):
337
337
338 def to_python(self, value, state):
338 def to_python(self, value, state):
339
339
340 try:
340 try:
341 value % {'user':'valid'}
341 value % {'user':'valid'}
342
342
343 if value.find('%(user)s') == -1:
343 if value.find('%(user)s') == -1:
344 raise formencode.Invalid(_("You need to specify %(user)s in "
344 raise formencode.Invalid(_("You need to specify %(user)s in "
345 "template for example uid=%(user)s "
345 "template for example uid=%(user)s "
346 ",dc=company...") ,
346 ",dc=company...") ,
347 value, state)
347 value, state)
348
348
349 except KeyError:
349 except KeyError:
350 raise formencode.Invalid(_("Wrong template used, only %(user)s "
350 raise formencode.Invalid(_("Wrong template used, only %(user)s "
351 "is an valid entry") ,
351 "is an valid entry") ,
352 value, state)
352 value, state)
353
353
354 return value
354 return value
355
355
356 #===============================================================================
356 #===============================================================================
357 # FORMS
357 # FORMS
358 #===============================================================================
358 #===============================================================================
359 class LoginForm(formencode.Schema):
359 class LoginForm(formencode.Schema):
360 allow_extra_fields = True
360 allow_extra_fields = True
361 filter_extra_fields = True
361 filter_extra_fields = True
362 username = UnicodeString(
362 username = UnicodeString(
363 strip=True,
363 strip=True,
364 min=1,
364 min=1,
365 not_empty=True,
365 not_empty=True,
366 messages={
366 messages={
367 'empty':_('Please enter a login'),
367 'empty':_('Please enter a login'),
368 'tooShort':_('Enter a value %(min)i characters long or more')}
368 'tooShort':_('Enter a value %(min)i characters long or more')}
369 )
369 )
370
370
371 password = UnicodeString(
371 password = UnicodeString(
372 strip=True,
372 strip=True,
373 min=6,
373 min=6,
374 not_empty=True,
374 not_empty=True,
375 messages={
375 messages={
376 'empty':_('Please enter a password'),
376 'empty':_('Please enter a password'),
377 'tooShort':_('Enter %(min)i characters or more')}
377 'tooShort':_('Enter %(min)i characters or more')}
378 )
378 )
379
379
380
380
381 #chained validators have access to all data
381 #chained validators have access to all data
382 chained_validators = [ValidAuth]
382 chained_validators = [ValidAuth]
383
383
384 def UserForm(edit=False, old_data={}):
384 def UserForm(edit=False, old_data={}):
385 class _UserForm(formencode.Schema):
385 class _UserForm(formencode.Schema):
386 allow_extra_fields = True
386 allow_extra_fields = True
387 filter_extra_fields = True
387 filter_extra_fields = True
388 username = All(UnicodeString(strip=True, min=1, not_empty=True),
388 username = All(UnicodeString(strip=True, min=1, not_empty=True),
389 ValidUsername(edit, old_data))
389 ValidUsername(edit, old_data))
390 if edit:
390 if edit:
391 new_password = All(UnicodeString(strip=True, min=6, not_empty=False))
391 new_password = All(UnicodeString(strip=True, min=6, not_empty=False))
392 admin = StringBoolean(if_missing=False)
392 admin = StringBoolean(if_missing=False)
393 else:
393 else:
394 password = All(UnicodeString(strip=True, min=6, not_empty=True))
394 password = All(UnicodeString(strip=True, min=6, not_empty=True))
395 active = StringBoolean(if_missing=False)
395 active = StringBoolean(if_missing=False)
396 name = UnicodeString(strip=True, min=1, not_empty=True)
396 name = UnicodeString(strip=True, min=1, not_empty=True)
397 lastname = UnicodeString(strip=True, min=1, not_empty=True)
397 lastname = UnicodeString(strip=True, min=1, not_empty=True)
398 email = All(Email(not_empty=True), UniqSystemEmail(old_data))
398 email = All(Email(not_empty=True), UniqSystemEmail(old_data))
399
399
400 chained_validators = [ValidPassword]
400 chained_validators = [ValidPassword]
401
401
402 return _UserForm
402 return _UserForm
403
403
404
404
405 def UsersGroupForm(edit=False, old_data={}):
405 def UsersGroupForm(edit=False, old_data={}):
406 class _UsersGroupForm(formencode.Schema):
406 class _UsersGroupForm(formencode.Schema):
407 allow_extra_fields = True
407 allow_extra_fields = True
408 filter_extra_fields = True
408 filter_extra_fields = True
409
409
410 users_group_name = All(UnicodeString(strip=True, min=1, not_empty=True),
410 users_group_name = All(UnicodeString(strip=True, min=1, not_empty=True),
411 ValidUsersGroup(edit, old_data))
411 ValidUsersGroup(edit, old_data))
412
412
413 users_group_active = StringBoolean(if_missing=False)
413 users_group_active = StringBoolean(if_missing=False)
414
414
415 return _UsersGroupForm
415 return _UsersGroupForm
416
416
417 def RegisterForm(edit=False, old_data={}):
417 def RegisterForm(edit=False, old_data={}):
418 class _RegisterForm(formencode.Schema):
418 class _RegisterForm(formencode.Schema):
419 allow_extra_fields = True
419 allow_extra_fields = True
420 filter_extra_fields = True
420 filter_extra_fields = True
421 username = All(ValidUsername(edit, old_data),
421 username = All(ValidUsername(edit, old_data),
422 UnicodeString(strip=True, min=1, not_empty=True))
422 UnicodeString(strip=True, min=1, not_empty=True))
423 password = All(UnicodeString(strip=True, min=6, not_empty=True))
423 password = All(UnicodeString(strip=True, min=6, not_empty=True))
424 password_confirmation = All(UnicodeString(strip=True, min=6, not_empty=True))
424 password_confirmation = All(UnicodeString(strip=True, min=6, not_empty=True))
425 active = StringBoolean(if_missing=False)
425 active = StringBoolean(if_missing=False)
426 name = UnicodeString(strip=True, min=1, not_empty=True)
426 name = UnicodeString(strip=True, min=1, not_empty=True)
427 lastname = UnicodeString(strip=True, min=1, not_empty=True)
427 lastname = UnicodeString(strip=True, min=1, not_empty=True)
428 email = All(Email(not_empty=True), UniqSystemEmail(old_data))
428 email = All(Email(not_empty=True), UniqSystemEmail(old_data))
429
429
430 chained_validators = [ValidPasswordsMatch, ValidPassword]
430 chained_validators = [ValidPasswordsMatch, ValidPassword]
431
431
432 return _RegisterForm
432 return _RegisterForm
433
433
434 def PasswordResetForm():
434 def PasswordResetForm():
435 class _PasswordResetForm(formencode.Schema):
435 class _PasswordResetForm(formencode.Schema):
436 allow_extra_fields = True
436 allow_extra_fields = True
437 filter_extra_fields = True
437 filter_extra_fields = True
438 email = All(ValidSystemEmail(), Email(not_empty=True))
438 email = All(ValidSystemEmail(), Email(not_empty=True))
439 return _PasswordResetForm
439 return _PasswordResetForm
440
440
441 def RepoForm(edit=False, old_data={}, supported_backends=BACKENDS.keys()):
441 def RepoForm(edit=False, old_data={}, supported_backends=BACKENDS.keys()):
442 class _RepoForm(formencode.Schema):
442 class _RepoForm(formencode.Schema):
443 allow_extra_fields = True
443 allow_extra_fields = True
444 filter_extra_fields = False
444 filter_extra_fields = False
445 repo_name = All(UnicodeString(strip=True, min=1, not_empty=True),
445 repo_name = All(UnicodeString(strip=True, min=1, not_empty=True),
446 ValidRepoName(edit, old_data))
446 ValidRepoName(edit, old_data))
447 description = UnicodeString(strip=True, min=1, not_empty=True)
447 description = UnicodeString(strip=True, min=1, not_empty=True)
448 private = StringBoolean(if_missing=False)
448 private = StringBoolean(if_missing=False)
449 enable_statistics = StringBoolean(if_missing=False)
449 enable_statistics = StringBoolean(if_missing=False)
450 enable_downloads = StringBoolean(if_missing=False)
450 repo_type = OneOf(supported_backends)
451 repo_type = OneOf(supported_backends)
451 if edit:
452 if edit:
452 user = All(Int(not_empty=True), ValidRepoUser)
453 user = All(Int(not_empty=True), ValidRepoUser)
453
454
454 chained_validators = [ValidPerms]
455 chained_validators = [ValidPerms]
455 return _RepoForm
456 return _RepoForm
456
457
457 def RepoForkForm(edit=False, old_data={}, supported_backends=BACKENDS.keys()):
458 def RepoForkForm(edit=False, old_data={}, supported_backends=BACKENDS.keys()):
458 class _RepoForkForm(formencode.Schema):
459 class _RepoForkForm(formencode.Schema):
459 allow_extra_fields = True
460 allow_extra_fields = True
460 filter_extra_fields = False
461 filter_extra_fields = False
461 fork_name = All(UnicodeString(strip=True, min=1, not_empty=True),
462 fork_name = All(UnicodeString(strip=True, min=1, not_empty=True),
462 ValidRepoName(edit, old_data))
463 ValidRepoName(edit, old_data))
463 description = UnicodeString(strip=True, min=1, not_empty=True)
464 description = UnicodeString(strip=True, min=1, not_empty=True)
464 private = StringBoolean(if_missing=False)
465 private = StringBoolean(if_missing=False)
465 repo_type = All(ValidForkType(old_data), OneOf(supported_backends))
466 repo_type = All(ValidForkType(old_data), OneOf(supported_backends))
466 return _RepoForkForm
467 return _RepoForkForm
467
468
468 def RepoSettingsForm(edit=False, old_data={}):
469 def RepoSettingsForm(edit=False, old_data={}):
469 class _RepoForm(formencode.Schema):
470 class _RepoForm(formencode.Schema):
470 allow_extra_fields = True
471 allow_extra_fields = True
471 filter_extra_fields = False
472 filter_extra_fields = False
472 repo_name = All(UnicodeString(strip=True, min=1, not_empty=True),
473 repo_name = All(UnicodeString(strip=True, min=1, not_empty=True),
473 ValidRepoName(edit, old_data))
474 ValidRepoName(edit, old_data))
474 description = UnicodeString(strip=True, min=1, not_empty=True)
475 description = UnicodeString(strip=True, min=1, not_empty=True)
475 private = StringBoolean(if_missing=False)
476 private = StringBoolean(if_missing=False)
476
477
477 chained_validators = [ValidPerms, ValidSettings]
478 chained_validators = [ValidPerms, ValidSettings]
478 return _RepoForm
479 return _RepoForm
479
480
480
481
481 def ApplicationSettingsForm():
482 def ApplicationSettingsForm():
482 class _ApplicationSettingsForm(formencode.Schema):
483 class _ApplicationSettingsForm(formencode.Schema):
483 allow_extra_fields = True
484 allow_extra_fields = True
484 filter_extra_fields = False
485 filter_extra_fields = False
485 rhodecode_title = UnicodeString(strip=True, min=1, not_empty=True)
486 rhodecode_title = UnicodeString(strip=True, min=1, not_empty=True)
486 rhodecode_realm = UnicodeString(strip=True, min=1, not_empty=True)
487 rhodecode_realm = UnicodeString(strip=True, min=1, not_empty=True)
487 rhodecode_ga_code = UnicodeString(strip=True, min=1, not_empty=False)
488 rhodecode_ga_code = UnicodeString(strip=True, min=1, not_empty=False)
488
489
489 return _ApplicationSettingsForm
490 return _ApplicationSettingsForm
490
491
491 def ApplicationUiSettingsForm():
492 def ApplicationUiSettingsForm():
492 class _ApplicationUiSettingsForm(formencode.Schema):
493 class _ApplicationUiSettingsForm(formencode.Schema):
493 allow_extra_fields = True
494 allow_extra_fields = True
494 filter_extra_fields = False
495 filter_extra_fields = False
495 web_push_ssl = OneOf(['true', 'false'], if_missing='false')
496 web_push_ssl = OneOf(['true', 'false'], if_missing='false')
496 paths_root_path = All(ValidPath(), UnicodeString(strip=True, min=1, not_empty=True))
497 paths_root_path = All(ValidPath(), UnicodeString(strip=True, min=1, not_empty=True))
497 hooks_changegroup_update = OneOf(['True', 'False'], if_missing=False)
498 hooks_changegroup_update = OneOf(['True', 'False'], if_missing=False)
498 hooks_changegroup_repo_size = OneOf(['True', 'False'], if_missing=False)
499 hooks_changegroup_repo_size = OneOf(['True', 'False'], if_missing=False)
499 hooks_pretxnchangegroup_push_logger = OneOf(['True', 'False'], if_missing=False)
500 hooks_pretxnchangegroup_push_logger = OneOf(['True', 'False'], if_missing=False)
500 hooks_preoutgoing_pull_logger = OneOf(['True', 'False'], if_missing=False)
501 hooks_preoutgoing_pull_logger = OneOf(['True', 'False'], if_missing=False)
501
502
502 return _ApplicationUiSettingsForm
503 return _ApplicationUiSettingsForm
503
504
504 def DefaultPermissionsForm(perms_choices, register_choices, create_choices):
505 def DefaultPermissionsForm(perms_choices, register_choices, create_choices):
505 class _DefaultPermissionsForm(formencode.Schema):
506 class _DefaultPermissionsForm(formencode.Schema):
506 allow_extra_fields = True
507 allow_extra_fields = True
507 filter_extra_fields = True
508 filter_extra_fields = True
508 overwrite_default = StringBoolean(if_missing=False)
509 overwrite_default = StringBoolean(if_missing=False)
509 anonymous = OneOf(['True', 'False'], if_missing=False)
510 anonymous = OneOf(['True', 'False'], if_missing=False)
510 default_perm = OneOf(perms_choices)
511 default_perm = OneOf(perms_choices)
511 default_register = OneOf(register_choices)
512 default_register = OneOf(register_choices)
512 default_create = OneOf(create_choices)
513 default_create = OneOf(create_choices)
513
514
514 return _DefaultPermissionsForm
515 return _DefaultPermissionsForm
515
516
516
517
517 def LdapSettingsForm():
518 def LdapSettingsForm():
518 class _LdapSettingsForm(formencode.Schema):
519 class _LdapSettingsForm(formencode.Schema):
519 allow_extra_fields = True
520 allow_extra_fields = True
520 filter_extra_fields = True
521 filter_extra_fields = True
521 pre_validators = [LdapLibValidator]
522 pre_validators = [LdapLibValidator]
522 ldap_active = StringBoolean(if_missing=False)
523 ldap_active = StringBoolean(if_missing=False)
523 ldap_host = UnicodeString(strip=True,)
524 ldap_host = UnicodeString(strip=True,)
524 ldap_port = Number(strip=True,)
525 ldap_port = Number(strip=True,)
525 ldap_ldaps = StringBoolean(if_missing=False)
526 ldap_ldaps = StringBoolean(if_missing=False)
526 ldap_dn_user = UnicodeString(strip=True,)
527 ldap_dn_user = UnicodeString(strip=True,)
527 ldap_dn_pass = UnicodeString(strip=True,)
528 ldap_dn_pass = UnicodeString(strip=True,)
528 ldap_base_dn = All(BaseDnValidator, UnicodeString(strip=True,))
529 ldap_base_dn = All(BaseDnValidator, UnicodeString(strip=True,))
529
530
530 return _LdapSettingsForm
531 return _LdapSettingsForm
@@ -1,336 +1,344 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 box-left">
21 <div class="box box-left">
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">
34 <div class="input">
35 ${h.text('repo_name',class_="medium")}
35 ${h.text('repo_name',class_="medium")}
36 </div>
36 </div>
37 </div>
37 </div>
38 <div class="field">
38 <div class="field">
39 <div class="label">
39 <div class="label">
40 <label for="repo_type">${_('Type')}:</label>
40 <label for="repo_type">${_('Type')}:</label>
41 </div>
41 </div>
42 <div class="input">
42 <div class="input">
43 ${h.select('repo_type','hg',c.backends,class_="medium")}
43 ${h.select('repo_type','hg',c.backends,class_="medium")}
44 </div>
44 </div>
45 </div>
45 </div>
46 <div class="field">
46 <div class="field">
47 <div class="label label-textarea">
47 <div class="label label-textarea">
48 <label for="description">${_('Description')}:</label>
48 <label for="description">${_('Description')}:</label>
49 </div>
49 </div>
50 <div class="textarea text-area editor">
50 <div class="textarea text-area editor">
51 ${h.textarea('description',cols=23,rows=5)}
51 ${h.textarea('description',cols=23,rows=5)}
52 </div>
52 </div>
53 </div>
53 </div>
54
54
55 <div class="field">
55 <div class="field">
56 <div class="label label-checkbox">
56 <div class="label label-checkbox">
57 <label for="private">${_('Private')}:</label>
57 <label for="private">${_('Private')}:</label>
58 </div>
58 </div>
59 <div class="checkboxes">
59 <div class="checkboxes">
60 ${h.checkbox('private',value="True")}
60 ${h.checkbox('private',value="True")}
61 </div>
61 </div>
62 </div>
62 </div>
63 <div class="field">
63 <div class="field">
64 <div class="label label-checkbox">
64 <div class="label label-checkbox">
65 <label for="enable_statistics">${_('Enable statistics')}:</label>
65 <label for="enable_statistics">${_('Enable statistics')}:</label>
66 </div>
66 </div>
67 <div class="checkboxes">
67 <div class="checkboxes">
68 ${h.checkbox('enable_statistics',value="True")}
68 ${h.checkbox('enable_statistics',value="True")}
69 </div>
69 </div>
70 </div>
70 </div>
71 <div class="field">
71 <div class="field">
72 <div class="label label-checkbox">
73 <label for="enable_downloads">${_('Enable downloads')}:</label>
74 </div>
75 <div class="checkboxes">
76 ${h.checkbox('enable_downloads',value="True")}
77 </div>
78 </div>
79 <div class="field">
72 <div class="label">
80 <div class="label">
73 <label for="user">${_('Owner')}:</label>
81 <label for="user">${_('Owner')}:</label>
74 </div>
82 </div>
75 <div class="input input-small ac">
83 <div class="input input-small ac">
76 <div class="perm_ac">
84 <div class="perm_ac">
77 ${h.text('user',class_='yui-ac-input')}
85 ${h.text('user',class_='yui-ac-input')}
78 <div id="owner_container"></div>
86 <div id="owner_container"></div>
79 </div>
87 </div>
80 </div>
88 </div>
81 </div>
89 </div>
82
90
83 <div class="field">
91 <div class="field">
84 <div class="label">
92 <div class="label">
85 <label for="input">${_('Permissions')}:</label>
93 <label for="input">${_('Permissions')}:</label>
86 </div>
94 </div>
87 <div class="input">
95 <div class="input">
88 <table id="permissions_manage">
96 <table id="permissions_manage">
89 <tr>
97 <tr>
90 <td>${_('none')}</td>
98 <td>${_('none')}</td>
91 <td>${_('read')}</td>
99 <td>${_('read')}</td>
92 <td>${_('write')}</td>
100 <td>${_('write')}</td>
93 <td>${_('admin')}</td>
101 <td>${_('admin')}</td>
94 <td>${_('user')}</td>
102 <td>${_('user')}</td>
95 <td></td>
103 <td></td>
96 </tr>
104 </tr>
97
105
98 %for r2p in c.repo_info.repo_to_perm:
106 %for r2p in c.repo_info.repo_to_perm:
99 %if r2p.user.username =='default' and c.repo_info.private:
107 %if r2p.user.username =='default' and c.repo_info.private:
100 <tr>
108 <tr>
101 <td colspan="4">
109 <td colspan="4">
102 <span class="private_repo_msg">
110 <span class="private_repo_msg">
103 ${_('private repository')}
111 ${_('private repository')}
104 </span>
112 </span>
105 </td>
113 </td>
106 <td class="private_repo_msg">${r2p.user.username}</td>
114 <td class="private_repo_msg">${r2p.user.username}</td>
107 </tr>
115 </tr>
108 %else:
116 %else:
109 <tr id="id${id(r2p.user.username)}">
117 <tr id="id${id(r2p.user.username)}">
110 <td>${h.radio('perm_%s' % r2p.user.username,'repository.none')}</td>
118 <td>${h.radio('perm_%s' % r2p.user.username,'repository.none')}</td>
111 <td>${h.radio('perm_%s' % r2p.user.username,'repository.read')}</td>
119 <td>${h.radio('perm_%s' % r2p.user.username,'repository.read')}</td>
112 <td>${h.radio('perm_%s' % r2p.user.username,'repository.write')}</td>
120 <td>${h.radio('perm_%s' % r2p.user.username,'repository.write')}</td>
113 <td>${h.radio('perm_%s' % r2p.user.username,'repository.admin')}</td>
121 <td>${h.radio('perm_%s' % r2p.user.username,'repository.admin')}</td>
114 <td>${r2p.user.username}</td>
122 <td>${r2p.user.username}</td>
115 <td>
123 <td>
116 %if r2p.user.username !='default':
124 %if r2p.user.username !='default':
117 <span class="delete_icon action_button" onclick="ajaxAction(${r2p.user.user_id},'${'id%s'%id(r2p.user.username)}')">
125 <span class="delete_icon action_button" onclick="ajaxAction(${r2p.user.user_id},'${'id%s'%id(r2p.user.username)}')">
118 <script type="text/javascript">
126 <script type="text/javascript">
119 function ajaxAction(user_id,field_id){
127 function ajaxAction(user_id,field_id){
120 var sUrl = "${h.url('delete_repo_user',repo_name=c.repo_name)}";
128 var sUrl = "${h.url('delete_repo_user',repo_name=c.repo_name)}";
121 var callback = { success:function(o){
129 var callback = { success:function(o){
122 var tr = YAHOO.util.Dom.get(String(field_id));
130 var tr = YAHOO.util.Dom.get(String(field_id));
123 tr.parentNode.removeChild(tr);},failure:function(o){
131 tr.parentNode.removeChild(tr);},failure:function(o){
124 alert("${_('Failed to remove user')}");},};
132 alert("${_('Failed to remove user')}");},};
125 var postData = '_method=delete&user_id='+user_id;
133 var postData = '_method=delete&user_id='+user_id;
126 var request = YAHOO.util.Connect.asyncRequest('POST', sUrl, callback, postData);};
134 var request = YAHOO.util.Connect.asyncRequest('POST', sUrl, callback, postData);};
127 </script>
135 </script>
128 </span>
136 </span>
129 %endif
137 %endif
130 </td>
138 </td>
131 </tr>
139 </tr>
132 %endif
140 %endif
133 %endfor
141 %endfor
134
142
135 <tr id="add_perm_input">
143 <tr id="add_perm_input">
136 <td>${h.radio('perm_new_user','repository.none')}</td>
144 <td>${h.radio('perm_new_user','repository.none')}</td>
137 <td>${h.radio('perm_new_user','repository.read')}</td>
145 <td>${h.radio('perm_new_user','repository.read')}</td>
138 <td>${h.radio('perm_new_user','repository.write')}</td>
146 <td>${h.radio('perm_new_user','repository.write')}</td>
139 <td>${h.radio('perm_new_user','repository.admin')}</td>
147 <td>${h.radio('perm_new_user','repository.admin')}</td>
140 <td class='ac'>
148 <td class='ac'>
141 <div class="perm_ac" id="perm_ac">
149 <div class="perm_ac" id="perm_ac">
142 ${h.text('perm_new_user_name',class_='yui-ac-input')}
150 ${h.text('perm_new_user_name',class_='yui-ac-input')}
143 <div id="perm_container"></div>
151 <div id="perm_container"></div>
144 </div>
152 </div>
145 </td>
153 </td>
146 <td></td>
154 <td></td>
147 </tr>
155 </tr>
148 <tr>
156 <tr>
149 <td colspan="6">
157 <td colspan="6">
150 <span id="add_perm" class="add_icon" style="cursor: pointer;">
158 <span id="add_perm" class="add_icon" style="cursor: pointer;">
151 ${_('Add another user')}
159 ${_('Add another user')}
152 </span>
160 </span>
153 </td>
161 </td>
154 </tr>
162 </tr>
155 </table>
163 </table>
156 </div>
164 </div>
157
165
158 <div class="buttons">
166 <div class="buttons">
159 ${h.submit('save','Save',class_="ui-button")}
167 ${h.submit('save','Save',class_="ui-button")}
160 ${h.reset('reset','Reset',class_="ui-button")}
168 ${h.reset('reset','Reset',class_="ui-button")}
161 </div>
169 </div>
162 </div>
170 </div>
163 </div>
171 </div>
164 </div>
172 </div>
165 ${h.end_form()}
173 ${h.end_form()}
166 <script type="text/javascript">
174 <script type="text/javascript">
167 YAHOO.util.Event.onDOMReady(function(){
175 YAHOO.util.Event.onDOMReady(function(){
168 var D = YAHOO.util.Dom;
176 var D = YAHOO.util.Dom;
169 if(!D.hasClass('perm_new_user_name','error')){
177 if(!D.hasClass('perm_new_user_name','error')){
170 D.setStyle('add_perm_input','display','none');
178 D.setStyle('add_perm_input','display','none');
171 }
179 }
172 YAHOO.util.Event.addListener('add_perm','click',function(){
180 YAHOO.util.Event.addListener('add_perm','click',function(){
173 D.setStyle('add_perm_input','display','');
181 D.setStyle('add_perm_input','display','');
174 D.setStyle('add_perm','opacity','0.6');
182 D.setStyle('add_perm','opacity','0.6');
175 D.setStyle('add_perm','cursor','default');
183 D.setStyle('add_perm','cursor','default');
176 });
184 });
177 });
185 });
178 </script>
186 </script>
179 <script type="text/javascript">
187 <script type="text/javascript">
180 YAHOO.example.FnMultipleFields = function(){
188 YAHOO.example.FnMultipleFields = function(){
181 var myContacts = ${c.users_array|n}
189 var myContacts = ${c.users_array|n}
182
190
183 // Define a custom search function for the DataSource
191 // Define a custom search function for the DataSource
184 var matchNames = function(sQuery) {
192 var matchNames = function(sQuery) {
185 // Case insensitive matching
193 // Case insensitive matching
186 var query = sQuery.toLowerCase(),
194 var query = sQuery.toLowerCase(),
187 contact,
195 contact,
188 i=0,
196 i=0,
189 l=myContacts.length,
197 l=myContacts.length,
190 matches = [];
198 matches = [];
191
199
192 // Match against each name of each contact
200 // Match against each name of each contact
193 for(; i<l; i++) {
201 for(; i<l; i++) {
194 contact = myContacts[i];
202 contact = myContacts[i];
195 if((contact.fname.toLowerCase().indexOf(query) > -1) ||
203 if((contact.fname.toLowerCase().indexOf(query) > -1) ||
196 (contact.lname.toLowerCase().indexOf(query) > -1) ||
204 (contact.lname.toLowerCase().indexOf(query) > -1) ||
197 (contact.nname && (contact.nname.toLowerCase().indexOf(query) > -1))) {
205 (contact.nname && (contact.nname.toLowerCase().indexOf(query) > -1))) {
198 matches[matches.length] = contact;
206 matches[matches.length] = contact;
199 }
207 }
200 }
208 }
201
209
202 return matches;
210 return matches;
203 };
211 };
204
212
205 // Use a FunctionDataSource
213 // Use a FunctionDataSource
206 var oDS = new YAHOO.util.FunctionDataSource(matchNames);
214 var oDS = new YAHOO.util.FunctionDataSource(matchNames);
207 oDS.responseSchema = {
215 oDS.responseSchema = {
208 fields: ["id", "fname", "lname", "nname"]
216 fields: ["id", "fname", "lname", "nname"]
209 }
217 }
210
218
211 // Instantiate AutoComplete for perms
219 // Instantiate AutoComplete for perms
212 var oAC_perms = new YAHOO.widget.AutoComplete("perm_new_user_name", "perm_container", oDS);
220 var oAC_perms = new YAHOO.widget.AutoComplete("perm_new_user_name", "perm_container", oDS);
213 oAC_perms.useShadow = false;
221 oAC_perms.useShadow = false;
214 oAC_perms.resultTypeList = false;
222 oAC_perms.resultTypeList = false;
215
223
216 // Instantiate AutoComplete for owner
224 // Instantiate AutoComplete for owner
217 var oAC_owner = new YAHOO.widget.AutoComplete("user", "owner_container", oDS);
225 var oAC_owner = new YAHOO.widget.AutoComplete("user", "owner_container", oDS);
218 oAC_owner.useShadow = false;
226 oAC_owner.useShadow = false;
219 oAC_owner.resultTypeList = false;
227 oAC_owner.resultTypeList = false;
220
228
221
229
222 // Custom formatter to highlight the matching letters
230 // Custom formatter to highlight the matching letters
223 var custom_formatter = function(oResultData, sQuery, sResultMatch) {
231 var custom_formatter = function(oResultData, sQuery, sResultMatch) {
224 var query = sQuery.toLowerCase(),
232 var query = sQuery.toLowerCase(),
225 fname = oResultData.fname,
233 fname = oResultData.fname,
226 lname = oResultData.lname,
234 lname = oResultData.lname,
227 nname = oResultData.nname || "", // Guard against null value
235 nname = oResultData.nname || "", // Guard against null value
228 query = sQuery.toLowerCase(),
236 query = sQuery.toLowerCase(),
229 fnameMatchIndex = fname.toLowerCase().indexOf(query),
237 fnameMatchIndex = fname.toLowerCase().indexOf(query),
230 lnameMatchIndex = lname.toLowerCase().indexOf(query),
238 lnameMatchIndex = lname.toLowerCase().indexOf(query),
231 nnameMatchIndex = nname.toLowerCase().indexOf(query),
239 nnameMatchIndex = nname.toLowerCase().indexOf(query),
232 displayfname, displaylname, displaynname;
240 displayfname, displaylname, displaynname;
233
241
234 if(fnameMatchIndex > -1) {
242 if(fnameMatchIndex > -1) {
235 displayfname = highlightMatch(fname, query, fnameMatchIndex);
243 displayfname = highlightMatch(fname, query, fnameMatchIndex);
236 }
244 }
237 else {
245 else {
238 displayfname = fname;
246 displayfname = fname;
239 }
247 }
240
248
241 if(lnameMatchIndex > -1) {
249 if(lnameMatchIndex > -1) {
242 displaylname = highlightMatch(lname, query, lnameMatchIndex);
250 displaylname = highlightMatch(lname, query, lnameMatchIndex);
243 }
251 }
244 else {
252 else {
245 displaylname = lname;
253 displaylname = lname;
246 }
254 }
247
255
248 if(nnameMatchIndex > -1) {
256 if(nnameMatchIndex > -1) {
249 displaynname = "(" + highlightMatch(nname, query, nnameMatchIndex) + ")";
257 displaynname = "(" + highlightMatch(nname, query, nnameMatchIndex) + ")";
250 }
258 }
251 else {
259 else {
252 displaynname = nname ? "(" + nname + ")" : "";
260 displaynname = nname ? "(" + nname + ")" : "";
253 }
261 }
254
262
255 return displayfname + " " + displaylname + " " + displaynname;
263 return displayfname + " " + displaylname + " " + displaynname;
256
264
257 };
265 };
258 oAC_perms.formatResult = custom_formatter;
266 oAC_perms.formatResult = custom_formatter;
259 oAC_owner.formatResult = custom_formatter;
267 oAC_owner.formatResult = custom_formatter;
260
268
261 // Helper function for the formatter
269 // Helper function for the formatter
262 var highlightMatch = function(full, snippet, matchindex) {
270 var highlightMatch = function(full, snippet, matchindex) {
263 return full.substring(0, matchindex) +
271 return full.substring(0, matchindex) +
264 "<span class='match'>" +
272 "<span class='match'>" +
265 full.substr(matchindex, snippet.length) +
273 full.substr(matchindex, snippet.length) +
266 "</span>" +
274 "</span>" +
267 full.substring(matchindex + snippet.length);
275 full.substring(matchindex + snippet.length);
268 };
276 };
269
277
270 var myHandler = function(sType, aArgs) {
278 var myHandler = function(sType, aArgs) {
271 var myAC = aArgs[0]; // reference back to the AC instance
279 var myAC = aArgs[0]; // reference back to the AC instance
272 var elLI = aArgs[1]; // reference to the selected LI element
280 var elLI = aArgs[1]; // reference to the selected LI element
273 var oData = aArgs[2]; // object literal of selected item's result data
281 var oData = aArgs[2]; // object literal of selected item's result data
274 myAC.getInputEl().value = oData.nname;
282 myAC.getInputEl().value = oData.nname;
275 };
283 };
276
284
277 oAC_perms.itemSelectEvent.subscribe(myHandler);
285 oAC_perms.itemSelectEvent.subscribe(myHandler);
278 oAC_owner.itemSelectEvent.subscribe(myHandler);
286 oAC_owner.itemSelectEvent.subscribe(myHandler);
279
287
280 return {
288 return {
281 oDS: oDS,
289 oDS: oDS,
282 oAC_perms: oAC_perms,
290 oAC_perms: oAC_perms,
283 oAC_owner: oAC_owner,
291 oAC_owner: oAC_owner,
284 };
292 };
285 }();
293 }();
286
294
287 </script>
295 </script>
288
296
289 </div>
297 </div>
290
298
291 <div class="box box-right">
299 <div class="box box-right">
292 <div class="title">
300 <div class="title">
293 <h5>${_('Administration')}</h5>
301 <h5>${_('Administration')}</h5>
294 </div>
302 </div>
295
303
296 <h3>${_('Statistics')}</h3>
304 <h3>${_('Statistics')}</h3>
297
305
298 ${h.form(url('repo_stats', repo_name=c.repo_info.repo_name),method='delete')}
306 ${h.form(url('repo_stats', repo_name=c.repo_info.repo_name),method='delete')}
299 <div class="form">
307 <div class="form">
300 <div class="fields">
308 <div class="fields">
301 ${h.submit('reset_stats_%s' % c.repo_info.repo_name,_('Reset current statistics'),class_="refresh_icon action_button",onclick="return confirm('Confirm to remove current statistics');")}
309 ${h.submit('reset_stats_%s' % c.repo_info.repo_name,_('Reset current statistics'),class_="refresh_icon action_button",onclick="return confirm('Confirm to remove current statistics');")}
302
310
303 <div class="field">
311 <div class="field">
304 <ul>
312 <ul>
305 <li>${_('Fetched to rev')}: ${c.stats_revision}/${c.repo_last_rev}</li>
313 <li>${_('Fetched to rev')}: ${c.stats_revision}/${c.repo_last_rev}</li>
306 <li>${_('Percentage of stats gathered')}: ${c.stats_percentage} %</li>
314 <li>${_('Percentage of stats gathered')}: ${c.stats_percentage} %</li>
307 </ul>
315 </ul>
308 </div>
316 </div>
309
317
310 </div>
318 </div>
311 </div>
319 </div>
312 ${h.end_form()}
320 ${h.end_form()}
313
321
314 <h3>${_('Cache')}</h3>
322 <h3>${_('Cache')}</h3>
315 ${h.form(url('repo_cache', repo_name=c.repo_info.repo_name),method='delete')}
323 ${h.form(url('repo_cache', repo_name=c.repo_info.repo_name),method='delete')}
316 <div class="form">
324 <div class="form">
317 <div class="fields">
325 <div class="fields">
318 ${h.submit('reset_cache_%s' % c.repo_info.repo_name,_('Invalidate repository cache'),class_="refresh_icon action_button",onclick="return confirm('Confirm to invalidate repository cache');")}
326 ${h.submit('reset_cache_%s' % c.repo_info.repo_name,_('Invalidate repository cache'),class_="refresh_icon action_button",onclick="return confirm('Confirm to invalidate repository cache');")}
319 </div>
327 </div>
320 </div>
328 </div>
321 ${h.end_form()}
329 ${h.end_form()}
322
330
323
331
324 <h3>${_('Delete')}</h3>
332 <h3>${_('Delete')}</h3>
325 ${h.form(url('repo', repo_name=c.repo_info.repo_name),method='delete')}
333 ${h.form(url('repo', repo_name=c.repo_info.repo_name),method='delete')}
326 <div class="form">
334 <div class="form">
327 <div class="fields">
335 <div class="fields">
328 ${h.submit('remove_%s' % c.repo_info.repo_name,_('Remove this repository'),class_="delete_icon action_button",onclick="return confirm('Confirm to delete this repository');")}
336 ${h.submit('remove_%s' % c.repo_info.repo_name,_('Remove this repository'),class_="delete_icon action_button",onclick="return confirm('Confirm to delete this repository');")}
329 </div>
337 </div>
330 </div>
338 </div>
331 ${h.end_form()}
339 ${h.end_form()}
332
340
333 </div>
341 </div>
334
342
335
343
336 </%def> No newline at end of file
344 </%def>
@@ -1,669 +1,674 b''
1 <%inherit file="/base/base.html"/>
1 <%inherit file="/base/base.html"/>
2
2
3 <%def name="title()">
3 <%def name="title()">
4 ${c.repo_name} ${_('Summary')} - ${c.rhodecode_name}
4 ${c.repo_name} ${_('Summary')} - ${c.rhodecode_name}
5 </%def>
5 </%def>
6
6
7 <%def name="breadcrumbs_links()">
7 <%def name="breadcrumbs_links()">
8 ${h.link_to(u'Home',h.url('/'))}
8 ${h.link_to(u'Home',h.url('/'))}
9 &raquo;
9 &raquo;
10 ${h.link_to(c.repo_name,h.url('summary_home',repo_name=c.repo_name))}
10 ${h.link_to(c.repo_name,h.url('summary_home',repo_name=c.repo_name))}
11 &raquo;
11 &raquo;
12 ${_('summary')}
12 ${_('summary')}
13 </%def>
13 </%def>
14
14
15 <%def name="page_nav()">
15 <%def name="page_nav()">
16 ${self.menu('summary')}
16 ${self.menu('summary')}
17 </%def>
17 </%def>
18
18
19 <%def name="main()">
19 <%def name="main()">
20 <div class="box box-left">
20 <div class="box box-left">
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 <!-- end box / title -->
25 <!-- end box / title -->
26 <div class="form">
26 <div class="form">
27 <div class="fields">
27 <div class="fields">
28
28
29 <div class="field">
29 <div class="field">
30 <div class="label">
30 <div class="label">
31 <label>${_('Name')}:</label>
31 <label>${_('Name')}:</label>
32 </div>
32 </div>
33 <div class="input-short">
33 <div class="input-short">
34 %if c.repo_info.dbrepo.repo_type =='hg':
34 %if c.repo_info.dbrepo.repo_type =='hg':
35 <img style="margin-bottom:2px" class="icon" title="${_('Mercurial repository')}" alt="${_('Mercurial repository')}" src="/images/icons/hgicon.png"/>
35 <img style="margin-bottom:2px" class="icon" title="${_('Mercurial repository')}" alt="${_('Mercurial repository')}" src="/images/icons/hgicon.png"/>
36 %endif
36 %endif
37 %if c.repo_info.dbrepo.repo_type =='git':
37 %if c.repo_info.dbrepo.repo_type =='git':
38 <img style="margin-bottom:2px" class="icon" title="${_('Git repository')}" alt="${_('Git repository')}" src="/images/icons/giticon.png"/>
38 <img style="margin-bottom:2px" class="icon" title="${_('Git repository')}" alt="${_('Git repository')}" src="/images/icons/giticon.png"/>
39 %endif
39 %endif
40
40
41 %if c.repo_info.dbrepo.private:
41 %if c.repo_info.dbrepo.private:
42 <img style="margin-bottom:2px" class="icon" title="${_('private repository')}" alt="${_('private repository')}" src="/images/icons/lock.png"/>
42 <img style="margin-bottom:2px" class="icon" title="${_('private repository')}" alt="${_('private repository')}" src="/images/icons/lock.png"/>
43 %else:
43 %else:
44 <img style="margin-bottom:2px" class="icon" title="${_('public repository')}" alt="${_('public repository')}" src="/images/icons/lock_open.png"/>
44 <img style="margin-bottom:2px" class="icon" title="${_('public repository')}" alt="${_('public repository')}" src="/images/icons/lock_open.png"/>
45 %endif
45 %endif
46 <span style="font-size: 1.6em;font-weight: bold;vertical-align: baseline;">${c.repo_info.name}</span>
46 <span style="font-size: 1.6em;font-weight: bold;vertical-align: baseline;">${c.repo_info.name}</span>
47 %if c.rhodecode_user.username != 'default':
47 %if c.rhodecode_user.username != 'default':
48 %if c.following:
48 %if c.following:
49 <span id="follow_toggle" class="following" title="${_('Stop following this repository')}"
49 <span id="follow_toggle" class="following" title="${_('Stop following this repository')}"
50 onclick="javascript:toggleFollowingRepo(${c.repo_info.dbrepo.repo_id},'${str(h.get_token())}')">
50 onclick="javascript:toggleFollowingRepo(${c.repo_info.dbrepo.repo_id},'${str(h.get_token())}')">
51 </span>
51 </span>
52 %else:
52 %else:
53 <span id="follow_toggle" class="follow" title="${_('Start following this repository')}"
53 <span id="follow_toggle" class="follow" title="${_('Start following this repository')}"
54 onclick="javascript:toggleFollowingRepo(${c.repo_info.dbrepo.repo_id},'${str(h.get_token())}')">
54 onclick="javascript:toggleFollowingRepo(${c.repo_info.dbrepo.repo_id},'${str(h.get_token())}')">
55 </span>
55 </span>
56 %endif
56 %endif
57 %endif:
57 %endif:
58 <br/>
58 <br/>
59 %if c.repo_info.dbrepo.fork:
59 %if c.repo_info.dbrepo.fork:
60 <span style="margin-top:5px">
60 <span style="margin-top:5px">
61 <a href="${h.url('summary_home',repo_name=c.repo_info.dbrepo.fork.repo_name)}">
61 <a href="${h.url('summary_home',repo_name=c.repo_info.dbrepo.fork.repo_name)}">
62 <img class="icon" alt="${_('public')}"
62 <img class="icon" alt="${_('public')}"
63 title="${_('Fork of')} ${c.repo_info.dbrepo.fork.repo_name}"
63 title="${_('Fork of')} ${c.repo_info.dbrepo.fork.repo_name}"
64 src="/images/icons/arrow_divide.png"/>
64 src="/images/icons/arrow_divide.png"/>
65 ${_('Fork of')} ${c.repo_info.dbrepo.fork.repo_name}
65 ${_('Fork of')} ${c.repo_info.dbrepo.fork.repo_name}
66 </a>
66 </a>
67 </span>
67 </span>
68 %endif
68 %endif
69 </div>
69 </div>
70 </div>
70 </div>
71
71
72
72
73 <div class="field">
73 <div class="field">
74 <div class="label">
74 <div class="label">
75 <label>${_('Description')}:</label>
75 <label>${_('Description')}:</label>
76 </div>
76 </div>
77 <div class="input-short">
77 <div class="input-short">
78 ${c.repo_info.dbrepo.description}
78 ${c.repo_info.dbrepo.description}
79 </div>
79 </div>
80 </div>
80 </div>
81
81
82
82
83 <div class="field">
83 <div class="field">
84 <div class="label">
84 <div class="label">
85 <label>${_('Contact')}:</label>
85 <label>${_('Contact')}:</label>
86 </div>
86 </div>
87 <div class="input-short">
87 <div class="input-short">
88 <div class="gravatar">
88 <div class="gravatar">
89 <img alt="gravatar" src="${h.gravatar_url(c.repo_info.dbrepo.user.email)}"/>
89 <img alt="gravatar" src="${h.gravatar_url(c.repo_info.dbrepo.user.email)}"/>
90 </div>
90 </div>
91 ${_('Username')}: ${c.repo_info.dbrepo.user.username}<br/>
91 ${_('Username')}: ${c.repo_info.dbrepo.user.username}<br/>
92 ${_('Name')}: ${c.repo_info.dbrepo.user.name} ${c.repo_info.dbrepo.user.lastname}<br/>
92 ${_('Name')}: ${c.repo_info.dbrepo.user.name} ${c.repo_info.dbrepo.user.lastname}<br/>
93 ${_('Email')}: <a href="mailto:${c.repo_info.dbrepo.user.email}">${c.repo_info.dbrepo.user.email}</a>
93 ${_('Email')}: <a href="mailto:${c.repo_info.dbrepo.user.email}">${c.repo_info.dbrepo.user.email}</a>
94 </div>
94 </div>
95 </div>
95 </div>
96
96
97 <div class="field">
97 <div class="field">
98 <div class="label">
98 <div class="label">
99 <label>${_('Last change')}:</label>
99 <label>${_('Last change')}:</label>
100 </div>
100 </div>
101 <div class="input-short">
101 <div class="input-short">
102 ${h.age(c.repo_info.last_change)} - ${c.repo_info.last_change}
102 ${h.age(c.repo_info.last_change)} - ${c.repo_info.last_change}
103 ${_('by')} ${h.get_changeset_safe(c.repo_info,'tip').author}
103 ${_('by')} ${h.get_changeset_safe(c.repo_info,'tip').author}
104
104
105 </div>
105 </div>
106 </div>
106 </div>
107
107
108 <div class="field">
108 <div class="field">
109 <div class="label">
109 <div class="label">
110 <label>${_('Clone url')}:</label>
110 <label>${_('Clone url')}:</label>
111 </div>
111 </div>
112 <div class="input-short">
112 <div class="input-short">
113 <input type="text" id="clone_url" readonly="readonly" value="hg clone ${c.clone_repo_url}" size="70"/>
113 <input type="text" id="clone_url" readonly="readonly" value="hg clone ${c.clone_repo_url}" size="70"/>
114 </div>
114 </div>
115 </div>
115 </div>
116
116
117 <div class="field">
117 <div class="field">
118 <div class="label">
118 <div class="label">
119 <label>${_('Trending source files')}:</label>
119 <label>${_('Trending source files')}:</label>
120 </div>
120 </div>
121 <div class="input-short">
121 <div class="input-short">
122 <div id="lang_stats"></div>
122 <div id="lang_stats"></div>
123 </div>
123 </div>
124 </div>
124 </div>
125
125
126 <div class="field">
126 <div class="field">
127 <div class="label">
127 <div class="label">
128 <label>${_('Download')}:</label>
128 <label>${_('Download')}:</label>
129 </div>
129 </div>
130 <div class="input-short">
130 <div class="input-short">
131 %if len(c.repo_info.revisions) == 0:
131 %if len(c.repo_info.revisions) == 0:
132 ${_('There are no downloads yet')}
132 ${_('There are no downloads yet')}
133 %elif c.enable_downloads is False:
134 ${_('Downloads are disabled for this repository')}
135 %if h.HasPermissionAll('hg.admin')('enable stats on from summary'):
136 [${h.link_to(_('enable'),h.url('edit_repo',repo_name=c.repo_name))}]
137 %endif
133 %else:
138 %else:
134 ${h.select('download_options',c.repo_info.get_changeset().raw_id,c.download_options)}
139 ${h.select('download_options',c.repo_info.get_changeset().raw_id,c.download_options)}
135 %for cnt,archive in enumerate(c.repo_info._get_archives()):
140 %for cnt,archive in enumerate(c.repo_info._get_archives()):
136 %if cnt >=1:
141 %if cnt >=1:
137 |
142 |
138 %endif
143 %endif
139 <span class="tooltip" title="${_('Download %s as %s') %('tip',archive['type'])}"
144 <span class="tooltip" title="${_('Download %s as %s') %('tip',archive['type'])}"
140 id="${archive['type']+'_link'}">${h.link_to(archive['type'],
145 id="${archive['type']+'_link'}">${h.link_to(archive['type'],
141 h.url('files_archive_home',repo_name=c.repo_info.name,
146 h.url('files_archive_home',repo_name=c.repo_info.name,
142 fname='tip'+archive['extension']),class_="archive_icon")}</span>
147 fname='tip'+archive['extension']),class_="archive_icon")}</span>
143 %endfor
148 %endfor
144 %endif
149 %endif
145 </div>
150 </div>
146 </div>
151 </div>
147
152
148 <div class="field">
153 <div class="field">
149 <div class="label">
154 <div class="label">
150 <label>${_('Feeds')}:</label>
155 <label>${_('Feeds')}:</label>
151 </div>
156 </div>
152 <div class="input-short">
157 <div class="input-short">
153 ${h.link_to(_('RSS'),h.url('rss_feed_home',repo_name=c.repo_info.name),class_='rss_icon')}
158 ${h.link_to(_('RSS'),h.url('rss_feed_home',repo_name=c.repo_info.name),class_='rss_icon')}
154 ${h.link_to(_('Atom'),h.url('atom_feed_home',repo_name=c.repo_info.name),class_='atom_icon')}
159 ${h.link_to(_('Atom'),h.url('atom_feed_home',repo_name=c.repo_info.name),class_='atom_icon')}
155 </div>
160 </div>
156 </div>
161 </div>
157 </div>
162 </div>
158 </div>
163 </div>
159 <script type="text/javascript">
164 <script type="text/javascript">
160 YUE.onDOMReady(function(e){
165 YUE.onDOMReady(function(e){
161 id = 'clone_url';
166 id = 'clone_url';
162 YUE.on(id,'click',function(e){
167 YUE.on(id,'click',function(e){
163 YUD.get('clone_url').select();
168 YUD.get('clone_url').select();
164 })
169 })
165 })
170 })
166 var data = ${c.trending_languages|n};
171 var data = ${c.trending_languages|n};
167 var total = 0;
172 var total = 0;
168 var no_data = true;
173 var no_data = true;
169 for (k in data){
174 for (k in data){
170 total += data[k];
175 total += data[k];
171 no_data = false;
176 no_data = false;
172 }
177 }
173 var tbl = document.createElement('table');
178 var tbl = document.createElement('table');
174 tbl.setAttribute('class','trending_language_tbl');
179 tbl.setAttribute('class','trending_language_tbl');
175 var cnt =0;
180 var cnt =0;
176 for (k in data){
181 for (k in data){
177 cnt+=1;
182 cnt+=1;
178 var hide = cnt>2;
183 var hide = cnt>2;
179 var tr = document.createElement('tr');
184 var tr = document.createElement('tr');
180 if (hide){
185 if (hide){
181 tr.setAttribute('style','display:none');
186 tr.setAttribute('style','display:none');
182 tr.setAttribute('class','stats_hidden');
187 tr.setAttribute('class','stats_hidden');
183 }
188 }
184 var percentage = Math.round((data[k]/total*100),2);
189 var percentage = Math.round((data[k]/total*100),2);
185 var value = data[k];
190 var value = data[k];
186 var td1 = document.createElement('td');
191 var td1 = document.createElement('td');
187 td1.width=150;
192 td1.width=150;
188 var trending_language_label = document.createElement('div');
193 var trending_language_label = document.createElement('div');
189 trending_language_label.innerHTML = k;
194 trending_language_label.innerHTML = k;
190 td1.appendChild(trending_language_label);
195 td1.appendChild(trending_language_label);
191
196
192 var td2 = document.createElement('td');
197 var td2 = document.createElement('td');
193 td2.setAttribute('style','padding-right:14px !important');
198 td2.setAttribute('style','padding-right:14px !important');
194 var trending_language = document.createElement('div');
199 var trending_language = document.createElement('div');
195 var nr_files = value+" ${_('files')}";
200 var nr_files = value+" ${_('files')}";
196
201
197 trending_language.title = k+" "+nr_files;
202 trending_language.title = k+" "+nr_files;
198
203
199 if (percentage>20){
204 if (percentage>20){
200 trending_language.innerHTML = "<b style='font-size:0.8em'>"+percentage+"% "+nr_files+ "</b>";
205 trending_language.innerHTML = "<b style='font-size:0.8em'>"+percentage+"% "+nr_files+ "</b>";
201 }
206 }
202 else{
207 else{
203 trending_language.innerHTML = "<b style='font-size:0.8em'>"+percentage+"%</b>";
208 trending_language.innerHTML = "<b style='font-size:0.8em'>"+percentage+"%</b>";
204 }
209 }
205
210
206 trending_language.setAttribute("class", 'trending_language top-right-rounded-corner bottom-right-rounded-corner');
211 trending_language.setAttribute("class", 'trending_language top-right-rounded-corner bottom-right-rounded-corner');
207 trending_language.style.width=percentage+"%";
212 trending_language.style.width=percentage+"%";
208 td2.appendChild(trending_language);
213 td2.appendChild(trending_language);
209
214
210 tr.appendChild(td1);
215 tr.appendChild(td1);
211 tr.appendChild(td2);
216 tr.appendChild(td2);
212 tbl.appendChild(tr);
217 tbl.appendChild(tr);
213 if(cnt == 2){
218 if(cnt == 2){
214 var show_more = document.createElement('tr');
219 var show_more = document.createElement('tr');
215 var td=document.createElement('td');
220 var td=document.createElement('td');
216 lnk = document.createElement('a');
221 lnk = document.createElement('a');
217 lnk.href='#';
222 lnk.href='#';
218 lnk.innerHTML = "${_("show more")}";
223 lnk.innerHTML = "${_("show more")}";
219 lnk.id='code_stats_show_more';
224 lnk.id='code_stats_show_more';
220 td.appendChild(lnk);
225 td.appendChild(lnk);
221 show_more.appendChild(td);
226 show_more.appendChild(td);
222 show_more.appendChild(document.createElement('td'));
227 show_more.appendChild(document.createElement('td'));
223 tbl.appendChild(show_more);
228 tbl.appendChild(show_more);
224 }
229 }
225
230
226 }
231 }
227 if(no_data){
232 if(no_data){
228 var tr = document.createElement('tr');
233 var tr = document.createElement('tr');
229 var td1 = document.createElement('td');
234 var td1 = document.createElement('td');
230 td1.innerHTML = "${c.no_data_msg}";
235 td1.innerHTML = "${c.no_data_msg}";
231 tr.appendChild(td1);
236 tr.appendChild(td1);
232 tbl.appendChild(tr);
237 tbl.appendChild(tr);
233 }
238 }
234 YUD.get('lang_stats').appendChild(tbl);
239 YUD.get('lang_stats').appendChild(tbl);
235 YUE.on('code_stats_show_more','click',function(){
240 YUE.on('code_stats_show_more','click',function(){
236 l = YUD.getElementsByClassName('stats_hidden')
241 l = YUD.getElementsByClassName('stats_hidden')
237 for (e in l){
242 for (e in l){
238 YUD.setStyle(l[e],'display','');
243 YUD.setStyle(l[e],'display','');
239 };
244 };
240 YUD.setStyle(YUD.get('code_stats_show_more'),
245 YUD.setStyle(YUD.get('code_stats_show_more'),
241 'display','none');
246 'display','none');
242 })
247 })
243
248
244
249
245 YUE.on('download_options','change',function(e){
250 YUE.on('download_options','change',function(e){
246 var new_cs = e.target.options[e.target.selectedIndex];
251 var new_cs = e.target.options[e.target.selectedIndex];
247 var tmpl_links = {}
252 var tmpl_links = {}
248 %for cnt,archive in enumerate(c.repo_info._get_archives()):
253 %for cnt,archive in enumerate(c.repo_info._get_archives()):
249 tmpl_links['${archive['type']}'] = '${h.link_to(archive['type'],
254 tmpl_links['${archive['type']}'] = '${h.link_to(archive['type'],
250 h.url('files_archive_home',repo_name=c.repo_info.name,
255 h.url('files_archive_home',repo_name=c.repo_info.name,
251 fname='__CS__'+archive['extension']),class_="archive_icon")}';
256 fname='__CS__'+archive['extension']),class_="archive_icon")}';
252 %endfor
257 %endfor
253
258
254
259
255 for(k in tmpl_links){
260 for(k in tmpl_links){
256 var s = YUD.get(k+'_link')
261 var s = YUD.get(k+'_link')
257 title_tmpl = "${_('Download %s as %s') % ('__CS_NAME__',archive['type'])}";
262 title_tmpl = "${_('Download %s as %s') % ('__CS_NAME__',archive['type'])}";
258 s.title = title_tmpl.replace('__CS_NAME__',new_cs.text)
263 s.title = title_tmpl.replace('__CS_NAME__',new_cs.text)
259 s.innerHTML = tmpl_links[k].replace('__CS__',new_cs.value);
264 s.innerHTML = tmpl_links[k].replace('__CS__',new_cs.value);
260 }
265 }
261
266
262 })
267 })
263
268
264 </script>
269 </script>
265 </div>
270 </div>
266
271
267 <div class="box box-right" style="min-height:455px">
272 <div class="box box-right" style="min-height:455px">
268 <!-- box / title -->
273 <!-- box / title -->
269 <div class="title">
274 <div class="title">
270 <h5>${_('Commit activity by day / author')}</h5>
275 <h5>${_('Commit activity by day / author')}</h5>
271 </div>
276 </div>
272
277
273 <div class="table">
278 <div class="table">
274 %if c.no_data:
279 %if c.no_data:
275 <div style="padding:0 10px 10px 15px;font-size: 1.2em;">${c.no_data_msg}
280 <div style="padding:0 10px 10px 15px;font-size: 1.2em;">${c.no_data_msg}
276 %if h.HasPermissionAll('hg.admin')('enable stats on from summary'):
281 %if h.HasPermissionAll('hg.admin')('enable stats on from summary'):
277 [${h.link_to(_('enable'),h.url('edit_repo',repo_name=c.repo_name))}]
282 [${h.link_to(_('enable'),h.url('edit_repo',repo_name=c.repo_name))}]
278 %endif
283 %endif
279 </div>
284 </div>
280 %endif:
285 %endif:
281 <div id="commit_history" style="width:460px;height:300px;float:left"></div>
286 <div id="commit_history" style="width:460px;height:300px;float:left"></div>
282 <div style="clear: both;height: 10px"></div>
287 <div style="clear: both;height: 10px"></div>
283 <div id="overview" style="width:460px;height:100px;float:left"></div>
288 <div id="overview" style="width:460px;height:100px;float:left"></div>
284
289
285 <div id="legend_data" style="clear:both;margin-top:10px;">
290 <div id="legend_data" style="clear:both;margin-top:10px;">
286 <div id="legend_container"></div>
291 <div id="legend_container"></div>
287 <div id="legend_choices">
292 <div id="legend_choices">
288 <table id="legend_choices_tables" style="font-size:smaller;color:#545454"></table>
293 <table id="legend_choices_tables" style="font-size:smaller;color:#545454"></table>
289 </div>
294 </div>
290 </div>
295 </div>
291 <script type="text/javascript">
296 <script type="text/javascript">
292 /**
297 /**
293 * Plots summary graph
298 * Plots summary graph
294 *
299 *
295 * @class SummaryPlot
300 * @class SummaryPlot
296 * @param {from} initial from for detailed graph
301 * @param {from} initial from for detailed graph
297 * @param {to} initial to for detailed graph
302 * @param {to} initial to for detailed graph
298 * @param {dataset}
303 * @param {dataset}
299 * @param {overview_dataset}
304 * @param {overview_dataset}
300 */
305 */
301 function SummaryPlot(from,to,dataset,overview_dataset) {
306 function SummaryPlot(from,to,dataset,overview_dataset) {
302 var initial_ranges = {
307 var initial_ranges = {
303 "xaxis":{
308 "xaxis":{
304 "from":from,
309 "from":from,
305 "to":to,
310 "to":to,
306 },
311 },
307 };
312 };
308 var dataset = dataset;
313 var dataset = dataset;
309 var overview_dataset = [overview_dataset];
314 var overview_dataset = [overview_dataset];
310 var choiceContainer = YUD.get("legend_choices");
315 var choiceContainer = YUD.get("legend_choices");
311 var choiceContainerTable = YUD.get("legend_choices_tables");
316 var choiceContainerTable = YUD.get("legend_choices_tables");
312 var plotContainer = YUD.get('commit_history');
317 var plotContainer = YUD.get('commit_history');
313 var overviewContainer = YUD.get('overview');
318 var overviewContainer = YUD.get('overview');
314
319
315 var plot_options = {
320 var plot_options = {
316 bars: {show:true,align:'center',lineWidth:4},
321 bars: {show:true,align:'center',lineWidth:4},
317 legend: {show:true, container:"legend_container"},
322 legend: {show:true, container:"legend_container"},
318 points: {show:true,radius:0,fill:false},
323 points: {show:true,radius:0,fill:false},
319 yaxis: {tickDecimals:0,},
324 yaxis: {tickDecimals:0,},
320 xaxis: {
325 xaxis: {
321 mode: "time",
326 mode: "time",
322 timeformat: "%d/%m",
327 timeformat: "%d/%m",
323 min:from,
328 min:from,
324 max:to,
329 max:to,
325 },
330 },
326 grid: {
331 grid: {
327 hoverable: true,
332 hoverable: true,
328 clickable: true,
333 clickable: true,
329 autoHighlight:true,
334 autoHighlight:true,
330 color: "#999"
335 color: "#999"
331 },
336 },
332 //selection: {mode: "x"}
337 //selection: {mode: "x"}
333 };
338 };
334 var overview_options = {
339 var overview_options = {
335 legend:{show:false},
340 legend:{show:false},
336 bars: {show:true,barWidth: 2,},
341 bars: {show:true,barWidth: 2,},
337 shadowSize: 0,
342 shadowSize: 0,
338 xaxis: {mode: "time", timeformat: "%d/%m/%y",},
343 xaxis: {mode: "time", timeformat: "%d/%m/%y",},
339 yaxis: {ticks: 3, min: 0,tickDecimals:0,},
344 yaxis: {ticks: 3, min: 0,tickDecimals:0,},
340 grid: {color: "#999",},
345 grid: {color: "#999",},
341 selection: {mode: "x"}
346 selection: {mode: "x"}
342 };
347 };
343
348
344 /**
349 /**
345 *get dummy data needed in few places
350 *get dummy data needed in few places
346 */
351 */
347 function getDummyData(label){
352 function getDummyData(label){
348 return {"label":label,
353 return {"label":label,
349 "data":[{"time":0,
354 "data":[{"time":0,
350 "commits":0,
355 "commits":0,
351 "added":0,
356 "added":0,
352 "changed":0,
357 "changed":0,
353 "removed":0,
358 "removed":0,
354 }],
359 }],
355 "schema":["commits"],
360 "schema":["commits"],
356 "color":'#ffffff',
361 "color":'#ffffff',
357 }
362 }
358 }
363 }
359
364
360 /**
365 /**
361 * generate checkboxes accordindly to data
366 * generate checkboxes accordindly to data
362 * @param keys
367 * @param keys
363 * @returns
368 * @returns
364 */
369 */
365 function generateCheckboxes(data) {
370 function generateCheckboxes(data) {
366 //append checkboxes
371 //append checkboxes
367 var i = 0;
372 var i = 0;
368 choiceContainerTable.innerHTML = '';
373 choiceContainerTable.innerHTML = '';
369 for(var pos in data) {
374 for(var pos in data) {
370
375
371 data[pos].color = i;
376 data[pos].color = i;
372 i++;
377 i++;
373 if(data[pos].label != ''){
378 if(data[pos].label != ''){
374 choiceContainerTable.innerHTML += '<tr><td>'+
379 choiceContainerTable.innerHTML += '<tr><td>'+
375 '<input type="checkbox" name="' + data[pos].label +'" checked="checked" />'
380 '<input type="checkbox" name="' + data[pos].label +'" checked="checked" />'
376 +data[pos].label+
381 +data[pos].label+
377 '</td></tr>';
382 '</td></tr>';
378 }
383 }
379 }
384 }
380 }
385 }
381
386
382 /**
387 /**
383 * ToolTip show
388 * ToolTip show
384 */
389 */
385 function showTooltip(x, y, contents) {
390 function showTooltip(x, y, contents) {
386 var div=document.getElementById('tooltip');
391 var div=document.getElementById('tooltip');
387 if(!div) {
392 if(!div) {
388 div = document.createElement('div');
393 div = document.createElement('div');
389 div.id="tooltip";
394 div.id="tooltip";
390 div.style.position="absolute";
395 div.style.position="absolute";
391 div.style.border='1px solid #fdd';
396 div.style.border='1px solid #fdd';
392 div.style.padding='2px';
397 div.style.padding='2px';
393 div.style.backgroundColor='#fee';
398 div.style.backgroundColor='#fee';
394 document.body.appendChild(div);
399 document.body.appendChild(div);
395 }
400 }
396 YUD.setStyle(div, 'opacity', 0);
401 YUD.setStyle(div, 'opacity', 0);
397 div.innerHTML = contents;
402 div.innerHTML = contents;
398 div.style.top=(y + 5) + "px";
403 div.style.top=(y + 5) + "px";
399 div.style.left=(x + 5) + "px";
404 div.style.left=(x + 5) + "px";
400
405
401 var anim = new YAHOO.util.Anim(div, {opacity: {to: 0.8}}, 0.2);
406 var anim = new YAHOO.util.Anim(div, {opacity: {to: 0.8}}, 0.2);
402 anim.animate();
407 anim.animate();
403 }
408 }
404
409
405 /**
410 /**
406 * This function will detect if selected period has some changesets
411 * This function will detect if selected period has some changesets
407 for this user if it does this data is then pushed for displaying
412 for this user if it does this data is then pushed for displaying
408 Additionally it will only display users that are selected by the checkbox
413 Additionally it will only display users that are selected by the checkbox
409 */
414 */
410 function getDataAccordingToRanges(ranges) {
415 function getDataAccordingToRanges(ranges) {
411
416
412 var data = [];
417 var data = [];
413 var keys = [];
418 var keys = [];
414 for(var key in dataset){
419 for(var key in dataset){
415 var push = false;
420 var push = false;
416
421
417 //method1 slow !!
422 //method1 slow !!
418 //*
423 //*
419 for(var ds in dataset[key].data){
424 for(var ds in dataset[key].data){
420 commit_data = dataset[key].data[ds];
425 commit_data = dataset[key].data[ds];
421 if (commit_data.time >= ranges.xaxis.from && commit_data.time <= ranges.xaxis.to){
426 if (commit_data.time >= ranges.xaxis.from && commit_data.time <= ranges.xaxis.to){
422 push = true;
427 push = true;
423 break;
428 break;
424 }
429 }
425 }
430 }
426 //*/
431 //*/
427
432
428 /*//method2 sorted commit data !!!
433 /*//method2 sorted commit data !!!
429
434
430 var first_commit = dataset[key].data[0].time;
435 var first_commit = dataset[key].data[0].time;
431 var last_commit = dataset[key].data[dataset[key].data.length-1].time;
436 var last_commit = dataset[key].data[dataset[key].data.length-1].time;
432
437
433 if (first_commit >= ranges.xaxis.from && last_commit <= ranges.xaxis.to){
438 if (first_commit >= ranges.xaxis.from && last_commit <= ranges.xaxis.to){
434 push = true;
439 push = true;
435 }
440 }
436 //*/
441 //*/
437
442
438 if(push){
443 if(push){
439 data.push(dataset[key]);
444 data.push(dataset[key]);
440 }
445 }
441 }
446 }
442 if(data.length >= 1){
447 if(data.length >= 1){
443 return data;
448 return data;
444 }
449 }
445 else{
450 else{
446 //just return dummy data for graph to plot itself
451 //just return dummy data for graph to plot itself
447 return [getDummyData('')];
452 return [getDummyData('')];
448 }
453 }
449
454
450 }
455 }
451
456
452 /**
457 /**
453 * redraw using new checkbox data
458 * redraw using new checkbox data
454 */
459 */
455 function plotchoiced(e,args){
460 function plotchoiced(e,args){
456 var cur_data = args[0];
461 var cur_data = args[0];
457 var cur_ranges = args[1];
462 var cur_ranges = args[1];
458
463
459 var new_data = [];
464 var new_data = [];
460 var inputs = choiceContainer.getElementsByTagName("input");
465 var inputs = choiceContainer.getElementsByTagName("input");
461
466
462 //show only checked labels
467 //show only checked labels
463 for(var i=0; i<inputs.length; i++) {
468 for(var i=0; i<inputs.length; i++) {
464 var checkbox_key = inputs[i].name;
469 var checkbox_key = inputs[i].name;
465
470
466 if(inputs[i].checked){
471 if(inputs[i].checked){
467 for(var d in cur_data){
472 for(var d in cur_data){
468 if(cur_data[d].label == checkbox_key){
473 if(cur_data[d].label == checkbox_key){
469 new_data.push(cur_data[d]);
474 new_data.push(cur_data[d]);
470 }
475 }
471 }
476 }
472 }
477 }
473 else{
478 else{
474 //push dummy data to not hide the label
479 //push dummy data to not hide the label
475 new_data.push(getDummyData(checkbox_key));
480 new_data.push(getDummyData(checkbox_key));
476 }
481 }
477 }
482 }
478
483
479 var new_options = YAHOO.lang.merge(plot_options, {
484 var new_options = YAHOO.lang.merge(plot_options, {
480 xaxis: {
485 xaxis: {
481 min: cur_ranges.xaxis.from,
486 min: cur_ranges.xaxis.from,
482 max: cur_ranges.xaxis.to,
487 max: cur_ranges.xaxis.to,
483 mode:"time",
488 mode:"time",
484 timeformat: "%d/%m",
489 timeformat: "%d/%m",
485 },
490 },
486 });
491 });
487 if (!new_data){
492 if (!new_data){
488 new_data = [[0,1]];
493 new_data = [[0,1]];
489 }
494 }
490 // do the zooming
495 // do the zooming
491 plot = YAHOO.widget.Flot(plotContainer, new_data, new_options);
496 plot = YAHOO.widget.Flot(plotContainer, new_data, new_options);
492
497
493 plot.subscribe("plotselected", plotselected);
498 plot.subscribe("plotselected", plotselected);
494
499
495 //resubscribe plothover
500 //resubscribe plothover
496 plot.subscribe("plothover", plothover);
501 plot.subscribe("plothover", plothover);
497
502
498 // don't fire event on the overview to prevent eternal loop
503 // don't fire event on the overview to prevent eternal loop
499 overview.setSelection(cur_ranges, true);
504 overview.setSelection(cur_ranges, true);
500
505
501 }
506 }
502
507
503 /**
508 /**
504 * plot only selected items from overview
509 * plot only selected items from overview
505 * @param ranges
510 * @param ranges
506 * @returns
511 * @returns
507 */
512 */
508 function plotselected(ranges,cur_data) {
513 function plotselected(ranges,cur_data) {
509 //updates the data for new plot
514 //updates the data for new plot
510 data = getDataAccordingToRanges(ranges);
515 data = getDataAccordingToRanges(ranges);
511 generateCheckboxes(data);
516 generateCheckboxes(data);
512
517
513 var new_options = YAHOO.lang.merge(plot_options, {
518 var new_options = YAHOO.lang.merge(plot_options, {
514 xaxis: {
519 xaxis: {
515 min: ranges.xaxis.from,
520 min: ranges.xaxis.from,
516 max: ranges.xaxis.to,
521 max: ranges.xaxis.to,
517 mode:"time",
522 mode:"time",
518 timeformat: "%d/%m",
523 timeformat: "%d/%m",
519 },
524 },
520 yaxis: {
525 yaxis: {
521 min: ranges.yaxis.from,
526 min: ranges.yaxis.from,
522 max: ranges.yaxis.to,
527 max: ranges.yaxis.to,
523 },
528 },
524
529
525 });
530 });
526 // do the zooming
531 // do the zooming
527 plot = YAHOO.widget.Flot(plotContainer, data, new_options);
532 plot = YAHOO.widget.Flot(plotContainer, data, new_options);
528
533
529 plot.subscribe("plotselected", plotselected);
534 plot.subscribe("plotselected", plotselected);
530
535
531 //resubscribe plothover
536 //resubscribe plothover
532 plot.subscribe("plothover", plothover);
537 plot.subscribe("plothover", plothover);
533
538
534 // don't fire event on the overview to prevent eternal loop
539 // don't fire event on the overview to prevent eternal loop
535 overview.setSelection(ranges, true);
540 overview.setSelection(ranges, true);
536
541
537 //resubscribe choiced
542 //resubscribe choiced
538 YUE.on(choiceContainer.getElementsByTagName("input"), "click", plotchoiced, [data, ranges]);
543 YUE.on(choiceContainer.getElementsByTagName("input"), "click", plotchoiced, [data, ranges]);
539 }
544 }
540
545
541 var previousPoint = null;
546 var previousPoint = null;
542
547
543 function plothover(o) {
548 function plothover(o) {
544 var pos = o.pos;
549 var pos = o.pos;
545 var item = o.item;
550 var item = o.item;
546
551
547 //YUD.get("x").innerHTML = pos.x.toFixed(2);
552 //YUD.get("x").innerHTML = pos.x.toFixed(2);
548 //YUD.get("y").innerHTML = pos.y.toFixed(2);
553 //YUD.get("y").innerHTML = pos.y.toFixed(2);
549 if (item) {
554 if (item) {
550 if (previousPoint != item.datapoint) {
555 if (previousPoint != item.datapoint) {
551 previousPoint = item.datapoint;
556 previousPoint = item.datapoint;
552
557
553 var tooltip = YUD.get("tooltip");
558 var tooltip = YUD.get("tooltip");
554 if(tooltip) {
559 if(tooltip) {
555 tooltip.parentNode.removeChild(tooltip);
560 tooltip.parentNode.removeChild(tooltip);
556 }
561 }
557 var x = item.datapoint.x.toFixed(2);
562 var x = item.datapoint.x.toFixed(2);
558 var y = item.datapoint.y.toFixed(2);
563 var y = item.datapoint.y.toFixed(2);
559
564
560 if (!item.series.label){
565 if (!item.series.label){
561 item.series.label = 'commits';
566 item.series.label = 'commits';
562 }
567 }
563 var d = new Date(x*1000);
568 var d = new Date(x*1000);
564 var fd = d.toDateString()
569 var fd = d.toDateString()
565 var nr_commits = parseInt(y);
570 var nr_commits = parseInt(y);
566
571
567 var cur_data = dataset[item.series.label].data[item.dataIndex];
572 var cur_data = dataset[item.series.label].data[item.dataIndex];
568 var added = cur_data.added;
573 var added = cur_data.added;
569 var changed = cur_data.changed;
574 var changed = cur_data.changed;
570 var removed = cur_data.removed;
575 var removed = cur_data.removed;
571
576
572 var nr_commits_suffix = " ${_('commits')} ";
577 var nr_commits_suffix = " ${_('commits')} ";
573 var added_suffix = " ${_('files added')} ";
578 var added_suffix = " ${_('files added')} ";
574 var changed_suffix = " ${_('files changed')} ";
579 var changed_suffix = " ${_('files changed')} ";
575 var removed_suffix = " ${_('files removed')} ";
580 var removed_suffix = " ${_('files removed')} ";
576
581
577
582
578 if(nr_commits == 1){nr_commits_suffix = " ${_('commit')} ";}
583 if(nr_commits == 1){nr_commits_suffix = " ${_('commit')} ";}
579 if(added==1){added_suffix=" ${_('file added')} ";}
584 if(added==1){added_suffix=" ${_('file added')} ";}
580 if(changed==1){changed_suffix=" ${_('file changed')} ";}
585 if(changed==1){changed_suffix=" ${_('file changed')} ";}
581 if(removed==1){removed_suffix=" ${_('file removed')} ";}
586 if(removed==1){removed_suffix=" ${_('file removed')} ";}
582
587
583 showTooltip(item.pageX, item.pageY, item.series.label + " on " + fd
588 showTooltip(item.pageX, item.pageY, item.series.label + " on " + fd
584 +'<br/>'+
589 +'<br/>'+
585 nr_commits + nr_commits_suffix+'<br/>'+
590 nr_commits + nr_commits_suffix+'<br/>'+
586 added + added_suffix +'<br/>'+
591 added + added_suffix +'<br/>'+
587 changed + changed_suffix + '<br/>'+
592 changed + changed_suffix + '<br/>'+
588 removed + removed_suffix + '<br/>');
593 removed + removed_suffix + '<br/>');
589 }
594 }
590 }
595 }
591 else {
596 else {
592 var tooltip = YUD.get("tooltip");
597 var tooltip = YUD.get("tooltip");
593
598
594 if(tooltip) {
599 if(tooltip) {
595 tooltip.parentNode.removeChild(tooltip);
600 tooltip.parentNode.removeChild(tooltip);
596 }
601 }
597 previousPoint = null;
602 previousPoint = null;
598 }
603 }
599 }
604 }
600
605
601 /**
606 /**
602 * MAIN EXECUTION
607 * MAIN EXECUTION
603 */
608 */
604
609
605 var data = getDataAccordingToRanges(initial_ranges);
610 var data = getDataAccordingToRanges(initial_ranges);
606 generateCheckboxes(data);
611 generateCheckboxes(data);
607
612
608 //main plot
613 //main plot
609 var plot = YAHOO.widget.Flot(plotContainer,data,plot_options);
614 var plot = YAHOO.widget.Flot(plotContainer,data,plot_options);
610
615
611 //overview
616 //overview
612 var overview = YAHOO.widget.Flot(overviewContainer, overview_dataset, overview_options);
617 var overview = YAHOO.widget.Flot(overviewContainer, overview_dataset, overview_options);
613
618
614 //show initial selection on overview
619 //show initial selection on overview
615 overview.setSelection(initial_ranges);
620 overview.setSelection(initial_ranges);
616
621
617 plot.subscribe("plotselected", plotselected);
622 plot.subscribe("plotselected", plotselected);
618
623
619 overview.subscribe("plotselected", function (ranges) {
624 overview.subscribe("plotselected", function (ranges) {
620 plot.setSelection(ranges);
625 plot.setSelection(ranges);
621 });
626 });
622
627
623 plot.subscribe("plothover", plothover);
628 plot.subscribe("plothover", plothover);
624
629
625 YUE.on(choiceContainer.getElementsByTagName("input"), "click", plotchoiced, [data, initial_ranges]);
630 YUE.on(choiceContainer.getElementsByTagName("input"), "click", plotchoiced, [data, initial_ranges]);
626 }
631 }
627 SummaryPlot(${c.ts_min},${c.ts_max},${c.commit_data|n},${c.overview_data|n});
632 SummaryPlot(${c.ts_min},${c.ts_max},${c.commit_data|n},${c.overview_data|n});
628 </script>
633 </script>
629
634
630 </div>
635 </div>
631 </div>
636 </div>
632
637
633 <div class="box">
638 <div class="box">
634 <div class="title">
639 <div class="title">
635 <div class="breadcrumbs">${h.link_to(_('Shortlog'),h.url('shortlog_home',repo_name=c.repo_name))}</div>
640 <div class="breadcrumbs">${h.link_to(_('Shortlog'),h.url('shortlog_home',repo_name=c.repo_name))}</div>
636 </div>
641 </div>
637 <div class="table">
642 <div class="table">
638 <div id="shortlog_data">
643 <div id="shortlog_data">
639 <%include file='../shortlog/shortlog_data.html'/>
644 <%include file='../shortlog/shortlog_data.html'/>
640 </div>
645 </div>
641 ##%if c.repo_changesets:
646 ##%if c.repo_changesets:
642 ## ${h.link_to(_('show more'),h.url('changelog_home',repo_name=c.repo_name))}
647 ## ${h.link_to(_('show more'),h.url('changelog_home',repo_name=c.repo_name))}
643 ##%endif
648 ##%endif
644 </div>
649 </div>
645 </div>
650 </div>
646 <div class="box">
651 <div class="box">
647 <div class="title">
652 <div class="title">
648 <div class="breadcrumbs">${h.link_to(_('Tags'),h.url('tags_home',repo_name=c.repo_name))}</div>
653 <div class="breadcrumbs">${h.link_to(_('Tags'),h.url('tags_home',repo_name=c.repo_name))}</div>
649 </div>
654 </div>
650 <div class="table">
655 <div class="table">
651 <%include file='../tags/tags_data.html'/>
656 <%include file='../tags/tags_data.html'/>
652 %if c.repo_changesets:
657 %if c.repo_changesets:
653 ${h.link_to(_('show more'),h.url('tags_home',repo_name=c.repo_name))}
658 ${h.link_to(_('show more'),h.url('tags_home',repo_name=c.repo_name))}
654 %endif
659 %endif
655 </div>
660 </div>
656 </div>
661 </div>
657 <div class="box">
662 <div class="box">
658 <div class="title">
663 <div class="title">
659 <div class="breadcrumbs">${h.link_to(_('Branches'),h.url('branches_home',repo_name=c.repo_name))}</div>
664 <div class="breadcrumbs">${h.link_to(_('Branches'),h.url('branches_home',repo_name=c.repo_name))}</div>
660 </div>
665 </div>
661 <div class="table">
666 <div class="table">
662 <%include file='../branches/branches_data.html'/>
667 <%include file='../branches/branches_data.html'/>
663 %if c.repo_changesets:
668 %if c.repo_changesets:
664 ${h.link_to(_('show more'),h.url('branches_home',repo_name=c.repo_name))}
669 ${h.link_to(_('show more'),h.url('branches_home',repo_name=c.repo_name))}
665 %endif
670 %endif
666 </div>
671 </div>
667 </div>
672 </div>
668
673
669 </%def>
674 </%def>
General Comments 0
You need to be logged in to leave comments. Login now