##// END OF EJS Templates
#179 Added followers page
marcink -
r1279:cb216757 beta
parent child Browse files
Show More
@@ -0,0 +1,57
1 # -*- coding: utf-8 -*-
2 """
3 rhodecode.controllers.followers
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
6 Followers controller for rhodecode
7
8 :created_on: Apr 23, 2011
9 :author: marcink
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
12 """
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
17 #
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
22 #
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 import logging
26
27 from pylons import tmpl_context as c, request
28
29 from rhodecode.lib.helpers import Page
30 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
31 from rhodecode.lib.base import BaseRepoController, render
32 from rhodecode.model.db import Repository, User, UserFollowing
33
34 log = logging.getLogger(__name__)
35
36
37 class FollowersController(BaseRepoController):
38
39 @LoginRequired()
40 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
41 'repository.admin')
42 def __before__(self):
43 super(FollowersController, self).__before__()
44
45 def followers(self, repo_name):
46 p = int(request.params.get('page', 1))
47 repo_id = getattr(Repository.by_repo_name(repo_name), 'repo_id')
48 d = UserFollowing.get_repo_followers(repo_id)\
49 .order_by(UserFollowing.follows_from)
50 c.followers_pager = Page(d, page=p, items_per_page=20)
51
52 c.followers_data = render('/followers/followers_data.html')
53
54 if request.params.get('partial'):
55 return c.followers_data
56
57 return render('/followers/followers.html')
@@ -0,0 +1,33
1 ## -*- coding: utf-8 -*-
2 <%inherit file="/base/base.html"/>
3
4 <%def name="title()">
5 ${c.repo_name} ${_('Followers')} - ${c.rhodecode_name}
6 </%def>
7
8
9 <%def name="breadcrumbs_links()">
10 ${h.link_to(u'Home',h.url('/'))}
11 &raquo;
12 ${h.link_to(c.repo_name,h.url('summary_home',repo_name=c.repo_name))}
13 &raquo;
14 ${_('followers')}
15 </%def>
16
17 <%def name="page_nav()">
18 ${self.menu('followers')}
19 </%def>
20 <%def name="main()">
21 <div class="box">
22 <!-- box / title -->
23 <div class="title">
24 ${self.breadcrumbs()}
25 </div>
26 <!-- end box / title -->
27 <div class="table">
28 <div id="followers">
29 ${c.followers_data}
30 </div>
31 </div>
32 </div>
33 </%def> No newline at end of file
@@ -0,0 +1,37
1 ## -*- coding: utf-8 -*-
2
3
4 % for f in c.followers_pager:
5 <div>
6 <div class="follower_user">
7 <div class="gravatar">
8 <img alt="gravatar" src="${h.gravatar_url(f.user.email,24)}"/>
9 </div>
10 <span style="font-size: 20px"> <b>${f.user.username}</b> (${f.user.name} ${f.user.lastname})</span>
11 </div>
12 <div style="clear:both;padding-top: 10px"></div>
13 <div class="follower_date">${_('Started following on')} - ${f.follows_from}</div>
14 <div style="border-bottom: 1px solid #DDD;margin:10px 0px 10px 0px"></div>
15 </div>
16 % endfor
17
18
19 <div class="pagination-wh pagination-left">
20 <script type="text/javascript">
21 var data_div = 'followers';
22 YAHOO.util.Event.onDOMReady(function(){
23 YAHOO.util.Event.addListener(
24 YUD.getElementsByClassName('pager_link'),"click",
25 function(){
26 YAHOO.util.Dom.setStyle(data_div,'opacity','0.3');
27 });
28 });
29 </script>
30
31 ${c.followers_pager.pager('$link_previous ~2~ $link_next',
32 onclick="""YAHOO.util.Connect.asyncRequest('GET','$partial_url',{
33 success:function(o){YAHOO.util.Dom.get(data_div).innerHTML=o.responseText;
34 YUE.on(YAHOO.util.Dom.getElementsByClassName('pager_link'),"click",function(){
35 YAHOO.util.Dom.setStyle(data_div,'opacity','0.3');});
36 YAHOO.util.Dom.setStyle(data_div,'opacity','1');}},null); return false;""")}
37 </div> No newline at end of file
@@ -1,339 +1,344
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
13 13 def make_map(config):
14 14 """Create, configure and return the routes Mapper"""
15 15 rmap = Mapper(directory=config['pylons.paths']['controllers'],
16 16 always_scan=config['debug'])
17 17 rmap.minimization = False
18 18 rmap.explicit = False
19 19
20 20 def check_repo(environ, match_dict):
21 21 """
22 22 check for valid repository for proper 404 handling
23 23 :param environ:
24 24 :param match_dict:
25 25 """
26 26 repo_name = match_dict.get('repo_name')
27 27 return not cr(repo_name, config['base_path'])
28 28
29 29 # The ErrorController route (handles 404/500 error pages); it should
30 30 # likely stay at the top, ensuring it can always be resolved
31 31 rmap.connect('/error/{action}', controller='error')
32 32 rmap.connect('/error/{action}/{id}', controller='error')
33 33
34 34 #==========================================================================
35 35 # CUSTOM ROUTES HERE
36 36 #==========================================================================
37 37
38 38 #MAIN PAGE
39 39 rmap.connect('home', '/', controller='home', action='index')
40 40 rmap.connect('repo_switcher', '/repos', controller='home',
41 41 action='repo_switcher')
42 42 rmap.connect('bugtracker',
43 43 "http://bitbucket.org/marcinkuzminski/rhodecode/issues",
44 44 _static=True)
45 45 rmap.connect('rhodecode_official', "http://rhodecode.org", _static=True)
46 46
47 47 #ADMIN REPOSITORY REST ROUTES
48 48 with rmap.submapper(path_prefix='/_admin', controller='admin/repos') as m:
49 49 m.connect("repos", "/repos",
50 50 action="create", conditions=dict(method=["POST"]))
51 51 m.connect("repos", "/repos",
52 52 action="index", conditions=dict(method=["GET"]))
53 53 m.connect("formatted_repos", "/repos.{format}",
54 54 action="index",
55 55 conditions=dict(method=["GET"]))
56 56 m.connect("new_repo", "/repos/new",
57 57 action="new", conditions=dict(method=["GET"]))
58 58 m.connect("formatted_new_repo", "/repos/new.{format}",
59 59 action="new", conditions=dict(method=["GET"]))
60 60 m.connect("/repos/{repo_name:.*}",
61 61 action="update", conditions=dict(method=["PUT"],
62 62 function=check_repo))
63 63 m.connect("/repos/{repo_name:.*}",
64 64 action="delete", conditions=dict(method=["DELETE"],
65 65 function=check_repo))
66 66 m.connect("edit_repo", "/repos/{repo_name:.*}/edit",
67 67 action="edit", conditions=dict(method=["GET"],
68 68 function=check_repo))
69 69 m.connect("formatted_edit_repo", "/repos/{repo_name:.*}.{format}/edit",
70 70 action="edit", conditions=dict(method=["GET"],
71 71 function=check_repo))
72 72 m.connect("repo", "/repos/{repo_name:.*}",
73 73 action="show", conditions=dict(method=["GET"],
74 74 function=check_repo))
75 75 m.connect("formatted_repo", "/repos/{repo_name:.*}.{format}",
76 76 action="show", conditions=dict(method=["GET"],
77 77 function=check_repo))
78 78 #ajax delete repo perm user
79 79 m.connect('delete_repo_user', "/repos_delete_user/{repo_name:.*}",
80 80 action="delete_perm_user", conditions=dict(method=["DELETE"],
81 81 function=check_repo))
82 82 #ajax delete repo perm users_group
83 83 m.connect('delete_repo_users_group',
84 84 "/repos_delete_users_group/{repo_name:.*}",
85 85 action="delete_perm_users_group",
86 86 conditions=dict(method=["DELETE"], function=check_repo))
87 87
88 88 #settings actions
89 89 m.connect('repo_stats', "/repos_stats/{repo_name:.*}",
90 90 action="repo_stats", conditions=dict(method=["DELETE"],
91 91 function=check_repo))
92 92 m.connect('repo_cache', "/repos_cache/{repo_name:.*}",
93 93 action="repo_cache", conditions=dict(method=["DELETE"],
94 94 function=check_repo))
95 95 m.connect('repo_public_journal',
96 96 "/repos_public_journal/{repo_name:.*}",
97 97 action="repo_public_journal", conditions=dict(method=["PUT"],
98 98 function=check_repo))
99 99 m.connect('repo_pull', "/repo_pull/{repo_name:.*}",
100 100 action="repo_pull", conditions=dict(method=["PUT"],
101 101 function=check_repo))
102 102
103 103 #ADMIN REPOS GROUP REST ROUTES
104 104 rmap.resource('repos_group', 'repos_groups',
105 105 controller='admin/repos_groups', path_prefix='/_admin')
106 106
107 107 #ADMIN USER REST ROUTES
108 108 with rmap.submapper(path_prefix='/_admin', controller='admin/users') as m:
109 109 m.connect("users", "/users",
110 110 action="create", conditions=dict(method=["POST"]))
111 111 m.connect("users", "/users",
112 112 action="index", conditions=dict(method=["GET"]))
113 113 m.connect("formatted_users", "/users.{format}",
114 114 action="index", conditions=dict(method=["GET"]))
115 115 m.connect("new_user", "/users/new",
116 116 action="new", conditions=dict(method=["GET"]))
117 117 m.connect("formatted_new_user", "/users/new.{format}",
118 118 action="new", conditions=dict(method=["GET"]))
119 119 m.connect("update_user", "/users/{id}",
120 120 action="update", conditions=dict(method=["PUT"]))
121 121 m.connect("delete_user", "/users/{id}",
122 122 action="delete", conditions=dict(method=["DELETE"]))
123 123 m.connect("edit_user", "/users/{id}/edit",
124 124 action="edit", conditions=dict(method=["GET"]))
125 125 m.connect("formatted_edit_user",
126 126 "/users/{id}.{format}/edit",
127 127 action="edit", conditions=dict(method=["GET"]))
128 128 m.connect("user", "/users/{id}",
129 129 action="show", conditions=dict(method=["GET"]))
130 130 m.connect("formatted_user", "/users/{id}.{format}",
131 131 action="show", conditions=dict(method=["GET"]))
132 132
133 133 #EXTRAS USER ROUTES
134 134 m.connect("user_perm", "/users_perm/{id}",
135 135 action="update_perm", conditions=dict(method=["PUT"]))
136 136
137 137 #ADMIN USERS REST ROUTES
138 138 with rmap.submapper(path_prefix='/_admin',
139 139 controller='admin/users_groups') as m:
140 140 m.connect("users_groups", "/users_groups",
141 141 action="create", conditions=dict(method=["POST"]))
142 142 m.connect("users_groups", "/users_groups",
143 143 action="index", conditions=dict(method=["GET"]))
144 144 m.connect("formatted_users_groups", "/users_groups.{format}",
145 145 action="index", conditions=dict(method=["GET"]))
146 146 m.connect("new_users_group", "/users_groups/new",
147 147 action="new", conditions=dict(method=["GET"]))
148 148 m.connect("formatted_new_users_group", "/users_groups/new.{format}",
149 149 action="new", conditions=dict(method=["GET"]))
150 150 m.connect("update_users_group", "/users_groups/{id}",
151 151 action="update", conditions=dict(method=["PUT"]))
152 152 m.connect("delete_users_group", "/users_groups/{id}",
153 153 action="delete", conditions=dict(method=["DELETE"]))
154 154 m.connect("edit_users_group", "/users_groups/{id}/edit",
155 155 action="edit", conditions=dict(method=["GET"]))
156 156 m.connect("formatted_edit_users_group",
157 157 "/users_groups/{id}.{format}/edit",
158 158 action="edit", conditions=dict(method=["GET"]))
159 159 m.connect("users_group", "/users_groups/{id}",
160 160 action="show", conditions=dict(method=["GET"]))
161 161 m.connect("formatted_users_group", "/users_groups/{id}.{format}",
162 162 action="show", conditions=dict(method=["GET"]))
163 163
164 164 #EXTRAS USER ROUTES
165 165 m.connect("users_group_perm", "/users_groups_perm/{id}",
166 166 action="update_perm", conditions=dict(method=["PUT"]))
167 167
168 168 #ADMIN GROUP REST ROUTES
169 169 rmap.resource('group', 'groups',
170 170 controller='admin/groups', path_prefix='/_admin')
171 171
172 172 #ADMIN PERMISSIONS REST ROUTES
173 173 rmap.resource('permission', 'permissions',
174 174 controller='admin/permissions', path_prefix='/_admin')
175 175
176 176 ##ADMIN LDAP SETTINGS
177 177 rmap.connect('ldap_settings', '/_admin/ldap',
178 178 controller='admin/ldap_settings', action='ldap_settings',
179 179 conditions=dict(method=["POST"]))
180 180
181 181 rmap.connect('ldap_home', '/_admin/ldap',
182 182 controller='admin/ldap_settings')
183 183
184 184 #ADMIN SETTINGS REST ROUTES
185 185 with rmap.submapper(path_prefix='/_admin',
186 186 controller='admin/settings') as m:
187 187 m.connect("admin_settings", "/settings",
188 188 action="create", conditions=dict(method=["POST"]))
189 189 m.connect("admin_settings", "/settings",
190 190 action="index", conditions=dict(method=["GET"]))
191 191 m.connect("formatted_admin_settings", "/settings.{format}",
192 192 action="index", conditions=dict(method=["GET"]))
193 193 m.connect("admin_new_setting", "/settings/new",
194 194 action="new", conditions=dict(method=["GET"]))
195 195 m.connect("formatted_admin_new_setting", "/settings/new.{format}",
196 196 action="new", conditions=dict(method=["GET"]))
197 197 m.connect("/settings/{setting_id}",
198 198 action="update", conditions=dict(method=["PUT"]))
199 199 m.connect("/settings/{setting_id}",
200 200 action="delete", conditions=dict(method=["DELETE"]))
201 201 m.connect("admin_edit_setting", "/settings/{setting_id}/edit",
202 202 action="edit", conditions=dict(method=["GET"]))
203 203 m.connect("formatted_admin_edit_setting",
204 204 "/settings/{setting_id}.{format}/edit",
205 205 action="edit", conditions=dict(method=["GET"]))
206 206 m.connect("admin_setting", "/settings/{setting_id}",
207 207 action="show", conditions=dict(method=["GET"]))
208 208 m.connect("formatted_admin_setting", "/settings/{setting_id}.{format}",
209 209 action="show", conditions=dict(method=["GET"]))
210 210 m.connect("admin_settings_my_account", "/my_account",
211 211 action="my_account", conditions=dict(method=["GET"]))
212 212 m.connect("admin_settings_my_account_update", "/my_account_update",
213 213 action="my_account_update", conditions=dict(method=["PUT"]))
214 214 m.connect("admin_settings_create_repository", "/create_repository",
215 215 action="create_repository", conditions=dict(method=["GET"]))
216 216
217 217 #ADMIN MAIN PAGES
218 218 with rmap.submapper(path_prefix='/_admin', controller='admin/admin') as m:
219 219 m.connect('admin_home', '', action='index')
220 220 m.connect('admin_add_repo', '/add_repo/{new_repo:[a-z0-9\. _-]*}',
221 221 action='add_repo')
222 222
223 223 #USER JOURNAL
224 224 rmap.connect('journal', '/_admin/journal', controller='journal')
225 225
226 226 rmap.connect('public_journal', '/_admin/public_journal',
227 227 controller='journal', action="public_journal")
228 228
229 229 rmap.connect('public_journal_rss', '/_admin/public_journal_rss',
230 230 controller='journal', action="public_journal_rss")
231 231
232 232 rmap.connect('public_journal_atom', '/_admin/public_journal_atom',
233 233 controller='journal', action="public_journal_atom")
234 234
235 235 rmap.connect('toggle_following', '/_admin/toggle_following',
236 236 controller='journal', action='toggle_following',
237 237 conditions=dict(method=["POST"]))
238 238
239 239 #SEARCH
240 240 rmap.connect('search', '/_admin/search', controller='search',)
241 241 rmap.connect('search_repo', '/_admin/search/{search_repo:.*}',
242 242 controller='search')
243 243
244 244 #LOGIN/LOGOUT/REGISTER/SIGN IN
245 245 rmap.connect('login_home', '/_admin/login', controller='login')
246 246 rmap.connect('logout_home', '/_admin/logout', controller='login',
247 247 action='logout')
248 248
249 249 rmap.connect('register', '/_admin/register', controller='login',
250 250 action='register')
251 251
252 252 rmap.connect('reset_password', '/_admin/password_reset',
253 253 controller='login', action='password_reset')
254 254
255 255 #FEEDS
256 256 rmap.connect('rss_feed_home', '/{repo_name:.*}/feed/rss',
257 257 controller='feed', action='rss',
258 258 conditions=dict(function=check_repo))
259 259
260 260 rmap.connect('atom_feed_home', '/{repo_name:.*}/feed/atom',
261 261 controller='feed', action='atom',
262 262 conditions=dict(function=check_repo))
263 263
264 #REPOSITORY ROUTES
264 #==========================================================================
265 # REPOSITORY ROUTES
266 #==========================================================================
265 267 rmap.connect('changeset_home', '/{repo_name:.*}/changeset/{revision}',
266 268 controller='changeset', revision='tip',
267 269 conditions=dict(function=check_repo))
268 270
269 271 rmap.connect('raw_changeset_home',
270 272 '/{repo_name:.*}/raw-changeset/{revision}',
271 273 controller='changeset', action='raw_changeset',
272 274 revision='tip', conditions=dict(function=check_repo))
273 275
274 276 rmap.connect('summary_home', '/{repo_name:.*}',
275 277 controller='summary', conditions=dict(function=check_repo))
276 278
277 279 rmap.connect('summary_home', '/{repo_name:.*}/summary',
278 280 controller='summary', conditions=dict(function=check_repo))
279 281
280 282 rmap.connect('shortlog_home', '/{repo_name:.*}/shortlog',
281 283 controller='shortlog', conditions=dict(function=check_repo))
282 284
283 285 rmap.connect('branches_home', '/{repo_name:.*}/branches',
284 286 controller='branches', conditions=dict(function=check_repo))
285 287
286 288 rmap.connect('tags_home', '/{repo_name:.*}/tags',
287 289 controller='tags', conditions=dict(function=check_repo))
288 290
289 291 rmap.connect('changelog_home', '/{repo_name:.*}/changelog',
290 292 controller='changelog', conditions=dict(function=check_repo))
291 293
292 294 rmap.connect('files_home', '/{repo_name:.*}/files/{revision}/{f_path:.*}',
293 295 controller='files', revision='tip', f_path='',
294 296 conditions=dict(function=check_repo))
295 297
296 298 rmap.connect('files_diff_home', '/{repo_name:.*}/diff/{f_path:.*}',
297 299 controller='files', action='diff', revision='tip', f_path='',
298 300 conditions=dict(function=check_repo))
299 301
300 302 rmap.connect('files_rawfile_home',
301 303 '/{repo_name:.*}/rawfile/{revision}/{f_path:.*}',
302 304 controller='files', action='rawfile', revision='tip',
303 305 f_path='', conditions=dict(function=check_repo))
304 306
305 307 rmap.connect('files_raw_home',
306 308 '/{repo_name:.*}/raw/{revision}/{f_path:.*}',
307 309 controller='files', action='raw', revision='tip', f_path='',
308 310 conditions=dict(function=check_repo))
309 311
310 312 rmap.connect('files_annotate_home',
311 313 '/{repo_name:.*}/annotate/{revision}/{f_path:.*}',
312 314 controller='files', action='annotate', revision='tip',
313 315 f_path='', conditions=dict(function=check_repo))
314 316
315 317 rmap.connect('files_archive_home', '/{repo_name:.*}/archive/{fname}',
316 318 controller='files', action='archivefile',
317 319 conditions=dict(function=check_repo))
318 320
319 321 rmap.connect('repo_settings_delete', '/{repo_name:.*}/settings',
320 322 controller='settings', action="delete",
321 323 conditions=dict(method=["DELETE"], function=check_repo))
322 324
323 325 rmap.connect('repo_settings_update', '/{repo_name:.*}/settings',
324 326 controller='settings', action="update",
325 327 conditions=dict(method=["PUT"], function=check_repo))
326 328
327 329 rmap.connect('repo_settings_home', '/{repo_name:.*}/settings',
328 330 controller='settings', action='index',
329 331 conditions=dict(function=check_repo))
330 332
331 333 rmap.connect('repo_fork_create_home', '/{repo_name:.*}/fork',
332 334 controller='settings', action='fork_create',
333 335 conditions=dict(function=check_repo, method=["POST"]))
334 336
335 337 rmap.connect('repo_fork_home', '/{repo_name:.*}/fork',
336 338 controller='settings', action='fork',
337 339 conditions=dict(function=check_repo))
338 340
341 rmap.connect('repo_followers_home', '/{repo_name:.*}/followers',
342 controller='followers', action='followers',
343 conditions=dict(function=check_repo))
339 344 return rmap
@@ -1,519 +1,526
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.model.db
4 4 ~~~~~~~~~~~~~~~~~~
5 5
6 6 Database Models for RhodeCode
7 7
8 8 :created_on: Apr 08, 2010
9 9 :author: marcink
10 10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 11 :license: GPLv3, see COPYING for more details.
12 12 """
13 13 # This program is free software: you can redistribute it and/or modify
14 14 # it under the terms of the GNU General Public License as published by
15 15 # the Free Software Foundation, either version 3 of the License, or
16 16 # (at your option) any later version.
17 17 #
18 18 # This program is distributed in the hope that it will be useful,
19 19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 21 # GNU General Public License for more details.
22 22 #
23 23 # You should have received a copy of the GNU General Public License
24 24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 25
26 26 import os
27 27 import logging
28 28 import datetime
29 29 from datetime import date
30 30
31 31 from sqlalchemy import *
32 32 from sqlalchemy.exc import DatabaseError
33 33 from sqlalchemy.orm import relationship, backref
34 34 from sqlalchemy.orm.interfaces import MapperExtension
35 35
36 36 from rhodecode.lib import str2bool
37 37 from rhodecode.model.meta import Base, Session
38 38 from rhodecode.model.caching_query import FromCache
39 39
40 40 log = logging.getLogger(__name__)
41 41
42 42 #==============================================================================
43 43 # MAPPER EXTENSIONS
44 44 #==============================================================================
45 45
46 46 class RepositoryMapper(MapperExtension):
47 47 def after_update(self, mapper, connection, instance):
48 48 pass
49 49
50 50
51 51 class RhodeCodeSettings(Base):
52 52 __tablename__ = 'rhodecode_settings'
53 53 __table_args__ = (UniqueConstraint('app_settings_name'), {'useexisting':True})
54 54 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
55 55 app_settings_name = Column("app_settings_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
56 56 app_settings_value = Column("app_settings_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
57 57
58 58 def __init__(self, k='', v=''):
59 59 self.app_settings_name = k
60 60 self.app_settings_value = v
61 61
62 62 def __repr__(self):
63 63 return "<%s('%s:%s')>" % (self.__class__.__name__,
64 64 self.app_settings_name, self.app_settings_value)
65 65
66 66
67 67 @classmethod
68 68 def get_app_settings(cls, cache=False):
69 69
70 70 ret = Session.query(cls)
71 71
72 72 if cache:
73 73 ret = ret.options(FromCache("sql_cache_short", "get_hg_settings"))
74 74
75 75 if not ret:
76 76 raise Exception('Could not get application settings !')
77 77 settings = {}
78 78 for each in ret:
79 79 settings['rhodecode_' + each.app_settings_name] = \
80 80 each.app_settings_value
81 81
82 82 return settings
83 83
84 84 @classmethod
85 85 def get_ldap_settings(cls, cache=False):
86 86 ret = Session.query(cls)\
87 87 .filter(cls.app_settings_name.startswith('ldap_'))\
88 88 .all()
89 89 fd = {}
90 90 for row in ret:
91 91 fd.update({row.app_settings_name:str2bool(row.app_settings_value)})
92 92 return fd
93 93
94 94
95 95 class RhodeCodeUi(Base):
96 96 __tablename__ = 'rhodecode_ui'
97 97 __table_args__ = {'useexisting':True}
98 98 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
99 99 ui_section = Column("ui_section", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
100 100 ui_key = Column("ui_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
101 101 ui_value = Column("ui_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
102 102 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
103 103
104 104
105 105 class User(Base):
106 106 __tablename__ = 'users'
107 107 __table_args__ = (UniqueConstraint('username'), UniqueConstraint('email'), {'useexisting':True})
108 108 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
109 109 username = Column("username", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
110 110 password = Column("password", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
111 111 active = Column("active", Boolean(), nullable=True, unique=None, default=None)
112 112 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
113 113 name = Column("name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
114 114 lastname = Column("lastname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
115 115 email = Column("email", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
116 116 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
117 117 ldap_dn = Column("ldap_dn", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
118 118 api_key = Column("api_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
119 119
120 120 user_log = relationship('UserLog', cascade='all')
121 121 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
122 122
123 123 repositories = relationship('Repository')
124 124 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
125 125
126 126 group_member = relationship('UsersGroupMember', cascade='all')
127 127
128 128 @property
129 129 def full_contact(self):
130 130 return '%s %s <%s>' % (self.name, self.lastname, self.email)
131 131
132 132 @property
133 133 def short_contact(self):
134 134 return '%s %s' % (self.name, self.lastname)
135 135
136 136
137 137 @property
138 138 def is_admin(self):
139 139 return self.admin
140 140
141 141 def __repr__(self):
142 142 return "<%s('id:%s:%s')>" % (self.__class__.__name__,
143 143 self.user_id, self.username)
144 144
145 145 @classmethod
146 146 def by_username(cls, username):
147 147 return Session.query(cls).filter(cls.username == username).one()
148 148
149 149
150 150 def update_lastlogin(self):
151 151 """Update user lastlogin"""
152 152
153 153 try:
154 154 session = Session.object_session(self)
155 155 self.last_login = datetime.datetime.now()
156 156 session.add(self)
157 157 session.commit()
158 158 log.debug('updated user %s lastlogin', self.username)
159 159 except (DatabaseError,):
160 160 session.rollback()
161 161
162 162
163 163 class UserLog(Base):
164 164 __tablename__ = 'user_logs'
165 165 __table_args__ = {'useexisting':True}
166 166 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
167 167 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
168 168 repository_id = Column("repository_id", Integer(length=None, convert_unicode=False, assert_unicode=None), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
169 169 repository_name = Column("repository_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
170 170 user_ip = Column("user_ip", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
171 171 action = Column("action", UnicodeText(length=1200000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
172 172 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
173 173
174 174 @property
175 175 def action_as_day(self):
176 176 return date(*self.action_date.timetuple()[:3])
177 177
178 178 user = relationship('User')
179 179 repository = relationship('Repository')
180 180
181 181
182 182 class UsersGroup(Base):
183 183 __tablename__ = 'users_groups'
184 184 __table_args__ = {'useexisting':True}
185 185
186 186 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
187 187 users_group_name = Column("users_group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
188 188 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
189 189
190 190 members = relationship('UsersGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
191 191
192 192
193 193 @classmethod
194 194 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
195 195 if case_insensitive:
196 196 gr = Session.query(cls)\
197 197 .filter(cls.users_group_name.ilike(group_name))
198 198 else:
199 199 gr = Session.query(UsersGroup)\
200 200 .filter(UsersGroup.users_group_name == group_name)
201 201 if cache:
202 202 gr = gr.options(FromCache("sql_cache_short",
203 203 "get_user_%s" % group_name))
204 204 return gr.scalar()
205 205
206 206 class UsersGroupMember(Base):
207 207 __tablename__ = 'users_groups_members'
208 208 __table_args__ = {'useexisting':True}
209 209
210 210 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
211 211 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
212 212 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
213 213
214 214 user = relationship('User', lazy='joined')
215 215 users_group = relationship('UsersGroup')
216 216
217 217 def __init__(self, gr_id='', u_id=''):
218 218 self.users_group_id = gr_id
219 219 self.user_id = u_id
220 220
221 221 class Repository(Base):
222 222 __tablename__ = 'repositories'
223 223 __table_args__ = (UniqueConstraint('repo_name'), {'useexisting':True},)
224 224 __mapper_args__ = {'extension':RepositoryMapper()}
225 225
226 226 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
227 227 repo_name = Column("repo_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
228 228 clone_uri = Column("clone_uri", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
229 229 repo_type = Column("repo_type", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default='hg')
230 230 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
231 231 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
232 232 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
233 233 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
234 234 description = Column("description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
235 235 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
236 236 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
237 237
238 238
239 239 user = relationship('User')
240 240 fork = relationship('Repository', remote_side=repo_id)
241 241 group = relationship('Group')
242 242 repo_to_perm = relationship('RepoToPerm', cascade='all', order_by='RepoToPerm.repo_to_perm_id')
243 243 users_group_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
244 244 stats = relationship('Statistics', cascade='all', uselist=False)
245 245
246 246 followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id', cascade='all')
247 247
248 248 logs = relationship('UserLog', cascade='all')
249 249
250 250 def __repr__(self):
251 251 return "<%s('%s:%s')>" % (self.__class__.__name__,
252 252 self.repo_id, self.repo_name)
253 253
254 254 @classmethod
255 255 def by_repo_name(cls, repo_name):
256 256 return Session.query(cls).filter(cls.repo_name == repo_name).one()
257 257
258 258 @property
259 259 def just_name(self):
260 260 return self.repo_name.split(os.sep)[-1]
261 261
262 262 @property
263 263 def groups_with_parents(self):
264 264 groups = []
265 265 if self.group is None:
266 266 return groups
267 267
268 268 cur_gr = self.group
269 269 groups.insert(0, cur_gr)
270 270 while 1:
271 271 gr = getattr(cur_gr, 'parent_group', None)
272 272 cur_gr = cur_gr.parent_group
273 273 if gr is None:
274 274 break
275 275 groups.insert(0, gr)
276 276
277 277 return groups
278 278
279 279 @property
280 280 def groups_and_repo(self):
281 281 return self.groups_with_parents, self.just_name
282 282
283 283
284 284 class Group(Base):
285 285 __tablename__ = 'groups'
286 286 __table_args__ = (UniqueConstraint('group_name'), {'useexisting':True},)
287 287
288 288 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
289 289 group_name = Column("group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
290 290 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
291 291
292 292 parent_group = relationship('Group', remote_side=group_id)
293 293
294 294
295 295 def __init__(self, group_name='', parent_group=None):
296 296 self.group_name = group_name
297 297 self.parent_group = parent_group
298 298
299 299 def __repr__(self):
300 300 return "<%s('%s:%s')>" % (self.__class__.__name__, self.group_id,
301 301 self.group_name)
302 302
303 303 @property
304 304 def parents(self):
305 305 groups = []
306 306 if self.parent_group is None:
307 307 return groups
308 308 cur_gr = self.parent_group
309 309 groups.insert(0, cur_gr)
310 310 while 1:
311 311 gr = getattr(cur_gr, 'parent_group', None)
312 312 cur_gr = cur_gr.parent_group
313 313 if gr is None:
314 314 break
315 315 groups.insert(0, gr)
316 316 return groups
317 317
318 318 @property
319 319 def repositories(self):
320 320 return Session.query(Repository).filter(Repository.group == self).all()
321 321
322 322 class Permission(Base):
323 323 __tablename__ = 'permissions'
324 324 __table_args__ = {'useexisting':True}
325 325 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
326 326 permission_name = Column("permission_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
327 327 permission_longname = Column("permission_longname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
328 328
329 329 def __repr__(self):
330 330 return "<%s('%s:%s')>" % (self.__class__.__name__,
331 331 self.permission_id, self.permission_name)
332 332
333 333 @classmethod
334 334 def get_by_key(cls, key):
335 335 return Session.query(cls).filter(cls.permission_name == key).scalar()
336 336
337 337 class RepoToPerm(Base):
338 338 __tablename__ = 'repo_to_perm'
339 339 __table_args__ = (UniqueConstraint('user_id', 'repository_id'), {'useexisting':True})
340 340 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
341 341 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
342 342 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
343 343 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
344 344
345 345 user = relationship('User')
346 346 permission = relationship('Permission')
347 347 repository = relationship('Repository')
348 348
349 349 class UserToPerm(Base):
350 350 __tablename__ = 'user_to_perm'
351 351 __table_args__ = (UniqueConstraint('user_id', 'permission_id'), {'useexisting':True})
352 352 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
353 353 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
354 354 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
355 355
356 356 user = relationship('User')
357 357 permission = relationship('Permission')
358 358
359 359 @classmethod
360 360 def has_perm(cls, user_id, perm):
361 361 if not isinstance(perm, Permission):
362 362 raise Exception('perm needs to be an instance of Permission class')
363 363
364 364 return Session.query(cls).filter(cls.user_id == user_id)\
365 365 .filter(cls.permission == perm).scalar() is not None
366 366
367 367 @classmethod
368 368 def grant_perm(cls, user_id, perm):
369 369 if not isinstance(perm, Permission):
370 370 raise Exception('perm needs to be an instance of Permission class')
371 371
372 372 new = cls()
373 373 new.user_id = user_id
374 374 new.permission = perm
375 375 try:
376 376 Session.add(new)
377 377 Session.commit()
378 378 except:
379 379 Session.rollback()
380 380
381 381
382 382 @classmethod
383 383 def revoke_perm(cls, user_id, perm):
384 384 if not isinstance(perm, Permission):
385 385 raise Exception('perm needs to be an instance of Permission class')
386 386
387 387 try:
388 388 Session.query(cls).filter(cls.user_id == user_id)\
389 389 .filter(cls.permission == perm).delete()
390 390 Session.commit()
391 391 except:
392 392 Session.rollback()
393 393
394 394 class UsersGroupRepoToPerm(Base):
395 395 __tablename__ = 'users_group_repo_to_perm'
396 396 __table_args__ = (UniqueConstraint('users_group_id', 'permission_id'), {'useexisting':True})
397 397 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
398 398 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
399 399 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
400 400 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
401 401
402 402 users_group = relationship('UsersGroup')
403 403 permission = relationship('Permission')
404 404 repository = relationship('Repository')
405 405
406 406
407 407 class UsersGroupToPerm(Base):
408 408 __tablename__ = 'users_group_to_perm'
409 409 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
410 410 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
411 411 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
412 412
413 413 users_group = relationship('UsersGroup')
414 414 permission = relationship('Permission')
415 415
416 416
417 417 @classmethod
418 418 def has_perm(cls, users_group_id, perm):
419 419 if not isinstance(perm, Permission):
420 420 raise Exception('perm needs to be an instance of Permission class')
421 421
422 422 return Session.query(cls).filter(cls.users_group_id ==
423 423 users_group_id)\
424 424 .filter(cls.permission == perm)\
425 425 .scalar() is not None
426 426
427 427 @classmethod
428 428 def grant_perm(cls, users_group_id, perm):
429 429 if not isinstance(perm, Permission):
430 430 raise Exception('perm needs to be an instance of Permission class')
431 431
432 432 new = cls()
433 433 new.users_group_id = users_group_id
434 434 new.permission = perm
435 435 try:
436 436 Session.add(new)
437 437 Session.commit()
438 438 except:
439 439 Session.rollback()
440 440
441 441
442 442 @classmethod
443 443 def revoke_perm(cls, users_group_id, perm):
444 444 if not isinstance(perm, Permission):
445 445 raise Exception('perm needs to be an instance of Permission class')
446 446
447 447 try:
448 448 Session.query(cls).filter(cls.users_group_id == users_group_id)\
449 449 .filter(cls.permission == perm).delete()
450 450 Session.commit()
451 451 except:
452 452 Session.rollback()
453 453
454 454
455 455 class GroupToPerm(Base):
456 456 __tablename__ = 'group_to_perm'
457 457 __table_args__ = (UniqueConstraint('group_id', 'permission_id'), {'useexisting':True})
458 458
459 459 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
460 460 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
461 461 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
462 462 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
463 463
464 464 user = relationship('User')
465 465 permission = relationship('Permission')
466 466 group = relationship('Group')
467 467
468 468 class Statistics(Base):
469 469 __tablename__ = 'statistics'
470 470 __table_args__ = (UniqueConstraint('repository_id'), {'useexisting':True})
471 471 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
472 472 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
473 473 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
474 474 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
475 475 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
476 476 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
477 477
478 478 repository = relationship('Repository', single_parent=True)
479 479
480 480 class UserFollowing(Base):
481 481 __tablename__ = 'user_followings'
482 482 __table_args__ = (UniqueConstraint('user_id', 'follows_repository_id'),
483 483 UniqueConstraint('user_id', 'follows_user_id')
484 484 , {'useexisting':True})
485 485
486 486 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
487 487 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
488 488 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
489 489 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
490 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
490 491
491 492 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
492 493
493 494 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
494 495 follows_repository = relationship('Repository', order_by='Repository.repo_name')
495 496
497
498
499 @classmethod
500 def get_repo_followers(cls, repo_id):
501 return Session.query(cls).filter(cls.follows_repo_id == repo_id)
502
496 503 class CacheInvalidation(Base):
497 504 __tablename__ = 'cache_invalidation'
498 505 __table_args__ = (UniqueConstraint('cache_key'), {'useexisting':True})
499 506 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
500 507 cache_key = Column("cache_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
501 508 cache_args = Column("cache_args", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
502 509 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
503 510
504 511
505 512 def __init__(self, cache_key, cache_args=''):
506 513 self.cache_key = cache_key
507 514 self.cache_args = cache_args
508 515 self.cache_active = False
509 516
510 517 def __repr__(self):
511 518 return "<%s('%s:%s')>" % (self.__class__.__name__,
512 519 self.cache_id, self.cache_key)
513 520
514 521 class DbMigrateVersion(Base):
515 522 __tablename__ = 'db_migrate_version'
516 523 __table_args__ = {'useexisting':True}
517 524 repository_id = Column('repository_id', String(250), primary_key=True)
518 525 repository_path = Column('repository_path', Text)
519 526 version = Column('version', Integer)
@@ -1,355 +1,355
1 1 ## -*- coding: utf-8 -*-
2 2 <%inherit file="root.html"/>
3 3
4 4 <!-- HEADER -->
5 5 <div id="header">
6 6 <!-- user -->
7 7 <ul id="logged-user">
8 8 <li class="first">
9 9 <div class="gravatar">
10 10 <img alt="gravatar" src="${h.gravatar_url(c.rhodecode_user.email,20)}" />
11 11 </div>
12 12 <div class="account">
13 13 %if c.rhodecode_user.username == 'default':
14 14 <a href="${h.url('public_journal')}">${_('Public journal')}</a>
15 15 %else:
16 16 ${h.link_to(c.rhodecode_user.username,h.url('admin_settings_my_account'),title='%s %s'%(c.rhodecode_user.name,c.rhodecode_user.lastname))}
17 17 %endif
18 18 </div>
19 19 </li>
20 20 <li>
21 21 <a href="${h.url('home')}">${_('Home')}</a>
22 22 </li>
23 23 %if c.rhodecode_user.username != 'default':
24 24 <li>
25 25 <a href="${h.url('journal')}">${_('Journal')}</a>
26 26 ##(${c.unread_journal}
27 27 </li>
28 28 %endif
29 29 %if c.rhodecode_user.username == 'default':
30 30 <li class="last highlight">${h.link_to(u'Login',h.url('login_home'))}</li>
31 31 %else:
32 32 <li class="last highlight">${h.link_to(u'Log Out',h.url('logout_home'))}</li>
33 33 %endif
34 34 </ul>
35 35 <!-- end user -->
36 36 <div id="header-inner" class="title top-left-rounded-corner top-right-rounded-corner">
37 37 <div id="logo">
38 38 <h1><a href="${h.url('home')}">${c.rhodecode_name}</a></h1>
39 39 </div>
40 40 <!-- MENU -->
41 41 ${self.page_nav()}
42 42 <!-- END MENU -->
43 43 ${self.body()}
44 44 </div>
45 45 </div>
46 46 <!-- END HEADER -->
47 47
48 48 <!-- CONTENT -->
49 49 <div id="content">
50 50 <div class="flash_msg">
51 51 <% messages = h.flash.pop_messages() %>
52 52 % if messages:
53 53 <ul id="flash-messages">
54 54 % for message in messages:
55 55 <li class="${message.category}_msg">${message}</li>
56 56 % endfor
57 57 </ul>
58 58 % endif
59 59 </div>
60 60 <div id="main">
61 61 ${next.main()}
62 62 </div>
63 63 </div>
64 64 <!-- END CONTENT -->
65 65
66 66 <!-- FOOTER -->
67 67 <div id="footer">
68 68 <div id="footer-inner" class="title bottom-left-rounded-corner bottom-right-rounded-corner">
69 69 <div>
70 70 <p class="footer-link">
71 71 <a href="${h.url('bugtracker')}">${_('Submit a bug')}</a>
72 72 </p>
73 73 <p class="footer-link-right">
74 74 <a href="${h.url('rhodecode_official')}">RhodeCode</a>
75 75 ${c.rhodecode_version} &copy; 2010-${h.datetime.today().year} by Marcin Kuzminski
76 76 </p>
77 77 </div>
78 78 </div>
79 79 <script type="text/javascript">
80 80 function tooltip_activate(){
81 81 ${h.tooltip.activate()}
82 82 }
83 83 tooltip_activate();
84 84 </script>
85 85 </div>
86 86 <!-- END FOOTER -->
87 87
88 88 ### MAKO DEFS ###
89 89 <%def name="page_nav()">
90 90 ${self.menu()}
91 91 </%def>
92 92
93 93 <%def name="breadcrumbs()">
94 94 <div class="breadcrumbs">
95 95 ${self.breadcrumbs_links()}
96 96 </div>
97 97 </%def>
98 98
99 99
100 100 <%def name="menu(current=None)">
101 101 <%
102 102 def is_current(selected):
103 103 if selected == current:
104 104 return h.literal('class="current"')
105 105 %>
106 106 %if current not in ['home','admin']:
107 107 ##REGULAR MENU
108 108 <ul id="quick">
109 109 <!-- repo switcher -->
110 110 <li>
111 111 <a id="repo_switcher" title="${_('Switch repository')}" href="#">
112 112 <span class="icon">
113 113 <img src="${h.url("/images/icons/database.png")}" alt="${_('Products')}" />
114 114 </span>
115 115 <span>&darr;</span>
116 116 </a>
117 117 <ul id="repo_switcher_list" class="repo_switcher">
118 118 <li>
119 119 <a href="#">${_('loading...')}</a>
120 120 </li>
121 121 </ul>
122 122 <script type="text/javascript">
123 123 YUE.on('repo_switcher','mouseover',function(){
124 124 function qfilter(){
125 125 var S = YAHOO.util.Selector;
126 126
127 127 var q_filter = YUD.get('q_filter_rs');
128 128 var F = YAHOO.namespace('q_filter_rs');
129 129
130 130 YUE.on(q_filter,'click',function(){
131 131 q_filter.value = '';
132 132 });
133 133
134 134 F.filterTimeout = null;
135 135
136 136 F.updateFilter = function() {
137 137 // Reset timeout
138 138 F.filterTimeout = null;
139 139
140 140 var obsolete = [];
141 141 var nodes = S.query('ul#repo_switcher_list li a.repo_name');
142 142 var req = YUD.get('q_filter_rs').value;
143 143 for (n in nodes){
144 144 YUD.setStyle(nodes[n].parentNode,'display','')
145 145 }
146 146 if (req){
147 147 for (n in nodes){
148 148 console.log(n);
149 149 if (nodes[n].innerHTML.toLowerCase().indexOf(req) == -1) {
150 150 obsolete.push(nodes[n]);
151 151 }
152 152 }
153 153 if(obsolete){
154 154 for (n in obsolete){
155 155 YUD.setStyle(obsolete[n].parentNode,'display','none');
156 156 }
157 157 }
158 158 }
159 159 }
160 160
161 161 YUE.on(q_filter,'keyup',function(e){
162 162 clearTimeout(F.filterTimeout);
163 163 setTimeout(F.updateFilter,600);
164 164 });
165 165 }
166 166 var loaded = YUD.hasClass('repo_switcher','loaded');
167 167 if(!loaded){
168 168 YUD.addClass('repo_switcher','loaded');
169 169 YAHOO.util.Connect.asyncRequest('GET',"${h.url('repo_switcher')}",{
170 170 success:function(o){
171 171 YUD.get('repo_switcher_list').innerHTML = o.responseText;
172 172 qfilter();
173 173 },
174 174 failure:function(o){
175 175 YUD.removeClass('repo_switcher','loaded');
176 176 }
177 177 },null);
178 178 }
179 179 return false;
180 180 });
181 181 </script>
182 182 </li>
183 183
184 184 <li ${is_current('summary')}>
185 185 <a title="${_('Summary')}" href="${h.url('summary_home',repo_name=c.repo_name)}">
186 186 <span class="icon">
187 187 <img src="${h.url("/images/icons/clipboard_16.png")}" alt="${_('Summary')}" />
188 188 </span>
189 189 <span>${_('Summary')}</span>
190 190 </a>
191 191 </li>
192 192 ##<li ${is_current('shortlog')}>
193 193 ## <a title="${_('Shortlog')}" href="${h.url('shortlog_home',repo_name=c.repo_name)}">
194 194 ## <span class="icon">
195 195 ## <img src="${h.url("/images/icons/application_view_list.png")}" alt="${_('Shortlog')}" />
196 196 ## </span>
197 197 ## <span>${_('Shortlog')}</span>
198 198 ## </a>
199 199 ##</li>
200 200 <li ${is_current('changelog')}>
201 201 <a title="${_('Changelog')}" href="${h.url('changelog_home',repo_name=c.repo_name)}">
202 202 <span class="icon">
203 203 <img src="${h.url("/images/icons/time.png")}" alt="${_('Changelog')}" />
204 204 </span>
205 205 <span>${_('Changelog')}</span>
206 206 </a>
207 207 </li>
208 208
209 209 <li ${is_current('switch_to')}>
210 210 <a title="${_('Switch to')}" href="#">
211 211 <span class="icon">
212 212 <img src="${h.url("/images/icons/arrow_switch.png")}" alt="${_('Switch to')}" />
213 213 </span>
214 214 <span>${_('Switch to')}</span>
215 215 </a>
216 216 <ul>
217 217 <li>
218 218 ${h.link_to('%s (%s)' % (_('branches'),len(c.rhodecode_repo.branches.values()),),h.url('branches_home',repo_name=c.repo_name),class_='branches childs')}
219 219 <ul>
220 220 %if c.rhodecode_repo.branches.values():
221 221 %for cnt,branch in enumerate(c.rhodecode_repo.branches.items()):
222 222 <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>
223 223 %endfor
224 224 %else:
225 225 <li>${h.link_to(_('There are no branches yet'),'#')}</li>
226 226 %endif
227 227 </ul>
228 228 </li>
229 229 <li>
230 230 ${h.link_to('%s (%s)' % (_('tags'),len(c.rhodecode_repo.tags.values()),),h.url('tags_home',repo_name=c.repo_name),class_='tags childs')}
231 231 <ul>
232 232 %if c.rhodecode_repo.tags.values():
233 233 %for cnt,tag in enumerate(c.rhodecode_repo.tags.items()):
234 234 <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>
235 235 %endfor
236 236 %else:
237 237 <li>${h.link_to(_('There are no tags yet'),'#')}</li>
238 238 %endif
239 239 </ul>
240 240 </li>
241 241 </ul>
242 242 </li>
243 243 <li ${is_current('files')}>
244 244 <a title="${_('Files')}" href="${h.url('files_home',repo_name=c.repo_name)}">
245 245 <span class="icon">
246 246 <img src="${h.url("/images/icons/file.png")}" alt="${_('Files')}" />
247 247 </span>
248 248 <span>${_('Files')}</span>
249 249 </a>
250 250 </li>
251 251
252 252 <li ${is_current('options')}>
253 253 <a title="${_('Options')}" href="#">
254 254 <span class="icon">
255 255 <img src="${h.url("/images/icons/table_gear.png")}" alt="${_('Admin')}" />
256 256 </span>
257 257 <span>${_('Options')}</span>
258 258 </a>
259 259 <ul>
260 260 %if h.HasRepoPermissionAll('repository.admin')(c.repo_name):
261 261 %if h.HasPermissionAll('hg.admin')('access settings on repository'):
262 262 <li>${h.link_to(_('settings'),h.url('edit_repo',repo_name=c.repo_name),class_='settings')}</li>
263 263 %else:
264 264 <li>${h.link_to(_('settings'),h.url('repo_settings_home',repo_name=c.repo_name),class_='settings')}</li>
265 265 %endif
266 266 %endif
267 267 <li>${h.link_to(_('fork'),h.url('repo_fork_home',repo_name=c.repo_name),class_='fork')}</li>
268 268 <li>${h.link_to(_('search'),h.url('search_repo',search_repo=c.repo_name),class_='search')}</li>
269 269
270 270 %if h.HasPermissionAll('hg.admin')('access admin main page'):
271 271 <li>
272 272 ${h.link_to(_('admin'),h.url('admin_home'),class_='admin')}
273 273 <%def name="admin_menu()">
274 274 <ul>
275 275 <li>${h.link_to(_('journal'),h.url('admin_home'),class_='journal')}</li>
276 276 <li>${h.link_to(_('repositories'),h.url('repos'),class_='repos')}</li>
277 277 <li>${h.link_to(_('users'),h.url('users'),class_='users')}</li>
278 278 <li>${h.link_to(_('users groups'),h.url('users_groups'),class_='groups')}</li>
279 279 <li>${h.link_to(_('permissions'),h.url('edit_permission',id='default'),class_='permissions')}</li>
280 280 <li>${h.link_to(_('ldap'),h.url('ldap_home'),class_='ldap')}</li>
281 281 <li class="last">${h.link_to(_('settings'),h.url('admin_settings'),class_='settings')}</li>
282 282 </ul>
283 283 </%def>
284 284
285 285 ${admin_menu()}
286 286 </li>
287 287 %endif
288 288
289 289 </ul>
290 290 </li>
291 291
292 292 <li>
293 <a title="${_('Followers')}" href="#">
293 <a title="${_('Followers')}" href="${h.url('repo_followers_home',repo_name=c.repo_name)}">
294 294 <span class="icon_short">
295 295 <img src="${h.url("/images/icons/heart.png")}" alt="${_('Followers')}" />
296 296 </span>
297 297 <span id="current_followers_count" class="short">${c.repository_followers}</span>
298 298 </a>
299 299 </li>
300 300 <li>
301 301 <a title="${_('Forks')}" href="#">
302 302 <span class="icon_short">
303 303 <img src="${h.url("/images/icons/arrow_divide.png")}" alt="${_('Forks')}" />
304 304 </span>
305 305 <span class="short">${c.repository_forks}</span>
306 306 </a>
307 307 </li>
308 308
309 309
310 310
311 311 </ul>
312 312 %else:
313 313 ##ROOT MENU
314 314 <ul id="quick">
315 315 <li>
316 316 <a title="${_('Home')}" href="${h.url('home')}">
317 317 <span class="icon">
318 318 <img src="${h.url("/images/icons/home_16.png")}" alt="${_('Home')}" />
319 319 </span>
320 320 <span>${_('Home')}</span>
321 321 </a>
322 322 </li>
323 323 %if c.rhodecode_user.username != 'default':
324 324 <li>
325 325 <a title="${_('Journal')}" href="${h.url('journal')}">
326 326 <span class="icon">
327 327 <img src="${h.url("/images/icons/book.png")}" alt="${_('Journal')}" />
328 328 </span>
329 329 <span>${_('Journal')}</span>
330 330 </a>
331 331 </li>
332 332 %endif
333 333 <li>
334 334 <a title="${_('Search')}" href="${h.url('search')}">
335 335 <span class="icon">
336 336 <img src="${h.url("/images/icons/search_16.png")}" alt="${_('Search')}" />
337 337 </span>
338 338 <span>${_('Search')}</span>
339 339 </a>
340 340 </li>
341 341
342 342 %if h.HasPermissionAll('hg.admin')('access admin main page'):
343 343 <li ${is_current('admin')}>
344 344 <a title="${_('Admin')}" href="${h.url('admin_home')}">
345 345 <span class="icon">
346 346 <img src="${h.url("/images/icons/cog_edit.png")}" alt="${_('Admin')}" />
347 347 </span>
348 348 <span>${_('Admin')}</span>
349 349 </a>
350 350 ${admin_menu()}
351 351 </li>
352 352 %endif
353 353 </ul>
354 354 %endif
355 355 </%def> No newline at end of file
General Comments 0
You need to be logged in to leave comments. Login now