##// END OF EJS Templates
Fixes for raw_id, needed for git...
marcink -
r636:ffd07396 beta
parent child Browse files
Show More
@@ -1,190 +1,190 b''
1 1 """
2 2 Routes configuration
3 3
4 4 The more specific and detailed routes should be defined first so they
5 5 may take precedent over the more generic routes. For more information
6 6 refer to the routes manual at http://routes.groovie.org/docs/
7 7 """
8 8 from __future__ import with_statement
9 9 from routes import Mapper
10 10 from rhodecode.lib.utils import check_repo_fast as cr
11 11
12 12 def make_map(config):
13 13 """Create, configure and return the routes Mapper"""
14 14 map = Mapper(directory=config['pylons.paths']['controllers'],
15 15 always_scan=config['debug'])
16 16 map.minimization = False
17 17 map.explicit = False
18 18
19 19 def check_repo(environ, match_dict):
20 20 """
21 21 check for valid repository for proper 404 handling
22 22 :param environ:
23 23 :param match_dict:
24 24 """
25 25 repo_name = match_dict.get('repo_name')
26 26 return not cr(repo_name, config['base_path'])
27 27
28 28 # The ErrorController route (handles 404/500 error pages); it should
29 29 # likely stay at the top, ensuring it can always be resolved
30 30 map.connect('/error/{action}', controller='error')
31 31 map.connect('/error/{action}/{id}', controller='error')
32 32
33 33 #==========================================================================
34 34 # CUSTOM ROUTES HERE
35 35 #==========================================================================
36 36
37 37 #MAIN PAGE
38 map.connect('hg_home', '/', controller='hg', action='index')
38 map.connect('home', '/', controller='home', action='index')
39 39 map.connect('bugtracker', "http://bitbucket.org/marcinkuzminski/rhodecode/issues", _static=True)
40 40 map.connect('gpl_license', "http://www.gnu.org/licenses/gpl.html", _static=True)
41 41 #ADMIN REPOSITORY REST ROUTES
42 42 with map.submapper(path_prefix='/_admin', controller='admin/repos') as m:
43 43 m.connect("repos", "/repos",
44 44 action="create", conditions=dict(method=["POST"]))
45 45 m.connect("repos", "/repos",
46 46 action="index", conditions=dict(method=["GET"]))
47 47 m.connect("formatted_repos", "/repos.{format}",
48 48 action="index",
49 49 conditions=dict(method=["GET"]))
50 50 m.connect("new_repo", "/repos/new",
51 51 action="new", conditions=dict(method=["GET"]))
52 52 m.connect("formatted_new_repo", "/repos/new.{format}",
53 53 action="new", conditions=dict(method=["GET"]))
54 54 m.connect("/repos/{repo_name:.*}",
55 55 action="update", conditions=dict(method=["PUT"],
56 56 function=check_repo))
57 57 m.connect("/repos/{repo_name:.*}",
58 58 action="delete", conditions=dict(method=["DELETE"],
59 59 function=check_repo))
60 60 m.connect("edit_repo", "/repos/{repo_name:.*}/edit",
61 61 action="edit", conditions=dict(method=["GET"],
62 62 function=check_repo))
63 63 m.connect("formatted_edit_repo", "/repos/{repo_name:.*}.{format}/edit",
64 64 action="edit", conditions=dict(method=["GET"],
65 65 function=check_repo))
66 66 m.connect("repo", "/repos/{repo_name:.*}",
67 67 action="show", conditions=dict(method=["GET"],
68 68 function=check_repo))
69 69 m.connect("formatted_repo", "/repos/{repo_name:.*}.{format}",
70 70 action="show", conditions=dict(method=["GET"],
71 71 function=check_repo))
72 72 #ajax delete repo perm user
73 73 m.connect('delete_repo_user', "/repos_delete_user/{repo_name:.*}",
74 74 action="delete_perm_user", conditions=dict(method=["DELETE"],
75 75 function=check_repo))
76 76
77 77 #ADMIN USER REST ROUTES
78 78 map.resource('user', 'users', controller='admin/users', path_prefix='/_admin')
79 79
80 80 #ADMIN PERMISSIONS REST ROUTES
81 81 map.resource('permission', 'permissions', controller='admin/permissions', path_prefix='/_admin')
82 82
83 83 #ADMIN SETTINGS REST ROUTES
84 84 with map.submapper(path_prefix='/_admin', controller='admin/settings') as m:
85 85 m.connect("admin_settings", "/settings",
86 86 action="create", conditions=dict(method=["POST"]))
87 87 m.connect("admin_settings", "/settings",
88 88 action="index", conditions=dict(method=["GET"]))
89 89 m.connect("formatted_admin_settings", "/settings.{format}",
90 90 action="index", conditions=dict(method=["GET"]))
91 91 m.connect("admin_new_setting", "/settings/new",
92 92 action="new", conditions=dict(method=["GET"]))
93 93 m.connect("formatted_admin_new_setting", "/settings/new.{format}",
94 94 action="new", conditions=dict(method=["GET"]))
95 95 m.connect("/settings/{setting_id}",
96 96 action="update", conditions=dict(method=["PUT"]))
97 97 m.connect("/settings/{setting_id}",
98 98 action="delete", conditions=dict(method=["DELETE"]))
99 99 m.connect("admin_edit_setting", "/settings/{setting_id}/edit",
100 100 action="edit", conditions=dict(method=["GET"]))
101 101 m.connect("formatted_admin_edit_setting", "/settings/{setting_id}.{format}/edit",
102 102 action="edit", conditions=dict(method=["GET"]))
103 103 m.connect("admin_setting", "/settings/{setting_id}",
104 104 action="show", conditions=dict(method=["GET"]))
105 105 m.connect("formatted_admin_setting", "/settings/{setting_id}.{format}",
106 106 action="show", conditions=dict(method=["GET"]))
107 107 m.connect("admin_settings_my_account", "/my_account",
108 108 action="my_account", conditions=dict(method=["GET"]))
109 109 m.connect("admin_settings_my_account_update", "/my_account_update",
110 110 action="my_account_update", conditions=dict(method=["PUT"]))
111 111 m.connect("admin_settings_create_repository", "/create_repository",
112 112 action="create_repository", conditions=dict(method=["GET"]))
113 113
114 114 #ADMIN MAIN PAGES
115 115 with map.submapper(path_prefix='/_admin', controller='admin/admin') as m:
116 116 m.connect('admin_home', '', action='index')#main page
117 117 m.connect('admin_add_repo', '/add_repo/{new_repo:[a-z0-9\. _-]*}',
118 118 action='add_repo')
119 119 #SEARCH
120 120 map.connect('search', '/_admin/search', controller='search',)
121 121 map.connect('search_repo', '/_admin/search/{search_repo:.*}', controller='search')
122 122
123 123 #LOGIN/LOGOUT/REGISTER/SIGN IN
124 124 map.connect('login_home', '/_admin/login', controller='login')
125 125 map.connect('logout_home', '/_admin/logout', controller='login', action='logout')
126 126 map.connect('register', '/_admin/register', controller='login', action='register')
127 127 map.connect('reset_password', '/_admin/password_reset', controller='login', action='password_reset')
128 128
129 129 #FEEDS
130 130 map.connect('rss_feed_home', '/{repo_name:.*}/feed/rss',
131 131 controller='feed', action='rss',
132 132 conditions=dict(function=check_repo))
133 133 map.connect('atom_feed_home', '/{repo_name:.*}/feed/atom',
134 134 controller='feed', action='atom',
135 135 conditions=dict(function=check_repo))
136 136
137 137
138 138 #REPOSITORY ROUTES
139 139 map.connect('changeset_home', '/{repo_name:.*}/changeset/{revision}',
140 140 controller='changeset', revision='tip',
141 141 conditions=dict(function=check_repo))
142 142 map.connect('raw_changeset_home', '/{repo_name:.*}/raw-changeset/{revision}',
143 143 controller='changeset', action='raw_changeset', revision='tip',
144 144 conditions=dict(function=check_repo))
145 145 map.connect('summary_home', '/{repo_name:.*}/summary',
146 146 controller='summary', conditions=dict(function=check_repo))
147 147 map.connect('shortlog_home', '/{repo_name:.*}/shortlog',
148 148 controller='shortlog', conditions=dict(function=check_repo))
149 149 map.connect('branches_home', '/{repo_name:.*}/branches',
150 150 controller='branches', conditions=dict(function=check_repo))
151 151 map.connect('tags_home', '/{repo_name:.*}/tags',
152 152 controller='tags', conditions=dict(function=check_repo))
153 153 map.connect('changelog_home', '/{repo_name:.*}/changelog',
154 154 controller='changelog', conditions=dict(function=check_repo))
155 155 map.connect('files_home', '/{repo_name:.*}/files/{revision}/{f_path:.*}',
156 156 controller='files', revision='tip', f_path='',
157 157 conditions=dict(function=check_repo))
158 158 map.connect('files_diff_home', '/{repo_name:.*}/diff/{f_path:.*}',
159 159 controller='files', action='diff', revision='tip', f_path='',
160 160 conditions=dict(function=check_repo))
161 161 map.connect('files_rawfile_home', '/{repo_name:.*}/rawfile/{revision}/{f_path:.*}',
162 162 controller='files', action='rawfile', revision='tip', f_path='',
163 163 conditions=dict(function=check_repo))
164 164 map.connect('files_raw_home', '/{repo_name:.*}/raw/{revision}/{f_path:.*}',
165 165 controller='files', action='raw', revision='tip', f_path='',
166 166 conditions=dict(function=check_repo))
167 167 map.connect('files_annotate_home', '/{repo_name:.*}/annotate/{revision}/{f_path:.*}',
168 168 controller='files', action='annotate', revision='tip', f_path='',
169 169 conditions=dict(function=check_repo))
170 170 map.connect('files_archive_home', '/{repo_name:.*}/archive/{revision}/{fileformat}',
171 171 controller='files', action='archivefile', revision='tip',
172 172 conditions=dict(function=check_repo))
173 173 map.connect('repo_settings_delete', '/{repo_name:.*}/settings',
174 174 controller='settings', action="delete",
175 175 conditions=dict(method=["DELETE"], function=check_repo))
176 176 map.connect('repo_settings_update', '/{repo_name:.*}/settings',
177 177 controller='settings', action="update",
178 178 conditions=dict(method=["PUT"], function=check_repo))
179 179 map.connect('repo_settings_home', '/{repo_name:.*}/settings',
180 180 controller='settings', action='index',
181 181 conditions=dict(function=check_repo))
182 182
183 183 map.connect('repo_fork_create_home', '/{repo_name:.*}/fork',
184 184 controller='settings', action='fork_create',
185 185 conditions=dict(function=check_repo, method=["POST"]))
186 186 map.connect('repo_fork_home', '/{repo_name:.*}/fork',
187 187 controller='settings', action='fork',
188 188 conditions=dict(function=check_repo))
189 189
190 190 return map
@@ -1,245 +1,245 b''
1 1 #!/usr/bin/env python
2 2 # encoding: utf-8
3 3 # repos controller for pylons
4 4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 5 #
6 6 # This program is free software; you can redistribute it and/or
7 7 # modify it under the terms of the GNU General Public License
8 8 # as published by the Free Software Foundation; version 2
9 9 # of the License or (at your opinion) any later version of the license.
10 10 #
11 11 # This program is distributed in the hope that it will be useful,
12 12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 14 # GNU General Public License for more details.
15 15 #
16 16 # You should have received a copy of the GNU General Public License
17 17 # along with this program; if not, write to the Free Software
18 18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 19 # MA 02110-1301, USA.
20 20 """
21 21 Created on April 7, 2010
22 22 admin controller for pylons
23 23 @author: marcink
24 24 """
25 25 from formencode import htmlfill
26 26 from operator import itemgetter
27 27 from paste.httpexceptions import HTTPInternalServerError
28 28 from pylons import request, response, session, tmpl_context as c, url
29 29 from pylons.controllers.util import abort, redirect
30 30 from pylons.i18n.translation import _
31 31 from rhodecode.lib import helpers as h
32 32 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator, \
33 33 HasPermissionAnyDecorator
34 34 from rhodecode.lib.base import BaseController, render
35 35 from rhodecode.lib.utils import invalidate_cache, action_logger
36 36 from rhodecode.model.db import User
37 37 from rhodecode.model.forms import RepoForm
38 38 from rhodecode.model.hg import HgModel
39 39 from rhodecode.model.repo import RepoModel
40 40 import formencode
41 41 import logging
42 42 import traceback
43 43
44 44 log = logging.getLogger(__name__)
45 45
46 46 class ReposController(BaseController):
47 47 """REST Controller styled on the Atom Publishing Protocol"""
48 48 # To properly map this controller, ensure your config/routing.py
49 49 # file has a resource setup:
50 50 # map.resource('repo', 'repos')
51
51
52 52 @LoginRequired()
53 53 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
54 54 def __before__(self):
55 55 c.admin_user = session.get('admin_user')
56 56 c.admin_username = session.get('admin_username')
57 57 super(ReposController, self).__before__()
58
59 @HasPermissionAllDecorator('hg.admin')
58
59 @HasPermissionAllDecorator('hg.admin')
60 60 def index(self, format='html'):
61 61 """GET /repos: All items in the collection"""
62 62 # url('repos')
63 63 cached_repo_list = HgModel().get_repos()
64 64 c.repos_list = sorted(cached_repo_list, key=itemgetter('name_sort'))
65 65 return render('admin/repos/repos.html')
66
66
67 67 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
68 68 def create(self):
69 69 """POST /repos: Create a new item"""
70 70 # url('repos')
71 71 repo_model = RepoModel()
72 72 _form = RepoForm()()
73 73 form_result = {}
74 74 try:
75 75 form_result = _form.to_python(dict(request.POST))
76 76 repo_model.create(form_result, c.rhodecode_user)
77 77 invalidate_cache('cached_repo_list')
78 78 h.flash(_('created repository %s') % form_result['repo_name'],
79 79 category='success')
80 80
81 81 if request.POST.get('user_created'):
82 82 action_logger(self.rhodecode_user, 'user_created_repo',
83 83 form_result['repo_name'], '', self.sa)
84 84 else:
85 85 action_logger(self.rhodecode_user, 'admin_created_repo',
86 form_result['repo_name'], '', self.sa)
87
86 form_result['repo_name'], '', self.sa)
87
88 88 except formencode.Invalid, errors:
89 89 c.new_repo = errors.value['repo_name']
90
90
91 91 if request.POST.get('user_created'):
92 92 r = render('admin/repos/repo_add_create_repository.html')
93 else:
93 else:
94 94 r = render('admin/repos/repo_add.html')
95
95
96 96 return htmlfill.render(
97 97 r,
98 98 defaults=errors.value,
99 99 errors=errors.error_dict or {},
100 100 prefix_error=False,
101 encoding="UTF-8")
101 encoding="UTF-8")
102 102
103 103 except Exception:
104 104 log.error(traceback.format_exc())
105 105 msg = _('error occured during creation of repository %s') \
106 106 % form_result.get('repo_name')
107 107 h.flash(msg, category='error')
108 108 if request.POST.get('user_created'):
109 return redirect(url('hg_home'))
109 return redirect(url('home'))
110 110 return redirect(url('repos'))
111
111
112 112 @HasPermissionAllDecorator('hg.admin')
113 113 def new(self, format='html'):
114 114 """GET /repos/new: Form to create a new item"""
115 115 new_repo = request.GET.get('repo', '')
116 116 c.new_repo = h.repo_name_slug(new_repo)
117 117
118 118 return render('admin/repos/repo_add.html')
119
119
120 120 @HasPermissionAllDecorator('hg.admin')
121 121 def update(self, repo_name):
122 122 """PUT /repos/repo_name: Update an existing item"""
123 123 # Forms posted to this method should contain a hidden field:
124 124 # <input type="hidden" name="_method" value="PUT" />
125 125 # Or using helpers:
126 126 # h.form(url('repo', repo_name=ID),
127 127 # method='put')
128 128 # url('repo', repo_name=ID)
129 129 repo_model = RepoModel()
130 130 changed_name = repo_name
131 131 _form = RepoForm(edit=True, old_data={'repo_name':repo_name})()
132
132
133 133 try:
134 134 form_result = _form.to_python(dict(request.POST))
135 135 repo_model.update(repo_name, form_result)
136 136 invalidate_cache('cached_repo_list')
137 137 h.flash(_('Repository %s updated succesfully' % repo_name),
138 138 category='success')
139 139 changed_name = form_result['repo_name']
140 140 except formencode.Invalid, errors:
141 141 c.repo_info = repo_model.get(repo_name)
142 142 c.users_array = repo_model.get_users_js()
143 143 errors.value.update({'user':c.repo_info.user.username})
144 144 return htmlfill.render(
145 145 render('admin/repos/repo_edit.html'),
146 146 defaults=errors.value,
147 147 errors=errors.error_dict or {},
148 148 prefix_error=False,
149 149 encoding="UTF-8")
150
150
151 151 except Exception:
152 152 log.error(traceback.format_exc())
153 153 h.flash(_('error occured during update of repository %s') \
154 154 % repo_name, category='error')
155
155
156 156 return redirect(url('edit_repo', repo_name=changed_name))
157
157
158 158 @HasPermissionAllDecorator('hg.admin')
159 159 def delete(self, repo_name):
160 160 """DELETE /repos/repo_name: Delete an existing item"""
161 161 # Forms posted to this method should contain a hidden field:
162 162 # <input type="hidden" name="_method" value="DELETE" />
163 163 # Or using helpers:
164 164 # h.form(url('repo', repo_name=ID),
165 165 # method='delete')
166 166 # url('repo', repo_name=ID)
167
167
168 168 repo_model = RepoModel()
169 169 repo = repo_model.get(repo_name)
170 170 if not repo:
171 h.flash(_('%s repository is not mapped to db perhaps'
171 h.flash(_('%s repository is not mapped to db perhaps'
172 172 ' it was moved or renamed from the filesystem'
173 173 ' please run the application again'
174 174 ' in order to rescan repositories') % repo_name,
175 175 category='error')
176
176
177 177 return redirect(url('repos'))
178 178 try:
179 179 action_logger(self.rhodecode_user, 'admin_deleted_repo',
180 180 repo_name, '', self.sa)
181 repo_model.delete(repo)
181 repo_model.delete(repo)
182 182 invalidate_cache('cached_repo_list')
183 183 h.flash(_('deleted repository %s') % repo_name, category='success')
184
184
185 185 except Exception, e:
186 186 log.error(traceback.format_exc())
187 187 h.flash(_('An error occured during deletion of %s') % repo_name,
188 188 category='error')
189
189
190 190 return redirect(url('repos'))
191
192 @HasPermissionAllDecorator('hg.admin')
191
192 @HasPermissionAllDecorator('hg.admin')
193 193 def delete_perm_user(self, repo_name):
194 194 """
195 195 DELETE an existing repository permission user
196 196 :param repo_name:
197 197 """
198
198
199 199 try:
200 200 repo_model = RepoModel()
201 repo_model.delete_perm_user(request.POST, repo_name)
201 repo_model.delete_perm_user(request.POST, repo_name)
202 202 except Exception, e:
203 203 h.flash(_('An error occured during deletion of repository user'),
204 204 category='error')
205 205 raise HTTPInternalServerError()
206
207 @HasPermissionAllDecorator('hg.admin')
206
207 @HasPermissionAllDecorator('hg.admin')
208 208 def show(self, repo_name, format='html'):
209 209 """GET /repos/repo_name: Show a specific item"""
210 210 # url('repo', repo_name=ID)
211
212 @HasPermissionAllDecorator('hg.admin')
211
212 @HasPermissionAllDecorator('hg.admin')
213 213 def edit(self, repo_name, format='html'):
214 214 """GET /repos/repo_name/edit: Form to edit an existing item"""
215 215 # url('edit_repo', repo_name=ID)
216 216 repo_model = RepoModel()
217 217 c.repo_info = repo = repo_model.get(repo_name)
218 218 if not repo:
219 h.flash(_('%s repository is not mapped to db perhaps'
219 h.flash(_('%s repository is not mapped to db perhaps'
220 220 ' it was created or renamed from the filesystem'
221 221 ' please run the application again'
222 222 ' in order to rescan repositories') % repo_name,
223 223 category='error')
224
225 return redirect(url('repos'))
224
225 return redirect(url('repos'))
226 226 defaults = c.repo_info.__dict__
227 227 if c.repo_info.user:
228 228 defaults.update({'user':c.repo_info.user.username})
229 229 else:
230 230 replacement_user = self.sa.query(User)\
231 231 .filter(User.admin == True).first().username
232 232 defaults.update({'user':replacement_user})
233
233
234 234 c.users_array = repo_model.get_users_js()
235
235
236 236 for p in c.repo_info.repo_to_perm:
237 defaults.update({'perm_%s' % p.user.username:
237 defaults.update({'perm_%s' % p.user.username:
238 238 p.permission.permission_name})
239
239
240 240 return htmlfill.render(
241 241 render('admin/repos/repo_edit.html'),
242 242 defaults=defaults,
243 243 encoding="UTF-8",
244 244 force_defaults=False
245 )
245 )
@@ -1,95 +1,97 b''
1 1 #!/usr/bin/env python
2 2 # encoding: utf-8
3 3 # changelog controller for pylons
4 4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 5 #
6 6 # This program is free software; you can redistribute it and/or
7 7 # modify it under the terms of the GNU General Public License
8 8 # as published by the Free Software Foundation; version 2
9 9 # of the License or (at your opinion) any later version of the license.
10 10 #
11 11 # This program is distributed in the hope that it will be useful,
12 12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 14 # GNU General Public License for more details.
15 15 #
16 16 # You should have received a copy of the GNU General Public License
17 17 # along with this program; if not, write to the Free Software
18 18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 19 # MA 02110-1301, USA.
20 20 """
21 21 Created on April 21, 2010
22 22 changelog controller for pylons
23 23 @author: marcink
24 24 """
25 25
26 26 try:
27 27 import json
28 28 except ImportError:
29 29 #python 2.5 compatibility
30 30 import simplejson as json
31 31 from mercurial.graphmod import colored, CHANGESET, revisions as graph_rev
32 32 from pylons import request, session, tmpl_context as c
33 33 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
34 34 from rhodecode.lib.base import BaseController, render
35 35 from rhodecode.model.hg import HgModel
36 36 from webhelpers.paginate import Page
37 37 import logging
38 38 log = logging.getLogger(__name__)
39 39
40 40 class ChangelogController(BaseController):
41
41
42 42 @LoginRequired()
43 43 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
44 'repository.admin')
44 'repository.admin')
45 45 def __before__(self):
46 46 super(ChangelogController, self).__before__()
47
47
48 48 def index(self):
49 49 limit = 100
50 50 default = 20
51 51 if request.params.get('size'):
52 52 try:
53 53 int_size = int(request.params.get('size'))
54 54 except ValueError:
55 55 int_size = default
56 int_size = int_size if int_size <= limit else limit
56 int_size = int_size if int_size <= limit else limit
57 57 c.size = int_size
58 58 session['changelog_size'] = c.size
59 59 session.save()
60 60 else:
61 61 c.size = int(session.get('changelog_size', default))
62 62
63 63 changesets = HgModel().get_repo(c.repo_name)
64
64
65 65 p = int(request.params.get('page', 1))
66 66 c.total_cs = len(changesets)
67 67 c.pagination = Page(changesets, page=p, item_count=c.total_cs,
68 68 items_per_page=c.size)
69
69
70 70 self._graph(changesets, c.size, p)
71
71
72 72 return render('changelog/changelog.html')
73 73
74 74
75 75 def _graph(self, repo, size, p):
76 76 revcount = size
77 77 if not repo.revisions:return json.dumps([]), 0
78
78
79 79 max_rev = repo.revisions[-1]
80
80 81 offset = 1 if p == 1 else ((p - 1) * revcount + 1)
82
81 83 rev_start = repo.revisions[(-1 * offset)]
82
84
83 85 revcount = min(max_rev, revcount)
84 86 rev_end = max(0, rev_start - revcount)
85 87 dag = graph_rev(repo.repo, rev_start, rev_end)
86
88
87 89 c.dag = tree = list(colored(dag))
88 90 data = []
89 91 for (id, type, ctx, vtx, edges) in tree:
90 92 if type != CHANGESET:
91 93 continue
92 94 data.append(('', vtx, edges))
93
94 c.jsdata = json.dumps(data)
95 95
96 c.jsdata = json.dumps(data)
97
@@ -1,181 +1,181 b''
1 1 #!/usr/bin/env python
2 2 # encoding: utf-8
3 3 # changeset controller for pylons
4 4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 5 # This program is free software; you can redistribute it and/or
6 6 # modify it under the terms of the GNU General Public License
7 7 # as published by the Free Software Foundation; version 2
8 8 # of the License or (at your opinion) any later version of the license.
9 9 #
10 10 # This program is distributed in the hope that it will be useful,
11 11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 13 # GNU General Public License for more details.
14 14 #
15 15 # You should have received a copy of the GNU General Public License
16 16 # along with this program; if not, write to the Free Software
17 17 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18 18 # MA 02110-1301, USA.
19 19 from rhodecode.lib.utils import EmptyChangeset
20 20 """
21 21 Created on April 25, 2010
22 22 changeset controller for pylons
23 23 @author: marcink
24 24 """
25 25 from pylons import tmpl_context as c, url, request, response
26 26 from pylons.i18n.translation import _
27 27 from pylons.controllers.util import redirect
28 28 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
29 29 from rhodecode.lib.base import BaseController, render
30 30 from rhodecode.model.hg import HgModel
31 31 from vcs.exceptions import RepositoryError, ChangesetError
32 32 from vcs.nodes import FileNode
33 33 from vcs.utils import diffs as differ
34 34 import logging
35 35 import traceback
36 36
37 37 log = logging.getLogger(__name__)
38 38
39 39 class ChangesetController(BaseController):
40
40
41 41 @LoginRequired()
42 42 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
43 'repository.admin')
43 'repository.admin')
44 44 def __before__(self):
45 45 super(ChangesetController, self).__before__()
46
46
47 47 def index(self, revision):
48 48 hg_model = HgModel()
49 49 cut_off_limit = 1024 * 250
50
50
51 51 def wrap_to_table(str):
52
52
53 53 return '''<table class="code-difftable">
54 54 <tr class="line">
55 55 <td class="lineno new"></td>
56 56 <td class="code"><pre>%s</pre></td>
57 57 </tr>
58 58 </table>''' % str
59
59
60 60 try:
61 61 c.changeset = hg_model.get_repo(c.repo_name).get_changeset(revision)
62 62 except RepositoryError:
63 63 log.error(traceback.format_exc())
64 return redirect(url('hg_home'))
64 return redirect(url('home'))
65 65 else:
66 66 try:
67 67 c.changeset_old = c.changeset.parents[0]
68 68 except IndexError:
69 69 c.changeset_old = None
70 70 c.changes = []
71
71
72 72 #===================================================================
73 73 # ADDED FILES
74 74 #===================================================================
75 75 c.sum_added = 0
76 76 for node in c.changeset.added:
77
77
78 78 filenode_old = FileNode(node.path, '', EmptyChangeset())
79 79 if filenode_old.is_binary or node.is_binary:
80 80 diff = wrap_to_table(_('binary file'))
81 81 else:
82 82 c.sum_added += node.size
83 83 if c.sum_added < cut_off_limit:
84 84 f_udiff = differ.get_udiff(filenode_old, node)
85 85 diff = differ.DiffProcessor(f_udiff).as_html()
86
86
87 87 else:
88 88 diff = wrap_to_table(_('Changeset is to big and was cut'
89 89 ' off, see raw changeset instead'))
90
90
91 91 cs1 = None
92 cs2 = node.last_changeset.short_id
92 cs2 = node.last_changeset.raw_id
93 93 c.changes.append(('added', node, diff, cs1, cs2))
94
94
95 95 #===================================================================
96 96 # CHANGED FILES
97 97 #===================================================================
98 c.sum_removed = 0
98 c.sum_removed = 0
99 99 for node in c.changeset.changed:
100 100 try:
101 101 filenode_old = c.changeset_old.get_node(node.path)
102 102 except ChangesetError:
103 103 filenode_old = FileNode(node.path, '', EmptyChangeset())
104
104
105 105 if filenode_old.is_binary or node.is_binary:
106 106 diff = wrap_to_table(_('binary file'))
107 107 else:
108
108
109 109 if c.sum_removed < cut_off_limit:
110 110 f_udiff = differ.get_udiff(filenode_old, node)
111 111 diff = differ.DiffProcessor(f_udiff).as_html()
112 112 if diff:
113 113 c.sum_removed += len(diff)
114 114 else:
115 115 diff = wrap_to_table(_('Changeset is to big and was cut'
116 116 ' off, see raw changeset instead'))
117
118
119 cs1 = filenode_old.last_changeset.short_id
120 cs2 = node.last_changeset.short_id
117
118
119 cs1 = filenode_old.last_changeset.raw_id
120 cs2 = node.last_changeset.raw_id
121 121 c.changes.append(('changed', node, diff, cs1, cs2))
122
122
123 123 #===================================================================
124 124 # REMOVED FILES
125 125 #===================================================================
126 126 for node in c.changeset.removed:
127 c.changes.append(('removed', node, None, None, None))
128
127 c.changes.append(('removed', node, None, None, None))
128
129 129 return render('changeset/changeset.html')
130 130
131 131 def raw_changeset(self, revision):
132
132
133 133 hg_model = HgModel()
134 134 method = request.GET.get('diff', 'show')
135 135 try:
136 136 c.changeset = hg_model.get_repo(c.repo_name).get_changeset(revision)
137 137 except RepositoryError:
138 138 log.error(traceback.format_exc())
139 return redirect(url('hg_home'))
139 return redirect(url('home'))
140 140 else:
141 141 try:
142 142 c.changeset_old = c.changeset.parents[0]
143 143 except IndexError:
144 144 c.changeset_old = None
145 145 c.changes = []
146
146
147 147 for node in c.changeset.added:
148 148 filenode_old = FileNode(node.path, '')
149 149 if filenode_old.is_binary or node.is_binary:
150 150 diff = _('binary file')
151 else:
151 else:
152 152 f_udiff = differ.get_udiff(filenode_old, node)
153 153 diff = differ.DiffProcessor(f_udiff).raw_diff()
154 154
155 155 cs1 = None
156 cs2 = node.last_changeset.short_id
156 cs2 = node.last_changeset.raw_id
157 157 c.changes.append(('added', node, diff, cs1, cs2))
158
158
159 159 for node in c.changeset.changed:
160 160 filenode_old = c.changeset_old.get_node(node.path)
161 161 if filenode_old.is_binary or node.is_binary:
162 162 diff = _('binary file')
163 else:
163 else:
164 164 f_udiff = differ.get_udiff(filenode_old, node)
165 165 diff = differ.DiffProcessor(f_udiff).raw_diff()
166 166
167 cs1 = filenode_old.last_changeset.short_id
168 cs2 = node.last_changeset.short_id
169 c.changes.append(('changed', node, diff, cs1, cs2))
170
167 cs1 = filenode_old.last_changeset.raw_id
168 cs2 = node.last_changeset.raw_id
169 c.changes.append(('changed', node, diff, cs1, cs2))
170
171 171 response.content_type = 'text/plain'
172 172 if method == 'download':
173 response.content_disposition = 'attachment; filename=%s.patch' % revision
173 response.content_disposition = 'attachment; filename=%s.patch' % revision
174 174 parent = True if len(c.changeset.parents) > 0 else False
175 175 c.parent_tmpl = 'Parent %s' % c.changeset.parents[0].raw_id if parent else ''
176
176
177 177 c.diffs = ''
178 178 for x in c.changes:
179 179 c.diffs += x[2]
180
180
181 181 return render('changeset/raw_changeset.html')
@@ -1,80 +1,80 b''
1 1 #!/usr/bin/env python
2 2 # encoding: utf-8
3 3 # feed controller for pylons
4 4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 5
6 6 # This program is free software; you can redistribute it and/or
7 7 # modify it under the terms of the GNU General Public License
8 8 # as published by the Free Software Foundation; version 2
9 9 # of the License or (at your opinion) any later version of the license.
10 10 #
11 11 # This program is distributed in the hope that it will be useful,
12 12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 14 # GNU General Public License for more details.
15 15 #
16 16 # You should have received a copy of the GNU General Public License
17 17 # along with this program; if not, write to the Free Software
18 18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 19 # MA 02110-1301, USA.
20 20 """
21 21 Created on April 23, 2010
22 22 feed controller for pylons
23 23 @author: marcink
24 24 """
25 25 from pylons import tmpl_context as c, url, response
26 26 from rhodecode.lib.base import BaseController, render
27 27 from rhodecode.model.hg import HgModel
28 28 from webhelpers.feedgenerator import Atom1Feed, Rss201rev2Feed
29 29 import logging
30 30 log = logging.getLogger(__name__)
31 31
32 32 class FeedController(BaseController):
33 33
34 34 #secure it or not ?
35 35 def __before__(self):
36 36 super(FeedController, self).__before__()
37 37 #common values for feeds
38 38 self.description = 'Changes on %s repository'
39 39 self.title = "%s feed"
40 40 self.language = 'en-us'
41 41 self.ttl = "5"
42 42 self.feed_nr = 10
43 43
44 44 def atom(self, repo_name):
45 45 """Produce an atom-1.0 feed via feedgenerator module"""
46 46 feed = Atom1Feed(title=self.title % repo_name,
47 47 link=url('summary_home', repo_name=repo_name, qualified=True),
48 48 description=self.description % repo_name,
49 49 language=self.language,
50 50 ttl=self.ttl)
51 51
52 52 changesets = HgModel().get_repo(repo_name)
53 53
54 54 for cs in changesets[:self.feed_nr]:
55 55 feed.add_item(title=cs.message,
56 56 link=url('changeset_home', repo_name=repo_name,
57 revision=cs.short_id, qualified=True),
57 revision=cs.raw_id, qualified=True),
58 58 description=str(cs.date))
59 59
60 60 response.content_type = feed.mime_type
61 61 return feed.writeString('utf-8')
62 62
63 63
64 64 def rss(self, repo_name):
65 65 """Produce an rss2 feed via feedgenerator module"""
66 66 feed = Rss201rev2Feed(title=self.title % repo_name,
67 67 link=url('summary_home', repo_name=repo_name, qualified=True),
68 68 description=self.description % repo_name,
69 69 language=self.language,
70 70 ttl=self.ttl)
71 71
72 72 changesets = HgModel().get_repo(repo_name)
73 73 for cs in changesets[:self.feed_nr]:
74 74 feed.add_item(title=cs.message,
75 75 link=url('changeset_home', repo_name=repo_name,
76 revision=cs.short_id, qualified=True),
76 revision=cs.raw_id, qualified=True),
77 77 description=str(cs.date))
78 78
79 79 response.content_type = feed.mime_type
80 80 return feed.writeString('utf-8')
@@ -1,215 +1,212 b''
1 1 #!/usr/bin/env python
2 2 # encoding: utf-8
3 3 # files controller for pylons
4 4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 5
6 6 # This program is free software; you can redistribute it and/or
7 7 # modify it under the terms of the GNU General Public License
8 8 # as published by the Free Software Foundation; version 2
9 9 # of the License or (at your opinion) any later version of the license.
10 10 #
11 11 # This program is distributed in the hope that it will be useful,
12 12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 14 # GNU General Public License for more details.
15 15 #
16 16 # You should have received a copy of the GNU General Public License
17 17 # along with this program; if not, write to the Free Software
18 18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 19 # MA 02110-1301, USA.
20 20 """
21 21 Created on April 21, 2010
22 22 files controller for pylons
23 23 @author: marcink
24 24 """
25 25 from mercurial import archival
26 26 from pylons import request, response, session, tmpl_context as c, url
27 27 from pylons.i18n.translation import _
28 28 from pylons.controllers.util import redirect
29 29 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
30 30 from rhodecode.lib.base import BaseController, render
31 31 from rhodecode.lib.utils import EmptyChangeset
32 32 from rhodecode.model.hg import HgModel
33 33 from vcs.exceptions import RepositoryError, ChangesetError
34 34 from vcs.nodes import FileNode
35 35 from vcs.utils import diffs as differ
36 36 import logging
37 37 import rhodecode.lib.helpers as h
38 38 import tempfile
39
39
40 40 log = logging.getLogger(__name__)
41 41
42 42 class FilesController(BaseController):
43
43
44 44 @LoginRequired()
45 45 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
46 'repository.admin')
46 'repository.admin')
47 47 def __before__(self):
48 48 super(FilesController, self).__before__()
49 49 c.file_size_limit = 250 * 1024 #limit of file size to display
50 50
51 51 def index(self, repo_name, revision, f_path):
52 52 hg_model = HgModel()
53 53 c.repo = repo = hg_model.get_repo(c.repo_name)
54 54 revision = request.POST.get('at_rev', None) or revision
55
55
56 56 def get_next_rev(cur):
57 57 max_rev = len(c.repo.revisions) - 1
58 58 r = cur + 1
59 59 if r > max_rev:
60 60 r = max_rev
61 61 return r
62
62
63 63 def get_prev_rev(cur):
64 64 r = cur - 1
65 65 return r
66 66
67 67 c.f_path = f_path
68
69
68
69
70 70 try:
71 71 cur_rev = repo.get_changeset(revision).revision
72 prev_rev = repo.get_changeset(get_prev_rev(cur_rev)).short_id
73 next_rev = repo.get_changeset(get_next_rev(cur_rev)).short_id
74
72 prev_rev = repo.get_changeset(get_prev_rev(cur_rev)).raw_id
73 next_rev = repo.get_changeset(get_next_rev(cur_rev)).raw_id
74
75 75 c.url_prev = url('files_home', repo_name=c.repo_name,
76 revision=prev_rev, f_path=f_path)
76 revision=prev_rev, f_path=f_path)
77 77 c.url_next = url('files_home', repo_name=c.repo_name,
78 revision=next_rev, f_path=f_path)
79
78 revision=next_rev, f_path=f_path)
79
80 80 c.changeset = repo.get_changeset(revision)
81
82 c.cur_rev = c.changeset.short_id
81
82 c.cur_rev = c.changeset.raw_id
83 83 c.rev_nr = c.changeset.revision
84 84 c.files_list = c.changeset.get_node(f_path)
85 85 c.file_history = self._get_history(repo, c.files_list, f_path)
86
86
87 87 except (RepositoryError, ChangesetError):
88 88 c.files_list = None
89
89
90 90 return render('files/files.html')
91 91
92 92 def rawfile(self, repo_name, revision, f_path):
93 93 hg_model = HgModel()
94 94 c.repo = hg_model.get_repo(c.repo_name)
95 95 file_node = c.repo.get_changeset(revision).get_node(f_path)
96 96 response.content_type = file_node.mimetype
97 97 response.content_disposition = 'attachment; filename=%s' \
98 % f_path.split('/')[-1]
98 % f_path.split('/')[-1]
99 99 return file_node.content
100 100
101 101 def raw(self, repo_name, revision, f_path):
102 102 hg_model = HgModel()
103 103 c.repo = hg_model.get_repo(c.repo_name)
104 104 file_node = c.repo.get_changeset(revision).get_node(f_path)
105 105 response.content_type = 'text/plain'
106
106
107 107 return file_node.content
108
108
109 109 def annotate(self, repo_name, revision, f_path):
110 110 hg_model = HgModel()
111 111 c.repo = hg_model.get_repo(c.repo_name)
112 112 cs = c.repo.get_changeset(revision)
113 113 c.file = cs.get_node(f_path)
114 114 c.file_msg = cs.get_file_message(f_path)
115 c.cur_rev = cs.short_id
116 c.rev_nr = cs.revision
115 c.cur_rev = cs.raw_id
116 c.rev_nr = cs.revision
117 117 c.f_path = f_path
118 118
119 119 return render('files/files_annotate.html')
120
120
121 121 def archivefile(self, repo_name, revision, fileformat):
122 122 archive_specs = {
123 123 '.tar.bz2': ('application/x-tar', 'tbz2'),
124 124 '.tar.gz': ('application/x-tar', 'tgz'),
125 125 '.zip': ('application/zip', 'zip'),
126 126 }
127 127 if not archive_specs.has_key(fileformat):
128 128 return 'Unknown archive type %s' % fileformat
129
129
130 130 def read_in_chunks(file_object, chunk_size=1024 * 40):
131 131 """Lazy function (generator) to read a file piece by piece.
132 132 Default chunk size: 40k."""
133 133 while True:
134 134 data = file_object.read(chunk_size)
135 135 if not data:
136 136 break
137 yield data
138
137 yield data
138
139 139 archive = tempfile.TemporaryFile()
140 140 repo = HgModel().get_repo(repo_name).repo
141 141 fname = '%s-%s%s' % (repo_name, revision, fileformat)
142 142 archival.archive(repo, archive, revision, archive_specs[fileformat][1],
143 143 prefix='%s-%s' % (repo_name, revision))
144 144 response.content_type = archive_specs[fileformat][0]
145 145 response.content_disposition = 'attachment; filename=%s' % fname
146 146 archive.seek(0)
147 147 return read_in_chunks(archive)
148
148
149 149 def diff(self, repo_name, f_path):
150 150 hg_model = HgModel()
151 151 diff1 = request.GET.get('diff1')
152 152 diff2 = request.GET.get('diff2')
153 153 c.action = request.GET.get('diff')
154 154 c.no_changes = diff1 == diff2
155 155 c.f_path = f_path
156 156 c.repo = hg_model.get_repo(c.repo_name)
157 157
158 158 try:
159 159 if diff1 not in ['', None, 'None', '0' * 12, '0' * 40]:
160 160 c.changeset_1 = c.repo.get_changeset(diff1)
161 161 node1 = c.changeset_1.get_node(f_path)
162 162 else:
163 163 c.changeset_1 = EmptyChangeset()
164 164 node1 = FileNode('.', '', changeset=c.changeset_1)
165
165
166 166 if diff2 not in ['', None, 'None', '0' * 12, '0' * 40]:
167 167 c.changeset_2 = c.repo.get_changeset(diff2)
168 168 node2 = c.changeset_2.get_node(f_path)
169 169 else:
170 170 c.changeset_2 = EmptyChangeset()
171 171 node2 = FileNode('.', '', changeset=c.changeset_2)
172 172 except RepositoryError:
173 173 return redirect(url('files_home',
174 174 repo_name=c.repo_name, f_path=f_path))
175 175
176 c.diff1 = 'r%s:%s' % (c.changeset_1.revision, c.changeset_1.short_id)
177 c.diff2 = 'r%s:%s' % (c.changeset_2.revision, c.changeset_2.short_id)
178
179 176 f_udiff = differ.get_udiff(node1, node2)
180 177 diff = differ.DiffProcessor(f_udiff)
181
178
182 179 if c.action == 'download':
183 180 diff_name = '%s_vs_%s.diff' % (diff1, diff2)
184 181 response.content_type = 'text/plain'
185 182 response.content_disposition = 'attachment; filename=%s' \
186 % diff_name
183 % diff_name
187 184 return diff.raw_diff()
188
185
189 186 elif c.action == 'raw':
190 187 c.cur_diff = '<pre class="raw">%s</pre>' % h.escape(diff.raw_diff())
191 188 elif c.action == 'diff':
192 189 if node1.size > c.file_size_limit or node2.size > c.file_size_limit:
193 190 c.cur_diff = _('Diff is to big to display')
194 191 else:
195 192 c.cur_diff = diff.as_html()
196 193 else:
197 194 #default option
198 195 if node1.size > c.file_size_limit or node2.size > c.file_size_limit:
199 196 c.cur_diff = _('Diff is to big to display')
200 197 else:
201 198 c.cur_diff = diff.as_html()
202
203 if not c.cur_diff: c.no_changes = True
199
200 if not c.cur_diff: c.no_changes = True
204 201 return render('files/file_diff.html')
205
202
206 203 def _get_history(self, repo, node, f_path):
207 204 from vcs.nodes import NodeKind
208 205 if not node.kind is NodeKind.FILE:
209 206 return []
210 207 changesets = node.history
211 208 hist_l = []
212 209 for chs in changesets:
213 210 n_desc = 'r%s:%s' % (chs.revision, chs.short_id)
214 hist_l.append((chs.short_id, n_desc,))
211 hist_l.append((chs.raw_id, n_desc,))
215 212 return hist_l
@@ -1,58 +1,58 b''
1 1 #!/usr/bin/env python
2 2 # encoding: utf-8
3 3 # hg controller for pylons
4 4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 5 #
6 6 # This program is free software; you can redistribute it and/or
7 7 # modify it under the terms of the GNU General Public License
8 8 # as published by the Free Software Foundation; version 2
9 9 # of the License or (at your opinion) any later version of the license.
10 10 #
11 11 # This program is distributed in the hope that it will be useful,
12 12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 14 # GNU General Public License for more details.
15 15 #
16 16 # You should have received a copy of the GNU General Public License
17 17 # along with this program; if not, write to the Free Software
18 18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 19 # MA 02110-1301, USA.
20 20 """
21 21 Created on February 18, 2010
22 22 hg controller for pylons
23 23 @author: marcink
24 24 """
25 25 from operator import itemgetter
26 26 from pylons import tmpl_context as c, request
27 27 from rhodecode.lib.auth import LoginRequired
28 28 from rhodecode.lib.base import BaseController, render
29 29 from rhodecode.model.hg import HgModel
30 30 import logging
31 31 log = logging.getLogger(__name__)
32 32
33 class HgController(BaseController):
33 class HomeController(BaseController):
34 34
35 35 @LoginRequired()
36 36 def __before__(self):
37 super(HgController, self).__before__()
38
37 super(HomeController, self).__before__()
38
39 39 def index(self):
40 40 sortables = ['name', 'description', 'last_change', 'tip', 'contact']
41 41 current_sort = request.GET.get('sort', 'name')
42 42 current_sort_slug = current_sort.replace('-', '')
43
43
44 44 if current_sort_slug not in sortables:
45 45 c.sort_by = 'name'
46 46 current_sort_slug = c.sort_by
47 47 else:
48 48 c.sort_by = current_sort
49 49 c.sort_slug = current_sort_slug
50 50 cached_repo_list = HgModel().get_repos()
51
51
52 52 sort_key = current_sort_slug + '_sort'
53 53 if c.sort_by.startswith('-'):
54 54 c.repos_list = sorted(cached_repo_list, key=itemgetter(sort_key), reverse=True)
55 55 else:
56 56 c.repos_list = sorted(cached_repo_list, key=itemgetter(sort_key), reverse=False)
57
57
58 58 return render('/index.html')
@@ -1,144 +1,144 b''
1 1 #!/usr/bin/env python
2 2 # encoding: utf-8
3 3 # login controller for pylons
4 4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 5 #
6 6 # This program is free software; you can redistribute it and/or
7 7 # modify it under the terms of the GNU General Public License
8 8 # as published by the Free Software Foundation; version 2
9 9 # of the License or (at your opinion) any later version of the license.
10 10 #
11 11 # This program is distributed in the hope that it will be useful,
12 12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 14 # GNU General Public License for more details.
15 15 #
16 16 # You should have received a copy of the GNU General Public License
17 17 # along with this program; if not, write to the Free Software
18 18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 19 # MA 02110-1301, USA.
20 20
21 21 """
22 22 Created on April 22, 2010
23 23 login controller for pylons
24 24 @author: marcink
25 25 """
26 26 from formencode import htmlfill
27 27 from pylons import request, response, session, tmpl_context as c, url
28 28 from pylons.controllers.util import abort, redirect
29 29 from rhodecode.lib.auth import AuthUser, HasPermissionAnyDecorator
30 30 from rhodecode.lib.base import BaseController, render
31 import rhodecode.lib.helpers as h
31 import rhodecode.lib.helpers as h
32 32 from pylons.i18n.translation import _
33 33 from rhodecode.model.forms import LoginForm, RegisterForm, PasswordResetForm
34 34 from rhodecode.model.user import UserModel
35 35 import formencode
36 36 import logging
37 37
38 38 log = logging.getLogger(__name__)
39 39
40 40 class LoginController(BaseController):
41 41
42 42 def __before__(self):
43 43 super(LoginController, self).__before__()
44 44
45 45 def index(self):
46 46 #redirect if already logged in
47 47 c.came_from = request.GET.get('came_from', None)
48
48
49 49 if c.rhodecode_user.is_authenticated:
50 return redirect(url('hg_home'))
51
50 return redirect(url('home'))
51
52 52 if request.POST:
53 53 #import Login Form validator class
54 54 login_form = LoginForm()
55 55 try:
56 56 c.form_result = login_form.to_python(dict(request.POST))
57 57 username = c.form_result['username']
58 58 user = UserModel().get_by_username(username)
59 59 auth_user = AuthUser()
60 60 auth_user.username = user.username
61 61 auth_user.is_authenticated = True
62 62 auth_user.is_admin = user.admin
63 63 auth_user.user_id = user.user_id
64 64 auth_user.name = user.name
65 65 auth_user.lastname = user.lastname
66 66 session['rhodecode_user'] = auth_user
67 67 session.save()
68 68 log.info('user %s is now authenticated', username)
69
69
70 70 user.update_lastlogin()
71
71
72 72 if c.came_from:
73 73 return redirect(c.came_from)
74 74 else:
75 return redirect(url('hg_home'))
76
75 return redirect(url('home'))
76
77 77 except formencode.Invalid, errors:
78 78 return htmlfill.render(
79 79 render('/login.html'),
80 80 defaults=errors.value,
81 81 errors=errors.error_dict or {},
82 82 prefix_error=False,
83 83 encoding="UTF-8")
84
84
85 85 return render('/login.html')
86
86
87 87 @HasPermissionAnyDecorator('hg.admin', 'hg.register.auto_activate',
88 88 'hg.register.manual_activate')
89 89 def register(self):
90 90 user_model = UserModel()
91 91 c.auto_active = False
92 92 for perm in user_model.get_by_username('default', cache=False).user_perms:
93 93 if perm.permission.permission_name == 'hg.register.auto_activate':
94 94 c.auto_active = True
95 95 break
96
96
97 97 if request.POST:
98
98
99 99 register_form = RegisterForm()()
100 100 try:
101 101 form_result = register_form.to_python(dict(request.POST))
102 102 form_result['active'] = c.auto_active
103 103 user_model.create_registration(form_result)
104 104 h.flash(_('You have successfully registered into rhodecode'),
105 category='success')
105 category='success')
106 106 return redirect(url('login_home'))
107
107
108 108 except formencode.Invalid, errors:
109 109 return htmlfill.render(
110 110 render('/register.html'),
111 111 defaults=errors.value,
112 112 errors=errors.error_dict or {},
113 113 prefix_error=False,
114 114 encoding="UTF-8")
115
115
116 116 return render('/register.html')
117 117
118 118 def password_reset(self):
119 119 user_model = UserModel()
120 120 if request.POST:
121
121
122 122 password_reset_form = PasswordResetForm()()
123 123 try:
124 124 form_result = password_reset_form.to_python(dict(request.POST))
125 125 user_model.reset_password(form_result)
126 126 h.flash(_('Your new password was sent'),
127 category='success')
127 category='success')
128 128 return redirect(url('login_home'))
129
129
130 130 except formencode.Invalid, errors:
131 131 return htmlfill.render(
132 132 render('/password_reset.html'),
133 133 defaults=errors.value,
134 134 errors=errors.error_dict or {},
135 135 prefix_error=False,
136 136 encoding="UTF-8")
137
137
138 138 return render('/password_reset.html')
139
139
140 140 def logout(self):
141 141 session['rhodecode_user'] = AuthUser()
142 142 session.save()
143 143 log.info('Logging out and setting user as Empty')
144 redirect(url('hg_home'))
144 redirect(url('home'))
@@ -1,175 +1,175 b''
1 1 #!/usr/bin/env python
2 2 # encoding: utf-8
3 3 # settings controller for pylons
4 4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 5 #
6 6 # This program is free software; you can redistribute it and/or
7 7 # modify it under the terms of the GNU General Public License
8 8 # as published by the Free Software Foundation; version 2
9 9 # of the License or (at your opinion) any later version of the license.
10 10 #
11 11 # This program is distributed in the hope that it will be useful,
12 12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 14 # GNU General Public License for more details.
15 15 #
16 16 # You should have received a copy of the GNU General Public License
17 17 # along with this program; if not, write to the Free Software
18 18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 19 # MA 02110-1301, USA.
20 20 """
21 21 Created on June 30, 2010
22 22 settings controller for pylons
23 23 @author: marcink
24 24 """
25 25 from formencode import htmlfill
26 26 from pylons import tmpl_context as c, request, url
27 27 from pylons.controllers.util import redirect
28 28 from pylons.i18n.translation import _
29 29 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAllDecorator
30 30 from rhodecode.lib.base import BaseController, render
31 31 from rhodecode.lib.utils import invalidate_cache, action_logger
32 32 from rhodecode.model.forms import RepoSettingsForm, RepoForkForm
33 33 from rhodecode.model.repo import RepoModel
34 34 import formencode
35 35 import logging
36 36 import rhodecode.lib.helpers as h
37 37 import traceback
38 38
39 39 log = logging.getLogger(__name__)
40 40
41 41 class SettingsController(BaseController):
42 42
43 43 @LoginRequired()
44 @HasRepoPermissionAllDecorator('repository.admin')
44 @HasRepoPermissionAllDecorator('repository.admin')
45 45 def __before__(self):
46 46 super(SettingsController, self).__before__()
47
47
48 48 def index(self, repo_name):
49 49 repo_model = RepoModel()
50 50 c.repo_info = repo = repo_model.get(repo_name)
51 51 if not repo:
52 h.flash(_('%s repository is not mapped to db perhaps'
52 h.flash(_('%s repository is not mapped to db perhaps'
53 53 ' it was created or renamed from the filesystem'
54 54 ' please run the application again'
55 55 ' in order to rescan repositories') % repo_name,
56 56 category='error')
57
58 return redirect(url('hg_home'))
57
58 return redirect(url('home'))
59 59 defaults = c.repo_info.__dict__
60 60 defaults.update({'user':c.repo_info.user.username})
61 61 c.users_array = repo_model.get_users_js()
62
62
63 63 for p in c.repo_info.repo_to_perm:
64 defaults.update({'perm_%s' % p.user.username:
64 defaults.update({'perm_%s' % p.user.username:
65 65 p.permission.permission_name})
66
66
67 67 return htmlfill.render(
68 68 render('settings/repo_settings.html'),
69 69 defaults=defaults,
70 70 encoding="UTF-8",
71 71 force_defaults=False
72 )
72 )
73 73
74 74 def update(self, repo_name):
75 75 repo_model = RepoModel()
76 76 changed_name = repo_name
77 77 _form = RepoSettingsForm(edit=True, old_data={'repo_name':repo_name})()
78 78 try:
79 79 form_result = _form.to_python(dict(request.POST))
80 80 repo_model.update(repo_name, form_result)
81 81 invalidate_cache('cached_repo_list')
82 82 h.flash(_('Repository %s updated successfully' % repo_name),
83 83 category='success')
84 changed_name = form_result['repo_name']
84 changed_name = form_result['repo_name']
85 85 except formencode.Invalid, errors:
86 86 c.repo_info = repo_model.get(repo_name)
87 87 c.users_array = repo_model.get_users_js()
88 88 errors.value.update({'user':c.repo_info.user.username})
89 89 return htmlfill.render(
90 90 render('settings/repo_settings.html'),
91 91 defaults=errors.value,
92 92 errors=errors.error_dict or {},
93 93 prefix_error=False,
94 encoding="UTF-8")
94 encoding="UTF-8")
95 95 except Exception:
96 96 log.error(traceback.format_exc())
97 97 h.flash(_('error occured during update of repository %s') \
98 98 % repo_name, category='error')
99
99
100 100 return redirect(url('repo_settings_home', repo_name=changed_name))
101 101
102 102
103 103
104 def delete(self, repo_name):
104 def delete(self, repo_name):
105 105 """DELETE /repos/repo_name: Delete an existing item"""
106 106 # Forms posted to this method should contain a hidden field:
107 107 # <input type="hidden" name="_method" value="DELETE" />
108 108 # Or using helpers:
109 109 # h.form(url('repo_settings_delete', repo_name=ID),
110 110 # method='delete')
111 111 # url('repo_settings_delete', repo_name=ID)
112
112
113 113 repo_model = RepoModel()
114 114 repo = repo_model.get(repo_name)
115 115 if not repo:
116 h.flash(_('%s repository is not mapped to db perhaps'
116 h.flash(_('%s repository is not mapped to db perhaps'
117 117 ' it was moved or renamed from the filesystem'
118 118 ' please run the application again'
119 119 ' in order to rescan repositories') % repo_name,
120 120 category='error')
121
122 return redirect(url('hg_home'))
121
122 return redirect(url('home'))
123 123 try:
124 124 action_logger(self.rhodecode_user, 'user_deleted_repo',
125 repo_name, '', self.sa)
126 repo_model.delete(repo)
125 repo_name, '', self.sa)
126 repo_model.delete(repo)
127 127 invalidate_cache('cached_repo_list')
128 128 h.flash(_('deleted repository %s') % repo_name, category='success')
129 129 except Exception:
130 130 h.flash(_('An error occurred during deletion of %s') % repo_name,
131 131 category='error')
132
133 return redirect(url('hg_home'))
134
132
133 return redirect(url('home'))
134
135 135 def fork(self, repo_name):
136 136 repo_model = RepoModel()
137 137 c.repo_info = repo = repo_model.get(repo_name)
138 138 if not repo:
139 h.flash(_('%s repository is not mapped to db perhaps'
139 h.flash(_('%s repository is not mapped to db perhaps'
140 140 ' it was created or renamed from the filesystem'
141 141 ' please run the application again'
142 142 ' in order to rescan repositories') % repo_name,
143 143 category='error')
144
145 return redirect(url('hg_home'))
146
144
145 return redirect(url('home'))
146
147 147 return render('settings/repo_fork.html')
148
149
150
148
149
150
151 151 def fork_create(self, repo_name):
152 152 repo_model = RepoModel()
153 153 c.repo_info = repo_model.get(repo_name)
154 154 _form = RepoForkForm()()
155 155 form_result = {}
156 156 try:
157 157 form_result = _form.to_python(dict(request.POST))
158 158 form_result.update({'repo_name':repo_name})
159 159 repo_model.create_fork(form_result, c.rhodecode_user)
160 160 h.flash(_('fork %s repository as %s task added') \
161 161 % (repo_name, form_result['fork_name']),
162 162 category='success')
163 163 action_logger(self.rhodecode_user, 'user_forked_repo',
164 repo_name, '', self.sa)
164 repo_name, '', self.sa)
165 165 except formencode.Invalid, errors:
166 166 c.new_repo = errors.value['fork_name']
167 167 r = render('settings/repo_fork.html')
168
168
169 169 return htmlfill.render(
170 170 r,
171 171 defaults=errors.value,
172 172 errors=errors.error_dict or {},
173 173 prefix_error=False,
174 encoding="UTF-8")
175 return redirect(url('hg_home'))
174 encoding="UTF-8")
175 return redirect(url('home'))
@@ -1,397 +1,398 b''
1 1 """Helper functions
2 2
3 3 Consists of functions to typically be used within templates, but also
4 4 available to Controllers. This module is available to both as 'h'.
5 5 """
6 6 from pygments.formatters import HtmlFormatter
7 7 from pygments import highlight as code_highlight
8 8 from pylons import url, app_globals as g
9 9 from pylons.i18n.translation import _, ungettext
10 10 from vcs.utils.annotate import annotate_highlight
11 11 from webhelpers.html import literal, HTML, escape
12 12 from webhelpers.html.tools import *
13 13 from webhelpers.html.builder import make_tag
14 14 from webhelpers.html.tags import auto_discovery_link, checkbox, css_classes, \
15 15 end_form, file, form, hidden, image, javascript_link, link_to, link_to_if, \
16 16 link_to_unless, ol, required_legend, select, stylesheet_link, submit, text, \
17 17 password, textarea, title, ul, xml_declaration, radio
18 18 from webhelpers.html.tools import auto_link, button_to, highlight, js_obfuscate, \
19 19 mail_to, strip_links, strip_tags, tag_re
20 20 from webhelpers.number import format_byte_size, format_bit_size
21 21 from webhelpers.pylonslib import Flash as _Flash
22 22 from webhelpers.pylonslib.secure_form import secure_form
23 23 from webhelpers.text import chop_at, collapse, convert_accented_entities, \
24 24 convert_misc_entities, lchop, plural, rchop, remove_formatting, \
25 25 replace_whitespace, urlify, truncate, wrap_paragraphs
26 26 from webhelpers.date import time_ago_in_words
27 27
28 28 #Custom helpers here :)
29 29 class _Link(object):
30 30 '''
31 31 Make a url based on label and url with help of url_for
32 32 :param label:name of link if not defined url is used
33 33 :param url: the url for link
34 34 '''
35 35
36 36 def __call__(self, label='', *url_, **urlargs):
37 37 if label is None or '':
38 38 label = url
39 39 link_fn = link_to(label, url(*url_, **urlargs))
40 40 return link_fn
41 41
42 42 link = _Link()
43 43
44 44 class _GetError(object):
45 45
46 46 def __call__(self, field_name, form_errors):
47 47 tmpl = """<span class="error_msg">%s</span>"""
48 48 if form_errors and form_errors.has_key(field_name):
49 49 return literal(tmpl % form_errors.get(field_name))
50 50
51 51 get_error = _GetError()
52 52
53 53 def recursive_replace(str, replace=' '):
54 54 """
55 55 Recursive replace of given sign to just one instance
56 56 :param str: given string
57 57 :param replace:char to find and replace multiple instances
58 58
59 59 Examples::
60 60 >>> recursive_replace("Mighty---Mighty-Bo--sstones",'-')
61 61 'Mighty-Mighty-Bo-sstones'
62 62 """
63 63
64 64 if str.find(replace * 2) == -1:
65 65 return str
66 66 else:
67 67 str = str.replace(replace * 2, replace)
68 68 return recursive_replace(str, replace)
69 69
70 70 class _ToolTip(object):
71 71
72 72 def __call__(self, tooltip_title, trim_at=50):
73 73 """
74 74 Special function just to wrap our text into nice formatted autowrapped
75 75 text
76 76 :param tooltip_title:
77 77 """
78 78
79 79 return wrap_paragraphs(escape(tooltip_title), trim_at)\
80 80 .replace('\n', '<br/>')
81 81
82 82 def activate(self):
83 83 """
84 84 Adds tooltip mechanism to the given Html all tooltips have to have
85 85 set class tooltip and set attribute tooltip_title.
86 86 Then a tooltip will be generated based on that
87 87 All with yui js tooltip
88 88 """
89 89
90 90 js = '''
91 91 YAHOO.util.Event.onDOMReady(function(){
92 92 function toolTipsId(){
93 93 var ids = [];
94 94 var tts = YAHOO.util.Dom.getElementsByClassName('tooltip');
95 95
96 96 for (var i = 0; i < tts.length; i++) {
97 97 //if element doesn not have and id autgenerate one for tooltip
98 98
99 99 if (!tts[i].id){
100 100 tts[i].id='tt'+i*100;
101 101 }
102 102 ids.push(tts[i].id);
103 103 }
104 104 return ids
105 105 };
106 106 var myToolTips = new YAHOO.widget.Tooltip("tooltip", {
107 107 context: toolTipsId(),
108 108 monitorresize:false,
109 109 xyoffset :[0,0],
110 110 autodismissdelay:300000,
111 111 hidedelay:5,
112 112 showdelay:20,
113 113 });
114 114
115 115 //Mouse Over event disabled for new repositories since they dont
116 116 //have last commit message
117 117 myToolTips.contextMouseOverEvent.subscribe(
118 118 function(type, args) {
119 119 var context = args[0];
120 120 var txt = context.getAttribute('tooltip_title');
121 121 if(txt){
122 122 return true;
123 123 }
124 124 else{
125 125 return false;
126 126 }
127 127 });
128 128
129 129
130 130 // Set the text for the tooltip just before we display it. Lazy method
131 131 myToolTips.contextTriggerEvent.subscribe(
132 132 function(type, args) {
133 133
134 134
135 135 var context = args[0];
136 136
137 137 var txt = context.getAttribute('tooltip_title');
138 138 this.cfg.setProperty("text", txt);
139 139
140 140
141 141 // positioning of tooltip
142 142 var tt_w = this.element.clientWidth;
143 143 var tt_h = this.element.clientHeight;
144 144
145 145 var context_w = context.offsetWidth;
146 146 var context_h = context.offsetHeight;
147 147
148 148 var pos_x = YAHOO.util.Dom.getX(context);
149 149 var pos_y = YAHOO.util.Dom.getY(context);
150 150
151 151 var display_strategy = 'top';
152 152 var xy_pos = [0,0];
153 153 switch (display_strategy){
154 154
155 155 case 'top':
156 156 var cur_x = (pos_x+context_w/2)-(tt_w/2);
157 157 var cur_y = pos_y-tt_h-4;
158 158 xy_pos = [cur_x,cur_y];
159 159 break;
160 160 case 'bottom':
161 161 var cur_x = (pos_x+context_w/2)-(tt_w/2);
162 162 var cur_y = pos_y+context_h+4;
163 163 xy_pos = [cur_x,cur_y];
164 164 break;
165 165 case 'left':
166 166 var cur_x = (pos_x-tt_w-4);
167 167 var cur_y = pos_y-((tt_h/2)-context_h/2);
168 168 xy_pos = [cur_x,cur_y];
169 169 break;
170 170 case 'right':
171 171 var cur_x = (pos_x+context_w+4);
172 172 var cur_y = pos_y-((tt_h/2)-context_h/2);
173 173 xy_pos = [cur_x,cur_y];
174 174 break;
175 175 default:
176 176 var cur_x = (pos_x+context_w/2)-(tt_w/2);
177 177 var cur_y = pos_y-tt_h-4;
178 178 xy_pos = [cur_x,cur_y];
179 179 break;
180 180
181 181 }
182 182
183 183 this.cfg.setProperty("xy",xy_pos);
184 184
185 185 });
186 186
187 187 //Mouse out
188 188 myToolTips.contextMouseOutEvent.subscribe(
189 189 function(type, args) {
190 190 var context = args[0];
191 191
192 192 });
193 193 });
194 194 '''
195 195 return literal(js)
196 196
197 197 tooltip = _ToolTip()
198 198
199 199 class _FilesBreadCrumbs(object):
200 200
201 201 def __call__(self, repo_name, rev, paths):
202 202 url_l = [link_to(repo_name, url('files_home',
203 203 repo_name=repo_name,
204 204 revision=rev, f_path=''))]
205 205 paths_l = paths.split('/')
206 206
207 207 for cnt, p in enumerate(paths_l, 1):
208 208 if p != '':
209 209 url_l.append(link_to(p, url('files_home',
210 210 repo_name=repo_name,
211 211 revision=rev,
212 212 f_path='/'.join(paths_l[:cnt]))))
213 213
214 214 return literal('/'.join(url_l))
215 215
216 216 files_breadcrumbs = _FilesBreadCrumbs()
217 217 class CodeHtmlFormatter(HtmlFormatter):
218 218
219 219 def wrap(self, source, outfile):
220 220 return self._wrap_div(self._wrap_pre(self._wrap_code(source)))
221 221
222 222 def _wrap_code(self, source):
223 223 for cnt, it in enumerate(source, 1):
224 224 i, t = it
225 225 t = '<div id="#S-%s">%s</div>' % (cnt, t)
226 226 yield i, t
227 227 def pygmentize(filenode, **kwargs):
228 228 """
229 229 pygmentize function using pygments
230 230 :param filenode:
231 231 """
232 232 return literal(code_highlight(filenode.content,
233 233 filenode.lexer, CodeHtmlFormatter(**kwargs)))
234 234
235 235 def pygmentize_annotation(filenode, **kwargs):
236 236 """
237 237 pygmentize function for annotation
238 238 :param filenode:
239 239 """
240 240
241 241 color_dict = {}
242 242 def gen_color():
243 243 """generator for getting 10k of evenly distibuted colors using hsv color
244 244 and golden ratio.
245 245 """
246 246 import colorsys
247 247 n = 10000
248 248 golden_ratio = 0.618033988749895
249 249 h = 0.22717784590367374
250 250 #generate 10k nice web friendly colors in the same order
251 251 for c in xrange(n):
252 252 h += golden_ratio
253 253 h %= 1
254 254 HSV_tuple = [h, 0.95, 0.95]
255 255 RGB_tuple = colorsys.hsv_to_rgb(*HSV_tuple)
256 256 yield map(lambda x:str(int(x * 256)), RGB_tuple)
257 257
258 258 cgenerator = gen_color()
259 259
260 260 def get_color_string(cs):
261 261 if color_dict.has_key(cs):
262 262 col = color_dict[cs]
263 263 else:
264 264 col = color_dict[cs] = cgenerator.next()
265 265 return "color: rgb(%s)! important;" % (', '.join(col))
266 266
267 267 def url_func(changeset):
268 268 tooltip_html = "<div style='font-size:0.8em'><b>Author:</b>" + \
269 269 " %s<br/><b>Date:</b> %s</b><br/><b>Message:</b> %s<br/></div>"
270 270
271 271 tooltip_html = tooltip_html % (changeset.author,
272 272 changeset.date,
273 273 tooltip(changeset.message))
274 274 lnk_format = 'r%-5s:%s' % (changeset.revision,
275 changeset.short_id)
275 changeset.raw_id)
276 276 uri = link_to(
277 277 lnk_format,
278 278 url('changeset_home', repo_name=changeset.repository.name,
279 revision=changeset.short_id),
280 style=get_color_string(changeset.short_id),
279 revision=changeset.raw_id),
280 style=get_color_string(changeset.raw_id),
281 281 class_='tooltip',
282 282 tooltip_title=tooltip_html
283 283 )
284 284
285 285 uri += '\n'
286 286 return uri
287 287 return literal(annotate_highlight(filenode, url_func, **kwargs))
288 288
289 289 def repo_name_slug(value):
290 290 """Return slug of name of repository
291 291 This function is called on each creation/modification
292 292 of repository to prevent bad names in repo
293 293 """
294 294 slug = remove_formatting(value)
295 295 slug = strip_tags(slug)
296 296
297 297 for c in """=[]\;'"<>,/~!@#$%^&*()+{}|: """:
298 298 slug = slug.replace(c, '-')
299 299 slug = recursive_replace(slug, '-')
300 300 slug = collapse(slug, '-')
301 301 return slug
302 302
303 303 def get_changeset_safe(repo, rev):
304 304 from vcs.backends.base import BaseRepository
305 305 from vcs.exceptions import RepositoryError
306 306 if not isinstance(repo, BaseRepository):
307 307 raise Exception('You must pass an Repository '
308 308 'object as first argument got %s', type(repo))
309 309
310 310 try:
311 311 cs = repo.get_changeset(rev)
312 312 except RepositoryError:
313 313 from rhodecode.lib.utils import EmptyChangeset
314 314 cs = EmptyChangeset()
315 315 return cs
316 316
317 317
318 318 flash = _Flash()
319 319
320 320
321 321 #==============================================================================
322 322 # MERCURIAL FILTERS available via h.
323 323 #==============================================================================
324 324 from mercurial import util
325 325 from mercurial.templatefilters import person as _person
326 326
327 327
328 328
329 329 def _age(curdate):
330 330 """turns a datetime into an age string."""
331 331
332 332 from datetime import timedelta, datetime
333 333 agescales = [("year", 3600 * 24 * 365),
334 334 ("month", 3600 * 24 * 30),
335 335 #("week", 3600 * 24 * 7),
336 336 ("day", 3600 * 24),
337 337 ("hour", 3600),
338 338 ("minute", 60),
339 339 ("second", 1)]
340 340
341 341 age = datetime.now() - curdate
342 342 age_seconds = (age.days * agescales[2][1]) + age.seconds
343 343
344 344 pos = 1
345 345 for scale in agescales:
346 346 if scale[1] <= age_seconds:
347 347 return time_ago_in_words(curdate, agescales[pos][0])
348 348 pos += 1
349 349
350 350 age = lambda x:_age(x)
351 351 capitalize = lambda x: x.capitalize()
352 352 email = util.email
353 353 email_or_none = lambda x: util.email(x) if util.email(x) != x else None
354 354 person = lambda x: _person(x)
355 short_id = lambda x: x[:12]
355 356
356 357 #==============================================================================
357 358 # PERMS
358 359 #==============================================================================
359 360 from rhodecode.lib.auth import HasPermissionAny, HasPermissionAll, \
360 361 HasRepoPermissionAny, HasRepoPermissionAll
361 362
362 363 #==============================================================================
363 364 # GRAVATAR URL
364 365 #==============================================================================
365 366 import hashlib
366 367 import urllib
367 368 from pylons import request
368 369
369 370 def gravatar_url(email_address, size=30):
370 371 ssl_enabled = 'https' == request.environ.get('HTTP_X_URL_SCHEME')
371 372 default = 'identicon'
372 373 baseurl_nossl = "http://www.gravatar.com/avatar/"
373 374 baseurl_ssl = "https://secure.gravatar.com/avatar/"
374 375 baseurl = baseurl_ssl if ssl_enabled else baseurl_nossl
375 376
376 377
377 378 # construct the url
378 379 gravatar_url = baseurl + hashlib.md5(email_address.lower()).hexdigest() + "?"
379 380 gravatar_url += urllib.urlencode({'d':default, 's':str(size)})
380 381
381 382 return gravatar_url
382 383
383 384 def safe_unicode(str):
384 385 """safe unicode function. In case of UnicodeDecode error we try to return
385 386 unicode with errors replace, if this failes we return unicode with
386 387 string_escape decoding """
387 388
388 389 try:
389 390 u_str = unicode(str)
390 391 except UnicodeDecodeError:
391 392 try:
392 393 u_str = unicode(str, 'utf-8', 'replace')
393 394 except UnicodeDecodeError:
394 395 #incase we have a decode error just represent as byte string
395 396 u_str = unicode(str(str).encode('string_escape'))
396 397
397 398 return u_str
@@ -1,537 +1,538 b''
1 1 #!/usr/bin/env python
2 2 # encoding: utf-8
3 3 # Utilities for RhodeCode
4 4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 5 # This program is free software; you can redistribute it and/or
6 6 # modify it under the terms of the GNU General Public License
7 7 # as published by the Free Software Foundation; version 2
8 8 # of the License or (at your opinion) any later version of the license.
9 9 #
10 10 # This program is distributed in the hope that it will be useful,
11 11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 13 # GNU General Public License for more details.
14 14 #
15 15 # You should have received a copy of the GNU General Public License
16 16 # along with this program; if not, write to the Free Software
17 17 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18 18 # MA 02110-1301, USA.
19 19 """
20 20 Created on April 18, 2010
21 21 Utilities for RhodeCode
22 22 @author: marcink
23 23 """
24 24
25 25 from UserDict import DictMixin
26 26 from mercurial import ui, config, hg
27 27 from mercurial.error import RepoError
28 28 from rhodecode.model import meta
29 29 from rhodecode.model.caching_query import FromCache
30 30 from rhodecode.model.db import Repository, User, RhodeCodeUi, RhodeCodeSettings, \
31 31 UserLog
32 32 from rhodecode.model.repo import RepoModel
33 33 from rhodecode.model.user import UserModel
34 34 from vcs.backends.base import BaseChangeset
35 35 from vcs.backends.git import GitRepository
36 36 from vcs.backends.hg import MercurialRepository
37 37 from vcs.utils.lazy import LazyProperty
38 38 import datetime
39 39 import logging
40 40 import os
41 41
42 42 log = logging.getLogger(__name__)
43 43
44 44
45 45 def get_repo_slug(request):
46 46 return request.environ['pylons.routes_dict'].get('repo_name')
47 47
48 48 def is_mercurial(environ):
49 49 """
50 50 Returns True if request's target is mercurial server - header
51 51 ``HTTP_ACCEPT`` of such request would start with ``application/mercurial``.
52 52 """
53 53 http_accept = environ.get('HTTP_ACCEPT')
54 54 if http_accept and http_accept.startswith('application/mercurial'):
55 55 return True
56 56 return False
57 57
58 58 def is_git(environ):
59 59 """
60 60 Returns True if request's target is git server. ``HTTP_USER_AGENT`` would
61 61 then have git client version given.
62 62
63 63 :param environ:
64 64 """
65 65 http_user_agent = environ.get('HTTP_USER_AGENT')
66 66 if http_user_agent.startswith('git'):
67 67 return True
68 68 return False
69 69
70 70 def action_logger(user, action, repo, ipaddr, sa=None):
71 71 """
72 72 Action logger for various action made by users
73 73 """
74 74
75 75 if not sa:
76 76 sa = meta.Session()
77 77
78 78 try:
79 79 if hasattr(user, 'user_id'):
80 80 user_id = user.user_id
81 81 elif isinstance(user, basestring):
82 82 user_id = UserModel(sa).get_by_username(user, cache=False).user_id
83 83 else:
84 84 raise Exception('You have to provide user object or username')
85 85
86 86 repo_name = repo.lstrip('/')
87 87 user_log = UserLog()
88 88 user_log.user_id = user_id
89 89 user_log.action = action
90 90 user_log.repository_name = repo_name
91 91 user_log.repository = RepoModel(sa).get(repo_name, cache=False)
92 92 user_log.action_date = datetime.datetime.now()
93 93 user_log.user_ip = ipaddr
94 94 sa.add(user_log)
95 95 sa.commit()
96 96
97 97 log.info('Adding user %s, action %s on %s',
98 98 user.username, action, repo)
99 99 except Exception, e:
100 100 sa.rollback()
101 101 log.error('could not log user action:%s', str(e))
102 102
103 103 def get_repos(path, recursive=False, initial=False):
104 104 """
105 105 Scans given path for repos and return (name,(type,path)) tuple
106 106 :param prefix:
107 107 :param path:
108 108 :param recursive:
109 109 :param initial:
110 110 """
111 111 from vcs.utils.helpers import get_scm
112 112 from vcs.exceptions import VCSError
113 113
114 114 try:
115 115 scm = get_scm(path)
116 116 except:
117 117 pass
118 118 else:
119 119 raise Exception('The given path %s should not be a repository got %s',
120 120 path, scm)
121 121
122 122 for dirpath in os.listdir(path):
123 123 try:
124 124 yield dirpath, get_scm(os.path.join(path, dirpath))
125 125 except VCSError:
126 126 pass
127 127
128 128 if __name__ == '__main__':
129 129 get_repos('', '/home/marcink/workspace-python')
130 130
131 131
132 132 def check_repo_fast(repo_name, base_path):
133 133 if os.path.isdir(os.path.join(base_path, repo_name)):return False
134 134 return True
135 135
136 136 def check_repo(repo_name, base_path, verify=True):
137 137
138 138 repo_path = os.path.join(base_path, repo_name)
139 139
140 140 try:
141 141 if not check_repo_fast(repo_name, base_path):
142 142 return False
143 143 r = hg.repository(ui.ui(), repo_path)
144 144 if verify:
145 145 hg.verify(r)
146 146 #here we hnow that repo exists it was verified
147 147 log.info('%s repo is already created', repo_name)
148 148 return False
149 149 except RepoError:
150 150 #it means that there is no valid repo there...
151 151 log.info('%s repo is free for creation', repo_name)
152 152 return True
153 153
154 154 def ask_ok(prompt, retries=4, complaint='Yes or no, please!'):
155 155 while True:
156 156 ok = raw_input(prompt)
157 157 if ok in ('y', 'ye', 'yes'): return True
158 158 if ok in ('n', 'no', 'nop', 'nope'): return False
159 159 retries = retries - 1
160 160 if retries < 0: raise IOError
161 161 print complaint
162 162
163 163 def get_hg_ui_cached():
164 164 try:
165 165 sa = meta.Session
166 166 ret = sa.query(RhodeCodeUi)\
167 167 .options(FromCache("sql_cache_short", "get_hg_ui_settings"))\
168 168 .all()
169 169 except:
170 170 pass
171 171 finally:
172 172 meta.Session.remove()
173 173 return ret
174 174
175 175
176 176 def get_hg_settings():
177 177 try:
178 178 sa = meta.Session()
179 179 ret = sa.query(RhodeCodeSettings)\
180 180 .options(FromCache("sql_cache_short", "get_hg_settings"))\
181 181 .all()
182 182 except:
183 183 pass
184 184 finally:
185 185 meta.Session.remove()
186 186
187 187 if not ret:
188 188 raise Exception('Could not get application settings !')
189 189 settings = {}
190 190 for each in ret:
191 191 settings['rhodecode_' + each.app_settings_name] = each.app_settings_value
192 192
193 193 return settings
194 194
195 195 def get_hg_ui_settings():
196 196 try:
197 197 sa = meta.Session()
198 198 ret = sa.query(RhodeCodeUi).all()
199 199 except:
200 200 pass
201 201 finally:
202 202 meta.Session.remove()
203 203
204 204 if not ret:
205 205 raise Exception('Could not get application ui settings !')
206 206 settings = {}
207 207 for each in ret:
208 208 k = each.ui_key
209 209 v = each.ui_value
210 210 if k == '/':
211 211 k = 'root_path'
212 212
213 213 if k.find('.') != -1:
214 214 k = k.replace('.', '_')
215 215
216 216 if each.ui_section == 'hooks':
217 217 v = each.ui_active
218 218
219 219 settings[each.ui_section + '_' + k] = v
220 220
221 221 return settings
222 222
223 223 #propagated from mercurial documentation
224 224 ui_sections = ['alias', 'auth',
225 225 'decode/encode', 'defaults',
226 226 'diff', 'email',
227 227 'extensions', 'format',
228 228 'merge-patterns', 'merge-tools',
229 229 'hooks', 'http_proxy',
230 230 'smtp', 'patch',
231 231 'paths', 'profiling',
232 232 'server', 'trusted',
233 233 'ui', 'web', ]
234 234
235 235 def make_ui(read_from='file', path=None, checkpaths=True):
236 236 """
237 237 A function that will read python rc files or database
238 238 and make an mercurial ui object from read options
239 239
240 240 :param path: path to mercurial config file
241 241 :param checkpaths: check the path
242 242 :param read_from: read from 'file' or 'db'
243 243 """
244 244
245 245 baseui = ui.ui()
246 246
247 247 if read_from == 'file':
248 248 if not os.path.isfile(path):
249 249 log.warning('Unable to read config file %s' % path)
250 250 return False
251 251 log.debug('reading hgrc from %s', path)
252 252 cfg = config.config()
253 253 cfg.read(path)
254 254 for section in ui_sections:
255 255 for k, v in cfg.items(section):
256 256 baseui.setconfig(section, k, v)
257 257 log.debug('settings ui from file[%s]%s:%s', section, k, v)
258 258
259 259 elif read_from == 'db':
260 260 hg_ui = get_hg_ui_cached()
261 261 for ui_ in hg_ui:
262 262 if ui_.ui_active:
263 263 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section, ui_.ui_key, ui_.ui_value)
264 264 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
265 265
266 266
267 267 return baseui
268 268
269 269
270 270 def set_rhodecode_config(config):
271 271 hgsettings = get_hg_settings()
272 272
273 273 for k, v in hgsettings.items():
274 274 config[k] = v
275 275
276 276 def invalidate_cache(name, *args):
277 277 """Invalidates given name cache"""
278 278
279 279 from beaker.cache import region_invalidate
280 280 log.info('INVALIDATING CACHE FOR %s', name)
281 281
282 282 """propagate our arguments to make sure invalidation works. First
283 283 argument has to be the name of cached func name give to cache decorator
284 284 without that the invalidation would not work"""
285 285 tmp = [name]
286 286 tmp.extend(args)
287 287 args = tuple(tmp)
288 288
289 289 if name == 'cached_repo_list':
290 290 from rhodecode.model.hg import _get_repos_cached
291 291 region_invalidate(_get_repos_cached, None, *args)
292 292
293 293 if name == 'full_changelog':
294 294 from rhodecode.model.hg import _full_changelog_cached
295 295 region_invalidate(_full_changelog_cached, None, *args)
296 296
297 297 class EmptyChangeset(BaseChangeset):
298 298 """
299 299 An dummy empty changeset.
300 300 """
301 301
302 302 revision = -1
303 303 message = ''
304 304 author = ''
305 305 date = ''
306
306 307 @LazyProperty
307 308 def raw_id(self):
308 309 """
309 310 Returns raw string identifying this changeset, useful for web
310 311 representation.
311 312 """
312 313 return '0' * 40
313 314
314 315 @LazyProperty
315 316 def short_id(self):
316 317 return self.raw_id[:12]
317 318
318 319 def get_file_changeset(self, path):
319 320 return self
320 321
321 322 def get_file_content(self, path):
322 323 return u''
323 324
324 325 def get_file_size(self, path):
325 326 return 0
326 327
327 328 def repo2db_mapper(initial_repo_list, remove_obsolete=False):
328 329 """
329 330 maps all found repositories into db
330 331 """
331 332
332 333 sa = meta.Session()
333 334 rm = RepoModel(sa)
334 335 user = sa.query(User).filter(User.admin == True).first()
335 336
336 337 for name, repo in initial_repo_list.items():
337 338 if not rm.get(name, cache=False):
338 339 log.info('repository %s not found creating default', name)
339 340
340 341 if isinstance(repo, MercurialRepository):
341 342 repo_type = 'hg'
342 343 if isinstance(repo, GitRepository):
343 344 repo_type = 'git'
344 345
345 346 form_data = {
346 347 'repo_name':name,
347 348 'repo_type':repo_type,
348 349 'description':repo.description if repo.description != 'unknown' else \
349 350 'auto description for %s' % name,
350 351 'private':False
351 352 }
352 353 rm.create(form_data, user, just_db=True)
353 354
354 355
355 356 if remove_obsolete:
356 357 #remove from database those repositories that are not in the filesystem
357 358 for repo in sa.query(Repository).all():
358 359 if repo.repo_name not in initial_repo_list.keys():
359 360 sa.delete(repo)
360 361 sa.commit()
361 362
362 363
363 364 meta.Session.remove()
364 365
365 366
366 367 class OrderedDict(dict, DictMixin):
367 368
368 369 def __init__(self, *args, **kwds):
369 370 if len(args) > 1:
370 371 raise TypeError('expected at most 1 arguments, got %d' % len(args))
371 372 try:
372 373 self.__end
373 374 except AttributeError:
374 375 self.clear()
375 376 self.update(*args, **kwds)
376 377
377 378 def clear(self):
378 379 self.__end = end = []
379 380 end += [None, end, end] # sentinel node for doubly linked list
380 381 self.__map = {} # key --> [key, prev, next]
381 382 dict.clear(self)
382 383
383 384 def __setitem__(self, key, value):
384 385 if key not in self:
385 386 end = self.__end
386 387 curr = end[1]
387 388 curr[2] = end[1] = self.__map[key] = [key, curr, end]
388 389 dict.__setitem__(self, key, value)
389 390
390 391 def __delitem__(self, key):
391 392 dict.__delitem__(self, key)
392 393 key, prev, next = self.__map.pop(key)
393 394 prev[2] = next
394 395 next[1] = prev
395 396
396 397 def __iter__(self):
397 398 end = self.__end
398 399 curr = end[2]
399 400 while curr is not end:
400 401 yield curr[0]
401 402 curr = curr[2]
402 403
403 404 def __reversed__(self):
404 405 end = self.__end
405 406 curr = end[1]
406 407 while curr is not end:
407 408 yield curr[0]
408 409 curr = curr[1]
409 410
410 411 def popitem(self, last=True):
411 412 if not self:
412 413 raise KeyError('dictionary is empty')
413 414 if last:
414 415 key = reversed(self).next()
415 416 else:
416 417 key = iter(self).next()
417 418 value = self.pop(key)
418 419 return key, value
419 420
420 421 def __reduce__(self):
421 422 items = [[k, self[k]] for k in self]
422 423 tmp = self.__map, self.__end
423 424 del self.__map, self.__end
424 425 inst_dict = vars(self).copy()
425 426 self.__map, self.__end = tmp
426 427 if inst_dict:
427 428 return (self.__class__, (items,), inst_dict)
428 429 return self.__class__, (items,)
429 430
430 431 def keys(self):
431 432 return list(self)
432 433
433 434 setdefault = DictMixin.setdefault
434 435 update = DictMixin.update
435 436 pop = DictMixin.pop
436 437 values = DictMixin.values
437 438 items = DictMixin.items
438 439 iterkeys = DictMixin.iterkeys
439 440 itervalues = DictMixin.itervalues
440 441 iteritems = DictMixin.iteritems
441 442
442 443 def __repr__(self):
443 444 if not self:
444 445 return '%s()' % (self.__class__.__name__,)
445 446 return '%s(%r)' % (self.__class__.__name__, self.items())
446 447
447 448 def copy(self):
448 449 return self.__class__(self)
449 450
450 451 @classmethod
451 452 def fromkeys(cls, iterable, value=None):
452 453 d = cls()
453 454 for key in iterable:
454 455 d[key] = value
455 456 return d
456 457
457 458 def __eq__(self, other):
458 459 if isinstance(other, OrderedDict):
459 460 return len(self) == len(other) and self.items() == other.items()
460 461 return dict.__eq__(self, other)
461 462
462 463 def __ne__(self, other):
463 464 return not self == other
464 465
465 466
466 467 #===============================================================================
467 468 # TEST FUNCTIONS AND CREATORS
468 469 #===============================================================================
469 470 def create_test_index(repo_location, full_index):
470 471 """Makes default test index
471 472 :param repo_location:
472 473 :param full_index:
473 474 """
474 475 from rhodecode.lib.indexers.daemon import WhooshIndexingDaemon
475 476 from rhodecode.lib.pidlock import DaemonLock, LockHeld
476 477 from rhodecode.lib.indexers import IDX_LOCATION
477 478 import shutil
478 479
479 480 if os.path.exists(IDX_LOCATION):
480 481 shutil.rmtree(IDX_LOCATION)
481 482
482 483 try:
483 484 l = DaemonLock()
484 485 WhooshIndexingDaemon(repo_location=repo_location)\
485 486 .run(full_index=full_index)
486 487 l.release()
487 488 except LockHeld:
488 489 pass
489 490
490 491 def create_test_env(repos_test_path, config):
491 492 """Makes a fresh database and
492 493 install test repository into tmp dir
493 494 """
494 495 from rhodecode.lib.db_manage import DbManage
495 496 import tarfile
496 497 import shutil
497 498 from os.path import dirname as dn, join as jn, abspath
498 499
499 500 log = logging.getLogger('TestEnvCreator')
500 501 # create logger
501 502 log.setLevel(logging.DEBUG)
502 503 log.propagate = True
503 504 # create console handler and set level to debug
504 505 ch = logging.StreamHandler()
505 506 ch.setLevel(logging.DEBUG)
506 507
507 508 # create formatter
508 509 formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
509 510
510 511 # add formatter to ch
511 512 ch.setFormatter(formatter)
512 513
513 514 # add ch to logger
514 515 log.addHandler(ch)
515 516
516 517 #PART ONE create db
517 518 dbname = config['sqlalchemy.db1.url'].split('/')[-1]
518 519 log.debug('making test db %s', dbname)
519 520
520 521 dbmanage = DbManage(log_sql=True, dbname=dbname, root=config['here'],
521 522 tests=True)
522 523 dbmanage.create_tables(override=True)
523 524 dbmanage.config_prompt(repos_test_path)
524 525 dbmanage.create_default_user()
525 526 dbmanage.admin_prompt()
526 527 dbmanage.create_permissions()
527 528 dbmanage.populate_default_permissions()
528 529
529 530 #PART TWO make test repo
530 531 log.debug('making test vcs repo')
531 532 if os.path.isdir('/tmp/vcs_test'):
532 533 shutil.rmtree('/tmp/vcs_test')
533 534
534 535 cur_dir = dn(dn(abspath(__file__)))
535 536 tar = tarfile.open(jn(cur_dir, 'tests', "vcs_test.tar.gz"))
536 537 tar.extractall('/tmp')
537 538 tar.close()
@@ -1,183 +1,183 b''
1 1 #!/usr/bin/env python
2 2 # encoding: utf-8
3 3 # Model for RhodeCode
4 4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 5 #
6 6 # This program is free software; you can redistribute it and/or
7 7 # modify it under the terms of the GNU General Public License
8 8 # as published by the Free Software Foundation; version 2
9 9 # of the License or (at your opinion) any later version of the license.
10 10 #
11 11 # This program is distributed in the hope that it will be useful,
12 12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 14 # GNU General Public License for more details.
15 15 #
16 16 # You should have received a copy of the GNU General Public License
17 17 # along with this program; if not, write to the Free Software
18 18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 19 # MA 02110-1301, USA.
20 20 """
21 21 Created on April 9, 2010
22 22 Model for RhodeCode
23 23 @author: marcink
24 24 """
25 25 from beaker.cache import cache_region
26 26 from mercurial import ui
27 27 from rhodecode.lib import helpers as h
28 28 from rhodecode.lib.utils import invalidate_cache
29 29 from rhodecode.lib.auth import HasRepoPermissionAny
30 30 from rhodecode.model import meta
31 31 from rhodecode.model.db import Repository, User
32 32 from sqlalchemy.orm import joinedload
33 33 from vcs.exceptions import RepositoryError, VCSError
34 34 import logging
35 35 import sys
36 36 import time
37 37
38 38 log = logging.getLogger(__name__)
39 39
40 40 try:
41 41 from vcs.backends.hg import MercurialRepository
42 42 from vcs.backends.git import GitRepository
43 43 except ImportError:
44 44 sys.stderr.write('You have to import vcs module')
45 45 raise Exception('Unable to import vcs')
46 46
47 47 def _get_repos_cached_initial(app_globals, initial):
48 48 """return cached dict with repos
49 49 """
50 50 g = app_globals
51 51 return HgModel().repo_scan(g.paths[0][1], g.baseui, initial)
52 52
53 53 @cache_region('long_term', 'cached_repo_list')
54 54 def _get_repos_cached():
55 55 """return cached dict with repos
56 56 """
57 57 log.info('getting all repositories list')
58 58 from pylons import app_globals as g
59 59 return HgModel().repo_scan(g.paths[0][1], g.baseui)
60 60
61 61 @cache_region('super_short_term', 'cached_repos_switcher_list')
62 62 def _get_repos_switcher_cached(cached_repo_list):
63 63 repos_lst = []
64 64 for repo in [x for x in cached_repo_list.values()]:
65 65 if HasRepoPermissionAny('repository.write', 'repository.read',
66 66 'repository.admin')(repo.name, 'main page check'):
67 67 repos_lst.append((repo.name, repo.dbrepo.private,))
68 68
69 69 return sorted(repos_lst, key=lambda k:k[0].lower())
70 70
71 71 @cache_region('long_term', 'full_changelog')
72 72 def _full_changelog_cached(repo_name):
73 73 log.info('getting full changelog for %s', repo_name)
74 74 return list(reversed(list(HgModel().get_repo(repo_name))))
75 75
76 76 class HgModel(object):
77 77 """
78 78 Mercurial Model
79 79 """
80 80
81 81 def __init__(self, sa=None):
82 82 if not sa:
83 83 self.sa = meta.Session()
84 84 else:
85 85 self.sa = sa
86 86
87 87 def repo_scan(self, repos_path, baseui, initial=False):
88 88 """
89 89 Listing of repositories in given path. This path should not be a
90 90 repository itself. Return a dictionary of repository objects
91 91
92 92 :param repos_path: path to directory containing repositories
93 93 :param baseui
94 94 :param initial: initial scann
95 95 """
96 96 log.info('scanning for repositories in %s', repos_path)
97 97
98 98 if not isinstance(baseui, ui.ui):
99 99 baseui = ui.ui()
100 100
101 101 from rhodecode.lib.utils import get_repos
102 102 repos = get_repos(repos_path)
103 103
104 104
105 105 repos_list = {}
106 106 for name, path in repos:
107 107 try:
108 108 #name = name.split('/')[-1]
109 109 if repos_list.has_key(name):
110 110 raise RepositoryError('Duplicate repository name %s found in'
111 111 ' %s' % (name, path))
112 112 else:
113 113 if path[0] == 'hg':
114 114 repos_list[name] = MercurialRepository(path[1], baseui=baseui)
115 115 repos_list[name].name = name
116 116
117 117 if path[0] == 'git':
118 118 repos_list[name] = GitRepository(path[1])
119 119 repos_list[name].name = name
120 120
121 121 dbrepo = None
122 122 if not initial:
123 123 #for initial scann on application first run we don't
124 124 #have db repos yet.
125 125 dbrepo = self.sa.query(Repository)\
126 126 .options(joinedload(Repository.fork))\
127 127 .filter(Repository.repo_name == name)\
128 128 .scalar()
129 129
130 130 if dbrepo:
131 131 log.info('Adding db instance to cached list')
132 132 repos_list[name].dbrepo = dbrepo
133 133 repos_list[name].description = dbrepo.description
134 134 if dbrepo.user:
135 135 repos_list[name].contact = dbrepo.user.full_contact
136 136 else:
137 137 repos_list[name].contact = self.sa.query(User)\
138 138 .filter(User.admin == True).first().full_contact
139 139 except OSError:
140 140 continue
141 141
142 142 return repos_list
143 143
144 144 def get_repos(self):
145 145 for name, repo in _get_repos_cached().items():
146 146
147 147 if isinstance(repo, MercurialRepository) and repo._get_hidden():
148 148 #skip hidden web repository
149 149 continue
150 150
151 151 last_change = repo.last_change
152 152 tip = h.get_changeset_safe(repo, 'tip')
153 153
154 154 tmp_d = {}
155 155 tmp_d['name'] = repo.name
156 156 tmp_d['name_sort'] = tmp_d['name'].lower()
157 157 tmp_d['description'] = repo.description
158 158 tmp_d['description_sort'] = tmp_d['description']
159 159 tmp_d['last_change'] = last_change
160 160 tmp_d['last_change_sort'] = time.mktime(last_change.timetuple())
161 tmp_d['tip'] = tip.short_id
161 tmp_d['tip'] = tip.raw_id
162 162 tmp_d['tip_sort'] = tip.revision
163 163 tmp_d['rev'] = tip.revision
164 164 tmp_d['contact'] = repo.contact
165 165 tmp_d['contact_sort'] = tmp_d['contact']
166 166 tmp_d['repo_archives'] = list(repo._get_archives())
167 167 tmp_d['last_msg'] = tip.message
168 168 tmp_d['repo'] = repo
169 169 yield tmp_d
170 170
171 171 def get_repo(self, repo_name):
172 172 try:
173 173 repo = _get_repos_cached()[repo_name]
174 174 return repo
175 175 except KeyError:
176 176 #i we're here and we got key errors let's try to invalidate the
177 177 #cahce and try again
178 178 invalidate_cache('cached_repo_list')
179 179 repo = _get_repos_cached()[repo_name]
180 180 return repo
181 181
182 182
183 183
@@ -1,2312 +1,2313 b''
1 1 html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,big,cite,code,del,dfn,em,font,img,ins,kbd,q,s,samp,small,strike,strong,sub,sup,tt,var,b,u,i,center,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption,tbody,tfoot,thead,tr,th,td {
2 2 border:0;
3 3 outline:0;
4 4 font-size:100%;
5 5 vertical-align:baseline;
6 6 background:transparent;
7 7 margin:0;
8 8 padding:0;
9 9 }
10 10
11 11 body {
12 12 line-height:1;
13 13 height:100%;
14 14 background:url("../images/background.png") repeat scroll 0 0 #B0B0B0;
15 15 font-family:Lucida Grande, Verdana, Lucida Sans Regular, Lucida Sans Unicode, Arial, sans-serif;
16 16 font-size:12px;
17 17 color:#000;
18 18 margin:0;
19 19 padding:0;
20 20 }
21 21
22 22 ol,ul {
23 23 list-style:none;
24 24 }
25 25
26 26 blockquote,q {
27 27 quotes:none;
28 28 }
29 29
30 30 blockquote:before,blockquote:after,q:before,q:after {
31 31 content:none;
32 32 }
33 33
34 34 :focus {
35 35 outline:0;
36 36 }
37 37
38 38 del {
39 39 text-decoration:line-through;
40 40 }
41 41
42 42 table {
43 43 border-collapse:collapse;
44 44 border-spacing:0;
45 45 }
46 46
47 47 html {
48 48 height:100%;
49 49 }
50 50
51 51 a {
52 52 color:#003367;
53 53 text-decoration:none;
54 54 cursor:pointer;
55 55 font-weight:700;
56 56 }
57 57
58 58 a:hover {
59 59 color:#316293;
60 60 text-decoration:underline;
61 61 }
62 62
63 63 h1,h2,h3,h4,h5,h6 {
64 64 color:#292929;
65 65 font-weight:700;
66 66 }
67 67
68 68 h1 {
69 69 font-size:22px;
70 70 }
71 71
72 72 h2 {
73 73 font-size:20px;
74 74 }
75 75
76 76 h3 {
77 77 font-size:18px;
78 78 }
79 79
80 80 h4 {
81 81 font-size:16px;
82 82 }
83 83
84 84 h5 {
85 85 font-size:14px;
86 86 }
87 87
88 88 h6 {
89 89 font-size:11px;
90 90 }
91 91
92 92 ul.circle {
93 93 list-style-type:circle;
94 94 }
95 95
96 96 ul.disc {
97 97 list-style-type:disc;
98 98 }
99 99
100 100 ul.square {
101 101 list-style-type:square;
102 102 }
103 103
104 104 ol.lower-roman {
105 105 list-style-type:lower-roman;
106 106 }
107 107
108 108 ol.upper-roman {
109 109 list-style-type:upper-roman;
110 110 }
111 111
112 112 ol.lower-alpha {
113 113 list-style-type:lower-alpha;
114 114 }
115 115
116 116 ol.upper-alpha {
117 117 list-style-type:upper-alpha;
118 118 }
119 119
120 120 ol.decimal {
121 121 list-style-type:decimal;
122 122 }
123 123
124 124 div.color {
125 125 clear:both;
126 126 overflow:hidden;
127 127 position:absolute;
128 128 background:#FFF;
129 129 margin:7px 0 0 60px;
130 130 padding:1px 1px 1px 0;
131 131 }
132 132
133 133 div.color a {
134 134 width:15px;
135 135 height:15px;
136 136 display:block;
137 137 float:left;
138 138 margin:0 0 0 1px;
139 139 padding:0;
140 140 }
141 141
142 142 div.options {
143 143 clear:both;
144 144 overflow:hidden;
145 145 position:absolute;
146 146 background:#FFF;
147 147 margin:7px 0 0 162px;
148 148 padding:0;
149 149 }
150 150
151 151 div.options a {
152 152 height:1%;
153 153 display:block;
154 154 text-decoration:none;
155 155 margin:0;
156 156 padding:3px 8px;
157 157 }
158 158
159 159 .top-left-rounded-corner {
160 160 -webkit-border-top-left-radius: 8px;
161 161 -khtml-border-radius-topleft: 8px;
162 162 -moz-border-radius-topleft: 8px;
163 163 border-top-left-radius: 8px;
164 164 }
165 165
166 166 .top-right-rounded-corner {
167 167 -webkit-border-top-right-radius: 8px;
168 168 -khtml-border-radius-topright: 8px;
169 169 -moz-border-radius-topright: 8px;
170 170 border-top-right-radius: 8px;
171 171 }
172 172
173 173 .bottom-left-rounded-corner {
174 174 -webkit-border-bottom-left-radius: 8px;
175 175 -khtml-border-radius-bottomleft: 8px;
176 176 -moz-border-radius-bottomleft: 8px;
177 177 border-bottom-left-radius: 8px;
178 178 }
179 179
180 180 .bottom-right-rounded-corner {
181 181 -webkit-border-bottom-right-radius: 8px;
182 182 -khtml-border-radius-bottomright: 8px;
183 183 -moz-border-radius-bottomright: 8px;
184 184 border-bottom-right-radius: 8px;
185 185 }
186 186
187 187
188 188 #header {
189 189 margin:0;
190 190 padding:0 30px;
191 191 }
192 192
193 193 #header ul#logged-user li {
194 194 list-style:none;
195 195 float:left;
196 196 border-left:1px solid #bbb;
197 197 border-right:1px solid #a5a5a5;
198 198 margin:-2px 0 0;
199 199 padding:10px 12px;
200 200 }
201 201
202 202 #header ul#logged-user li.first {
203 203 border-left:none;
204 204 margin:-6px;
205 205 }
206 206
207 207 #header ul#logged-user li.first div.account {
208 208 padding-top:4px;
209 209 float:left;
210 210 }
211 211
212 212 #header ul#logged-user li.last {
213 213 border-right:none;
214 214 }
215 215
216 216 #header ul#logged-user li a {
217 217 color:#4e4e4e;
218 218 font-weight:700;
219 219 text-decoration:none;
220 220 }
221 221
222 222 #header ul#logged-user li a:hover {
223 223 color:#376ea6;
224 224 text-decoration:underline;
225 225 }
226 226
227 227 #header ul#logged-user li.highlight a {
228 228 color:#fff;
229 229 }
230 230
231 231 #header ul#logged-user li.highlight a:hover {
232 232 color:#376ea6;
233 233 }
234 234
235 235 #header #header-inner {
236 236 height:40px;
237 237 clear:both;
238 238 position:relative;
239 239 background:#003367 url("../images/header_inner.png") repeat-x;
240 240 border-bottom:2px solid #fff;
241 241 margin:0;
242 242 padding:0;
243 243 }
244 244
245 245 #header #header-inner #home a {
246 246 height:40px;
247 247 width:46px;
248 248 display:block;
249 249 background:url("../images/button_home.png");
250 250 background-position:0 0;
251 251 margin:0;
252 252 padding:0;
253 253 }
254 254
255 255 #header #header-inner #home a:hover {
256 256 background-position:0 -40px;
257 257 }
258 258
259 259 #header #header-inner #logo h1 {
260 260 color:#FFF;
261 261 font-size:14px;
262 262 text-transform:uppercase;
263 263 margin:13px 0 0 13px;
264 264 padding:0;
265 265 }
266 266
267 267 #header #header-inner #logo a {
268 268 color:#fff;
269 269 text-decoration:none;
270 270 }
271 271
272 272 #header #header-inner #logo a:hover {
273 273 color:#dabf29;
274 274 }
275 275
276 276 #header #header-inner #quick,#header #header-inner #quick ul {
277 277 position:relative;
278 278 float:right;
279 279 list-style-type:none;
280 280 list-style-position:outside;
281 281 margin:10px 5px 0 0;
282 282 padding:0;
283 283 }
284 284
285 285 #header #header-inner #quick li {
286 286 position:relative;
287 287 float:left;
288 288 margin:0 5px 0 0;
289 289 padding:0;
290 290 }
291 291
292 292 #header #header-inner #quick li a {
293 293 top:0;
294 294 left:0;
295 295 height:1%;
296 296 display:block;
297 297 clear:both;
298 298 overflow:hidden;
299 299 color:#FFF;
300 300 font-weight:700;
301 301 text-decoration:none;
302 302 background:#369 url("../../images/quick_l.png") no-repeat top left;
303 303 padding:0;
304 304 }
305 305
306 306 #header #header-inner #quick li span {
307 307 top:0;
308 308 right:0;
309 309 height:1%;
310 310 display:block;
311 311 float:left;
312 312 background:url("../../images/quick_r.png") no-repeat top right;
313 313 border-left:1px solid #3f6f9f;
314 314 margin:0;
315 315 padding:10px 12px 8px 10px;
316 316 }
317 317
318 318 #header #header-inner #quick li span.normal {
319 319 border:none;
320 320 padding:10px 12px 8px;
321 321 }
322 322
323 323 #header #header-inner #quick li span.icon {
324 324 top:0;
325 325 left:0;
326 326 border-left:none;
327 327 background:url("../../images/quick_l.png") no-repeat top left;
328 328 border-right:1px solid #2e5c89;
329 329 padding:8px 8px 4px;
330 330 }
331 331
332 332 #header #header-inner #quick li a:hover {
333 333 background:#4e4e4e url("../../images/quick_l_selected.png") no-repeat top left;
334 334 }
335 335
336 336 #header #header-inner #quick li a:hover span {
337 337 border-left:1px solid #545454;
338 338 background:url("../../images/quick_r_selected.png") no-repeat top right;
339 339 }
340 340
341 341 #header #header-inner #quick li a:hover span.icon {
342 342 border-left:none;
343 343 border-right:1px solid #464646;
344 344 background:url("../../images/quick_l_selected.png") no-repeat top left;
345 345 }
346 346
347 347 #header #header-inner #quick ul {
348 348 top:29px;
349 349 right:0;
350 350 min-width:200px;
351 351 display:none;
352 352 position:absolute;
353 353 background:#FFF;
354 354 border:1px solid #666;
355 355 border-top:1px solid #003367;
356 356 z-index:100;
357 357 margin:0;
358 358 padding:0;
359 359 }
360 360
361 361 #header #header-inner #quick ul.repo_switcher {
362 362 max-height:275px;
363 363 overflow-x:hidden;
364 364 overflow-y:auto;
365 365 }
366 366
367 367 #header #header-inner #quick li ul li {
368 368 border-bottom:1px solid #ddd;
369 369 }
370 370
371 371 #header #header-inner #quick li ul li a {
372 372 width:182px;
373 373 height:auto;
374 374 display:block;
375 375 float:left;
376 376 background:#FFF;
377 377 color:#003367;
378 378 font-weight:400;
379 379 margin:0;
380 380 padding:7px 9px;
381 381 }
382 382
383 383 #header #header-inner #quick li ul li a:hover {
384 384 color:#000;
385 385 background:#FFF;
386 386 }
387 387
388 388 #header #header-inner #quick ul ul {
389 389 top:auto;
390 390 }
391 391
392 392 #header #header-inner #quick li ul ul {
393 393 right:200px;
394 394 max-height:275px;
395 395 overflow:auto;
396 396 overflow-x:hidden;
397 397 white-space:nowrap;
398 398 }
399 399
400 400 #header #header-inner #quick li ul li a.journal,#header #header-inner #quick li ul li a.journal:hover {
401 401 background:url("../images/icons/book.png") no-repeat scroll 4px 9px #FFF;
402 402 width:167px;
403 403 margin:0;
404 404 padding:12px 9px 7px 24px;
405 405 }
406 406
407 407 #header #header-inner #quick li ul li a.private_repo,#header #header-inner #quick li ul li a.private_repo:hover {
408 408 background:url("../images/icons/lock.png") no-repeat scroll 4px 9px #FFF;
409 409 min-width:167px;
410 410 margin:0;
411 411 padding:12px 9px 7px 24px;
412 412 }
413 413
414 414 #header #header-inner #quick li ul li a.public_repo,#header #header-inner #quick li ul li a.public_repo:hover {
415 415 background:url("../images/icons/lock_open.png") no-repeat scroll 4px 9px #FFF;
416 416 min-width:167px;
417 417 margin:0;
418 418 padding:12px 9px 7px 24px;
419 419 }
420 420
421 421 #header #header-inner #quick li ul li a.repos,#header #header-inner #quick li ul li a.repos:hover {
422 422 background:url("../images/icons/folder_edit.png") no-repeat scroll 4px 9px #FFF;
423 423 width:167px;
424 424 margin:0;
425 425 padding:12px 9px 7px 24px;
426 426 }
427 427
428 428 #header #header-inner #quick li ul li a.users,#header #header-inner #quick li ul li a.users:hover {
429 429 background:#FFF url("../images/icons/user_edit.png") no-repeat 4px 9px;
430 430 width:167px;
431 431 margin:0;
432 432 padding:12px 9px 7px 24px;
433 433 }
434 434
435 435 #header #header-inner #quick li ul li a.settings,#header #header-inner #quick li ul li a.settings:hover {
436 436 background:#FFF url("../images/icons/cog.png") no-repeat 4px 9px;
437 437 width:167px;
438 438 margin:0;
439 439 padding:12px 9px 7px 24px;
440 440 }
441 441
442 442 #header #header-inner #quick li ul li a.permissions,#header #header-inner #quick li ul li a.permissions:hover {
443 443 background:#FFF url("../images/icons/key.png") no-repeat 4px 9px;
444 444 width:167px;
445 445 margin:0;
446 446 padding:12px 9px 7px 24px;
447 447 }
448 448
449 449 #header #header-inner #quick li ul li a.fork,#header #header-inner #quick li ul li a.fork:hover {
450 450 background:#FFF url("../images/icons/arrow_divide.png") no-repeat 4px 9px;
451 451 width:167px;
452 452 margin:0;
453 453 padding:12px 9px 7px 24px;
454 454 }
455 455
456 456 #header #header-inner #quick li ul li a.search,#header #header-inner #quick li ul li a.search:hover {
457 457 background:#FFF url("../images/icons/search_16.png") no-repeat 4px 9px;
458 458 width:167px;
459 459 margin:0;
460 460 padding:12px 9px 7px 24px;
461 461 }
462 462
463 463 #header #header-inner #quick li ul li a.delete,#header #header-inner #quick li ul li a.delete:hover {
464 464 background:#FFF url("../images/icons/delete.png") no-repeat 4px 9px;
465 465 width:167px;
466 466 margin:0;
467 467 padding:12px 9px 7px 24px;
468 468 }
469 469
470 470 #header #header-inner #quick li ul li a.branches,#header #header-inner #quick li ul li a.branches:hover {
471 471 background:#FFF url("../images/icons/arrow_branch.png") no-repeat 4px 9px;
472 472 width:167px;
473 473 margin:0;
474 474 padding:12px 9px 7px 24px;
475 475 }
476 476
477 477 #header #header-inner #quick li ul li a.tags,#header #header-inner #quick li ul li a.tags:hover {
478 478 background:#FFF url("../images/icons/tag_blue.png") no-repeat 4px 9px;
479 479 width:167px;
480 480 margin:0;
481 481 padding:12px 9px 7px 24px;
482 482 }
483 483
484 484 #header #header-inner #quick li ul li a.admin,#header #header-inner #quick li ul li a.admin:hover {
485 485 background:#FFF url("../images/icons/cog_edit.png") no-repeat 4px 9px;
486 486 width:167px;
487 487 margin:0;
488 488 padding:12px 9px 7px 24px;
489 489 }
490 490
491 491 #content #left {
492 492 left:0;
493 493 width:280px;
494 494 position:absolute;
495 495 }
496 496
497 497 #content #right {
498 498 margin:0 60px 10px 290px;
499 499 }
500 500
501 501 #content div.box {
502 502 clear:both;
503 503 overflow:hidden;
504 504 background:#fff;
505 505 margin:0 0 10px;
506 506 padding:0 0 10px;
507 507 }
508 508
509 509 #content div.box-left {
510 510 width:49%;
511 511 clear:none;
512 512 float:left;
513 513 margin:0 0 10px;
514 514 }
515 515
516 516 #content div.box-right {
517 517 width:49%;
518 518 clear:none;
519 519 float:right;
520 520 margin:0 0 10px;
521 521 }
522 522
523 523 #content div.box div.title {
524 524 clear:both;
525 525 overflow:hidden;
526 526 background:#369 url("../images/header_inner.png") repeat-x;
527 527 margin:0 0 20px;
528 528 padding:0;
529 529 }
530 530
531 531 #content div.box div.title h5 {
532 532 float:left;
533 533 border:none;
534 534 color:#fff;
535 535 text-transform:uppercase;
536 536 margin:0;
537 537 padding:11px 0 11px 10px;
538 538 }
539 539
540 540 #content div.box div.title ul.links li {
541 541 list-style:none;
542 542 float:left;
543 543 margin:0;
544 544 padding:0;
545 545 }
546 546
547 547 #content div.box div.title ul.links li a {
548 548 height:1%;
549 549 display:block;
550 550 float:left;
551 551 border-left:1px solid #316293;
552 552 color:#fff;
553 553 font-size:11px;
554 554 font-weight:700;
555 555 text-decoration:none;
556 556 margin:0;
557 557 padding:13px 16px 12px;
558 558 }
559 559
560 560 #content div.box h1,#content div.box h2,#content div.box h3,#content div.box h4,#content div.box h5,#content div.box h6 {
561 561 clear:both;
562 562 overflow:hidden;
563 563 border-bottom:1px solid #DDD;
564 564 margin:10px 20px;
565 565 padding:0 0 15px;
566 566 }
567 567
568 568 #content div.box p {
569 569 color:#5f5f5f;
570 570 font-size:12px;
571 571 line-height:150%;
572 572 margin:0 24px 10px;
573 573 padding:0;
574 574 }
575 575
576 576 #content div.box blockquote {
577 577 border-left:4px solid #DDD;
578 578 color:#5f5f5f;
579 579 font-size:11px;
580 580 line-height:150%;
581 581 margin:0 34px;
582 582 padding:0 0 0 14px;
583 583 }
584 584
585 585 #content div.box blockquote p {
586 586 margin:10px 0;
587 587 padding:0;
588 588 }
589 589
590 590 #content div.box dl {
591 591 margin:10px 24px;
592 592 }
593 593
594 594 #content div.box dt {
595 595 font-size:12px;
596 596 margin:0;
597 597 }
598 598
599 599 #content div.box dd {
600 600 font-size:12px;
601 601 margin:0;
602 602 padding:8px 0 8px 15px;
603 603 }
604 604
605 605 #content div.box li {
606 606 font-size:12px;
607 607 padding:4px 0;
608 608 }
609 609
610 610 #content div.box ul.disc,#content div.box ul.circle {
611 611 margin:10px 24px 10px 38px;
612 612 }
613 613
614 614 #content div.box ul.square {
615 615 margin:10px 24px 10px 40px;
616 616 }
617 617
618 618 #content div.box img.left {
619 619 border:none;
620 620 float:left;
621 621 margin:10px 10px 10px 0;
622 622 }
623 623
624 624 #content div.box img.right {
625 625 border:none;
626 626 float:right;
627 627 margin:10px 0 10px 10px;
628 628 }
629 629
630 630 #content div.box div.messages {
631 631 clear:both;
632 632 overflow:hidden;
633 633 margin:0 20px;
634 634 padding:0;
635 635 }
636 636
637 637 #content div.box div.message {
638 638 clear:both;
639 639 overflow:hidden;
640 640 margin:0;
641 641 padding:10px 0;
642 642 }
643 643
644 644 #content div.box div.message a {
645 645 font-weight:400 !important;
646 646 }
647 647
648 648 #content div.box div.message div.image {
649 649 float:left;
650 650 margin:9px 0 0 5px;
651 651 padding:6px;
652 652 }
653 653
654 654 #content div.box div.message div.image img {
655 655 vertical-align:middle;
656 656 margin:0;
657 657 }
658 658
659 659 #content div.box div.message div.text {
660 660 float:left;
661 661 margin:0;
662 662 padding:9px 6px;
663 663 }
664 664
665 665 #content div.box div.message div.dismiss a {
666 666 height:16px;
667 667 width:16px;
668 668 display:block;
669 669 background:url("../images/icons/cross.png") no-repeat;
670 670 margin:15px 14px 0 0;
671 671 padding:0;
672 672 }
673 673
674 674 #content div.box div.message div.text h1,#content div.box div.message div.text h2,#content div.box div.message div.text h3,#content div.box div.message div.text h4,#content div.box div.message div.text h5,#content div.box div.message div.text h6 {
675 675 border:none;
676 676 margin:0;
677 677 padding:0;
678 678 }
679 679
680 680 #content div.box div.message div.text span {
681 681 height:1%;
682 682 display:block;
683 683 margin:0;
684 684 padding:5px 0 0;
685 685 }
686 686
687 687 #content div.box div.message-error {
688 688 height:1%;
689 689 clear:both;
690 690 overflow:hidden;
691 691 background:#FBE3E4;
692 692 border:1px solid #FBC2C4;
693 693 color:#860006;
694 694 }
695 695
696 696 #content div.box div.message-error h6 {
697 697 color:#860006;
698 698 }
699 699
700 700 #content div.box div.message-warning {
701 701 height:1%;
702 702 clear:both;
703 703 overflow:hidden;
704 704 background:#FFF6BF;
705 705 border:1px solid #FFD324;
706 706 color:#5f5200;
707 707 }
708 708
709 709 #content div.box div.message-warning h6 {
710 710 color:#5f5200;
711 711 }
712 712
713 713 #content div.box div.message-notice {
714 714 height:1%;
715 715 clear:both;
716 716 overflow:hidden;
717 717 background:#8FBDE0;
718 718 border:1px solid #6BACDE;
719 719 color:#003863;
720 720 }
721 721
722 722 #content div.box div.message-notice h6 {
723 723 color:#003863;
724 724 }
725 725
726 726 #content div.box div.message-success {
727 727 height:1%;
728 728 clear:both;
729 729 overflow:hidden;
730 730 background:#E6EFC2;
731 731 border:1px solid #C6D880;
732 732 color:#4e6100;
733 733 }
734 734
735 735 #content div.box div.message-success h6 {
736 736 color:#4e6100;
737 737 }
738 738
739 739 #content div.box div.form div.fields div.field {
740 740 height:1%;
741 741 border-bottom:1px solid #DDD;
742 742 clear:both;
743 743 margin:0;
744 744 padding:10px 0;
745 745 }
746 746
747 747 #content div.box div.form div.fields div.field-first {
748 748 padding:0 0 10px;
749 749 }
750 750
751 751 #content div.box div.form div.fields div.field-noborder {
752 752 border-bottom:0 !important;
753 753 }
754 754
755 755 #content div.box div.form div.fields div.field span.error-message {
756 756 height:1%;
757 757 display:inline-block;
758 758 color:red;
759 759 margin:8px 0 0 4px;
760 760 padding:0;
761 761 }
762 762
763 763 #content div.box div.form div.fields div.field span.success {
764 764 height:1%;
765 765 display:block;
766 766 color:#316309;
767 767 margin:8px 0 0;
768 768 padding:0;
769 769 }
770 770
771 771 #content div.box div.form div.fields div.field div.label {
772 772 left:80px;
773 773 width:auto;
774 774 position:absolute;
775 775 margin:0;
776 776 padding:8px 0 0 5px;
777 777 }
778 778
779 779 #content div.box-left div.form div.fields div.field div.label,#content div.box-right div.form div.fields div.field div.label {
780 780 clear:both;
781 781 overflow:hidden;
782 782 left:0;
783 783 width:auto;
784 784 position:relative;
785 785 margin:0;
786 786 padding:0 0 8px;
787 787 }
788 788
789 789 #content div.box div.form div.fields div.field div.label-select {
790 790 padding:2px 0 0 5px;
791 791 }
792 792
793 793 #content div.box-left div.form div.fields div.field div.label-select,#content div.box-right div.form div.fields div.field div.label-select {
794 794 padding:0 0 8px;
795 795 }
796 796
797 797 #content div.box-left div.form div.fields div.field div.label-textarea,#content div.box-right div.form div.fields div.field div.label-textarea {
798 798 padding:0 0 8px !important;
799 799 }
800 800
801 801 #content div.box div.form div.fields div.field div.label label {
802 802 color:#393939;
803 803 font-weight:700;
804 804 }
805 805
806 806 #content div.box div.form div.fields div.field div.input {
807 807 margin:0 0 0 200px;
808 808 }
809 809
810 810 #content div.box-left div.form div.fields div.field div.input,#content div.box-right div.form div.fields div.field div.input {
811 811 clear:both;
812 812 overflow:hidden;
813 813 border-top:1px solid #b3b3b3;
814 814 border-left:1px solid #b3b3b3;
815 815 border-right:1px solid #eaeaea;
816 816 border-bottom:1px solid #eaeaea;
817 817 margin:0;
818 818 padding:7px 7px 6px;
819 819 }
820 820
821 821 #content div.box div.form div.fields div.field div.input input {
822 822 background:#FFF;
823 823 border-top:1px solid #b3b3b3;
824 824 border-left:1px solid #b3b3b3;
825 825 border-right:1px solid #eaeaea;
826 826 border-bottom:1px solid #eaeaea;
827 827 color:#000;
828 828 font-family:Lucida Grande, Verdana, Lucida Sans Regular, Lucida Sans Unicode, Arial, sans-serif;
829 829 font-size:11px;
830 830 margin:0;
831 831 padding:7px 7px 6px;
832 832 }
833 833
834 834 #content div.box-left div.form div.fields div.field div.input input,#content div.box-right div.form div.fields div.field div.input input {
835 835 width:100%;
836 836 border:none;
837 837 padding:0;
838 838 }
839 839
840 840 #content div.box div.form div.fields div.field div.input input.small {
841 841 width:30%;
842 842 }
843 843
844 844 #content div.box div.form div.fields div.field div.input input.medium {
845 845 width:55%;
846 846 }
847 847
848 848 #content div.box div.form div.fields div.field div.input input.large {
849 849 width:85%;
850 850 }
851 851
852 852 #content div.box div.form div.fields div.field div.input input.date {
853 853 width:177px;
854 854 }
855 855
856 856 #content div.box div.form div.fields div.field div.input input.button {
857 857 background:#D4D0C8;
858 858 border-top:1px solid #FFF;
859 859 border-left:1px solid #FFF;
860 860 border-right:1px solid #404040;
861 861 border-bottom:1px solid #404040;
862 862 color:#000;
863 863 margin:0;
864 864 padding:4px 8px;
865 865 }
866 866
867 867 #content div.box div.form div.fields div.field div.input a.ui-input-file {
868 868 width:28px;
869 869 height:28px;
870 870 display:inline;
871 871 position:absolute;
872 872 overflow:hidden;
873 873 cursor:pointer;
874 874 background:#e5e3e3 url("../images/button_browse.png") no-repeat;
875 875 border:none;
876 876 text-decoration:none;
877 877 margin:0 0 0 6px;
878 878 padding:0;
879 879 }
880 880
881 881 #content div.box div.form div.fields div.field div.textarea {
882 882 border-top:1px solid #b3b3b3;
883 883 border-left:1px solid #b3b3b3;
884 884 border-right:1px solid #eaeaea;
885 885 border-bottom:1px solid #eaeaea;
886 886 margin:0 0 0 200px;
887 887 padding:10px;
888 888 }
889 889
890 890 #content div.box div.form div.fields div.field div.textarea-editor {
891 891 border:1px solid #ddd;
892 892 padding:0;
893 893 }
894 894
895 895 #content div.box div.form div.fields div.field div.textarea textarea {
896 896 width:100%;
897 897 height:220px;
898 898 overflow:hidden;
899 899 background:#FFF;
900 900 color:#000;
901 901 font-family:Lucida Grande, Verdana, Lucida Sans Regular, Lucida Sans Unicode, Arial, sans-serif;
902 902 font-size:11px;
903 903 outline:none;
904 904 border-width:0;
905 905 margin:0;
906 906 padding:0;
907 907 }
908 908
909 909 #content div.box-left div.form div.fields div.field div.textarea textarea,#content div.box-right div.form div.fields div.field div.textarea textarea {
910 910 width:100%;
911 911 height:100px;
912 912 }
913 913
914 914 #content div.box div.form div.fields div.field div.textarea table {
915 915 width:100%;
916 916 border:none;
917 917 margin:0;
918 918 padding:0;
919 919 }
920 920
921 921 #content div.box div.form div.fields div.field div.textarea table td {
922 922 background:#DDD;
923 923 border:none;
924 924 padding:0;
925 925 }
926 926
927 927 #content div.box div.form div.fields div.field div.textarea table td table {
928 928 width:auto;
929 929 border:none;
930 930 margin:0;
931 931 padding:0;
932 932 }
933 933
934 934 #content div.box div.form div.fields div.field div.textarea table td table td {
935 935 font-family:Lucida Grande, Verdana, Lucida Sans Regular, Lucida Sans Unicode, Arial, sans-serif;
936 936 font-size:11px;
937 937 padding:5px 5px 5px 0;
938 938 }
939 939
940 940 #content div.box div.form div.fields div.field div.textarea table td table td a.mceButtonActive {
941 941 background:#b1b1b1;
942 942 }
943 943
944 944 #content div.box div.form div.fields div.field div.select a.ui-selectmenu {
945 945 color:#565656;
946 946 text-decoration:none;
947 947 }
948 948
949 949 #content div.box div.form div.fields div.field input[type=text]:focus,#content div.box div.form div.fields div.field input[type=password]:focus,#content div.box div.form div.fields div.field input[type=file]:focus,#content div.box div.form div.fields div.field textarea:focus,#content div.box div.form div.fields div.field select:focus {
950 950 background:#f6f6f6;
951 951 border-color:#666;
952 952 }
953 953
954 954 div.form div.fields div.field div.button {
955 955 margin:0;
956 956 padding:0 0 0 8px;
957 957 }
958 958
959 959 div.form div.fields div.field div.highlight .ui-state-default {
960 960 background:#4e85bb url("../images/button_highlight.png") repeat-x;
961 961 border-top:1px solid #5c91a4;
962 962 border-left:1px solid #2a6f89;
963 963 border-right:1px solid #2b7089;
964 964 border-bottom:1px solid #1a6480;
965 965 color:#FFF;
966 966 margin:0;
967 967 padding:6px 12px;
968 968 }
969 969
970 970 div.form div.fields div.field div.highlight .ui-state-hover {
971 971 background:#46a0c1 url("../images/button_highlight_selected.png") repeat-x;
972 972 border-top:1px solid #78acbf;
973 973 border-left:1px solid #34819e;
974 974 border-right:1px solid #35829f;
975 975 border-bottom:1px solid #257897;
976 976 color:#FFF;
977 977 margin:0;
978 978 padding:6px 12px;
979 979 }
980 980
981 981 #content div.box div.form div.fields div.buttons div.highlight input.ui-state-default {
982 982 background:#4e85bb url("../../images/button_highlight.png") repeat-x;
983 983 border-top:1px solid #5c91a4;
984 984 border-left:1px solid #2a6f89;
985 985 border-right:1px solid #2b7089;
986 986 border-bottom:1px solid #1a6480;
987 987 color:#fff;
988 988 margin:0;
989 989 padding:6px 12px;
990 990 }
991 991
992 992 #content div.box div.form div.fields div.buttons div.highlight input.ui-state-hover {
993 993 background:#46a0c1 url("../../images/button_highlight_selected.png") repeat-x;
994 994 border-top:1px solid #78acbf;
995 995 border-left:1px solid #34819e;
996 996 border-right:1px solid #35829f;
997 997 border-bottom:1px solid #257897;
998 998 color:#fff;
999 999 margin:0;
1000 1000 padding:6px 12px;
1001 1001 }
1002 1002
1003 1003 #content div.box table {
1004 1004 width:100%;
1005 1005 border-collapse:collapse;
1006 1006 margin:0;
1007 1007 padding:0;
1008 1008 }
1009 1009
1010 1010 #content div.box table th {
1011 1011 background:#eee;
1012 1012 border-bottom:1px solid #ddd;
1013 1013 padding:5px 0px 5px 5px;
1014 1014 }
1015 1015
1016 1016 #content div.box table th.left {
1017 1017 text-align:left;
1018 1018 }
1019 1019
1020 1020 #content div.box table th.right {
1021 1021 text-align:right;
1022 1022 }
1023 1023
1024 1024 #content div.box table th.center {
1025 1025 text-align:center;
1026 1026 }
1027 1027
1028 1028 #content div.box table th.selected {
1029 1029 vertical-align:middle;
1030 1030 padding:0;
1031 1031 }
1032 1032
1033 1033 #content div.box table td {
1034 1034 background:#fff;
1035 1035 border-bottom:1px solid #cdcdcd;
1036 1036 vertical-align:middle;
1037 1037 padding:5px;
1038 1038 }
1039 1039
1040 1040 #content div.box table tr.selected td {
1041 1041 background:#FFC;
1042 1042 }
1043 1043
1044 1044 #content div.box table td.selected {
1045 1045 width:3%;
1046 1046 text-align:center;
1047 1047 vertical-align:middle;
1048 1048 padding:0;
1049 1049 }
1050 1050
1051 1051 #content div.box table td.action {
1052 1052 width:45%;
1053 1053 text-align:left;
1054 1054 }
1055 1055
1056 1056 #content div.box table td.date {
1057 1057 width:33%;
1058 1058 text-align:center;
1059 1059 }
1060 1060
1061 1061 #content div.box div.action {
1062 1062 float:right;
1063 1063 background:#FFF;
1064 1064 text-align:right;
1065 1065 margin:10px 0 0;
1066 1066 padding:0;
1067 1067 }
1068 1068
1069 1069 #content div.box div.action select {
1070 1070 font-family:Lucida Grande, Verdana, Lucida Sans Regular, Lucida Sans Unicode, Arial, sans-serif;
1071 1071 font-size:11px;
1072 1072 margin:0;
1073 1073 }
1074 1074
1075 1075 #content div.box div.action .ui-selectmenu {
1076 1076 margin:0;
1077 1077 padding:0;
1078 1078 }
1079 1079
1080 1080 #content div.box div.pagination {
1081 1081 height:1%;
1082 1082 clear:both;
1083 1083 overflow:hidden;
1084 1084 margin:10px 0 0;
1085 1085 padding:0;
1086 1086 }
1087 1087
1088 1088 #content div.box div.pagination ul.pager {
1089 1089 float:right;
1090 1090 text-align:right;
1091 1091 margin:0;
1092 1092 padding:0;
1093 1093 }
1094 1094
1095 1095 #content div.box div.pagination ul.pager li {
1096 1096 height:1%;
1097 1097 float:left;
1098 1098 list-style:none;
1099 1099 background:#ebebeb url("../images/pager.png") repeat-x;
1100 1100 border-top:1px solid #dedede;
1101 1101 border-left:1px solid #cfcfcf;
1102 1102 border-right:1px solid #c4c4c4;
1103 1103 border-bottom:1px solid #c4c4c4;
1104 1104 color:#4A4A4A;
1105 1105 font-weight:700;
1106 1106 margin:0 0 0 4px;
1107 1107 padding:0;
1108 1108 }
1109 1109
1110 1110 #content div.box div.pagination ul.pager li.separator {
1111 1111 padding:6px;
1112 1112 }
1113 1113
1114 1114 #content div.box div.pagination ul.pager li.current {
1115 1115 background:#b4b4b4 url("../images/pager_selected.png") repeat-x;
1116 1116 border-top:1px solid #ccc;
1117 1117 border-left:1px solid #bebebe;
1118 1118 border-right:1px solid #b1b1b1;
1119 1119 border-bottom:1px solid #afafaf;
1120 1120 color:#515151;
1121 1121 padding:6px;
1122 1122 }
1123 1123
1124 1124 #content div.box div.pagination ul.pager li a {
1125 1125 height:1%;
1126 1126 display:block;
1127 1127 float:left;
1128 1128 color:#515151;
1129 1129 text-decoration:none;
1130 1130 margin:0;
1131 1131 padding:6px;
1132 1132 }
1133 1133
1134 1134 #content div.box div.pagination ul.pager li a:hover,#content div.box div.pagination ul.pager li a:active {
1135 1135 background:#b4b4b4 url("../images/pager_selected.png") repeat-x;
1136 1136 border-top:1px solid #ccc;
1137 1137 border-left:1px solid #bebebe;
1138 1138 border-right:1px solid #b1b1b1;
1139 1139 border-bottom:1px solid #afafaf;
1140 1140 margin:-1px;
1141 1141 }
1142 1142
1143 1143 #content div.box div.pagination-wh {
1144 1144 height:1%;
1145 1145 clear:both;
1146 1146 overflow:hidden;
1147 1147 text-align:right;
1148 1148 margin:10px 0 0;
1149 1149 padding:0;
1150 1150 }
1151 1151
1152 1152 #content div.box div.pagination-right {
1153 1153 float:right;
1154 1154 }
1155 1155
1156 1156 #content div.box div.pagination-wh a,#content div.box div.pagination-wh span.pager_dotdot {
1157 1157 height:1%;
1158 1158 float:left;
1159 1159 background:#ebebeb url("../images/pager.png") repeat-x;
1160 1160 border-top:1px solid #dedede;
1161 1161 border-left:1px solid #cfcfcf;
1162 1162 border-right:1px solid #c4c4c4;
1163 1163 border-bottom:1px solid #c4c4c4;
1164 1164 color:#4A4A4A;
1165 1165 font-weight:700;
1166 1166 margin:0 0 0 4px;
1167 1167 padding:6px;
1168 1168 }
1169 1169
1170 1170 #content div.box div.pagination-wh span.pager_curpage {
1171 1171 height:1%;
1172 1172 float:left;
1173 1173 background:#b4b4b4 url("../images/pager_selected.png") repeat-x;
1174 1174 border-top:1px solid #ccc;
1175 1175 border-left:1px solid #bebebe;
1176 1176 border-right:1px solid #b1b1b1;
1177 1177 border-bottom:1px solid #afafaf;
1178 1178 color:#515151;
1179 1179 font-weight:700;
1180 1180 margin:0 0 0 4px;
1181 1181 padding:6px;
1182 1182 }
1183 1183
1184 1184 #content div.box div.pagination-wh a:hover,#content div.box div.pagination-wh a:active {
1185 1185 background:#b4b4b4 url("../images/pager_selected.png") repeat-x;
1186 1186 border-top:1px solid #ccc;
1187 1187 border-left:1px solid #bebebe;
1188 1188 border-right:1px solid #b1b1b1;
1189 1189 border-bottom:1px solid #afafaf;
1190 1190 text-decoration:none;
1191 1191 }
1192 1192
1193 1193 #content div.box div.traffic div.legend {
1194 1194 clear:both;
1195 1195 overflow:hidden;
1196 1196 border-bottom:1px solid #ddd;
1197 1197 margin:0 0 10px;
1198 1198 padding:0 0 10px;
1199 1199 }
1200 1200
1201 1201 #content div.box div.traffic div.legend h6 {
1202 1202 float:left;
1203 1203 border:none;
1204 1204 margin:0;
1205 1205 padding:0;
1206 1206 }
1207 1207
1208 1208 #content div.box div.traffic div.legend li {
1209 1209 list-style:none;
1210 1210 float:left;
1211 1211 font-size:11px;
1212 1212 margin:0;
1213 1213 padding:0 8px 0 4px;
1214 1214 }
1215 1215
1216 1216 #content div.box div.traffic div.legend li.visits {
1217 1217 border-left:12px solid #edc240;
1218 1218 }
1219 1219
1220 1220 #content div.box div.traffic div.legend li.pageviews {
1221 1221 border-left:12px solid #afd8f8;
1222 1222 }
1223 1223
1224 1224 #content div.box div.traffic table {
1225 1225 width:auto;
1226 1226 }
1227 1227
1228 1228 #content div.box div.traffic table td {
1229 1229 background:transparent;
1230 1230 border:none;
1231 1231 padding:2px 3px 3px;
1232 1232 }
1233 1233
1234 1234 #content div.box div.traffic table td.legendLabel {
1235 1235 padding:0 3px 2px;
1236 1236 }
1237 1237
1238 1238 #footer {
1239 1239 clear:both;
1240 1240 overflow:hidden;
1241 1241 text-align:right;
1242 1242 margin:0;
1243 1243 padding:0 30px 4px;
1244 1244 margin:-10px 0 0;
1245 1245 }
1246 1246
1247 1247 #footer div#footer-inner {
1248 1248 background:url("../images/header_inner.png") repeat-x scroll 0 0 #003367;
1249 1249 border-top:2px solid #FFFFFF;
1250 1250 }
1251 1251
1252 1252 #footer div#footer-inner p {
1253 1253 padding:15px 25px 15px 0;
1254 1254 color:#FFF;
1255 1255 font-weight:700;
1256 1256 }
1257 1257 #footer div#footer-inner .footer-link {
1258 1258 float:left;
1259 1259 padding-left:10px;
1260 1260 }
1261 1261 #footer div#footer-inner .footer-link a {
1262 1262 color:#FFF;
1263 1263 }
1264 1264
1265 1265 #login div.title {
1266 1266 width:420px;
1267 1267 clear:both;
1268 1268 overflow:hidden;
1269 1269 position:relative;
1270 1270 background:#003367 url("../../images/header_inner.png") repeat-x;
1271 1271 margin:0 auto;
1272 1272 padding:0;
1273 1273 }
1274 1274
1275 1275 #login div.inner {
1276 1276 width:380px;
1277 1277 background:#FFF url("../images/login.png") no-repeat top left;
1278 1278 border-top:none;
1279 1279 border-bottom:none;
1280 1280 margin:0 auto;
1281 1281 padding:20px;
1282 1282 }
1283 1283
1284 1284 #login div.form div.fields div.field div.label {
1285 1285 width:173px;
1286 1286 float:left;
1287 1287 text-align:right;
1288 1288 margin:2px 10px 0 0;
1289 1289 padding:5px 0 0 5px;
1290 1290 }
1291 1291
1292 1292 #login div.form div.fields div.field div.input input {
1293 1293 width:176px;
1294 1294 background:#FFF;
1295 1295 border-top:1px solid #b3b3b3;
1296 1296 border-left:1px solid #b3b3b3;
1297 1297 border-right:1px solid #eaeaea;
1298 1298 border-bottom:1px solid #eaeaea;
1299 1299 color:#000;
1300 1300 font-family:Lucida Grande, Verdana, Lucida Sans Regular, Lucida Sans Unicode, Arial, sans-serif;
1301 1301 font-size:11px;
1302 1302 margin:0;
1303 1303 padding:7px 7px 6px;
1304 1304 }
1305 1305
1306 1306 #login div.form div.fields div.buttons {
1307 1307 clear:both;
1308 1308 overflow:hidden;
1309 1309 border-top:1px solid #DDD;
1310 1310 text-align:right;
1311 1311 margin:0;
1312 1312 padding:10px 0 0;
1313 1313 }
1314 1314
1315 1315 #login div.form div.links {
1316 1316 clear:both;
1317 1317 overflow:hidden;
1318 1318 margin:10px 0 0;
1319 1319 padding:0 0 2px;
1320 1320 }
1321 1321
1322 1322 #register div.title {
1323 1323 width:420px;
1324 1324 clear:both;
1325 1325 overflow:hidden;
1326 1326 position:relative;
1327 1327 background:#003367 url("../images/header_inner.png") repeat-x;
1328 1328 margin:0 auto;
1329 1329 padding:0;
1330 1330 }
1331 1331
1332 1332 #register div.inner {
1333 1333 width:380px;
1334 1334 background:#FFF;
1335 1335 border-top:none;
1336 1336 border-bottom:none;
1337 1337 margin:0 auto;
1338 1338 padding:20px;
1339 1339 }
1340 1340
1341 1341 #register div.form div.fields div.field div.label {
1342 1342 width:100px;
1343 1343 float:left;
1344 1344 text-align:right;
1345 1345 margin:2px 10px 0 0;
1346 1346 padding:5px 0 0 5px;
1347 1347 }
1348 1348
1349 1349 #register div.form div.fields div.field div.input input {
1350 1350 width:245px;
1351 1351 background:#FFF;
1352 1352 border-top:1px solid #b3b3b3;
1353 1353 border-left:1px solid #b3b3b3;
1354 1354 border-right:1px solid #eaeaea;
1355 1355 border-bottom:1px solid #eaeaea;
1356 1356 color:#000;
1357 1357 font-family:Lucida Grande, Verdana, Lucida Sans Regular, Lucida Sans Unicode, Arial, sans-serif;
1358 1358 font-size:11px;
1359 1359 margin:0;
1360 1360 padding:7px 7px 6px;
1361 1361 }
1362 1362
1363 1363 #register div.form div.fields div.buttons {
1364 1364 clear:both;
1365 1365 overflow:hidden;
1366 1366 border-top:1px solid #DDD;
1367 1367 text-align:left;
1368 1368 margin:0;
1369 1369 padding:10px 0 0 114px;
1370 1370 }
1371 1371
1372 1372 #register div.form div.fields div.buttons div.highlight input.ui-state-default {
1373 1373 background:url("../images/button_highlight.png") repeat-x scroll 0 0 #4E85BB;
1374 1374 color:#FFF;
1375 1375 border-color:#5C91A4 #2B7089 #1A6480 #2A6F89;
1376 1376 border-style:solid;
1377 1377 border-width:1px;
1378 1378 }
1379 1379
1380 1380 #register div.form div.activation_msg {
1381 1381 padding-top:4px;
1382 1382 padding-bottom:4px;
1383 1383 }
1384 1384
1385 1385 .trending_language_tbl,.trending_language_tbl td {
1386 1386 border:0 !important;
1387 1387 margin:0 !important;
1388 1388 padding:0 !important;
1389 1389 }
1390 1390
1391 1391 .trending_language {
1392 1392 background-color:#003367;
1393 1393 color:#FFF;
1394 1394 display:block;
1395 1395 min-width:20px;
1396 1396 max-width:400px;
1397 1397 text-decoration:none;
1398 1398 height:12px;
1399 1399 margin-bottom:4px;
1400 1400 margin-left:5px;
1401 1401 white-space:pre;
1402 1402 padding:3px;
1403 1403 }
1404 1404
1405 1405 h3.files_location {
1406 1406 font-size:1.8em;
1407 1407 font-weight:700;
1408 1408 border-bottom:none !important;
1409 1409 margin:10px 0 !important;
1410 1410 }
1411 1411
1412 1412 #files_data dl dt {
1413 1413 float:left;
1414 1414 width:115px;
1415 1415 margin:0 !important;
1416 1416 padding:5px;
1417 1417 }
1418 1418
1419 1419 #files_data dl dd {
1420 1420 margin:0 !important;
1421 1421 padding:5px !important;
1422 1422 }
1423 1423
1424 1424 #changeset_content {
1425 1425 border:1px solid #CCC;
1426 1426 padding:5px;
1427 1427 }
1428 1428
1429 1429 #changeset_content .container {
1430 1430 min-height:120px;
1431 1431 font-size:1.2em;
1432 1432 overflow:hidden;
1433 1433 }
1434 1434
1435 1435 #changeset_content .container .right {
1436 1436 float:right;
1437 1437 width:25%;
1438 1438 text-align:right;
1439 1439 }
1440 1440
1441 1441 #changeset_content .container .left .message {
1442 1442 font-style:italic;
1443 1443 color:#556CB5;
1444 1444 white-space:pre-wrap;
1445 1445 }
1446 1446
1447 1447 .cs_files .cs_added {
1448 1448 background:url("../images/icons/page_white_add.png") no-repeat scroll 3px;
1449 1449 height:16px;
1450 1450 padding-left:20px;
1451 1451 margin-top:7px;
1452 1452 text-align:left;
1453 1453 }
1454 1454
1455 1455 .cs_files .cs_changed {
1456 1456 background:url("../images/icons/page_white_edit.png") no-repeat scroll 3px;
1457 1457 height:16px;
1458 1458 padding-left:20px;
1459 1459 margin-top:7px;
1460 1460 text-align:left;
1461 1461 }
1462 1462
1463 1463 .cs_files .cs_removed {
1464 1464 background:url("../images/icons/page_white_delete.png") no-repeat scroll 3px;
1465 1465 height:16px;
1466 1466 padding-left:20px;
1467 1467 margin-top:7px;
1468 1468 text-align:left;
1469 1469 }
1470 1470
1471 1471 #graph {
1472 1472 overflow:hidden;
1473 1473 }
1474 1474
1475 1475 #graph_nodes {
1476 1476 width:160px;
1477 1477 float:left;
1478 1478 margin-left:-50px;
1479 1479 margin-top:5px;
1480 1480 }
1481 1481
1482 1482 #graph_content {
1483 1483 width:800px;
1484 1484 float:left;
1485 1485 }
1486 1486
1487 1487 #graph_content .container_header {
1488 1488 border:1px solid #CCC;
1489 1489 padding:10px;
1490 1490 }
1491 1491
1492 1492 #graph_content .container {
1493 1493 border-bottom:1px solid #CCC;
1494 1494 border-left:1px solid #CCC;
1495 1495 border-right:1px solid #CCC;
1496 1496 min-height:80px;
1497 1497 overflow:hidden;
1498 1498 font-size:1.2em;
1499 1499 }
1500 1500
1501 1501 #graph_content .container .right {
1502 1502 float:right;
1503 1503 width:28%;
1504 1504 text-align:right;
1505 1505 padding-bottom:5px;
1506 1506 }
1507 1507
1508 1508 #graph_content .container .left .date {
1509 1509 font-weight:700;
1510 1510 padding-bottom:5px;
1511 1511 }
1512 1512
1513 1513 #graph_content .container .left .message {
1514 1514 font-size:100%;
1515 1515 padding-top:3px;
1516 1516 white-space:pre-wrap;
1517 1517 }
1518 1518
1519 1519 .right div {
1520 1520 clear:both;
1521 1521 }
1522 1522
1523 1523 .right .changes .added,.changed,.removed {
1524 1524 border:1px solid #DDD;
1525 1525 display:block;
1526 1526 float:right;
1527 1527 text-align:center;
1528 1528 min-width:15px;
1529 1529 }
1530 1530
1531 1531 .right .changes .added {
1532 1532 background:#BFB;
1533 1533 }
1534 1534
1535 1535 .right .changes .changed {
1536 1536 background:#FD8;
1537 1537 }
1538 1538
1539 1539 .right .changes .removed {
1540 1540 background:#F88;
1541 1541 }
1542 1542
1543 1543 .right .merge {
1544 1544 vertical-align:top;
1545 1545 font-size:60%;
1546 1546 font-weight:700;
1547 1547 }
1548 1548
1549 1549 .right .parent {
1550 1550 font-size:90%;
1551 1551 font-family:monospace;
1552 1552 }
1553 1553
1554 1554 .right .logtags .branchtag {
1555 1555 background:#FFF url("../images/icons/arrow_branch.png") no-repeat right 6px;
1556 1556 display:block;
1557 1557 padding:8px 16px 0 0;
1558 1558 }
1559 1559
1560 1560 .right .logtags .tagtag {
1561 1561 background:#FFF url("../images/icons/tag_blue.png") no-repeat right 6px;
1562 1562 display:block;
1563 1563 padding:6px 18px 0 0;
1564 1564 }
1565 1565
1566 1566 div.browserblock {
1567 1567 overflow:hidden;
1568 1568 border:1px solid #ccc;
1569 1569 background:#f8f8f8;
1570 1570 font-size:100%;
1571 1571 line-height:125%;
1572 1572 padding:0;
1573 1573 }
1574 1574
1575 1575 div.browserblock .browser-header {
1576 1576 border-bottom:1px solid #CCC;
1577 1577 background:#FFF;
1578 1578 color:blue;
1579 1579 padding:10px 0;
1580 1580 }
1581 1581
1582 1582 div.browserblock .browser-header span {
1583 1583 margin-left:25px;
1584 1584 font-weight:700;
1585 1585 }
1586 1586
1587 1587 div.browserblock .browser-body {
1588 1588 background:#EEE;
1589 1589 }
1590 1590
1591 1591 table.code-browser {
1592 1592 border-collapse:collapse;
1593 1593 width:100%;
1594 1594 }
1595 1595
1596 1596 table.code-browser tr {
1597 1597 margin:3px;
1598 1598 }
1599 1599
1600 1600 table.code-browser thead th {
1601 1601 background-color:#EEE;
1602 1602 height:20px;
1603 1603 font-size:1.1em;
1604 1604 font-weight:700;
1605 1605 text-align:left;
1606 1606 padding-left:10px;
1607 1607 }
1608 1608
1609 1609 table.code-browser tbody td {
1610 1610 padding-left:10px;
1611 1611 height:20px;
1612 1612 }
1613 1613
1614 1614 table.code-browser .browser-file {
1615 1615 background:url("../images/icons/document_16.png") no-repeat scroll 3px;
1616 1616 height:16px;
1617 1617 padding-left:20px;
1618 1618 text-align:left;
1619 1619 }
1620 1620
1621 1621 table.code-browser .browser-dir {
1622 1622 background:url("../images/icons/folder_16.png") no-repeat scroll 3px;
1623 1623 height:16px;
1624 1624 padding-left:20px;
1625 1625 text-align:left;
1626 1626 }
1627 1627
1628 1628 .box .search {
1629 1629 clear:both;
1630 1630 overflow:hidden;
1631 1631 margin:0;
1632 1632 padding:0 20px 10px;
1633 1633 }
1634 1634
1635 1635 .box .search div.search_path {
1636 1636 background:none repeat scroll 0 0 #EEE;
1637 1637 border:1px solid #CCC;
1638 1638 color:blue;
1639 1639 margin-bottom:10px;
1640 1640 padding:10px 0;
1641 1641 }
1642 1642
1643 1643 .box .search div.search_path div.link {
1644 1644 font-weight:700;
1645 1645 margin-left:25px;
1646 1646 }
1647 1647
1648 1648 .box .search div.search_path div.link a {
1649 1649 color:#003367;
1650 1650 cursor:pointer;
1651 1651 text-decoration:none;
1652 1652 }
1653 1653
1654 1654 #path_unlock {
1655 1655 color:red;
1656 1656 font-size:1.2em;
1657 1657 padding-left:4px;
1658 1658 }
1659 1659
1660 1660 .info_box * {
1661 1661 background:url("../../images/pager.png") repeat-x scroll 0 0 #EBEBEB;
1662 1662 color:#4A4A4A;
1663 1663 font-weight:700;
1664 1664 height:1%;
1665 1665 display:inline;
1666 1666 border-color:#DEDEDE #C4C4C4 #C4C4C4 #CFCFCF;
1667 1667 border-style:solid;
1668 1668 border-width:1px;
1669 1669 padding:4px 6px;
1670 1670 }
1671 1671
1672 1672 .info_box span {
1673 1673 margin-left:3px;
1674 1674 margin-right:3px;
1675 1675 }
1676 1676
1677 1677 .info_box input#at_rev {
1678 1678 text-align:center;
1679 1679 padding:5px 3px 3px 2px;
1680 1680 }
1681 1681
1682 1682 .info_box input#view {
1683 1683 text-align:center;
1684 1684 padding:4px 3px 2px 2px;
1685 1685 }
1686 1686
1687 1687 .yui-overlay,.yui-panel-container {
1688 1688 visibility:hidden;
1689 1689 position:absolute;
1690 1690 z-index:2;
1691 1691 }
1692 1692
1693 1693 .yui-tt {
1694 1694 visibility:hidden;
1695 1695 position:absolute;
1696 1696 color:#666;
1697 1697 background-color:#FFF;
1698 1698 font-family:arial, helvetica, verdana, sans-serif;
1699 1699 border:2px solid #003367;
1700 1700 font:100% sans-serif;
1701 1701 width:auto;
1702 1702 opacity:1px;
1703 1703 padding:8px;
1704 white-space: pre;
1704 1705 }
1705 1706
1706 1707 .ac {
1707 1708 vertical-align:top;
1708 1709 }
1709 1710
1710 1711 .ac .yui-ac {
1711 1712 position:relative;
1712 1713 font-family:arial;
1713 1714 font-size:100%;
1714 1715 }
1715 1716
1716 1717 .ac .perm_ac {
1717 1718 width:15em;
1718 1719 }
1719 1720
1720 1721 .ac .yui-ac-input {
1721 1722 width:100%;
1722 1723 }
1723 1724
1724 1725 .ac .yui-ac-container {
1725 1726 position:absolute;
1726 1727 top:1.6em;
1727 1728 width:100%;
1728 1729 }
1729 1730
1730 1731 .ac .yui-ac-content {
1731 1732 position:absolute;
1732 1733 width:100%;
1733 1734 border:1px solid gray;
1734 1735 background:#fff;
1735 1736 overflow:hidden;
1736 1737 z-index:9050;
1737 1738 }
1738 1739
1739 1740 .ac .yui-ac-shadow {
1740 1741 position:absolute;
1741 1742 width:100%;
1742 1743 background:#000;
1743 1744 -moz-opacity:0.1px;
1744 1745 opacity:.10;
1745 1746 filter:alpha(opacity = 10);
1746 1747 z-index:9049;
1747 1748 margin:.3em;
1748 1749 }
1749 1750
1750 1751 .ac .yui-ac-content ul {
1751 1752 width:100%;
1752 1753 margin:0;
1753 1754 padding:0;
1754 1755 }
1755 1756
1756 1757 .ac .yui-ac-content li {
1757 1758 cursor:default;
1758 1759 white-space:nowrap;
1759 1760 margin:0;
1760 1761 padding:2px 5px;
1761 1762 }
1762 1763
1763 1764 .ac .yui-ac-content li.yui-ac-prehighlight {
1764 1765 background:#B3D4FF;
1765 1766 }
1766 1767
1767 1768 .ac .yui-ac-content li.yui-ac-highlight {
1768 1769 background:#556CB5;
1769 1770 color:#FFF;
1770 1771 }
1771 1772
1772 1773 .add_icon {
1773 1774 background:url("../images/icons/add.png") no-repeat scroll 3px;
1774 1775 height:16px;
1775 1776 padding-left:20px;
1776 1777 padding-top:1px;
1777 1778 text-align:left;
1778 1779 }
1779 1780
1780 1781 .edit_icon {
1781 1782 background:url("../images/icons/folder_edit.png") no-repeat scroll 3px;
1782 1783 height:16px;
1783 1784 padding-left:20px;
1784 1785 padding-top:1px;
1785 1786 text-align:left;
1786 1787 }
1787 1788
1788 1789 .delete_icon {
1789 1790 background:url("../images/icons/delete.png") no-repeat scroll 3px;
1790 1791 height:16px;
1791 1792 padding-left:20px;
1792 1793 padding-top:1px;
1793 1794 text-align:left;
1794 1795 }
1795 1796
1796 1797 .rss_icon {
1797 1798 background:url("../images/icons/rss_16.png") no-repeat scroll 3px;
1798 1799 height:16px;
1799 1800 padding-left:20px;
1800 1801 padding-top:1px;
1801 1802 text-align:left;
1802 1803 }
1803 1804
1804 1805 .atom_icon {
1805 1806 background:url("../images/icons/atom.png") no-repeat scroll 3px;
1806 1807 height:16px;
1807 1808 padding-left:20px;
1808 1809 padding-top:1px;
1809 1810 text-align:left;
1810 1811 }
1811 1812
1812 1813 .archive_icon {
1813 1814 background:url("../images/icons/compress.png") no-repeat scroll 3px;
1814 1815 height:16px;
1815 1816 padding-left:20px;
1816 1817 text-align:left;
1817 1818 padding-top:1px;
1818 1819 }
1819 1820
1820 1821 .action_button {
1821 1822 border:0;
1822 1823 display:block;
1823 1824 }
1824 1825
1825 1826 .action_button:hover {
1826 1827 border:0;
1827 1828 text-decoration:underline;
1828 1829 cursor:pointer;
1829 1830 }
1830 1831
1831 1832 #switch_repos {
1832 1833 position:absolute;
1833 1834 height:25px;
1834 1835 z-index:1;
1835 1836 }
1836 1837
1837 1838 #switch_repos select {
1838 1839 min-width:150px;
1839 1840 max-height:250px;
1840 1841 z-index:1;
1841 1842 }
1842 1843
1843 1844 .breadcrumbs {
1844 1845 border:medium none;
1845 1846 color:#FFF;
1846 1847 float:left;
1847 1848 text-transform:uppercase;
1848 1849 font-weight:700;
1849 1850 font-size:14px;
1850 1851 margin:0;
1851 1852 padding:11px 0 11px 10px;
1852 1853 }
1853 1854
1854 1855 .breadcrumbs a {
1855 1856 color:#FFF;
1856 1857 }
1857 1858
1858 1859 .flash_msg ul {
1859 1860 margin:0;
1860 1861 padding:0 0 10px;
1861 1862 }
1862 1863
1863 1864 .error_msg {
1864 1865 background-color:#FFCFCF;
1865 1866 background-image:url("../../images/icons/error_msg.png");
1866 1867 border:1px solid #FF9595;
1867 1868 color:#C30;
1868 1869 }
1869 1870
1870 1871 .warning_msg {
1871 1872 background-color:#FFFBCC;
1872 1873 background-image:url("../../images/icons/warning_msg.png");
1873 1874 border:1px solid #FFF35E;
1874 1875 color:#C69E00;
1875 1876 }
1876 1877
1877 1878 .success_msg {
1878 1879 background-color:#D5FFCF;
1879 1880 background-image:url("../../images/icons/success_msg.png");
1880 1881 border:1px solid #97FF88;
1881 1882 color:#090;
1882 1883 }
1883 1884
1884 1885 .notice_msg {
1885 1886 background-color:#DCE3FF;
1886 1887 background-image:url("../../images/icons/notice_msg.png");
1887 1888 border:1px solid #93A8FF;
1888 1889 color:#556CB5;
1889 1890 }
1890 1891
1891 1892 .success_msg,.error_msg,.notice_msg,.warning_msg {
1892 1893 background-position:10px center;
1893 1894 background-repeat:no-repeat;
1894 1895 font-size:12px;
1895 1896 font-weight:700;
1896 1897 min-height:14px;
1897 1898 line-height:14px;
1898 1899 margin-bottom:0;
1899 1900 margin-top:0;
1900 1901 display:block;
1901 1902 overflow:auto;
1902 1903 padding:6px 10px 6px 40px;
1903 1904 }
1904 1905
1905 1906 #msg_close {
1906 1907 background:transparent url("../../icons/cross_grey_small.png") no-repeat scroll 0 0;
1907 1908 cursor:pointer;
1908 1909 height:16px;
1909 1910 position:absolute;
1910 1911 right:5px;
1911 1912 top:5px;
1912 1913 width:16px;
1913 1914 }
1914 1915
1915 1916 div#legend_container table,div#legend_choices table {
1916 1917 width:auto !important;
1917 1918 }
1918 1919
1919 1920 table#permissions_manage {
1920 1921 width:0 !important;
1921 1922 }
1922 1923
1923 1924 table#permissions_manage span.private_repo_msg {
1924 1925 font-size:0.8em;
1925 1926 opacity:0.6px;
1926 1927 }
1927 1928
1928 1929 table#permissions_manage td.private_repo_msg {
1929 1930 font-size:0.8em;
1930 1931 }
1931 1932
1932 1933 table#permissions_manage tr#add_perm_input td {
1933 1934 vertical-align:middle;
1934 1935 }
1935 1936
1936 1937 div.gravatar {
1937 1938 background-color:#FFF;
1938 1939 border:1px solid #D0D0D0;
1939 1940 float:left;
1940 1941 margin-right:0.7em;
1941 1942 padding:2px 2px 0;
1942 1943 }
1943 1944
1944 1945 #header,#content,#footer {
1945 1946 min-width:1224px;
1946 1947 }
1947 1948
1948 1949 #content {
1949 1950 min-height:100%;
1950 1951 clear:both;
1951 1952 overflow:hidden;
1952 1953 padding:14px 30px;
1953 1954 }
1954 1955
1955 1956 #content div.box div.title div.search {
1956 1957 background:url("../../images/title_link.png") no-repeat top left;
1957 1958 border-left:1px solid #316293;
1958 1959 }
1959 1960
1960 1961 #content div.box div.title div.search div.input input {
1961 1962 border:1px solid #316293;
1962 1963 }
1963 1964
1964 1965 #content div.box div.title div.search div.button input.ui-state-default {
1965 1966 background:#4e85bb url("../../images/button_highlight.png") repeat-x;
1966 1967 border:1px solid #316293;
1967 1968 border-left:none;
1968 1969 color:#FFF;
1969 1970 }
1970 1971
1971 1972 #content div.box div.title div.search div.button input.ui-state-hover {
1972 1973 background:#46a0c1 url("../../images/button_highlight_selected.png") repeat-x;
1973 1974 border:1px solid #316293;
1974 1975 border-left:none;
1975 1976 color:#FFF;
1976 1977 }
1977 1978
1978 1979 #content div.box div.form div.fields div.field div.highlight .ui-state-default {
1979 1980 background:#4e85bb url("../../images/button_highlight.png") repeat-x;
1980 1981 border-top:1px solid #5c91a4;
1981 1982 border-left:1px solid #2a6f89;
1982 1983 border-right:1px solid #2b7089;
1983 1984 border-bottom:1px solid #1a6480;
1984 1985 color:#fff;
1985 1986 }
1986 1987
1987 1988 #content div.box div.form div.fields div.field div.highlight .ui-state-hover {
1988 1989 background:#46a0c1 url("../../images/button_highlight_selected.png") repeat-x;
1989 1990 border-top:1px solid #78acbf;
1990 1991 border-left:1px solid #34819e;
1991 1992 border-right:1px solid #35829f;
1992 1993 border-bottom:1px solid #257897;
1993 1994 color:#fff;
1994 1995 }
1995 1996
1996 1997 ins,div.options a:hover {
1997 1998 text-decoration:none;
1998 1999 }
1999 2000
2000 2001 img,#header #header-inner #quick li a:hover span.normal,#header #header-inner #quick li ul li.last,#content div.box div.form div.fields div.field div.textarea table td table td a,#clone_url {
2001 2002 border:none;
2002 2003 }
2003 2004
2004 2005 img.icon,.right .merge img {
2005 2006 vertical-align:bottom;
2006 2007 }
2007 2008
2008 2009 #header ul#logged-user,#content div.box div.title ul.links,#content div.box div.message div.dismiss,#content div.box div.traffic div.legend ul {
2009 2010 float:right;
2010 2011 margin:0;
2011 2012 padding:0;
2012 2013 }
2013 2014
2014 2015 #header #header-inner #home,#header #header-inner #logo,#content div.box ul.left,#content div.box ol.left,#content div.box div.pagination-left,div#commit_history,div#legend_data,div#legend_container,div#legend_choices {
2015 2016 float:left;
2016 2017 }
2017 2018
2018 2019 #header #header-inner #quick li:hover ul ul,#header #header-inner #quick li:hover ul ul ul,#header #header-inner #quick li:hover ul ul ul ul,#content #left #menu ul.closed,#content #left #menu li ul.collapsed,.yui-tt-shadow {
2019 2020 display:none;
2020 2021 }
2021 2022
2022 2023 #header #header-inner #quick li:hover ul,#header #header-inner #quick li li:hover ul,#header #header-inner #quick li li li:hover ul,#header #header-inner #quick li li li li:hover ul,#content #left #menu ul.opened,#content #left #menu li ul.expanded {
2023 2024 display:block;
2024 2025 }
2025 2026
2026 2027 #content div.box div.title ul.links li a:hover,#content div.box div.title ul.links li.ui-tabs-selected a {
2027 2028 background:url("../../images/title_tab_selected.png") no-repeat bottom center;
2028 2029 color:#bfe3ff;
2029 2030 }
2030 2031
2031 2032 #content div.box ol.lower-roman,#content div.box ol.upper-roman,#content div.box ol.lower-alpha,#content div.box ol.upper-alpha,#content div.box ol.decimal {
2032 2033 margin:10px 24px 10px 44px;
2033 2034 }
2034 2035
2035 2036 #content div.box div.form,#content div.box div.table,#content div.box div.traffic {
2036 2037 clear:both;
2037 2038 overflow:hidden;
2038 2039 margin:0;
2039 2040 padding:0 20px 10px;
2040 2041 }
2041 2042
2042 2043 #content div.box div.form div.fields,#login div.form,#login div.form div.fields,#register div.form,#register div.form div.fields {
2043 2044 clear:both;
2044 2045 overflow:hidden;
2045 2046 margin:0;
2046 2047 padding:0;
2047 2048 }
2048 2049
2049 2050 #content div.box div.form div.fields div.field div.label-checkbox,#content div.box div.form div.fields div.field div.label-radio,#content div.box div.form div.fields div.field div.label-textarea {
2050 2051 padding:0 0 0 5px !important;
2051 2052 }
2052 2053
2053 2054 #content div.box div.form div.fields div.field div.label span,#login div.form div.fields div.field div.label span,#register div.form div.fields div.field div.label span {
2054 2055 height:1%;
2055 2056 display:block;
2056 2057 color:#363636;
2057 2058 margin:0;
2058 2059 padding:2px 0 0;
2059 2060 }
2060 2061
2061 2062 #content div.box div.form div.fields div.field div.input input.error,#login div.form div.fields div.field div.input input.error,#register div.form div.fields div.field div.input input.error {
2062 2063 background:#FBE3E4;
2063 2064 border-top:1px solid #e1b2b3;
2064 2065 border-left:1px solid #e1b2b3;
2065 2066 border-right:1px solid #FBC2C4;
2066 2067 border-bottom:1px solid #FBC2C4;
2067 2068 }
2068 2069
2069 2070 #content div.box div.form div.fields div.field div.input input.success,#login div.form div.fields div.field div.input input.success,#register div.form div.fields div.field div.input input.success {
2070 2071 background:#E6EFC2;
2071 2072 border-top:1px solid #cebb98;
2072 2073 border-left:1px solid #cebb98;
2073 2074 border-right:1px solid #c6d880;
2074 2075 border-bottom:1px solid #c6d880;
2075 2076 }
2076 2077
2077 2078 #content div.box-left div.form div.fields div.field div.textarea,#content div.box-right div.form div.fields div.field div.textarea,#content div.box div.form div.fields div.field div.select select,#content div.box table th.selected input,#content div.box table td.selected input {
2078 2079 margin:0;
2079 2080 }
2080 2081
2081 2082 #content div.box div.form div.fields div.field div.select,#content div.box div.form div.fields div.field div.checkboxes,#content div.box div.form div.fields div.field div.radios {
2082 2083 margin:0 0 0 200px;
2083 2084 padding:0;
2084 2085 }
2085 2086
2086 2087 #content div.box div.form div.fields div.field div.select a:hover,#content div.box div.form div.fields div.field div.select a.ui-selectmenu:hover,#content div.box div.action a:hover {
2087 2088 color:#000;
2088 2089 text-decoration:none;
2089 2090 }
2090 2091
2091 2092 #content div.box div.form div.fields div.field div.select a.ui-selectmenu-focus,#content div.box div.action a.ui-selectmenu-focus {
2092 2093 border:1px solid #666;
2093 2094 }
2094 2095
2095 2096 #content div.box div.form div.fields div.field div.checkboxes div.checkbox,#content div.box div.form div.fields div.field div.radios div.radio {
2096 2097 clear:both;
2097 2098 overflow:hidden;
2098 2099 margin:0;
2099 2100 padding:2px 0;
2100 2101 }
2101 2102
2102 2103 #content div.box div.form div.fields div.field div.checkboxes div.checkbox input,#content div.box div.form div.fields div.field div.radios div.radio input {
2103 2104 float:left;
2104 2105 margin:0;
2105 2106 }
2106 2107
2107 2108 #content div.box div.form div.fields div.field div.checkboxes div.checkbox label,#content div.box div.form div.fields div.field div.radios div.radio label {
2108 2109 height:1%;
2109 2110 display:block;
2110 2111 float:left;
2111 2112 margin:3px 0 0 4px;
2112 2113 }
2113 2114
2114 2115 div.form div.fields div.field div.button input,#content div.box div.form div.fields div.buttons input,div.form div.fields div.buttons input,#content div.box div.action div.button input {
2115 2116 color:#000;
2116 2117 font-family:Lucida Grande, Verdana, Lucida Sans Regular, Lucida Sans Unicode, Arial, sans-serif;
2117 2118 font-size:11px;
2118 2119 font-weight:700;
2119 2120 margin:0;
2120 2121 }
2121 2122
2122 2123 div.form div.fields div.field div.button .ui-state-default,#content div.box div.form div.fields div.buttons input.ui-state-default {
2123 2124 background:#e5e3e3 url("../images/button.png") repeat-x;
2124 2125 border-top:1px solid #DDD;
2125 2126 border-left:1px solid #c6c6c6;
2126 2127 border-right:1px solid #DDD;
2127 2128 border-bottom:1px solid #c6c6c6;
2128 2129 color:#515151;
2129 2130 outline:none;
2130 2131 margin:0;
2131 2132 padding:6px 12px;
2132 2133 }
2133 2134
2134 2135 div.form div.fields div.field div.button .ui-state-hover,#content div.box div.form div.fields div.buttons input.ui-state-hover {
2135 2136 background:#b4b4b4 url("../images/button_selected.png") repeat-x;
2136 2137 border-top:1px solid #ccc;
2137 2138 border-left:1px solid #bebebe;
2138 2139 border-right:1px solid #b1b1b1;
2139 2140 border-bottom:1px solid #afafaf;
2140 2141 color:#515151;
2141 2142 outline:none;
2142 2143 margin:0;
2143 2144 padding:6px 12px;
2144 2145 }
2145 2146
2146 2147 div.form div.fields div.field div.highlight,#content div.box div.form div.fields div.buttons div.highlight {
2147 2148 display:inline;
2148 2149 }
2149 2150
2150 2151 #content div.box div.form div.fields div.buttons,div.form div.fields div.buttons {
2151 2152 margin:10px 0 0 200px;
2152 2153 padding:0;
2153 2154 }
2154 2155
2155 2156 #content div.box-left div.form div.fields div.buttons,#content div.box-right div.form div.fields div.buttons,div.box-left div.form div.fields div.buttons,div.box-right div.form div.fields div.buttons {
2156 2157 margin:10px 0 0;
2157 2158 }
2158 2159
2159 2160 #content div.box table td.user,#content div.box table td.address {
2160 2161 width:10%;
2161 2162 text-align:center;
2162 2163 }
2163 2164
2164 2165 #content div.box div.action div.button,#login div.form div.fields div.field div.input div.link,#register div.form div.fields div.field div.input div.link {
2165 2166 text-align:right;
2166 2167 margin:6px 0 0;
2167 2168 padding:0;
2168 2169 }
2169 2170
2170 2171 #content div.box div.action div.button input.ui-state-default,#login div.form div.fields div.buttons input.ui-state-default,#register div.form div.fields div.buttons input.ui-state-default {
2171 2172 background:#e5e3e3 url("../images/button.png") repeat-x;
2172 2173 border-top:1px solid #DDD;
2173 2174 border-left:1px solid #c6c6c6;
2174 2175 border-right:1px solid #DDD;
2175 2176 border-bottom:1px solid #c6c6c6;
2176 2177 color:#515151;
2177 2178 margin:0;
2178 2179 padding:6px 12px;
2179 2180 }
2180 2181
2181 2182 #content div.box div.action div.button input.ui-state-hover,#login div.form div.fields div.buttons input.ui-state-hover,#register div.form div.fields div.buttons input.ui-state-hover {
2182 2183 background:#b4b4b4 url("../images/button_selected.png") repeat-x;
2183 2184 border-top:1px solid #ccc;
2184 2185 border-left:1px solid #bebebe;
2185 2186 border-right:1px solid #b1b1b1;
2186 2187 border-bottom:1px solid #afafaf;
2187 2188 color:#515151;
2188 2189 margin:0;
2189 2190 padding:6px 12px;
2190 2191 }
2191 2192
2192 2193 #content div.box div.pagination div.results,#content div.box div.pagination-wh div.results {
2193 2194 text-align:left;
2194 2195 float:left;
2195 2196 margin:0;
2196 2197 padding:0;
2197 2198 }
2198 2199
2199 2200 #content div.box div.pagination div.results span,#content div.box div.pagination-wh div.results span {
2200 2201 height:1%;
2201 2202 display:block;
2202 2203 float:left;
2203 2204 background:#ebebeb url("../images/pager.png") repeat-x;
2204 2205 border-top:1px solid #dedede;
2205 2206 border-left:1px solid #cfcfcf;
2206 2207 border-right:1px solid #c4c4c4;
2207 2208 border-bottom:1px solid #c4c4c4;
2208 2209 color:#4A4A4A;
2209 2210 font-weight:700;
2210 2211 margin:0;
2211 2212 padding:6px 8px;
2212 2213 }
2213 2214
2214 2215 #content div.box div.pagination ul.pager li.disabled,#content div.box div.pagination-wh a.disabled {
2215 2216 color:#B4B4B4;
2216 2217 padding:6px;
2217 2218 }
2218 2219
2219 2220 #login,#register {
2220 2221 width:420px;
2221 2222 margin:10% auto 0;
2222 2223 padding:0;
2223 2224 }
2224 2225
2225 2226 #login div.color,#register div.color {
2226 2227 clear:both;
2227 2228 overflow:hidden;
2228 2229 background:#FFF;
2229 2230 margin:10px auto 0;
2230 2231 padding:3px 3px 3px 0;
2231 2232 }
2232 2233
2233 2234 #login div.color a,#register div.color a {
2234 2235 width:20px;
2235 2236 height:20px;
2236 2237 display:block;
2237 2238 float:left;
2238 2239 margin:0 0 0 3px;
2239 2240 padding:0;
2240 2241 }
2241 2242
2242 2243 #login div.title h5,#register div.title h5 {
2243 2244 color:#fff;
2244 2245 margin:10px;
2245 2246 padding:0;
2246 2247 }
2247 2248
2248 2249 #login div.form div.fields div.field,#register div.form div.fields div.field {
2249 2250 clear:both;
2250 2251 overflow:hidden;
2251 2252 margin:0;
2252 2253 padding:0 0 10px;
2253 2254 }
2254 2255
2255 2256 #login div.form div.fields div.field span.error-message,#register div.form div.fields div.field span.error-message {
2256 2257 height:1%;
2257 2258 display:block;
2258 2259 color:red;
2259 2260 margin:8px 0 0;
2260 2261 padding:0;
2261 2262 }
2262 2263
2263 2264 #login div.form div.fields div.field div.label label,#register div.form div.fields div.field div.label label {
2264 2265 color:#000;
2265 2266 font-weight:700;
2266 2267 }
2267 2268
2268 2269 #login div.form div.fields div.field div.input,#register div.form div.fields div.field div.input {
2269 2270 float:left;
2270 2271 margin:0;
2271 2272 padding:0;
2272 2273 }
2273 2274
2274 2275 #login div.form div.fields div.field div.checkbox,#register div.form div.fields div.field div.checkbox {
2275 2276 margin:0 0 0 184px;
2276 2277 padding:0;
2277 2278 }
2278 2279
2279 2280 #login div.form div.fields div.field div.checkbox label,#register div.form div.fields div.field div.checkbox label {
2280 2281 color:#565656;
2281 2282 font-weight:700;
2282 2283 }
2283 2284
2284 2285 #login div.form div.fields div.buttons input,#register div.form div.fields div.buttons input {
2285 2286 color:#000;
2286 2287 font-size:1em;
2287 2288 font-weight:700;
2288 2289 font-family:Verdana, Helvetica, Sans-Serif;
2289 2290 margin:0;
2290 2291 }
2291 2292
2292 2293 #changeset_content .container .wrapper,#graph_content .container .wrapper {
2293 2294 width:600px;
2294 2295 }
2295 2296
2296 2297 #changeset_content .container .left,#graph_content .container .left {
2297 2298 float:left;
2298 2299 width:70%;
2299 2300 padding-left:5px;
2300 2301 }
2301 2302
2302 2303 #changeset_content .container .left .date,.ac .match {
2303 2304 font-weight:700;
2304 2305 padding-top: 5px;
2305 2306 padding-bottom:5px;
2306 2307 }
2307 2308
2308 2309 div#legend_container table td,div#legend_choices table td {
2309 2310 border:none !important;
2310 2311 height:20px !important;
2311 2312 padding:0 !important;
2312 2313 } No newline at end of file
@@ -1,283 +1,283 b''
1 1 ## -*- coding: utf-8 -*-
2 2 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
3 3 <html xmlns="http://www.w3.org/1999/xhtml" id="mainhtml">
4 4 <head>
5 5 <title>${next.title()}</title>
6 6 <link rel="icon" href="/images/hgicon.png" type="image/png" />
7 7 <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
8 8 <meta name="robots" content="index, nofollow"/>
9 9 <!-- stylesheets -->
10 10 ${self.css()}
11 11 <!-- scripts -->
12 12 ${self.js()}
13 13 </head>
14 14 <body>
15 15 <!-- header -->
16 16 <div id="header">
17 17 <!-- user -->
18 18 <ul id="logged-user">
19 19 <li class="first">
20 20 <div class="gravatar">
21 21 <img alt="gravatar" src="${h.gravatar_url(c.rhodecode_user.email,24)}" />
22 22 </div>
23 23 <div class="account">
24 24 ${h.link_to('%s %s'%(c.rhodecode_user.name,c.rhodecode_user.lastname),h.url('admin_settings_my_account'))}<br/>
25 25 ${h.link_to(c.rhodecode_user.username,h.url('admin_settings_my_account'))}
26 26 </div>
27 27 </li>
28 28 <li class="last highlight">${h.link_to(u'Logout',h.url('logout_home'))}</li>
29 29 </ul>
30 30 <!-- end user -->
31 31 <div id="header-inner" class="title top-left-rounded-corner top-right-rounded-corner">
32 32 <!-- logo -->
33 33 <div id="logo">
34 <h1><a href="${h.url('hg_home')}">${c.rhodecode_name}</a></h1>
34 <h1><a href="${h.url('home')}">${c.rhodecode_name}</a></h1>
35 35 </div>
36 36 <!-- end logo -->
37 37 <!-- menu -->
38 38 ${self.page_nav()}
39 39 <!-- quick -->
40 40 </div>
41 41 </div>
42 42 <!-- end header -->
43 43
44 44 <!-- CONTENT -->
45 45 <div id="content">
46 46 <div class="flash_msg">
47 47 <% messages = h.flash.pop_messages() %>
48 48 % if messages:
49 49 <ul id="flash-messages">
50 50 % for message in messages:
51 51 <li class="${message.category}_msg">${message}</li>
52 52 % endfor
53 53 </ul>
54 54 % endif
55 55 </div>
56 56 <div id="main">
57 57 ${next.main()}
58 58 </div>
59 59 </div>
60 60 <!-- END CONTENT -->
61 61
62 62 <!-- footer -->
63 63 <div id="footer">
64 64 <div id="footer-inner" class="title bottom-left-rounded-corner bottom-right-rounded-corner">
65 65 <div>
66 66 <p class="footer-link">${h.link_to(_('Submit a bug'),h.url('bugtracker'))}</p>
67 67 <p class="footer-link">${h.link_to(_('GPL license'),h.url('gpl_license'))}</p>
68 68 <p>RhodeCode ${c.rhodecode_version} &copy; 2010 by Marcin Kuzminski</p>
69 69 </div>
70 70 </div>
71 71 <script type="text/javascript">${h.tooltip.activate()}</script>
72 72 </div>
73 73 <!-- end footer -->
74 74 </body>
75 75
76 76 </html>
77 77
78 78 ### MAKO DEFS ###
79 79 <%def name="page_nav()">
80 80 ${self.menu()}
81 81 </%def>
82 82
83 83 <%def name="menu(current=None)">
84 84 <%
85 85 def is_current(selected):
86 86 if selected == current:
87 87 return h.literal('class="current"')
88 88 %>
89 89 %if current not in ['home','admin']:
90 90 ##REGULAR MENU
91 91 <ul id="quick">
92 92 <!-- repo switcher -->
93 93 <li>
94 94 <a id="repo_switcher" title="${_('Switch repository')}" href="#">
95 95 <span class="icon">
96 96 <img src="/images/icons/database.png" alt="${_('Products')}" />
97 97 </span>
98 98 <span>&darr;</span>
99 99 </a>
100 100 <ul class="repo_switcher">
101 101 %for repo,private in c.repo_switcher_list:
102 102 %if private:
103 103 <li>${h.link_to(repo,h.url('summary_home',repo_name=repo),class_="private_repo")}</li>
104 104 %else:
105 105 <li>${h.link_to(repo,h.url('summary_home',repo_name=repo),class_="public_repo")}</li>
106 106 %endif
107 107 %endfor
108 108 </ul>
109 109 </li>
110 110
111 111 <li ${is_current('summary')}>
112 112 <a title="${_('Summary')}" href="${h.url('summary_home',repo_name=c.repo_name)}">
113 113 <span class="icon">
114 114 <img src="/images/icons/clipboard_16.png" alt="${_('Summary')}" />
115 115 </span>
116 116 <span>${_('Summary')}</span>
117 117 </a>
118 118 </li>
119 119 <li ${is_current('shortlog')}>
120 120 <a title="${_('Shortlog')}" href="${h.url('shortlog_home',repo_name=c.repo_name)}">
121 121 <span class="icon">
122 122 <img src="/images/icons/application_view_list.png" alt="${_('Shortlog')}" />
123 123 </span>
124 124 <span>${_('Shortlog')}</span>
125 125 </a>
126 126 </li>
127 127 <li ${is_current('changelog')}>
128 128 <a title="${_('Changelog')}" href="${h.url('changelog_home',repo_name=c.repo_name)}">
129 129 <span class="icon">
130 130 <img src="/images/icons/time.png" alt="${_('Changelog')}" />
131 131 </span>
132 132 <span>${_('Changelog')}</span>
133 133 </a>
134 134 </li>
135 135
136 136 <li ${is_current('switch_to')}>
137 137 <a title="${_('Switch to')}" href="#">
138 138 <span class="icon">
139 139 <img src="/images/icons/arrow_switch.png" alt="${_('Switch to')}" />
140 140 </span>
141 141 <span>${_('Switch to')}</span>
142 142 </a>
143 143 <ul>
144 144 <li>
145 145 ${h.link_to(_('branches'),h.url('branches_home',repo_name=c.repo_name),class_='branches childs')}
146 146 <ul>
147 147 %if c.repository_branches.values():
148 148 %for cnt,branch in enumerate(c.repository_branches.items()):
149 <li>${h.link_to('%s - %s' % (branch[0],branch[1]),h.url('files_home',repo_name=c.repo_name,revision=branch[1]))}</li>
149 <li>${h.link_to('%s - %s' % (branch[0],h.short_id(branch[1])),h.url('files_home',repo_name=c.repo_name,revision=branch[1]))}</li>
150 150 %endfor
151 151 %else:
152 152 <li>${h.link_to(_('There are no branches yet'),'#')}</li>
153 153 %endif
154 154 </ul>
155 155 </li>
156 156 <li>
157 157 ${h.link_to(_('tags'),h.url('tags_home',repo_name=c.repo_name),class_='tags childs')}
158 158 <ul>
159 159 %if c.repository_tags.values():
160 160 %for cnt,tag in enumerate(c.repository_tags.items()):
161 <li>${h.link_to('%s - %s' % (tag[0],tag[1]),h.url('files_home',repo_name=c.repo_name,revision=tag[1]))}</li>
161 <li>${h.link_to('%s - %s' % (tag[0],h.short_id(tag[1])),h.url('files_home',repo_name=c.repo_name,revision=tag[1]))}</li>
162 162 %endfor
163 163 %else:
164 164 <li>${h.link_to(_('There are no tags yet'),'#')}</li>
165 165 %endif
166 166 </ul>
167 167 </li>
168 168 </ul>
169 169 </li>
170 170 <li ${is_current('files')}>
171 171 <a title="${_('Files')}" href="${h.url('files_home',repo_name=c.repo_name)}">
172 172 <span class="icon">
173 173 <img src="/images/icons/file.png" alt="${_('Files')}" />
174 174 </span>
175 175 <span>${_('Files')}</span>
176 176 </a>
177 177 </li>
178 178
179 179 <li ${is_current('options')}>
180 180 <a title="${_('Options')}" href="#">
181 181 <span class="icon">
182 182 <img src="/images/icons/table_gear.png" alt="${_('Admin')}" />
183 183 </span>
184 184 <span>${_('Options')}</span>
185 185 </a>
186 186 <ul>
187 187 %if h.HasRepoPermissionAll('repository.admin')(c.repo_name):
188 188 <li>${h.link_to(_('settings'),h.url('repo_settings_home',repo_name=c.repo_name),class_='settings')}</li>
189 189 %endif
190 190 <li>${h.link_to(_('fork'),h.url('repo_fork_home',repo_name=c.repo_name),class_='fork')}</li>
191 191 <li>${h.link_to(_('search'),h.url('search_repo',search_repo=c.repo_name),class_='search')}</li>
192 192
193 193 %if h.HasPermissionAll('hg.admin')('access admin main page'):
194 194 <li>
195 195 ${h.link_to(_('admin'),h.url('admin_home'),class_='admin')}
196 196 <ul>
197 197 <li>${h.link_to(_('journal'),h.url('admin_home'),class_='journal')}</li>
198 198 <li>${h.link_to(_('repositories'),h.url('repos'),class_='repos')}</li>
199 199 <li>${h.link_to(_('users'),h.url('users'),class_='users')}</li>
200 200 <li>${h.link_to(_('permissions'),h.url('edit_permission',id='default'),class_='permissions')}</li>
201 201 <li class="last">${h.link_to(_('settings'),h.url('admin_settings'),class_='settings')}</li>
202 202 </ul>
203 203 </li>
204 204 %endif
205 205
206 206
207 207 ## %if h.HasRepoPermissionAll('repository.admin')(c.repo_name):
208 208 ## <li class="last">
209 209 ## ${h.link_to(_('delete'),'#',class_='delete')}
210 210 ## ${h.form(url('repo_settings_delete', repo_name=c.repo_name),method='delete')}
211 211 ## ${h.submit('remove_%s' % c.repo_name,'delete',class_="delete_icon action_button",onclick="return confirm('Confirm to delete this repository');")}
212 212 ## ${h.end_form()}
213 213 ## </li>
214 214 ## %endif
215 215 </ul>
216 216 </li>
217 217 </ul>
218 218 %else:
219 219 ##ROOT MENU
220 220 <ul id="quick">
221 221 <li>
222 <a title="${_('Home')}" href="${h.url('hg_home')}">
222 <a title="${_('Home')}" href="${h.url('home')}">
223 223 <span class="icon">
224 224 <img src="/images/icons/home_16.png" alt="${_('Home')}" />
225 225 </span>
226 226 <span>${_('Home')}</span>
227 227 </a>
228 228 </li>
229 229
230 230 <li>
231 231 <a title="${_('Search')}" href="${h.url('search')}">
232 232 <span class="icon">
233 233 <img src="/images/icons/search_16.png" alt="${_('Search')}" />
234 234 </span>
235 235 <span>${_('Search')}</span>
236 236 </a>
237 237 </li>
238 238
239 239 %if h.HasPermissionAll('hg.admin')('access admin main page'):
240 240 <li ${is_current('admin')}>
241 241 <a title="${_('Admin')}" href="${h.url('admin_home')}">
242 242 <span class="icon">
243 243 <img src="/images/icons/cog_edit.png" alt="${_('Admin')}" />
244 244 </span>
245 245 <span>${_('Admin')}</span>
246 246 </a>
247 247 <ul>
248 248 <li>${h.link_to(_('journal'),h.url('admin_home'),class_='journal')}</li>
249 249 <li>${h.link_to(_('repositories'),h.url('repos'),class_='repos')}</li>
250 250 <li>${h.link_to(_('users'),h.url('users'),class_='users')}</li>
251 251 <li>${h.link_to(_('permissions'),h.url('edit_permission',id='default'),class_='permissions')}</li>
252 252 <li class="last">${h.link_to(_('settings'),h.url('admin_settings'),class_='settings')}</li>
253 253 </ul>
254 254 </li>
255 255 %endif
256 256
257 257 </ul>
258 258 %endif
259 259 </%def>
260 260
261 261
262 262 <%def name="css()">
263 263 <link rel="stylesheet" type="text/css" href="/css/style.css" media="screen" />
264 264 <link rel="stylesheet" type="text/css" href="/css/pygments.css" />
265 265 <link rel="stylesheet" type="text/css" href="/css/diff.css" />
266 266 </%def>
267 267
268 268 <%def name="js()">
269 269 ##<script type="text/javascript" src="/js/yui/utilities/utilities.js"></script>
270 270 ##<script type="text/javascript" src="/js/yui/container/container.js"></script>
271 271 ##<script type="text/javascript" src="/js/yui/datasource/datasource.js"></script>
272 272 ##<script type="text/javascript" src="/js/yui/autocomplete/autocomplete.js"></script>
273 273
274 274 <script type="text/javascript" src="/js/yui2.js"></script>
275 275 <!--[if IE]><script language="javascript" type="text/javascript" src="/js/excanvas.min.js"></script><![endif]-->
276 276 <script type="text/javascript" src="/js/yui.flot.js"></script>
277 277 </%def>
278 278
279 279 <%def name="breadcrumbs()">
280 280 <div class="breadcrumbs">
281 281 ${self.breadcrumbs_links()}
282 282 </div>
283 283 </%def> No newline at end of file
@@ -1,30 +1,32 b''
1 1 % if c.repo_branches:
2 2 <table class="table_disp">
3 3 <tr>
4 4 <th class="left">${_('date')}</th>
5 <th class="left">${_('name')}</th>
6 <th class="left">${_('author')}</th>
5 7 <th class="left">${_('revision')}</th>
6 <th class="left">${_('name')}</th>
7 8 <th class="left">${_('links')}</th>
8 9 </tr>
9 10 %for cnt,branch in enumerate(c.repo_branches.items()):
10 11 <tr class="parity${cnt%2}">
11 <td>${h.age(branch[1].date)}</td>
12 <td>r${branch[1].revision}:${branch[1].short_id}</td>
13 <td>
14 <span class="logtags">
15 <span class="branchtag">${h.link_to(branch[0],
16 h.url('changeset_home',repo_name=c.repo_name,revision=branch[1].short_id))}</span>
17 </span>
18 </td>
12 <td>${branch[1].date} - ${h.age(branch[1].date)}</td>
13 <td>
14 <span class="logtags">
15 <span class="branchtag">${h.link_to(branch[0],
16 h.url('changeset_home',repo_name=c.repo_name,revision=branch[1].raw_id))}</span>
17 </span>
18 </td>
19 <td title="${branch[1].author}">${h.person(branch[1].author)}</td>
20 <td>r${branch[1].revision}:${h.short_id(branch[1].raw_id)}</td>
19 21 <td class="nowrap">
20 ${h.link_to(_('changeset'),h.url('changeset_home',repo_name=c.repo_name,revision=branch[1].short_id))}
22 ${h.link_to(_('changeset'),h.url('changeset_home',repo_name=c.repo_name,revision=branch[1].raw_id))}
21 23 |
22 ${h.link_to(_('files'),h.url('files_home',repo_name=c.repo_name,revision=branch[1].short_id))}
24 ${h.link_to(_('files'),h.url('files_home',repo_name=c.repo_name,revision=branch[1].raw_id))}
23 25 </td>
24 26 </tr>
25 27 %endfor
26 28 </table>
27 29 %else:
28 30 ${_('There are no branches yet')}
29 31 %endif
30 32
@@ -1,121 +1,121 b''
1 1 ## -*- coding: utf-8 -*-
2 2
3 3 <%inherit file="/base/base.html"/>
4 4
5 5 <%def name="title()">
6 6 ${c.repo_name} ${_('Changelog')} - ${c.rhodecode_name}
7 7 </%def>
8 8
9 9 <%def name="breadcrumbs_links()">
10 10 ${h.link_to(u'Home',h.url('/'))}
11 11 &raquo;
12 12 ${h.link_to(c.repo_name,h.url('summary_home',repo_name=c.repo_name))}
13 13 &raquo;
14 14 ${_('Changelog')} - ${_('showing ')} ${c.size if c.size <= c.total_cs else c.total_cs} ${_('out of')} ${c.total_cs} ${_('revisions')}
15 15 </%def>
16 16
17 17 <%def name="page_nav()">
18 18 ${self.menu('changelog')}
19 19 </%def>
20 20
21 21 <%def name="main()">
22 22 <div class="box">
23 23 <!-- box / title -->
24 24 <div class="title">
25 25 ${self.breadcrumbs()}
26 26 </div>
27 27 <div class="table">
28 28 % if c.pagination:
29 29 <div id="graph">
30 30 <div id="graph_nodes">
31 31 <canvas id="graph_canvas"></canvas>
32 32 </div>
33 33 <div id="graph_content">
34 34 <div class="container_header">
35 35
36 36 ${h.form(h.url.current(),method='get')}
37 37 <div class="info_box">
38 38 <span>${_('Show')}:</span>
39 39 ${h.text('size',size=1,value=c.size)}
40 40 <span>${_('revisions')}</span>
41 41 ${h.submit('set',_('set'))}
42 42 </div>
43 43 ${h.end_form()}
44 44
45 45 </div>
46 46 %for cnt,cs in enumerate(c.pagination):
47 47 <div id="chg_${cnt+1}" class="container">
48 48 <div class="left">
49 <div class="date">${_('commit')} ${cs.revision}: ${cs.short_id}@${cs.date}</div>
49 <div class="date">${_('commit')} ${cs.revision}: ${cs.raw_id}@${cs.date}</div>
50 50 <div class="author">
51 51 <div class="gravatar">
52 52 <img alt="gravatar" src="${h.gravatar_url(h.email(cs.author),20)}"/>
53 53 </div>
54 54 <span>${h.person(cs.author)}</span><br/>
55 55 <span><a href="mailto:${h.email_or_none(cs.author)}">${h.email_or_none(cs.author)}</a></span><br/>
56 56 </div>
57 <div class="message">${h.link_to(h.wrap_paragraphs(cs.message),h.url('changeset_home',repo_name=c.repo_name,revision=cs.short_id))}</div>
57 <div class="message">${h.link_to(h.wrap_paragraphs(cs.message),h.url('changeset_home',repo_name=c.repo_name,revision=cs.raw_id))}</div>
58 58 </div>
59 59 <div class="right">
60 60 <div class="changes">
61 61 <span class="removed" title="${_('removed')}">${len(cs.removed)}</span>
62 62 <span class="changed" title="${_('changed')}">${len(cs.changed)}</span>
63 63 <span class="added" title="${_('added')}">${len(cs.added)}</span>
64 64 </div>
65 65 %if len(cs.parents)>1:
66 66 <div class="merge">
67 67 ${_('merge')}<img alt="merge" src="/images/icons/arrow_join.png"/>
68 68 </div>
69 69 %endif
70 70 %if cs.parents:
71 71 %for p_cs in reversed(cs.parents):
72 <div class="parent">${_('Parent')} ${p_cs.revision}: ${h.link_to(p_cs.short_id,
73 h.url('changeset_home',repo_name=c.repo_name,revision=p_cs.short_id),title=p_cs.message)}
72 <div class="parent">${_('Parent')} ${p_cs.revision}: ${h.link_to(p_cs.raw_id,
73 h.url('changeset_home',repo_name=c.repo_name,revision=p_cs.raw_id),title=p_cs.message)}
74 74 </div>
75 75 %endfor
76 76 %else:
77 77 <div class="parent">${_('No parents')}</div>
78 78 %endif
79 79
80 80 <span class="logtags">
81 81 <span class="branchtag" title="${'%s %s' % (_('branch'),cs.branch)}">
82 ${h.link_to(cs.branch,h.url('files_home',repo_name=c.repo_name,revision=cs.short_id))}</span>
82 ${h.link_to(cs.branch,h.url('files_home',repo_name=c.repo_name,revision=cs.raw_id))}</span>
83 83 %for tag in cs.tags:
84 84 <span class="tagtag" title="${'%s %s' % (_('tag'),tag)}">
85 ${h.link_to(tag,h.url('files_home',repo_name=c.repo_name,revision=cs.short_id))}</span>
85 ${h.link_to(tag,h.url('files_home',repo_name=c.repo_name,revision=cs.raw_id))}</span>
86 86 %endfor
87 87 </span>
88 88 </div>
89 89 </div>
90 90
91 91 %endfor
92 92 <div class="pagination-wh pagination-left">
93 93 ${c.pagination.pager('$link_previous ~2~ $link_next')}
94 94 </div>
95 95 </div>
96 96 </div>
97 97
98 98 <script type="text/javascript" src="/js/graph.js"></script>
99 99 <script type="text/javascript">
100 100 YAHOO.util.Event.onDOMReady(function(){
101 101 function set_canvas() {
102 102 var c = document.getElementById('graph_nodes');
103 103 var t = document.getElementById('graph_content');
104 104 canvas = document.getElementById('graph_canvas');
105 105 var div_h = t.clientHeight;
106 106 c.style.height=div_h+'px';
107 107 canvas.setAttribute('height',div_h);
108 108 canvas.setAttribute('width',160);
109 109 };
110 110 set_canvas();
111 111 var jsdata = ${c.jsdata|n};
112 112 var r = new BranchRenderer();
113 113 r.render(jsdata);
114 114 });
115 115 </script>
116 116 %else:
117 117 ${_('There are no changes yet')}
118 118 %endif
119 119 </div>
120 120 </div>
121 121 </%def> No newline at end of file
@@ -1,124 +1,124 b''
1 1 <%inherit file="/base/base.html"/>
2 2
3 3 <%def name="title()">
4 ${c.repo_name} ${_('Changeset')} - r${c.changeset.revision}:${c.changeset.short_id} - ${c.rhodecode_name}
4 ${c.repo_name} ${_('Changeset')} - r${c.changeset.revision}:${h.short_id(c.changeset.raw_id)} - ${c.rhodecode_name}
5 5 </%def>
6 6
7 7 <%def name="breadcrumbs_links()">
8 8 ${h.link_to(u'Home',h.url('/'))}
9 9 &raquo;
10 10 ${h.link_to(c.repo_name,h.url('summary_home',repo_name=c.repo_name))}
11 11 &raquo;
12 ${_('Changeset')} - r${c.changeset.revision}:${c.changeset.short_id}
12 ${_('Changeset')} - r${c.changeset.revision}:${h.short_id(c.changeset.raw_id)}
13 13 </%def>
14 14
15 15 <%def name="page_nav()">
16 16 ${self.menu('changelog')}
17 17 </%def>
18 18
19 19 <%def name="main()">
20 20 <div class="box">
21 21 <!-- box / title -->
22 22 <div class="title">
23 23 ${self.breadcrumbs()}
24 24 </div>
25 25 <div class="table">
26 26 <div id="body" class="diffblock">
27 27 <div class="code-header">
28 28 <div>
29 ${_('Changeset')} - r${c.changeset.revision}:${c.changeset.short_id}
29 ${_('Changeset')} - r${c.changeset.revision}:${h.short_id(c.changeset.raw_id)}
30 30 &raquo; <span>${h.link_to(_('raw diff'),
31 h.url('raw_changeset_home',repo_name=c.repo_name,revision=c.changeset.short_id,diff='show'))}</span>
31 h.url('raw_changeset_home',repo_name=c.repo_name,revision=c.changeset.raw_id,diff='show'))}</span>
32 32 &raquo; <span>${h.link_to(_('download diff'),
33 h.url('raw_changeset_home',repo_name=c.repo_name,revision=c.changeset.short_id,diff='download'))}</span>
33 h.url('raw_changeset_home',repo_name=c.repo_name,revision=c.changeset.raw_id,diff='download'))}</span>
34 34 </div>
35 35 </div>
36 36 </div>
37 37 <div id="changeset_content">
38 38 <div class="container">
39 39 <div class="left">
40 <div class="date">${_('commit')} ${c.changeset.revision}: ${c.changeset.short_id}@${c.changeset.date}</div>
40 <div class="date">${_('commit')} ${c.changeset.revision}: ${h.short_id(c.changeset.raw_id)}@${c.changeset.date}</div>
41 41 <div class="author">
42 42 <div class="gravatar">
43 43 <img alt="gravatar" src="${h.gravatar_url(h.email(c.changeset.author),20)}"/>
44 44 </div>
45 45 <span>${h.person(c.changeset.author)}</span><br/>
46 46 <span><a href="mailto:${h.email_or_none(c.changeset.author)}">${h.email_or_none(c.changeset.author)}</a></span><br/>
47 47 </div>
48 <div class="message">${h.link_to(h.wrap_paragraphs(c.changeset.message),h.url('changeset_home',repo_name=c.repo_name,revision=c.changeset.short_id))}</div>
48 <div class="message">${h.link_to(h.wrap_paragraphs(c.changeset.message),h.url('changeset_home',repo_name=c.repo_name,revision=c.changeset.raw_id))}</div>
49 49 </div>
50 50 <div class="right">
51 51 <div class="changes">
52 52 <span class="removed" title="${_('removed')}">${len(c.changeset.removed)}</span>
53 53 <span class="changed" title="${_('changed')}">${len(c.changeset.changed)}</span>
54 54 <span class="added" title="${_('added')}">${len(c.changeset.added)}</span>
55 55 </div>
56 56 %if len(c.changeset.parents)>1:
57 57 <div class="merge">
58 58 ${_('merge')}<img alt="merge" src="/images/icons/arrow_join.png"/>
59 59 </div>
60 60 %endif
61 61
62 62 %if c.changeset.parents:
63 63 %for p_cs in reversed(c.changeset.parents):
64 <div class="parent">${_('Parent')} ${p_cs.revision}: ${h.link_to(p_cs.short_id,
65 h.url('changeset_home',repo_name=c.repo_name,revision=p_cs.short_id),title=p_cs.message)}
64 <div class="parent">${_('Parent')} ${p_cs.revision}: ${h.link_to(h.short_id(p_cs.raw_id),
65 h.url('changeset_home',repo_name=c.repo_name,revision=p_cs.raw_id),title=p_cs.message)}
66 66 </div>
67 67 %endfor
68 68 %else:
69 69 <div class="parent">${_('No parents')}</div>
70 70 %endif
71 71 <span class="logtags">
72 72 <span class="branchtag" title="${'%s %s' % (_('branch'),c.changeset.branch)}">
73 ${h.link_to(c.changeset.branch,h.url('files_home',repo_name=c.repo_name,revision=c.changeset.short_id))}</span>
73 ${h.link_to(c.changeset.branch,h.url('files_home',repo_name=c.repo_name,revision=c.changeset.raw_id))}</span>
74 74 %for tag in c.changeset.tags:
75 75 <span class="tagtag" title="${'%s %s' % (_('tag'),tag)}">
76 ${h.link_to(tag,h.url('files_home',repo_name=c.repo_name,revision=c.changeset.short_id))}</span>
76 ${h.link_to(tag,h.url('files_home',repo_name=c.repo_name,revision=c.changeset.raw_id))}</span>
77 77 %endfor
78 78 </span>
79 79 </div>
80 80 </div>
81 81 <span style="font-size:1.1em;font-weight: bold">${_('Files affected')}</span>
82 82 <div class="cs_files">
83 83 %for change,filenode,diff,cs1,cs2 in c.changes:
84 84 <div class="cs_${change}">${h.link_to(filenode.path,h.url.current(anchor='CHANGE-%s'%filenode.path))}</div>
85 85 %endfor
86 86 </div>
87 87 </div>
88 88
89 89 </div>
90 90
91 91 %for change,filenode,diff,cs1,cs2 in c.changes:
92 92 %if change !='removed':
93 93 <div style="clear:both;height:10px"></div>
94 94 <div id="body" class="diffblock">
95 95 <div id="${'CHANGE-%s'%filenode.path}" class="code-header">
96 96 <div>
97 97 <span>
98 98 ${h.link_to_if(change!='removed',filenode.path,h.url('files_home',repo_name=c.repo_name,
99 revision=filenode.changeset.short_id,f_path=filenode.path))}
99 revision=filenode.changeset.raw_id,f_path=filenode.path))}
100 100 </span>
101 101 %if 1:
102 102 &raquo; <span>${h.link_to(_('diff'),
103 103 h.url('files_diff_home',repo_name=c.repo_name,f_path=filenode.path,diff2=cs2,diff1=cs1,diff='diff'))}</span>
104 104 &raquo; <span>${h.link_to(_('raw diff'),
105 105 h.url('files_diff_home',repo_name=c.repo_name,f_path=filenode.path,diff2=cs2,diff1=cs1,diff='raw'))}</span>
106 106 &raquo; <span>${h.link_to(_('download diff'),
107 107 h.url('files_diff_home',repo_name=c.repo_name,f_path=filenode.path,diff2=cs2,diff1=cs1,diff='download'))}</span>
108 108 %endif
109 109 </div>
110 110 </div>
111 111 <div class="code-body">
112 112 %if diff:
113 113 ${diff|n}
114 114 %else:
115 115 ${_('No changes in this file')}
116 116 %endif
117 117 </div>
118 118 </div>
119 119 %endif
120 120 %endfor
121 121 </div>
122 122 </div>
123 123
124 124 </%def> No newline at end of file
@@ -1,32 +1,32 b''
1 1 ## -*- coding: utf-8 -*-
2 2 <%inherit file="./../base/base.html"/>
3 3
4 4 <%def name="title()">
5 5 ${_('Repository not found')}
6 6 </%def>
7 7
8 8 <%def name="breadcrumbs()">
9 ${h.link_to(u'Home',h.url('hg_home'))}
9 ${h.link_to(u'Home',h.url('home'))}
10 10 /
11 11 ${h.link_to(u'Admin',h.url('admin_home'))}
12 12 </%def>
13 13
14 14 <%def name="page_nav()">
15 15 ${self.menu('admin')}
16 16 </%def>
17 17 <%def name="js()">
18 18
19 19 </%def>
20 20 <%def name="main()">
21 21
22 22 <h2 class="no-link no-border">${_('Not Found')}</h2>
23 23 <p class="normal">${_('The specified repository "%s" is unknown, sorry.') % c.repo_name}</p>
24 24 <p class="normal">
25 25 <a href="${h.url('new_repo',repo=c.repo_name_cleaned)}">
26 26 ${_('Create "%s" repository as %s' % (c.repo_name,c.repo_name_cleaned))}</a>
27 27
28 28 </p>
29 <p class="normal">${h.link_to(_('Go back to the main repository list page'),h.url('hg_home'))}</p>
29 <p class="normal">${h.link_to(_('Go back to the main repository list page'),h.url('home'))}</p>
30 30 <div class="page-footer">
31 31 </div>
32 32 </%def> No newline at end of file
@@ -1,50 +1,50 b''
1 1 <%inherit file="/base/base.html"/>
2 2
3 3 <%def name="title()">
4 4 ${c.repo_name} ${_('File diff')} - ${c.rhodecode_name}
5 5 </%def>
6 6
7 7 <%def name="breadcrumbs_links()">
8 8 ${h.link_to(u'Home',h.url('/'))}
9 9 &raquo;
10 10 ${h.link_to(c.repo_name,h.url('summary_home',repo_name=c.repo_name))}
11 11 &raquo;
12 ${'%s: %s %s %s' % (_('File diff'),c.diff2,'&rarr;',c.diff1)|n}
12 ${_('File diff')} r${c.changeset_1.revision}:${h.short_id(c.changeset_1.raw_id)} &rarr; r${c.changeset_2.revision}:${h.short_id(c.changeset_2.raw_id)}
13 13 </%def>
14 14
15 15 <%def name="page_nav()">
16 16 ${self.menu('files')}
17 17 </%def>
18 18 <%def name="main()">
19 19 <div class="box">
20 20 <!-- box / title -->
21 21 <div class="title">
22 22 ${self.breadcrumbs()}
23 23 </div>
24 24 <div class="table">
25 25 <div id="body" class="diffblock">
26 26 <div class="code-header">
27 27 <div>
28 28 <span>${h.link_to(c.f_path,h.url('files_home',repo_name=c.repo_name,
29 revision=c.diff2.split(':')[1],f_path=c.f_path))}</span>
29 revision=c.changeset_2.raw_id,f_path=c.f_path))}</span>
30 30 &raquo; <span>${h.link_to(_('diff'),
31 h.url.current(diff2=c.diff2.split(':')[-1],diff1=c.diff1.split(':')[-1],diff='diff'))}</span>
31 h.url.current(diff2=c.changeset_2.raw_id,diff1=c.changeset_1.raw_id,diff='diff'))}</span>
32 32 &raquo; <span>${h.link_to(_('raw diff'),
33 h.url.current(diff2=c.diff2.split(':')[-1],diff1=c.diff1.split(':')[-1],diff='raw'))}</span>
33 h.url.current(diff2=c.changeset_2.raw_id,diff1=c.changeset_1.raw_id,diff='raw'))}</span>
34 34 &raquo; <span>${h.link_to(_('download diff'),
35 h.url.current(diff2=c.diff2.split(':')[-1],diff1=c.diff1.split(':')[-1],diff='download'))}</span>
35 h.url.current(diff2=c.changeset_2.raw_id,diff1=c.changeset_1.raw_id,diff='download'))}</span>
36 36 </div>
37 37 </div>
38 38 <div class="code-body">
39 39 %if c.no_changes:
40 40 ${_('No changes')}
41 41 %else:
42 42 ${c.cur_diff|n}
43 43 %endif
44 44 </div>
45 45 </div>
46 46 </div>
47 47 </div>
48 48 </%def>
49 49
50 50 No newline at end of file
@@ -1,48 +1,48 b''
1 1 <%inherit file="/base/base.html"/>
2 2
3 3 <%def name="title()">
4 4 ${c.repo_name} ${_('Files')} - ${c.rhodecode_name}
5 5 </%def>
6 6
7 7 <%def name="breadcrumbs_links()">
8 8 ${h.link_to(u'Home',h.url('/'))}
9 9 &raquo;
10 10 ${h.link_to(c.repo_name,h.url('files_home',repo_name=c.repo_name))}
11 11 &raquo;
12 12 ${_('files')}
13 13 %if c.files_list:
14 @ R${c.rev_nr}:${c.cur_rev}
14 @ R${c.rev_nr}:${h.short_id(c.cur_rev)}
15 15 %endif
16 16 </%def>
17 17
18 18 <%def name="page_nav()">
19 19 ${self.menu('files')}
20 20 </%def>
21 21
22 22 <%def name="main()">
23 23 <div class="box">
24 24 <!-- box / title -->
25 25 <div class="title">
26 26 ${self.breadcrumbs()}
27 27 </div>
28 28 <div class="table">
29 29 <div id="files_data">
30 30 %if c.files_list:
31 31 <h3 class="files_location">${_('Location')}: ${h.files_breadcrumbs(c.repo_name,c.cur_rev,c.files_list.path)}</h3>
32 32 %if c.files_list.is_dir():
33 33 <%include file='files_browser.html'/>
34 34 %else:
35 35 <%include file='files_source.html'/>
36 36 %endif
37 37 %else:
38 38 <h2>
39 39 <a href="#" onClick="javascript:parent.history.back();" target="main">${_('Go back')}</a>
40 40 ${_('No files at given path')}: "${c.f_path or "/"}"
41 41 </h2>
42 42 %endif
43 43
44 44 </div>
45 45 </div>
46 46 </div>
47 47
48 48 </%def> No newline at end of file
@@ -1,61 +1,61 b''
1 1 <%inherit file="/base/base.html"/>
2 2
3 3 <%def name="title()">
4 4 ${c.repo_name} ${_('File annotate')} - ${c.rhodecode_name}
5 5 </%def>
6 6
7 7 <%def name="breadcrumbs_links()">
8 8 ${h.link_to(u'Home',h.url('/'))}
9 9 &raquo;
10 10 ${h.link_to(c.repo_name,h.url('summary_home',repo_name=c.repo_name))}
11 11 &raquo;
12 12 ${_('annotate')} @ R${c.rev_nr}:${c.cur_rev}
13 13 </%def>
14 14
15 15 <%def name="page_nav()">
16 16 ${self.menu('files')}
17 17 </%def>
18 18 <%def name="main()">
19 19 <div class="box">
20 20 <!-- box / title -->
21 21 <div class="title">
22 22 ${self.breadcrumbs()}
23 23 </div>
24 24 <div class="table">
25 25 <div id="files_data">
26 26 <h3 class="files_location">${_('Location')}: ${h.files_breadcrumbs(c.repo_name,c.cur_rev,c.file.path)}</h3>
27 27 <dl class="overview">
28 28 <dt>${_('Last revision')}</dt>
29 <dd>${h.link_to("r%s:%s" % (c.file.last_changeset.revision,c.file.last_changeset.short_id),
30 h.url('files_annotate_home',repo_name=c.repo_name,revision=c.file.last_changeset.short_id,f_path=c.f_path))} </dd>
29 <dd>${h.link_to("r%s:%s" % (c.file.last_changeset.revision,c.file.last_changeset.raw_id),
30 h.url('files_annotate_home',repo_name=c.repo_name,revision=c.file.last_changeset.raw_id,f_path=c.f_path))} </dd>
31 31 <dt>${_('Size')}</dt>
32 32 <dd>${h.format_byte_size(c.file.size,binary=True)}</dd>
33 33 <dt>${_('Mimetype')}</dt>
34 34 <dd>${c.file.mimetype}</dd>
35 35 <dt>${_('Options')}</dt>
36 36 <dd>${h.link_to(_('show source'),
37 37 h.url('files_home',repo_name=c.repo_name,revision=c.cur_rev,f_path=c.f_path))}
38 38 / ${h.link_to(_('show as raw'),
39 39 h.url('files_raw_home',repo_name=c.repo_name,revision=c.cur_rev,f_path=c.f_path))}
40 40 / ${h.link_to(_('download as raw'),
41 41 h.url('files_rawfile_home',repo_name=c.repo_name,revision=c.cur_rev,f_path=c.f_path))}
42 42 </dd>
43 43 </dl>
44 44 <div id="body" class="codeblock">
45 45 <div class="code-header">
46 <div class="revision">${c.file.name}@r${c.file.last_changeset.revision}:${c.file.last_changeset.short_id}</div>
46 <div class="revision">${c.file.name}@r${c.file.last_changeset.revision}:${c.file.last_changeset.raw_id}</div>
47 47 <div class="commit">"${c.file_msg}"</div>
48 48 </div>
49 49 <div class="code-body">
50 50 % if c.file.size < c.file_size_limit:
51 51 ${h.pygmentize_annotation(c.file,linenos=True,anchorlinenos=True,lineanchors='S',cssclass="code-highlight")}
52 52 %else:
53 53 ${_('File is to big to display')} ${h.link_to(_('show as raw'),
54 54 h.url('files_raw_home',repo_name=c.repo_name,revision=c.cur_rev,f_path=c.f_path))}
55 55 %endif
56 56 </div>
57 57 </div>
58 58 </div>
59 59 </div>
60 60 </div>
61 61 </%def> No newline at end of file
@@ -1,57 +1,57 b''
1 1 <dl>
2 2 <dt>${_('Last revision')}</dt>
3 3 <dd>
4 ${h.link_to("r%s:%s" % (c.files_list.last_changeset.revision,c.files_list.last_changeset.short_id),
5 h.url('files_home',repo_name=c.repo_name,revision=c.files_list.last_changeset.short_id,f_path=c.f_path))}
4 ${h.link_to("r%s:%s" % (c.files_list.last_changeset.revision,h.short_id(c.files_list.last_changeset.raw_id)),
5 h.url('files_home',repo_name=c.repo_name,revision=c.files_list.last_changeset.raw_id,f_path=c.f_path))}
6 6 </dd>
7 7 <dt>${_('Size')}</dt>
8 8 <dd>${h.format_byte_size(c.files_list.size,binary=True)}</dd>
9 9 <dt>${_('Mimetype')}</dt>
10 10 <dd>${c.files_list.mimetype}</dd>
11 11 <dt>${_('Options')}</dt>
12 12 <dd>${h.link_to(_('show annotation'),
13 13 h.url('files_annotate_home',repo_name=c.repo_name,revision=c.cur_rev,f_path=c.f_path))}
14 14 / ${h.link_to(_('show as raw'),
15 15 h.url('files_raw_home',repo_name=c.repo_name,revision=c.cur_rev,f_path=c.f_path))}
16 16 / ${h.link_to(_('download as raw'),
17 17 h.url('files_rawfile_home',repo_name=c.repo_name,revision=c.cur_rev,f_path=c.f_path))}
18 18 </dd>
19 19 <dt>${_('History')}</dt>
20 20 <dd>
21 21 <div>
22 22 ${h.form(h.url('files_diff_home',repo_name=c.repo_name,f_path=c.f_path),method='get')}
23 ${h.hidden('diff2',c.files_list.last_changeset.short_id)}
24 ${h.select('diff1',c.files_list.last_changeset.short_id,c.file_history)}
23 ${h.hidden('diff2',c.files_list.last_changeset.raw_id)}
24 ${h.select('diff1',c.files_list.last_changeset.raw_id,c.file_history)}
25 25 ${h.submit('diff','diff to revision',class_="ui-button ui-widget ui-state-default ui-corner-all")}
26 26 ${h.submit('show_rev','show at revision',class_="ui-button ui-widget ui-state-default ui-corner-all")}
27 27 ${h.end_form()}
28 28 </div>
29 29 </dd>
30 30 </dl>
31 31
32 32
33 33 <div id="body" class="codeblock">
34 34 <div class="code-header">
35 <div class="revision">${c.files_list.name}@r${c.files_list.last_changeset.revision}:${c.files_list.last_changeset.short_id}</div>
35 <div class="revision">${c.files_list.name}@r${c.files_list.last_changeset.revision}:${h.short_id(c.files_list.last_changeset.raw_id)}</div>
36 36 <div class="commit">"${c.files_list.last_changeset.message}"</div>
37 37 </div>
38 38 <div class="code-body">
39 39 % if c.files_list.size < c.file_size_limit:
40 40 ${h.pygmentize(c.files_list,linenos=True,anchorlinenos=True,lineanchors='S',cssclass="code-highlight")}
41 41 %else:
42 42 ${_('File is to big to display')} ${h.link_to(_('show as raw'),
43 43 h.url('files_raw_home',repo_name=c.repo_name,revision=c.cur_rev,f_path=c.f_path))}
44 44 %endif
45 45 </div>
46 46 </div>
47 47
48 48 <script type="text/javascript">
49 49 YAHOO.util.Event.onDOMReady(function(){
50 50 YAHOO.util.Event.addListener('show_rev','click',function(e){
51 51 YAHOO.util.Event.preventDefault(e);
52 52 var cs = YAHOO.util.Dom.get('diff1').value;
53 53 var url = "${h.url('files_home',repo_name=c.repo_name,revision='__CS__',f_path=c.f_path)}".replace('__CS__',cs);
54 54 window.location = url;
55 55 });
56 56 });
57 57 </script> No newline at end of file
@@ -1,107 +1,110 b''
1 1 ## -*- coding: utf-8 -*-
2 2 <%inherit file="base/base.html"/>
3 3 <%def name="title()">
4 4 ${_('Dashboard')} - ${c.rhodecode_name}
5 5 </%def>
6 6 <%def name="breadcrumbs()">
7 7 ${c.rhodecode_name}
8 8 </%def>
9 9 <%def name="page_nav()">
10 10 ${self.menu('home')}
11 11 </%def>
12 12 <%def name="main()">
13 13 <%def name="get_sort(name)">
14 14 <%name_slug = name.lower().replace(' ','_') %>
15 15
16 16 %if name_slug == c.sort_slug:
17 17 %if c.sort_by.startswith('-'):
18 18 <a href="?sort=${name_slug}">${name}&uarr;</a>
19 19 %else:
20 20 <a href="?sort=-${name_slug}">${name}&darr;</a>
21 21 %endif:
22 22 %else:
23 23 <a href="?sort=${name_slug}">${name}</a>
24 24 %endif
25 25 </%def>
26 26
27 27 <div class="box">
28 28 <!-- box / title -->
29 29 <div class="title">
30 30 <h5>${_('Dashboard')}</h5>
31 31 %if h.HasPermissionAny('hg.admin','hg.create.repository')():
32 32 <ul class="links">
33 33 <li>
34 34 <span>${h.link_to(_('ADD NEW REPOSITORY'),h.url('admin_settings_create_repository'))}</span>
35 35 </li>
36 36 </ul>
37 37 %endif
38 38 </div>
39 39 <!-- end box / title -->
40 40 <div class="table">
41 41 <table>
42 42 <thead>
43 43 <tr>
44 44 <th class="left">${get_sort(_('Name'))}</th>
45 45 <th class="left">${get_sort(_('Description'))}</th>
46 46 <th class="left">${get_sort(_('Last change'))}</th>
47 47 <th class="left">${get_sort(_('Tip'))}</th>
48 48 <th class="left">${get_sort(_('Owner'))}</th>
49 49 <th class="left">${_('RSS')}</th>
50 50 <th class="left">${_('Atom')}</th>
51 51 </tr>
52 52 </thead>
53 53 <tbody>
54 54 %for cnt,repo in enumerate(c.repos_list):
55 55 %if h.HasRepoPermissionAny('repository.write','repository.read','repository.admin')(repo['name'],'main page check'):
56 56 <tr class="parity${cnt%2}">
57 57 <td>
58 58 %if repo['repo'].dbrepo.repo_type =='hg':
59 <img class="icon" alt="${_('Mercurial repository')}" src="/images/icons/hgicon.png"/>
59 <img class="icon" title="${_('Mercurial repository')}" alt="${_('Mercurial repository')}" src="/images/icons/hgicon.png"/>
60 60 %elif repo['repo'].dbrepo.repo_type =='git':
61 <img class="icon" alt="${_('Git repository')}" src="/images/icons/giticon.png"/>
61 <img class="icon" title="${_('Git repository')}" alt="${_('Git repository')}" src="/images/icons/giticon.png"/>
62 62 %else:
63 63
64 64 %endif
65 65
66 66 %if repo['repo'].dbrepo.private:
67 <img class="icon" alt="${_('private')}" src="/images/icons/lock.png"/>
67 <img class="icon" title="${_('private repository')}" alt="${_('private repository')}" src="/images/icons/lock.png"/>
68 68 %else:
69 <img class="icon" alt="${_('public')}" src="/images/icons/lock_open.png"/>
69 <img class="icon" title="${_('public repository')}" alt="${_('public repository')}" src="/images/icons/lock_open.png"/>
70 70 %endif
71 71 ${h.link_to(repo['name'],
72 72 h.url('summary_home',repo_name=repo['name']))}
73 73 %if repo['repo'].dbrepo.fork:
74 74 <a href="${h.url('summary_home',repo_name=repo['repo'].dbrepo.fork.repo_name)}">
75 <img class="icon" alt="${_('public')}"
75 <img class="icon" alt="${_('fork')}"
76 76 title="${_('Fork of')} ${repo['repo'].dbrepo.fork.repo_name}"
77 77 src="/images/icons/arrow_divide.png"/></a>
78 78 %endif
79 79 </td>
80 <td title="${repo['description']}">${h.truncate(repo['description'],60)}</td>
80 <td><span class="tooltip" tooltip_title="${repo['description']}">
81 ${h.truncate(repo['description'],60)}</span>
82 </td>
81 83 <td><span class="tooltip" tooltip_title="${repo['last_change']}">
82 ${h.age(repo['last_change'])} </span></td>
84 ${h.age(repo['last_change'])} </span>
85 </td>
83 86 <td>
84 87 %if repo['rev']>=0:
85 ${h.link_to('r%s:%s' % (repo['rev'],repo['tip']),
88 ${h.link_to('r%s:%s' % (repo['rev'],h.short_id(repo['tip'])),
86 89 h.url('changeset_home',repo_name=repo['name'],revision=repo['tip']),
87 90 class_="tooltip",
88 91 tooltip_title=h.tooltip(repo['last_msg']))}
89 92 %else:
90 93 ${_('No changesets yet')}
91 94 %endif
92 95 </td>
93 96 <td title="${repo['contact']}">${h.person(repo['contact'])}</td>
94 97 <td>
95 98 <a title="${_('Subscribe to %s rss feed')%repo['name']}" class="rss_icon" href="${h.url('rss_feed_home',repo_name=repo['name'])}"></a>
96 99 </td>
97 100 <td>
98 101 <a title="${_('Subscribe to %s atom feed')%repo['name']}" class="atom_icon" href="${h.url('atom_feed_home',repo_name=repo['name'])}"></a>
99 102 </td>
100 103 </tr>
101 104 %endif
102 105 %endfor
103 106 </tbody>
104 107 </table>
105 108 </div>
106 109 </div>
107 110 </%def>
@@ -1,63 +1,63 b''
1 1 ## -*- coding: utf-8 -*-
2 2 % if c.repo_changesets:
3 3 <table>
4 4 <tr>
5 5 <th class="left">${_('date')}</th>
6 <th class="left">${_('commit message')}</th>
6 7 <th class="left">${_('author')}</th>
7 8 <th class="left">${_('revision')}</th>
8 <th class="left">${_('commit message')}</th>
9 9 <th class="left">${_('branch')}</th>
10 10 <th class="left">${_('tags')}</th>
11 11 <th class="left">${_('links')}</th>
12 12
13 13 </tr>
14 14 %for cnt,cs in enumerate(c.repo_changesets):
15 15 <tr class="parity${cnt%2}">
16 <td>${h.age(cs.date)} - ${cs.date} </td>
16 <td>${cs.date} - ${h.age(cs.date)}</td>
17 <td>
18 ${h.link_to(h.truncate(cs.message,60),
19 h.url('changeset_home',repo_name=c.repo_name,revision=cs.raw_id),
20 title=cs.message)}
21 </td>
17 22 <td title="${cs.author}">${h.person(cs.author)}</td>
18 <td>r${cs.revision}:${cs.short_id}</td>
19 <td>
20 ${h.link_to(h.truncate(cs.message,60),
21 h.url('changeset_home',repo_name=c.repo_name,revision=cs.short_id),
22 title=cs.message)}
23 </td>
23 <td>r${cs.revision}:${h.short_id(cs.raw_id)}</td>
24 24 <td>
25 25 <span class="logtags">
26 26 <span class="branchtag">${cs.branch}</span>
27 27 </span>
28 28 </td>
29 29 <td>
30 30 <span class="logtags">
31 31 %for tag in cs.tags:
32 32 <span class="tagtag">${tag}</span>
33 33 %endfor
34 34 </span>
35 35 </td>
36 36 <td class="nowrap">
37 ${h.link_to(_('changeset'),h.url('changeset_home',repo_name=c.repo_name,revision=cs.short_id))}
37 ${h.link_to(_('changeset'),h.url('changeset_home',repo_name=c.repo_name,revision=cs.raw_id))}
38 38 |
39 ${h.link_to(_('files'),h.url('files_home',repo_name=c.repo_name,revision=cs.short_id))}
39 ${h.link_to(_('files'),h.url('files_home',repo_name=c.repo_name,revision=cs.raw_id))}
40 40 </td>
41 41 </tr>
42 42 %endfor
43 43
44 44 </table>
45 45
46 46 <script type="text/javascript">
47 47 var data_div = 'shortlog_data';
48 48 YAHOO.util.Event.onDOMReady(function(){
49 49 YAHOO.util.Event.addListener(YAHOO.util.Dom.getElementsByClassName('pager_link'),"click",function(){
50 50 YAHOO.util.Dom.setStyle('shortlog_data','opacity','0.3');});});
51 51 </script>
52 52
53 53 <div class="pagination-wh pagination-left">
54 54 ${c.repo_changesets.pager('$link_previous ~2~ $link_next',
55 55 onclick="""YAHOO.util.Connect.asyncRequest('GET','$partial_url',{
56 56 success:function(o){YAHOO.util.Dom.get(data_div).innerHTML=o.responseText;
57 57 YAHOO.util.Event.addListener(YAHOO.util.Dom.getElementsByClassName('pager_link'),"click",function(){
58 58 YAHOO.util.Dom.setStyle(data_div,'opacity','0.3');});
59 59 YAHOO.util.Dom.setStyle(data_div,'opacity','1');}},null); return false;""")}
60 60 </div>
61 61 %else:
62 62 ${_('There are no changes yet')}
63 63 %endif
@@ -1,584 +1,593 b''
1 1 <%inherit file="/base/base.html"/>
2 2
3 3 <%def name="title()">
4 4 ${c.repo_name} ${_('Summary')} - ${c.rhodecode_name}
5 5 </%def>
6 6
7 7 <%def name="breadcrumbs_links()">
8 8 ${h.link_to(u'Home',h.url('/'))}
9 9 &raquo;
10 10 ${h.link_to(c.repo_name,h.url('summary_home',repo_name=c.repo_name))}
11 11 &raquo;
12 12 ${_('summary')}
13 13 </%def>
14 14
15 15 <%def name="page_nav()">
16 16 ${self.menu('summary')}
17 17 </%def>
18 18
19 19 <%def name="main()">
20 20 <script type="text/javascript">
21 21 var E = YAHOO.util.Event;
22 22 var D = YAHOO.util.Dom;
23 23
24 24 E.onDOMReady(function(e){
25 25 id = 'clone_url';
26 26 E.addListener(id,'click',function(e){
27 27 D.get('clone_url').select();
28 28 })
29 29 })
30 30 </script>
31 31 <div class="box box-left">
32 32 <!-- box / title -->
33 33 <div class="title">
34 34 ${self.breadcrumbs()}
35 35 </div>
36 36 <!-- end box / title -->
37 37 <div class="form">
38 38 <div class="fields">
39 39
40 40 <div class="field">
41 41 <div class="label">
42 42 <label>${_('Name')}:</label>
43 43 </div>
44 44 <div class="input-short">
45
46 %if c.repo_info.dbrepo.repo_type =='hg':
47 <img style="margin-bottom:2px" class="icon" title="${_('Mercurial repository')}" alt="${_('Mercurial repository')}" src="/images/icons/hgicon.png"/>
48 %elif c.repo_info.dbrepo.repo_type =='git':
49 <img style="margin-bottom:2px" class="icon" title="${_('Git repository')}" alt="${_('Git repository')}" src="/images/icons/giticon.png"/>
50 %else:
51
52 %endif
53
45 54 %if c.repo_info.dbrepo.private:
46 <img style="margin-bottom:2px" class="icon" title="${_('private repository')}" alt="${_('private')}" src="/images/icons/lock.png"/>
55 <img style="margin-bottom:2px" class="icon" title="${_('private repository')}" alt="${_('private repository')}" src="/images/icons/lock.png"/>
47 56 %else:
48 <img style="margin-bottom:2px" class="icon" title="${_('public repository')}" alt="${_('public')}" src="/images/icons/lock_open.png"/>
57 <img style="margin-bottom:2px" class="icon" title="${_('public repository')}" alt="${_('public repository')}" src="/images/icons/lock_open.png"/>
49 58 %endif
50 59 <span style="font-size: 1.6em;font-weight: bold;vertical-align: baseline;">${c.repo_info.name}</span>
51 60 <br/>
52 61 %if c.repo_info.dbrepo.fork:
53 62 <span style="margin-top:5px">
54 63 <a href="${h.url('summary_home',repo_name=c.repo_info.dbrepo.fork.repo_name)}">
55 64 <img class="icon" alt="${_('public')}"
56 65 title="${_('Fork of')} ${c.repo_info.dbrepo.fork.repo_name}"
57 66 src="/images/icons/arrow_divide.png"/>
58 67 ${_('Fork of')} ${c.repo_info.dbrepo.fork.repo_name}
59 68 </a>
60 69 </span>
61 70 %endif
62 71 </div>
63 72 </div>
64 73
65 74
66 75 <div class="field">
67 76 <div class="label">
68 77 <label>${_('Description')}:</label>
69 78 </div>
70 79 <div class="input-short">
71 80 ${c.repo_info.description}
72 81 </div>
73 82 </div>
74 83
75 84
76 85 <div class="field">
77 86 <div class="label">
78 87 <label>${_('Contact')}:</label>
79 88 </div>
80 89 <div class="input-short">
81 90 <div class="gravatar">
82 91 <img alt="gravatar" src="${h.gravatar_url(c.repo_info.dbrepo.user.email)}"/>
83 92 </div>
84 93 ${_('Username')}: ${c.repo_info.dbrepo.user.username}<br/>
85 94 ${_('Name')}: ${c.repo_info.dbrepo.user.name} ${c.repo_info.dbrepo.user.lastname}<br/>
86 95 ${_('Email')}: <a href="mailto:${c.repo_info.dbrepo.user.email}">${c.repo_info.dbrepo.user.email}</a>
87 96 </div>
88 97 </div>
89 98
90 99 <div class="field">
91 100 <div class="label">
92 101 <label>${_('Last change')}:</label>
93 102 </div>
94 103 <div class="input-short">
95 104 ${h.age(c.repo_info.last_change)} - ${c.repo_info.last_change}
96 105 ${_('by')} ${h.get_changeset_safe(c.repo_info,'tip').author}
97 106
98 107 </div>
99 108 </div>
100 109
101 110 <div class="field">
102 111 <div class="label">
103 112 <label>${_('Clone url')}:</label>
104 113 </div>
105 114 <div class="input-short">
106 115 <input type="text" id="clone_url" readonly="readonly" value="hg clone ${c.clone_repo_url}" size="70"/>
107 116 </div>
108 117 </div>
109 118
110 119 <div class="field">
111 120 <div class="label">
112 121 <label>${_('Trending languages')}:</label>
113 122 </div>
114 123 <div class="input-short">
115 124 <div id="lang_stats">
116 125
117 126 </div>
118 127 <script type="text/javascript">
119 128 var data = ${c.trending_languages|n};
120 129 var total = 0;
121 130 var no_data = true;
122 131 for (k in data){
123 132 total += data[k];
124 133 no_data = false;
125 134 }
126 135 var tbl = document.createElement('table');
127 136 tbl.setAttribute('class','trending_language_tbl');
128 137 for (k in data){
129 138 var tr = document.createElement('tr');
130 139 var percentage = Math.round((data[k]/total*100),2);
131 140 var value = data[k];
132 141 var td1 = document.createElement('td');
133 142 td1.width=150;
134 143 var trending_language_label = document.createElement('div');
135 144 trending_language_label.innerHTML = k;
136 145 td1.appendChild(trending_language_label);
137 146
138 147 var td2 = document.createElement('td');
139 148 var trending_language = document.createElement('div');
140 149 trending_language.title = k;
141 150 trending_language.innerHTML = "<b>"+percentage+"% "+value+" ${_('files')}</b>";
142 151 trending_language.setAttribute("class", 'trending_language top-right-rounded-corner bottom-right-rounded-corner');
143 152 trending_language.style.width=percentage+"%";
144 153 td2.appendChild(trending_language);
145 154
146 155 tr.appendChild(td1);
147 156 tr.appendChild(td2);
148 157 tbl.appendChild(tr);
149 158
150 159 }
151 160 if(no_data){
152 161 var tr = document.createElement('tr');
153 162 var td1 = document.createElement('td');
154 163 td1.innerHTML = "${_('No data loaded yet')}";
155 164 tr.appendChild(td1);
156 165 tbl.appendChild(tr);
157 166 }
158 167 YAHOO.util.Dom.get('lang_stats').appendChild(tbl);
159 168 </script>
160 169
161 170 </div>
162 171 </div>
163 172
164 173 <div class="field">
165 174 <div class="label">
166 175 <label>${_('Download')}:</label>
167 176 </div>
168 177 <div class="input-short">
169 178 %for cnt,archive in enumerate(c.repo_info._get_archives()):
170 179 %if cnt >=1:
171 180 |
172 181 %endif
173 182 ${h.link_to(c.repo_info.name+'.'+archive['type'],
174 183 h.url('files_archive_home',repo_name=c.repo_info.name,
175 184 revision='tip',fileformat=archive['extension']),class_="archive_icon")}
176 185 %endfor
177 186 </div>
178 187 </div>
179 188
180 189 <div class="field">
181 190 <div class="label">
182 191 <label>${_('Feeds')}:</label>
183 192 </div>
184 193 <div class="input-short">
185 194 ${h.link_to(_('RSS'),h.url('rss_feed_home',repo_name=c.repo_info.name),class_='rss_icon')}
186 195 ${h.link_to(_('Atom'),h.url('atom_feed_home',repo_name=c.repo_info.name),class_='atom_icon')}
187 196 </div>
188 197 </div>
189 198 </div>
190 199 </div>
191 200 </div>
192 201
193 202 <div class="box box-right" style="min-height:455px">
194 203 <!-- box / title -->
195 204 <div class="title">
196 205 <h5>${_('Commit activity by day / author')}</h5>
197 206 </div>
198 207
199 208 <div class="table">
200 209 <div id="commit_history" style="width:560px;height:300px;float:left"></div>
201 210 <div style="clear: both;height: 10px"></div>
202 211 <div id="overview" style="width:560px;height:100px;float:left"></div>
203 212
204 213 <div id="legend_data" style="clear:both;margin-top:10px;">
205 214 <div id="legend_container"></div>
206 215 <div id="legend_choices">
207 216 <table id="legend_choices_tables" style="font-size:smaller;color:#545454"></table>
208 217 </div>
209 218 </div>
210 219 <script type="text/javascript">
211 220 /**
212 221 * Plots summary graph
213 222 *
214 223 * @class SummaryPlot
215 224 * @param {from} initial from for detailed graph
216 225 * @param {to} initial to for detailed graph
217 226 * @param {dataset}
218 227 * @param {overview_dataset}
219 228 */
220 229 function SummaryPlot(from,to,dataset,overview_dataset) {
221 230 var initial_ranges = {
222 231 "xaxis":{
223 232 "from":from,
224 233 "to":to,
225 234 },
226 235 };
227 236 var dataset = dataset;
228 237 var overview_dataset = [overview_dataset];
229 238 var choiceContainer = YAHOO.util.Dom.get("legend_choices");
230 239 var choiceContainerTable = YAHOO.util.Dom.get("legend_choices_tables");
231 240 var plotContainer = YAHOO.util.Dom.get('commit_history');
232 241 var overviewContainer = YAHOO.util.Dom.get('overview');
233 242
234 243 var plot_options = {
235 244 bars: {show:true,align:'center',lineWidth:4},
236 245 legend: {show:true, container:"legend_container"},
237 246 points: {show:true,radius:0,fill:false},
238 247 yaxis: {tickDecimals:0,},
239 248 xaxis: {
240 249 mode: "time",
241 250 timeformat: "%d/%m",
242 251 min:from,
243 252 max:to,
244 253 },
245 254 grid: {
246 255 hoverable: true,
247 256 clickable: true,
248 257 autoHighlight:true,
249 258 color: "#999"
250 259 },
251 260 //selection: {mode: "x"}
252 261 };
253 262 var overview_options = {
254 263 legend:{show:false},
255 264 bars: {show:true,barWidth: 2,},
256 265 shadowSize: 0,
257 266 xaxis: {mode: "time", timeformat: "%d/%m/%y",},
258 267 yaxis: {ticks: 3, min: 0,},
259 268 grid: {color: "#999",},
260 269 selection: {mode: "x"}
261 270 };
262 271
263 272 /**
264 273 *get dummy data needed in few places
265 274 */
266 275 function getDummyData(label){
267 276 return {"label":label,
268 277 "data":[{"time":0,
269 278 "commits":0,
270 279 "added":0,
271 280 "changed":0,
272 281 "removed":0,
273 282 }],
274 283 "schema":["commits"],
275 284 "color":'#ffffff',
276 285 }
277 286 }
278 287
279 288 /**
280 289 * generate checkboxes accordindly to data
281 290 * @param keys
282 291 * @returns
283 292 */
284 293 function generateCheckboxes(data) {
285 294 //append checkboxes
286 295 var i = 0;
287 296 choiceContainerTable.innerHTML = '';
288 297 for(var pos in data) {
289 298
290 299 data[pos].color = i;
291 300 i++;
292 301 if(data[pos].label != ''){
293 302 choiceContainerTable.innerHTML += '<tr><td>'+
294 303 '<input type="checkbox" name="' + data[pos].label +'" checked="checked" />'
295 304 +data[pos].label+
296 305 '</td></tr>';
297 306 }
298 307 }
299 308 }
300 309
301 310 /**
302 311 * ToolTip show
303 312 */
304 313 function showTooltip(x, y, contents) {
305 314 var div=document.getElementById('tooltip');
306 315 if(!div) {
307 316 div = document.createElement('div');
308 317 div.id="tooltip";
309 318 div.style.position="absolute";
310 319 div.style.border='1px solid #fdd';
311 320 div.style.padding='2px';
312 321 div.style.backgroundColor='#fee';
313 322 document.body.appendChild(div);
314 323 }
315 324 YAHOO.util.Dom.setStyle(div, 'opacity', 0);
316 325 div.innerHTML = contents;
317 326 div.style.top=(y + 5) + "px";
318 327 div.style.left=(x + 5) + "px";
319 328
320 329 var anim = new YAHOO.util.Anim(div, {opacity: {to: 0.8}}, 0.2);
321 330 anim.animate();
322 331 }
323 332
324 333 /**
325 334 * This function will detect if selected period has some changesets for this user
326 335 if it does this data is then pushed for displaying
327 336 Additionally it will only display users that are selected by the checkbox
328 337 */
329 338 function getDataAccordingToRanges(ranges) {
330 339
331 340 var data = [];
332 341 var keys = [];
333 342 for(var key in dataset){
334 343 var push = false;
335 344 //method1 slow !!
336 345 ///*
337 346 for(var ds in dataset[key].data){
338 347 commit_data = dataset[key].data[ds];
339 348 //console.log(key);
340 349 //console.log(new Date(commit_data.time*1000));
341 350 //console.log(new Date(ranges.xaxis.from*1000));
342 351 //console.log(new Date(ranges.xaxis.to*1000));
343 352 if (commit_data.time >= ranges.xaxis.from && commit_data.time <= ranges.xaxis.to){
344 353 push = true;
345 354 break;
346 355 }
347 356 }
348 357 //*/
349 358 /*//method2 sorted commit data !!!
350 359 var first_commit = dataset[key].data[0].time;
351 360 var last_commit = dataset[key].data[dataset[key].data.length-1].time;
352 361
353 362 console.log(first_commit);
354 363 console.log(last_commit);
355 364
356 365 if (first_commit >= ranges.xaxis.from && last_commit <= ranges.xaxis.to){
357 366 push = true;
358 367 }
359 368 */
360 369 if(push){
361 370 data.push(dataset[key]);
362 371 }
363 372 }
364 373 if(data.length >= 1){
365 374 return data;
366 375 }
367 376 else{
368 377 //just return dummy data for graph to plot itself
369 378 return [getDummyData('')];
370 379 }
371 380
372 381 }
373 382
374 383 /**
375 384 * redraw using new checkbox data
376 385 */
377 386 function plotchoiced(e,args){
378 387 var cur_data = args[0];
379 388 var cur_ranges = args[1];
380 389
381 390 var new_data = [];
382 391 var inputs = choiceContainer.getElementsByTagName("input");
383 392
384 393 //show only checked labels
385 394 for(var i=0; i<inputs.length; i++) {
386 395 var checkbox_key = inputs[i].name;
387 396
388 397 if(inputs[i].checked){
389 398 for(var d in cur_data){
390 399 if(cur_data[d].label == checkbox_key){
391 400 new_data.push(cur_data[d]);
392 401 }
393 402 }
394 403 }
395 404 else{
396 405 //push dummy data to not hide the label
397 406 new_data.push(getDummyData(checkbox_key));
398 407 }
399 408 }
400 409
401 410 var new_options = YAHOO.lang.merge(plot_options, {
402 411 xaxis: {
403 412 min: cur_ranges.xaxis.from,
404 413 max: cur_ranges.xaxis.to,
405 414 mode:"time",
406 415 timeformat: "%d/%m",
407 416 }
408 417 });
409 418 if (!new_data){
410 419 new_data = [[0,1]];
411 420 }
412 421 // do the zooming
413 422 plot = YAHOO.widget.Flot(plotContainer, new_data, new_options);
414 423
415 424 plot.subscribe("plotselected", plotselected);
416 425
417 426 //resubscribe plothover
418 427 plot.subscribe("plothover", plothover);
419 428
420 429 // don't fire event on the overview to prevent eternal loop
421 430 overview.setSelection(cur_ranges, true);
422 431
423 432 }
424 433
425 434 /**
426 435 * plot only selected items from overview
427 436 * @param ranges
428 437 * @returns
429 438 */
430 439 function plotselected(ranges,cur_data) {
431 440 //updates the data for new plot
432 441 data = getDataAccordingToRanges(ranges);
433 442 generateCheckboxes(data);
434 443
435 444 var new_options = YAHOO.lang.merge(plot_options, {
436 445 xaxis: {
437 446 min: ranges.xaxis.from,
438 447 max: ranges.xaxis.to,
439 448 mode:"time",
440 449 timeformat: "%d/%m",
441 450 }
442 451 });
443 452 // do the zooming
444 453 plot = YAHOO.widget.Flot(plotContainer, data, new_options);
445 454
446 455 plot.subscribe("plotselected", plotselected);
447 456
448 457 //resubscribe plothover
449 458 plot.subscribe("plothover", plothover);
450 459
451 460 // don't fire event on the overview to prevent eternal loop
452 461 overview.setSelection(ranges, true);
453 462
454 463 //resubscribe choiced
455 464 YAHOO.util.Event.on(choiceContainer.getElementsByTagName("input"), "click", plotchoiced, [data, ranges]);
456 465 }
457 466
458 467 var previousPoint = null;
459 468
460 469 function plothover(o) {
461 470 var pos = o.pos;
462 471 var item = o.item;
463 472
464 473 //YAHOO.util.Dom.get("x").innerHTML = pos.x.toFixed(2);
465 474 //YAHOO.util.Dom.get("y").innerHTML = pos.y.toFixed(2);
466 475 if (item) {
467 476 if (previousPoint != item.datapoint) {
468 477 previousPoint = item.datapoint;
469 478
470 479 var tooltip = YAHOO.util.Dom.get("tooltip");
471 480 if(tooltip) {
472 481 tooltip.parentNode.removeChild(tooltip);
473 482 }
474 483 var x = item.datapoint.x.toFixed(2);
475 484 var y = item.datapoint.y.toFixed(2);
476 485
477 486 if (!item.series.label){
478 487 item.series.label = 'commits';
479 488 }
480 489 var d = new Date(x*1000);
481 490 var fd = d.toDateString()
482 491 var nr_commits = parseInt(y);
483 492
484 493 var cur_data = dataset[item.series.label].data[item.dataIndex];
485 494 var added = cur_data.added;
486 495 var changed = cur_data.changed;
487 496 var removed = cur_data.removed;
488 497
489 498 var nr_commits_suffix = " ${_('commits')} ";
490 499 var added_suffix = " ${_('files added')} ";
491 500 var changed_suffix = " ${_('files changed')} ";
492 501 var removed_suffix = " ${_('files removed')} ";
493 502
494 503
495 504 if(nr_commits == 1){nr_commits_suffix = " ${_('commit')} ";}
496 505 if(added==1){added_suffix=" ${_('file added')} ";}
497 506 if(changed==1){changed_suffix=" ${_('file changed')} ";}
498 507 if(removed==1){removed_suffix=" ${_('file removed')} ";}
499 508
500 509 showTooltip(item.pageX, item.pageY, item.series.label + " on " + fd
501 510 +'<br/>'+
502 511 nr_commits + nr_commits_suffix+'<br/>'+
503 512 added + added_suffix +'<br/>'+
504 513 changed + changed_suffix + '<br/>'+
505 514 removed + removed_suffix + '<br/>');
506 515 }
507 516 }
508 517 else {
509 518 var tooltip = YAHOO.util.Dom.get("tooltip");
510 519
511 520 if(tooltip) {
512 521 tooltip.parentNode.removeChild(tooltip);
513 522 }
514 523 previousPoint = null;
515 524 }
516 525 }
517 526
518 527 /**
519 528 * MAIN EXECUTION
520 529 */
521 530
522 531 var data = getDataAccordingToRanges(initial_ranges);
523 532 generateCheckboxes(data);
524 533
525 534 //main plot
526 535 var plot = YAHOO.widget.Flot(plotContainer,data,plot_options);
527 536
528 537 //overview
529 538 var overview = YAHOO.widget.Flot(overviewContainer, overview_dataset, overview_options);
530 539
531 540 //show initial selection on overview
532 541 overview.setSelection(initial_ranges);
533 542
534 543 plot.subscribe("plotselected", plotselected);
535 544
536 545 overview.subscribe("plotselected", function (ranges) {
537 546 plot.setSelection(ranges);
538 547 });
539 548
540 549 plot.subscribe("plothover", plothover);
541 550
542 551 YAHOO.util.Event.on(choiceContainer.getElementsByTagName("input"), "click", plotchoiced, [data, initial_ranges]);
543 552 }
544 553 SummaryPlot(${c.ts_min},${c.ts_max},${c.commit_data|n},${c.overview_data|n});
545 554 </script>
546 555
547 556 </div>
548 557 </div>
549 558
550 559 <div class="box">
551 560 <div class="title">
552 561 <div class="breadcrumbs">${h.link_to(_('Last ten changes'),h.url('changelog_home',repo_name=c.repo_name))}</div>
553 562 </div>
554 563 <div class="table">
555 564 <%include file='../shortlog/shortlog_data.html'/>
556 565 %if c.repo_changesets:
557 566 ${h.link_to(_('show more'),h.url('changelog_home',repo_name=c.repo_name))}
558 567 %endif
559 568 </div>
560 569 </div>
561 570 <div class="box">
562 571 <div class="title">
563 572 <div class="breadcrumbs">${h.link_to(_('Last ten tags'),h.url('tags_home',repo_name=c.repo_name))}</div>
564 573 </div>
565 574 <div class="table">
566 575 <%include file='../tags/tags_data.html'/>
567 576 %if c.repo_changesets:
568 577 ${h.link_to(_('show more'),h.url('tags_home',repo_name=c.repo_name))}
569 578 %endif
570 579 </div>
571 580 </div>
572 581 <div class="box">
573 582 <div class="title">
574 583 <div class="breadcrumbs">${h.link_to(_('Last ten branches'),h.url('branches_home',repo_name=c.repo_name))}</div>
575 584 </div>
576 585 <div class="table">
577 586 <%include file='../branches/branches_data.html'/>
578 587 %if c.repo_changesets:
579 588 ${h.link_to(_('show more'),h.url('branches_home',repo_name=c.repo_name))}
580 589 %endif
581 590 </div>
582 591 </div>
583 592
584 593 </%def> No newline at end of file
@@ -1,29 +1,31 b''
1 1 %if c.repo_tags:
2 2 <table>
3 3 <tr>
4 <th class="left">${_('date')}</th>
5 <th class="left">${_('revision')}</th>
6 <th class="left">${_('name')}</th>
4 <th class="left">${_('date')}</th>
5 <th class="left">${_('name')}</th>
6 <th class="left">${_('author')}</th>
7 <th class="left">${_('revision')}</th>
7 8 <th class="left">${_('links')}</th>
8 9 </tr>
9 10 %for cnt,tag in enumerate(c.repo_tags.items()):
10 <tr class="parity${cnt%2}">
11 <td>${h.age(tag[1].date)}</td>
12 <td>r${tag[1].revision}:${tag[1].short_id}</td>
13 <td>
14 <span class="logtags">
15 <span class="tagtag">${h.link_to(tag[0],
16 h.url('changeset_home',repo_name=c.repo_name,revision=tag[1].short_id))}</span>
17 </span>
18 </td>
11 <tr class="parity${cnt%2}">
12 <td>${tag[1].date} - ${h.age(tag[1].date)}</td>
13 <td>
14 <span class="logtags">
15 <span class="tagtag">${h.link_to(tag[0],
16 h.url('changeset_home',repo_name=c.repo_name,revision=tag[1].raw_id))}</span>
17 </span>
18 </td>
19 <td title="${tag[1].author}">${h.person(tag[1].author)}</td>
20 <td>r${tag[1].revision}:${h.short_id(tag[1].raw_id)}</td>
19 21 <td class="nowrap">
20 ${h.link_to(_('changeset'),h.url('changeset_home',repo_name=c.repo_name,revision=tag[1].short_id))}
22 ${h.link_to(_('changeset'),h.url('changeset_home',repo_name=c.repo_name,revision=tag[1].raw_id))}
21 23 |
22 ${h.link_to(_('files'),h.url('files_home',repo_name=c.repo_name,revision=tag[1].short_id))}
24 ${h.link_to(_('files'),h.url('files_home',repo_name=c.repo_name,revision=tag[1].raw_id))}
23 25 </td>
24 26 </tr>
25 27 %endfor
26 28 </table>
27 29 %else:
28 30 ${_('There are no tags yet')}
29 31 %endif No newline at end of file
General Comments 0
You need to be logged in to leave comments. Login now