##// END OF EJS Templates
Added simple forks page, resolves issue #179
marcink -
r1301:7e75af30 beta
parent child Browse files
Show More
@@ -0,0 +1,56 b''
1 # -*- coding: utf-8 -*-
2 """
3 rhodecode.controllers.forks
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
6 forks 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 ForksController(BaseRepoController):
38
39 @LoginRequired()
40 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
41 'repository.admin')
42 def __before__(self):
43 super(ForksController, self).__before__()
44
45 def forks(self, repo_name):
46 p = int(request.params.get('page', 1))
47 repo_id = Repository.by_repo_name(repo_name).repo_id
48 d = Repository.get_repo_forks(repo_id)
49 c.forks_pager = Page(d, page=p, items_per_page=20)
50
51 c.forks_data = render('/forks/forks_data.html')
52
53 if request.params.get('partial'):
54 return c.forks_data
55
56 return render('/forks/forks.html')
@@ -0,0 +1,32 b''
1 ## -*- coding: utf-8 -*-
2 <%inherit file="/base/base.html"/>
3
4 <%def name="title()">
5 ${c.repo_name} ${_('Forks')} - ${c.rhodecode_name}
6 </%def>
7
8 <%def name="breadcrumbs_links()">
9 ${h.link_to(u'Home',h.url('/'))}
10 &raquo;
11 ${h.link_to(c.repo_name,h.url('summary_home',repo_name=c.repo_name))}
12 &raquo;
13 ${_('forks')}
14 </%def>
15
16 <%def name="page_nav()">
17 ${self.menu('forks')}
18 </%def>
19 <%def name="main()">
20 <div class="box">
21 <!-- box / title -->
22 <div class="title">
23 ${self.breadcrumbs()}
24 </div>
25 <!-- end box / title -->
26 <div class="table">
27 <div id="forks">
28 ${c.forks_data}
29 </div>
30 </div>
31 </div>
32 </%def> No newline at end of file
@@ -0,0 +1,40 b''
1 ## -*- coding: utf-8 -*-
2
3 % for f in c.forks_pager:
4 <div>
5 <div class="fork_user">
6 <div class="gravatar">
7 <img alt="gravatar" src="${h.gravatar_url(f.user.email,24)}"/>
8 </div>
9 <span style="font-size: 20px">
10 <b>${f.user.username}</b> (${f.user.name} ${f.user.lastname}) /
11 ${h.link_to(f.repo_name,h.url('summary_home',repo_name=f.repo_name))}
12 </span>
13 <div style="padding:5px 3px 3px 42px;">${f.description}</div>
14 </div>
15 <div style="clear:both;padding-top: 10px"></div>
16 <div class="follower_date">${_('forked')} -
17 <span class="tooltip" title="${f.created_on}"> ${h.age(f.created_on)}</span></div>
18 <div style="border-bottom: 1px solid #DDD;margin:10px 0px 10px 0px"></div>
19 </div>
20 % endfor
21
22 <div class="pagination-wh pagination-left">
23 <script type="text/javascript">
24 var data_div = 'forks';
25 YAHOO.util.Event.onDOMReady(function(){
26 YAHOO.util.Event.addListener(
27 YUD.getElementsByClassName('pager_link'),"click",
28 function(){
29 YAHOO.util.Dom.setStyle(data_div,'opacity','0.3');
30 });
31 });
32 </script>
33
34 ${c.forks_pager.pager('$link_previous ~2~ $link_next',
35 onclick="""YAHOO.util.Connect.asyncRequest('GET','$partial_url',{
36 success:function(o){YAHOO.util.Dom.get(data_div).innerHTML=o.responseText;
37 YUE.on(YAHOO.util.Dom.getElementsByClassName('pager_link'),"click",function(){
38 YAHOO.util.Dom.setStyle(data_div,'opacity','0.3');});
39 YAHOO.util.Dom.setStyle(data_div,'opacity','1');}},null); return false;""")}
40 </div> No newline at end of file
@@ -1,344 +1,348 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
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 264 #==========================================================================
265 265 # REPOSITORY ROUTES
266 266 #==========================================================================
267 267 rmap.connect('changeset_home', '/{repo_name:.*}/changeset/{revision}',
268 268 controller='changeset', revision='tip',
269 269 conditions=dict(function=check_repo))
270 270
271 271 rmap.connect('raw_changeset_home',
272 272 '/{repo_name:.*}/raw-changeset/{revision}',
273 273 controller='changeset', action='raw_changeset',
274 274 revision='tip', conditions=dict(function=check_repo))
275 275
276 276 rmap.connect('summary_home', '/{repo_name:.*}',
277 277 controller='summary', conditions=dict(function=check_repo))
278 278
279 279 rmap.connect('summary_home', '/{repo_name:.*}/summary',
280 280 controller='summary', conditions=dict(function=check_repo))
281 281
282 282 rmap.connect('shortlog_home', '/{repo_name:.*}/shortlog',
283 283 controller='shortlog', conditions=dict(function=check_repo))
284 284
285 285 rmap.connect('branches_home', '/{repo_name:.*}/branches',
286 286 controller='branches', conditions=dict(function=check_repo))
287 287
288 288 rmap.connect('tags_home', '/{repo_name:.*}/tags',
289 289 controller='tags', conditions=dict(function=check_repo))
290 290
291 291 rmap.connect('changelog_home', '/{repo_name:.*}/changelog',
292 292 controller='changelog', conditions=dict(function=check_repo))
293 293
294 294 rmap.connect('files_home', '/{repo_name:.*}/files/{revision}/{f_path:.*}',
295 295 controller='files', revision='tip', f_path='',
296 296 conditions=dict(function=check_repo))
297 297
298 298 rmap.connect('files_diff_home', '/{repo_name:.*}/diff/{f_path:.*}',
299 299 controller='files', action='diff', revision='tip', f_path='',
300 300 conditions=dict(function=check_repo))
301 301
302 302 rmap.connect('files_rawfile_home',
303 303 '/{repo_name:.*}/rawfile/{revision}/{f_path:.*}',
304 304 controller='files', action='rawfile', revision='tip',
305 305 f_path='', conditions=dict(function=check_repo))
306 306
307 307 rmap.connect('files_raw_home',
308 308 '/{repo_name:.*}/raw/{revision}/{f_path:.*}',
309 309 controller='files', action='raw', revision='tip', f_path='',
310 310 conditions=dict(function=check_repo))
311 311
312 312 rmap.connect('files_annotate_home',
313 313 '/{repo_name:.*}/annotate/{revision}/{f_path:.*}',
314 314 controller='files', action='annotate', revision='tip',
315 315 f_path='', conditions=dict(function=check_repo))
316 316
317 317 rmap.connect('files_archive_home', '/{repo_name:.*}/archive/{fname}',
318 318 controller='files', action='archivefile',
319 319 conditions=dict(function=check_repo))
320 320
321 321 rmap.connect('repo_settings_delete', '/{repo_name:.*}/settings',
322 322 controller='settings', action="delete",
323 323 conditions=dict(method=["DELETE"], function=check_repo))
324 324
325 325 rmap.connect('repo_settings_update', '/{repo_name:.*}/settings',
326 326 controller='settings', action="update",
327 327 conditions=dict(method=["PUT"], function=check_repo))
328 328
329 329 rmap.connect('repo_settings_home', '/{repo_name:.*}/settings',
330 330 controller='settings', action='index',
331 331 conditions=dict(function=check_repo))
332 332
333 333 rmap.connect('repo_fork_create_home', '/{repo_name:.*}/fork',
334 334 controller='settings', action='fork_create',
335 335 conditions=dict(function=check_repo, method=["POST"]))
336 336
337 337 rmap.connect('repo_fork_home', '/{repo_name:.*}/fork',
338 338 controller='settings', action='fork',
339 339 conditions=dict(function=check_repo))
340 340
341 341 rmap.connect('repo_followers_home', '/{repo_name:.*}/followers',
342 342 controller='followers', action='followers',
343 343 conditions=dict(function=check_repo))
344
345 rmap.connect('repo_forks_home', '/{repo_name:.*}/forks',
346 controller='forks', action='forks',
347 conditions=dict(function=check_repo))
344 348 return rmap
@@ -1,57 +1,57 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.controllers.followers
4 4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 Followers controller for rhodecode
7 7
8 8 :created_on: Apr 23, 2011
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 import logging
26 26
27 27 from pylons import tmpl_context as c, request
28 28
29 29 from rhodecode.lib.helpers import Page
30 30 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
31 31 from rhodecode.lib.base import BaseRepoController, render
32 32 from rhodecode.model.db import Repository, User, UserFollowing
33 33
34 34 log = logging.getLogger(__name__)
35 35
36 36
37 37 class FollowersController(BaseRepoController):
38 38
39 39 @LoginRequired()
40 40 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
41 41 'repository.admin')
42 42 def __before__(self):
43 43 super(FollowersController, self).__before__()
44 44
45 45 def followers(self, repo_name):
46 46 p = int(request.params.get('page', 1))
47 repo_id = getattr(Repository.by_repo_name(repo_name), 'repo_id')
47 repo_id = Repository.by_repo_name(repo_name).repo_id
48 48 d = UserFollowing.get_repo_followers(repo_id)\
49 49 .order_by(UserFollowing.follows_from)
50 50 c.followers_pager = Page(d, page=p, items_per_page=20)
51 51
52 52 c.followers_data = render('/followers/followers_data.html')
53 53
54 54 if request.params.get('partial'):
55 55 return c.followers_data
56 56
57 57 return render('/followers/followers.html')
@@ -1,531 +1,538 b''
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_by_name(cls, ldap_key):
69 69 return Session.query(cls)\
70 70 .filter(cls.app_settings_name == ldap_key).scalar()
71 71
72 72 @classmethod
73 73 def get_app_settings(cls, cache=False):
74 74
75 75 ret = Session.query(cls)
76 76
77 77 if cache:
78 78 ret = ret.options(FromCache("sql_cache_short", "get_hg_settings"))
79 79
80 80 if not ret:
81 81 raise Exception('Could not get application settings !')
82 82 settings = {}
83 83 for each in ret:
84 84 settings['rhodecode_' + each.app_settings_name] = \
85 85 each.app_settings_value
86 86
87 87 return settings
88 88
89 89 @classmethod
90 90 def get_ldap_settings(cls, cache=False):
91 91 ret = Session.query(cls)\
92 92 .filter(cls.app_settings_name.startswith('ldap_'))\
93 93 .all()
94 94 fd = {}
95 95 for row in ret:
96 96 fd.update({row.app_settings_name:row.app_settings_value})
97 97 return fd
98 98
99 99
100 100 class RhodeCodeUi(Base):
101 101 __tablename__ = 'rhodecode_ui'
102 102 __table_args__ = {'useexisting':True}
103 103 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
104 104 ui_section = Column("ui_section", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
105 105 ui_key = Column("ui_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
106 106 ui_value = Column("ui_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
107 107 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
108 108
109 109
110 110 class User(Base):
111 111 __tablename__ = 'users'
112 112 __table_args__ = (UniqueConstraint('username'), UniqueConstraint('email'), {'useexisting':True})
113 113 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
114 114 username = Column("username", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
115 115 password = Column("password", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
116 116 active = Column("active", Boolean(), nullable=True, unique=None, default=None)
117 117 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
118 118 name = Column("name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
119 119 lastname = Column("lastname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
120 120 email = Column("email", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
121 121 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
122 122 ldap_dn = Column("ldap_dn", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
123 123 api_key = Column("api_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
124 124
125 125 user_log = relationship('UserLog', cascade='all')
126 126 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
127 127
128 128 repositories = relationship('Repository')
129 129 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
130 130
131 131 group_member = relationship('UsersGroupMember', cascade='all')
132 132
133 133 @property
134 134 def full_contact(self):
135 135 return '%s %s <%s>' % (self.name, self.lastname, self.email)
136 136
137 137 @property
138 138 def short_contact(self):
139 139 return '%s %s' % (self.name, self.lastname)
140 140
141 141
142 142 @property
143 143 def is_admin(self):
144 144 return self.admin
145 145
146 146 def __repr__(self):
147 147 return "<%s('id:%s:%s')>" % (self.__class__.__name__,
148 148 self.user_id, self.username)
149 149
150 150 @classmethod
151 151 def by_username(cls, username):
152 152 return Session.query(cls).filter(cls.username == username).one()
153 153
154 154
155 155 def update_lastlogin(self):
156 156 """Update user lastlogin"""
157 157
158 158 try:
159 159 session = Session.object_session(self)
160 160 self.last_login = datetime.datetime.now()
161 161 session.add(self)
162 162 session.commit()
163 163 log.debug('updated user %s lastlogin', self.username)
164 164 except (DatabaseError,):
165 165 session.rollback()
166 166
167 167
168 168 class UserLog(Base):
169 169 __tablename__ = 'user_logs'
170 170 __table_args__ = {'useexisting':True}
171 171 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
172 172 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
173 173 repository_id = Column("repository_id", Integer(length=None, convert_unicode=False, assert_unicode=None), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
174 174 repository_name = Column("repository_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
175 175 user_ip = Column("user_ip", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
176 176 action = Column("action", UnicodeText(length=1200000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
177 177 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
178 178
179 179 @property
180 180 def action_as_day(self):
181 181 return date(*self.action_date.timetuple()[:3])
182 182
183 183 user = relationship('User')
184 184 repository = relationship('Repository')
185 185
186 186
187 187 class UsersGroup(Base):
188 188 __tablename__ = 'users_groups'
189 189 __table_args__ = {'useexisting':True}
190 190
191 191 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
192 192 users_group_name = Column("users_group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
193 193 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
194 194
195 195 members = relationship('UsersGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
196 196
197 197
198 198 @classmethod
199 199 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
200 200 if case_insensitive:
201 201 gr = Session.query(cls)\
202 202 .filter(cls.users_group_name.ilike(group_name))
203 203 else:
204 204 gr = Session.query(UsersGroup)\
205 205 .filter(UsersGroup.users_group_name == group_name)
206 206 if cache:
207 207 gr = gr.options(FromCache("sql_cache_short",
208 208 "get_user_%s" % group_name))
209 209 return gr.scalar()
210 210
211 211 class UsersGroupMember(Base):
212 212 __tablename__ = 'users_groups_members'
213 213 __table_args__ = {'useexisting':True}
214 214
215 215 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
216 216 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
217 217 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
218 218
219 219 user = relationship('User', lazy='joined')
220 220 users_group = relationship('UsersGroup')
221 221
222 222 def __init__(self, gr_id='', u_id=''):
223 223 self.users_group_id = gr_id
224 224 self.user_id = u_id
225 225
226 226 class Repository(Base):
227 227 __tablename__ = 'repositories'
228 228 __table_args__ = (UniqueConstraint('repo_name'), {'useexisting':True},)
229 229 __mapper_args__ = {'extension':RepositoryMapper()}
230 230
231 231 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
232 232 repo_name = Column("repo_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
233 233 clone_uri = Column("clone_uri", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
234 234 repo_type = Column("repo_type", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default='hg')
235 235 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
236 236 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
237 237 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
238 238 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
239 239 description = Column("description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
240 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
241
240 242 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
241 243 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
242 244
243 245
244 246 user = relationship('User')
245 247 fork = relationship('Repository', remote_side=repo_id)
246 248 group = relationship('Group')
247 249 repo_to_perm = relationship('RepoToPerm', cascade='all', order_by='RepoToPerm.repo_to_perm_id')
248 250 users_group_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
249 251 stats = relationship('Statistics', cascade='all', uselist=False)
250 252
251 253 followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id', cascade='all')
252 254
253 255 logs = relationship('UserLog', cascade='all')
254 256
255 257 def __repr__(self):
256 258 return "<%s('%s:%s')>" % (self.__class__.__name__,
257 259 self.repo_id, self.repo_name)
258 260
259 261 @classmethod
260 262 def by_repo_name(cls, repo_name):
261 263 return Session.query(cls).filter(cls.repo_name == repo_name).one()
262 264
265
266 @classmethod
267 def get_repo_forks(cls, repo_id):
268 return Session.query(cls).filter(Repository.fork_id == repo_id)
269
263 270 @property
264 271 def just_name(self):
265 272 return self.repo_name.split(os.sep)[-1]
266 273
267 274 @property
268 275 def groups_with_parents(self):
269 276 groups = []
270 277 if self.group is None:
271 278 return groups
272 279
273 280 cur_gr = self.group
274 281 groups.insert(0, cur_gr)
275 282 while 1:
276 283 gr = getattr(cur_gr, 'parent_group', None)
277 284 cur_gr = cur_gr.parent_group
278 285 if gr is None:
279 286 break
280 287 groups.insert(0, gr)
281 288
282 289 return groups
283 290
284 291 @property
285 292 def groups_and_repo(self):
286 293 return self.groups_with_parents, self.just_name
287 294
288 295
289 296 class Group(Base):
290 297 __tablename__ = 'groups'
291 298 __table_args__ = (UniqueConstraint('group_name'), {'useexisting':True},)
292 299
293 300 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
294 301 group_name = Column("group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
295 302 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
296 303
297 304 parent_group = relationship('Group', remote_side=group_id)
298 305
299 306
300 307 def __init__(self, group_name='', parent_group=None):
301 308 self.group_name = group_name
302 309 self.parent_group = parent_group
303 310
304 311 def __repr__(self):
305 312 return "<%s('%s:%s')>" % (self.__class__.__name__, self.group_id,
306 313 self.group_name)
307 314
308 315 @property
309 316 def parents(self):
310 317 groups = []
311 318 if self.parent_group is None:
312 319 return groups
313 320 cur_gr = self.parent_group
314 321 groups.insert(0, cur_gr)
315 322 while 1:
316 323 gr = getattr(cur_gr, 'parent_group', None)
317 324 cur_gr = cur_gr.parent_group
318 325 if gr is None:
319 326 break
320 327 groups.insert(0, gr)
321 328 return groups
322 329
323 330 @property
324 331 def repositories(self):
325 332 return Session.query(Repository).filter(Repository.group == self).all()
326 333
327 334 class Permission(Base):
328 335 __tablename__ = 'permissions'
329 336 __table_args__ = {'useexisting':True}
330 337 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
331 338 permission_name = Column("permission_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
332 339 permission_longname = Column("permission_longname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
333 340
334 341 def __repr__(self):
335 342 return "<%s('%s:%s')>" % (self.__class__.__name__,
336 343 self.permission_id, self.permission_name)
337 344
338 345 @classmethod
339 346 def get_by_key(cls, key):
340 347 return Session.query(cls).filter(cls.permission_name == key).scalar()
341 348
342 349 class RepoToPerm(Base):
343 350 __tablename__ = 'repo_to_perm'
344 351 __table_args__ = (UniqueConstraint('user_id', 'repository_id'), {'useexisting':True})
345 352 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
346 353 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
347 354 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
348 355 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
349 356
350 357 user = relationship('User')
351 358 permission = relationship('Permission')
352 359 repository = relationship('Repository')
353 360
354 361 class UserToPerm(Base):
355 362 __tablename__ = 'user_to_perm'
356 363 __table_args__ = (UniqueConstraint('user_id', 'permission_id'), {'useexisting':True})
357 364 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
358 365 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
359 366 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
360 367
361 368 user = relationship('User')
362 369 permission = relationship('Permission')
363 370
364 371 @classmethod
365 372 def has_perm(cls, user_id, perm):
366 373 if not isinstance(perm, Permission):
367 374 raise Exception('perm needs to be an instance of Permission class')
368 375
369 376 return Session.query(cls).filter(cls.user_id == user_id)\
370 377 .filter(cls.permission == perm).scalar() is not None
371 378
372 379 @classmethod
373 380 def grant_perm(cls, user_id, perm):
374 381 if not isinstance(perm, Permission):
375 382 raise Exception('perm needs to be an instance of Permission class')
376 383
377 384 new = cls()
378 385 new.user_id = user_id
379 386 new.permission = perm
380 387 try:
381 388 Session.add(new)
382 389 Session.commit()
383 390 except:
384 391 Session.rollback()
385 392
386 393
387 394 @classmethod
388 395 def revoke_perm(cls, user_id, perm):
389 396 if not isinstance(perm, Permission):
390 397 raise Exception('perm needs to be an instance of Permission class')
391 398
392 399 try:
393 400 Session.query(cls).filter(cls.user_id == user_id)\
394 401 .filter(cls.permission == perm).delete()
395 402 Session.commit()
396 403 except:
397 404 Session.rollback()
398 405
399 406 class UsersGroupRepoToPerm(Base):
400 407 __tablename__ = 'users_group_repo_to_perm'
401 408 __table_args__ = (UniqueConstraint('users_group_id', 'permission_id'), {'useexisting':True})
402 409 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
403 410 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
404 411 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
405 412 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
406 413
407 414 users_group = relationship('UsersGroup')
408 415 permission = relationship('Permission')
409 416 repository = relationship('Repository')
410 417
411 418
412 419 class UsersGroupToPerm(Base):
413 420 __tablename__ = 'users_group_to_perm'
414 421 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
415 422 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
416 423 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
417 424
418 425 users_group = relationship('UsersGroup')
419 426 permission = relationship('Permission')
420 427
421 428
422 429 @classmethod
423 430 def has_perm(cls, users_group_id, perm):
424 431 if not isinstance(perm, Permission):
425 432 raise Exception('perm needs to be an instance of Permission class')
426 433
427 434 return Session.query(cls).filter(cls.users_group_id ==
428 435 users_group_id)\
429 436 .filter(cls.permission == perm)\
430 437 .scalar() is not None
431 438
432 439 @classmethod
433 440 def grant_perm(cls, users_group_id, perm):
434 441 if not isinstance(perm, Permission):
435 442 raise Exception('perm needs to be an instance of Permission class')
436 443
437 444 new = cls()
438 445 new.users_group_id = users_group_id
439 446 new.permission = perm
440 447 try:
441 448 Session.add(new)
442 449 Session.commit()
443 450 except:
444 451 Session.rollback()
445 452
446 453
447 454 @classmethod
448 455 def revoke_perm(cls, users_group_id, perm):
449 456 if not isinstance(perm, Permission):
450 457 raise Exception('perm needs to be an instance of Permission class')
451 458
452 459 try:
453 460 Session.query(cls).filter(cls.users_group_id == users_group_id)\
454 461 .filter(cls.permission == perm).delete()
455 462 Session.commit()
456 463 except:
457 464 Session.rollback()
458 465
459 466
460 467 class GroupToPerm(Base):
461 468 __tablename__ = 'group_to_perm'
462 469 __table_args__ = (UniqueConstraint('group_id', 'permission_id'), {'useexisting':True})
463 470
464 471 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
465 472 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
466 473 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
467 474 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
468 475
469 476 user = relationship('User')
470 477 permission = relationship('Permission')
471 478 group = relationship('Group')
472 479
473 480 class Statistics(Base):
474 481 __tablename__ = 'statistics'
475 482 __table_args__ = (UniqueConstraint('repository_id'), {'useexisting':True})
476 483 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
477 484 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
478 485 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
479 486 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
480 487 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
481 488 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
482 489
483 490 repository = relationship('Repository', single_parent=True)
484 491
485 492 class UserFollowing(Base):
486 493 __tablename__ = 'user_followings'
487 494 __table_args__ = (UniqueConstraint('user_id', 'follows_repository_id'),
488 495 UniqueConstraint('user_id', 'follows_user_id')
489 496 , {'useexisting':True})
490 497
491 498 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
492 499 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
493 500 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
494 501 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
495 502 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
496 503
497 504 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
498 505
499 506 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
500 507 follows_repository = relationship('Repository', order_by='Repository.repo_name')
501 508
502 509
503 510
504 511 @classmethod
505 512 def get_repo_followers(cls, repo_id):
506 513 return Session.query(cls).filter(cls.follows_repo_id == repo_id)
507 514
508 515 class CacheInvalidation(Base):
509 516 __tablename__ = 'cache_invalidation'
510 517 __table_args__ = (UniqueConstraint('cache_key'), {'useexisting':True})
511 518 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
512 519 cache_key = Column("cache_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
513 520 cache_args = Column("cache_args", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
514 521 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
515 522
516 523
517 524 def __init__(self, cache_key, cache_args=''):
518 525 self.cache_key = cache_key
519 526 self.cache_args = cache_args
520 527 self.cache_active = False
521 528
522 529 def __repr__(self):
523 530 return "<%s('%s:%s')>" % (self.__class__.__name__,
524 531 self.cache_id, self.cache_key)
525 532
526 533 class DbMigrateVersion(Base):
527 534 __tablename__ = 'db_migrate_version'
528 535 __table_args__ = {'useexisting':True}
529 536 repository_id = Column('repository_id', String(250), primary_key=True)
530 537 repository_path = Column('repository_path', Text)
531 538 version = Column('version', Integer)
@@ -1,355 +1,355 b''
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 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 <a title="${_('Forks')}" href="#">
301 <a title="${_('Forks')}" href="${h.url('repo_forks_home',repo_name=c.repo_name)}">
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