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 | » | |
|
12 | ${h.link_to(c.repo_name,h.url('summary_home',repo_name=c.repo_name))} | |
|
13 | » | |
|
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 |
@@ -261,7 +261,9 def make_map(config): | |||
|
261 | 261 | controller='feed', action='atom', |
|
262 | 262 | conditions=dict(function=check_repo)) |
|
263 | 263 | |
|
264 | #========================================================================== | |
|
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)) |
@@ -336,4 +338,7 def make_map(config): | |||
|
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 |
@@ -487,12 +487,19 class UserFollowing(Base): | |||
|
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}) |
@@ -290,7 +290,7 | |||
|
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> |
General Comments 0
You need to be logged in to leave comments.
Login now