Changeset - 3025b0f9475e
[Not reviewed]
beta
0 3 0
Marcin Kuzminski - 15 years ago 2010-11-29 22:54:45
marcin@python-works.com
disabled journal for anonymous users
3 files changed with 13 insertions and 6 deletions:
0 comments (0 inline, 0 general)
rhodecode/controllers/journal.py
Show inline comments
 
#!/usr/bin/env python
 
# encoding: utf-8
 
# journal controller for pylons
 
# Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
 
# 
 
# This program is free software; you can redistribute it and/or
 
# modify it under the terms of the GNU General Public License
 
# as published by the Free Software Foundation; version 2
 
# of the License or (at your opinion) any later version of the license.
 
# 
 
# This program is distributed in the hope that it will be useful,
 
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
# GNU General Public License for more details.
 
# 
 
# You should have received a copy of the GNU General Public License
 
# along with this program; if not, write to the Free Software
 
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 
# MA  02110-1301, USA.
 
"""
 
Created on November 21, 2010
 
journal controller for pylons
 
@author: marcink
 
"""
 

	
 
from pylons import request, response, session, tmpl_context as c, url
 
from pylons.controllers.util import abort, redirect
 
from rhodecode.lib.auth import LoginRequired
 
from rhodecode.lib.auth import LoginRequired, NotAnonymous
 
from rhodecode.lib.base import BaseController, render
 
from rhodecode.lib.helpers import get_token
 
from rhodecode.lib.utils import action_logger
 
from rhodecode.model.db import UserLog, UserFollowing
 
from rhodecode.model.scm import ScmModel
 
from sqlalchemy import or_
 
import logging
 
from paste.httpexceptions import HTTPInternalServerError, HTTPNotFound
 

	
 
log = logging.getLogger(__name__)
 

	
 
class JournalController(BaseController):
 

	
 

	
 
    @LoginRequired()
 
    @NotAnonymous()
 
    def __before__(self):
 
        super(JournalController, self).__before__()
 

	
 
    def index(self):
 
        # Return a rendered template
 

	
 
        c.following = self.sa.query(UserFollowing)\
 
            .filter(UserFollowing.user_id == c.rhodecode_user.user_id).all()
 
        
 
        repo_ids = [x.follows_repository.repo_id for x in c.following 
 
                    if x.follows_repository is not None]
 
        user_ids = [x.follows_user.user_id for x in c.following 
 
                    if x.follows_user is not None]
 
        
 
        c.journal = self.sa.query(UserLog)\
 
            .filter(or_(
 
                        UserLog.repository_id.in_(repo_ids),
 
                        UserLog.user_id.in_(user_ids),
 
                        ))\
 
            .order_by(UserLog.action_date.desc())\
 
            .limit(20)\
 
            .all()
 
        return render('/journal.html')
 

	
 

	
 
    def toggle_following(self):
 

	
 
        if request.POST.get('auth_token') == get_token():
 
            scm_model = ScmModel()
 

	
 
            user_id = request.POST.get('follows_user_id')
 
            if user_id:
 
                try:
 
                    scm_model.toggle_following_user(user_id,
 
                                                    c.rhodecode_user.user_id)
 
                    return 'ok'
 
                except:
 
                    raise HTTPInternalServerError()
 

	
 
            repo_id = request.POST.get('follows_repo_id')
 
            if repo_id:
 
                try:
 
                    scm_model.toggle_following_repo(repo_id,
 
                                                    c.rhodecode_user.user_id)
 
                    return 'ok'
 
                except:
 
                    raise HTTPInternalServerError()
 

	
 

	
 

	
 
        raise HTTPInternalServerError()
rhodecode/templates/base/base.html
Show inline comments
 
## -*- coding: utf-8 -*-
 
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
 
<html xmlns="http://www.w3.org/1999/xhtml" id="mainhtml">
 
<head>
 
    <title>${next.title()}</title>
 
    <link rel="icon" href="/images/icons/database_gear.png" type="image/png" />
 
    <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
 
    <meta name="robots" content="index, nofollow"/>
 
    <!-- stylesheets -->
 
    ${self.css()}
 
    <!-- scripts -->
 
    ${self.js()}
 
</head>
 
<body>
 
    <!-- header -->
 
    <div id="header">
 
        <!-- user -->
 
        <ul id="logged-user">
 
	            <li class="first">
 
	                <div class="gravatar">
 
	                    <img alt="gravatar" src="${h.gravatar_url(c.rhodecode_user.email,20)}" />
 
	                </div>
 
		            <div class="account">
 
		            %if c.rhodecode_user.username == 'default':
 
                        %if h.HasPermissionAny('hg.admin', 'hg.register.auto_activate', 'hg.register.manual_activate')(): 
 
                        ${h.link_to('anonymous',h.url('register'),title='%s %s'%(c.rhodecode_user.name,c.rhodecode_user.lastname))}
 
                    %else:                        		            
 
                            ${h.link_to('anonymous',h.url('#'),title='%s %s'%(c.rhodecode_user.name,c.rhodecode_user.lastname))}
 
                        %endif		            
 
                        
 
                    %else:                        		            
 
		            	${h.link_to(c.rhodecode_user.username,h.url('admin_settings_my_account'),title='%s %s'%(c.rhodecode_user.name,c.rhodecode_user.lastname))}
 
		            %endif
 
		            </div>	
 
	            </li>
 
	            <li>
 
	               <a href="${h.url('home')}">${_('Home')}</a>
 
	            </li>
 
	            %if c.rhodecode_user.username != 'default':
 
                <li>
 
                   <a href="${h.url('journal')}">${_('Journal')}</a> 
 
                   ##(${c.unread_journal})</a>
 
                </li>
 
                %endif
 
                %if c.rhodecode_user.username == 'default':
 
                    <li class="last highlight">${h.link_to(u'Login',h.url('login_home'))}</li>
 
                %else:
 
                    <li class="last highlight">${h.link_to(u'Log Out',h.url('logout_home'))}</li>
 
                %endif
 
        </ul>
 
        <!-- end user -->
 
        <div id="header-inner" class="title top-left-rounded-corner top-right-rounded-corner">
 
            <!-- logo -->
 
            <div id="logo">
 
                <h1><a href="${h.url('home')}">${c.rhodecode_name}</a></h1>
 
            </div>
 
            <!-- end logo -->
 
            <!-- menu -->
 
            ${self.page_nav()}
 
            <!-- quick -->
 
        </div>
 
    </div>     
 
    <!-- end header -->
 
    
 
	<!-- CONTENT -->
 
	<div id="content"> 
 
        <div class="flash_msg">
 
            <% messages = h.flash.pop_messages() %>
 
            % if messages:
 
            <ul id="flash-messages">
 
                % for message in messages:
 
                <li class="${message.category}_msg">${message}</li>
 
                % endfor
 
            </ul>
 
            % endif
 
        </div>	    
 
	    <div id="main"> 
 
	        ${next.main()}
 
	    </div>
 
	</div> 
 
    <!-- END CONTENT -->
 

	
 
	<!-- footer -->
 
	<div id="footer">
 
	   <div id="footer-inner" class="title bottom-left-rounded-corner bottom-right-rounded-corner">
 
	       <div>
 
	           <p class="footer-link">${h.link_to(_('Submit a bug'),h.url('bugtracker'))}</p>
 
		       <p class="footer-link">${h.link_to(_('GPL license'),h.url('gpl_license'))}</p>
 
		       <p>RhodeCode ${c.rhodecode_version} &copy; 2010 by Marcin Kuzminski</p>
 
	       </div>
 
	   </div>
 
        <script type="text/javascript">${h.tooltip.activate()}</script>
 
	</div>
 
	<!-- end footer -->
 
</body>
 

	
 
</html>
 

	
 
### MAKO DEFS ### 
 
<%def name="page_nav()">
 
	${self.menu()}
 
</%def>
 

	
 
<%def name="menu(current=None)">
 
		<% 
 
		def is_current(selected):
 
			if selected == current:
 
				return h.literal('class="current"')
 
		%>
 
		%if current not in ['home','admin']:           		
 
		   ##REGULAR MENU            
 
	        <ul id="quick">
 
				<!-- repo switcher -->
 
				<li>
 
					<a id="repo_switcher" title="${_('Switch repository')}" href="#">
 
                    <span class="icon">
 
                        <img src="/images/icons/database.png" alt="${_('Products')}" />
 
                    </span>
 
                    <span>&darr;</span>					
 
					</a>
 
					<ul class="repo_switcher">
 
                        %for repo in c.cached_repo_list:
 
                        
 
                          %if repo['repo'].dbrepo.private:
 
                             <li><img src="/images/icons/lock.png" alt="${_('Private repository')}" class="repo_switcher_type"/>${h.link_to(repo['repo'].name,h.url('summary_home',repo_name=repo['repo'].name),class_="%s" % repo['repo'].dbrepo.repo_type)}</li>
 
                          %else:
 
                             <li><img src="/images/icons/lock_open.png" alt="${_('Public repository')}" class="repo_switcher_type" />${h.link_to(repo['repo'].name,h.url('summary_home',repo_name=repo['repo'].name),class_="%s" % repo['repo'].dbrepo.repo_type)}</li>
 
                          %endif  
 
                        %endfor					
 
					</ul>			
 
				</li>
 
				
 
	            <li ${is_current('summary')}>
 
	               <a title="${_('Summary')}" href="${h.url('summary_home',repo_name=c.repo_name)}">
 
	               <span class="icon">
 
	                   <img src="/images/icons/clipboard_16.png" alt="${_('Summary')}" />
 
	               </span>
 
	               <span>${_('Summary')}</span>                 
 
	               </a>	            
 
	            </li>
 
@@ -164,195 +171,195 @@
 
						            <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>
 
						        %endfor
 
						    %else:
 
						    	<li>${h.link_to(_('There are no branches yet'),'#')}</li>
 
						    %endif
 
                            </ul>                        
 
                        </li>
 
                        <li>
 
                            ${h.link_to('%s (%s)' % (_('tags'),len(c.repository_tags.values()),),h.url('tags_home',repo_name=c.repo_name),class_='tags childs')}
 
                            <ul>
 
                            %if c.repository_tags.values():
 
                                %for cnt,tag in enumerate(c.repository_tags.items()):
 
                                 <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>
 
                                %endfor
 
                            %else:
 
                            	<li>${h.link_to(_('There are no tags yet'),'#')}</li>
 
                            %endif
 
                            </ul>                        
 
                        </li>                        
 
                    </ul>
 
                </li>
 
                <li ${is_current('files')}>
 
                   <a title="${_('Files')}" href="${h.url('files_home',repo_name=c.repo_name)}">
 
                   <span class="icon">
 
                       <img src="/images/icons/file.png" alt="${_('Files')}" />
 
                   </span>
 
                   <span>${_('Files')}</span>                 
 
                   </a>             
 
                </li>                            
 
				
 
                <li ${is_current('options')}>
 
                   <a title="${_('Options')}" href="#">
 
                   <span class="icon">
 
                       <img src="/images/icons/table_gear.png" alt="${_('Admin')}" />
 
                   </span>
 
                   <span>${_('Options')}</span>                 
 
                   </a>
 
                   <ul>
 
                   %if h.HasRepoPermissionAll('repository.admin')(c.repo_name):
 
                   	<li>${h.link_to(_('settings'),h.url('repo_settings_home',repo_name=c.repo_name),class_='settings')}</li>
 
                   	<li>${h.link_to(_('fork'),h.url('repo_fork_home',repo_name=c.repo_name),class_='fork')}</li>
 
                   %endif  
 
                   	<li>${h.link_to(_('search'),h.url('search_repo',search_repo=c.repo_name),class_='search')}</li>
 
                    
 
                    %if h.HasPermissionAll('hg.admin')('access admin main page'):
 
                    <li>
 
                       ${h.link_to(_('admin'),h.url('admin_home'),class_='admin')}  
 
                        <%def name="admin_menu()">
 
                        <ul>
 
                            <li>${h.link_to(_('journal'),h.url('admin_home'),class_='journal')}</li>
 
                            <li>${h.link_to(_('repositories'),h.url('repos'),class_='repos')}</li>
 
                            <li>${h.link_to(_('users'),h.url('users'),class_='users')}</li>
 
                            <li>${h.link_to(_('permissions'),h.url('edit_permission',id='default'),class_='permissions')}</li>
 
                            <li>${h.link_to(_('ldap'),h.url('ldap_home'),class_='ldap')}</li>
 
                            <li class="last">${h.link_to(_('settings'),h.url('admin_settings'),class_='settings')}</li>        
 
                        </ul>
 
                        </%def>
 
                        
 
                        ${admin_menu()}
 
                    </li>
 
                    %endif
 

	
 
                   </ul>             
 
                </li>
 
                
 
                <li>
 
                    <a title="${_('Followers')}" href="#">
 
                    <span class="icon_short">
 
                        <img src="/images/icons/heart.png" alt="${_('Followers')}" />
 
                    </span>
 
                    <span class="short">${c.repository_followers}</span>
 
                    </a>
 
                </li>
 
                <li>
 
                    <a title="${_('Forks')}" href="#">
 
                    <span class="icon_short">
 
                        <img src="/images/icons/arrow_divide.png" alt="${_('Forks')}" />
 
                    </span>
 
                    <span class="short">${c.repository_forks}</span>
 
                    </a>
 
                </li>                
 
                
 
                
 
                
 
	        </ul>
 
		%else:
 
		    ##ROOT MENU
 
            <ul id="quick">
 
                <li>
 
                    <a title="${_('Home')}"  href="${h.url('home')}">
 
                    <span class="icon">
 
                        <img src="/images/icons/home_16.png" alt="${_('Home')}" />
 
                    </span>
 
                    <span>${_('Home')}</span>                 
 
                    </a>        
 
                </li>
 
                
 
                %if c.rhodecode_user.username != 'default':
 
                <li>
 
                    <a title="${_('Journal')}"  href="${h.url('journal')}">
 
                    <span class="icon">
 
                        <img src="/images/icons/book.png" alt="${_('Journal')}" />
 
                    </span>
 
                    <span>${_('Journal')}</span>                 
 
                    </a>        
 
                </li>
 
                                
 
                %endif          
 
                <li>
 
                    <a title="${_('Search')}"  href="${h.url('search')}">
 
                    <span class="icon">
 
                        <img src="/images/icons/search_16.png" alt="${_('Search')}" />
 
                    </span>
 
                    <span>${_('Search')}</span>                 
 
                    </a>        
 
                </li>
 
                
 
				%if h.HasPermissionAll('hg.admin')('access admin main page'):
 
                <li ${is_current('admin')}>
 
                   <a title="${_('Admin')}" href="${h.url('admin_home')}">
 
                   <span class="icon">
 
                       <img src="/images/icons/cog_edit.png" alt="${_('Admin')}" />
 
                   </span>
 
                   <span>${_('Admin')}</span>                 
 
                   </a>
 
                    ${admin_menu()}
 
                </li>
 
				%endif
 
			</ul>
 
		%endif    
 
</%def>
 

	
 

	
 
<%def name="css()">
 
<link rel="stylesheet" type="text/css" href="/css/style.css" media="screen" />
 
<link rel="stylesheet" type="text/css" href="/css/pygments.css"  />
 
<link rel="stylesheet" type="text/css" href="/css/diff.css"  />
 
</%def>
 

	
 
<%def name="js()">
 
##<script type="text/javascript" src="/js/yui/utilities/utilities.js"></script>
 
##<script type="text/javascript" src="/js/yui/container/container.js"></script>
 
##<script type="text/javascript" src="/js/yui/datasource/datasource.js"></script>
 
##<script type="text/javascript" src="/js/yui/autocomplete/autocomplete.js"></script>
 
##<script type="text/javascript" src="/js/yui/selector/selector-min.js"></script>
 

	
 
<script type="text/javascript" src="/js/yui2a.js"></script>
 
<!--[if IE]><script language="javascript" type="text/javascript" src="/js/excanvas.min.js"></script><![endif]-->
 
<script type="text/javascript" src="/js/yui.flot.js"></script>
 

	
 
<script type="text/javascript">
 
var base_url  ='/_admin/toggle_following';
 
var YUC = YAHOO.util.Connect;
 
var YUD = YAHOO.util.Dom;
 
var YUE = YAHOO.util.Event;
 

	
 
function onSuccess(){
 
	
 
	var f = YUD.get('follow_toggle');
 
    if(f.getAttribute('class')=='follow'){
 
        f.setAttribute('class','following');
 
        f.setAttribute('title',"${_('Stop following this repository')}");
 
    }
 
    else{
 
        f.setAttribute('class','follow');
 
        f.setAttribute('title',"${_('Start following this repository')}");
 
    }
 
}
 

	
 
function toggleFollowingUser(fallows_user_id,token){
 
    args = 'follows_user_id='+fallows_user_id;
 
    args+= '&auth_token='+token;
 
    YUC.asyncRequest('POST',base_url,{
 
        success:function(o){
 
        	onSuccess();
 
        }
 
    },args); return false;
 
}
 

	
 
function toggleFollowingRepo(fallows_repo_id,token){
 
    args = 'follows_repo_id='+fallows_repo_id;
 
    args+= '&auth_token='+token;
 
    YUC.asyncRequest('POST',base_url,{
 
        success:function(o){
 
        	onSuccess();
 
        }
 
    },args); return false;
 
}    
 
</script>
 

	
 
</%def>
 

	
 
<%def name="breadcrumbs()">
 
    <div class="breadcrumbs">
 
    ${self.breadcrumbs_links()}
 
    </div>
 
</%def>
 
\ No newline at end of file
rhodecode/templates/summary/summary.html
Show inline comments
 
<%inherit file="/base/base.html"/>
 

	
 
<%def name="title()">
 
    ${c.repo_name} ${_('Summary')} - ${c.rhodecode_name}
 
</%def>
 

	
 
<%def name="breadcrumbs_links()">
 
    ${h.link_to(u'Home',h.url('/'))}
 
    &raquo; 
 
    ${h.link_to(c.repo_name,h.url('summary_home',repo_name=c.repo_name))}
 
    &raquo;
 
    ${_('summary')}
 
</%def>
 

	
 
<%def name="page_nav()">
 
	${self.menu('summary')}    
 
</%def>
 

	
 
<%def name="main()">
 
<div class="box box-left">
 
    <!-- box / title -->
 
    <div class="title">
 
        ${self.breadcrumbs()}
 
    </div>
 
    <!-- end box / title -->
 
	<div class="form">
 
	  <div class="fields">
 
		 
 
			 <div class="field">
 
			  <div class="label">
 
			      <label>${_('Name')}:</label>
 
			  </div>
 
			  <div class="input-short">
 
		         %if c.repo_info.dbrepo.repo_type =='hg':
 
		           <img style="margin-bottom:2px" class="icon" title="${_('Mercurial repository')}" alt="${_('Mercurial repository')}" src="/images/icons/hgicon.png"/>
 
		         %endif
 
		         %if c.repo_info.dbrepo.repo_type =='git':
 
		           <img style="margin-bottom:2px" class="icon" title="${_('Git repository')}" alt="${_('Git repository')}" src="/images/icons/giticon.png"/>
 
		         %endif 
 
                                 			  
 
	             %if c.repo_info.dbrepo.private:
 
	                <img style="margin-bottom:2px" class="icon" title="${_('private repository')}" alt="${_('private repository')}" src="/images/icons/lock.png"/>
 
	             %else:
 
	                <img style="margin-bottom:2px" class="icon" title="${_('public repository')}" alt="${_('public repository')}" src="/images/icons/lock_open.png"/>
 
	             %endif
 
			      <span style="font-size: 1.6em;font-weight: bold;vertical-align: baseline;">${c.repo_info.name}</span>
 
			      
 
			      %if c.rhodecode_user.username != 'default':
 
			      %if c.following:
 
                  <span id="follow_toggle" class="following" title="${_('Stop following this repository')}"
 
                        onclick="javascript:toggleFollowingRepo(${c.repo_info.dbrepo.repo_id},'${str(h.get_token())}')">
 
                  </span>			      
 
			      %else:
 
			      <span id="follow_toggle" class="follow" title="${_('Start following this repository')}"
 
			            onclick="javascript:toggleFollowingRepo(${c.repo_info.dbrepo.repo_id},'${str(h.get_token())}')">
 
			      </span>
 
			      %endif
 
				  %endif:
 
			      <br/>
 
		            %if c.repo_info.dbrepo.fork:
 
		            	<span style="margin-top:5px">
 
		            	<a href="${h.url('summary_home',repo_name=c.repo_info.dbrepo.fork.repo_name)}">
 
		            	<img class="icon" alt="${_('public')}"
 
		            	title="${_('Fork of')} ${c.repo_info.dbrepo.fork.repo_name}" 
 
		            	src="/images/icons/arrow_divide.png"/>
 
		            	${_('Fork of')} ${c.repo_info.dbrepo.fork.repo_name}
 
		            	</a>
 
		            	</span>
 
		            %endif			      
 
			  </div>
 
			 </div>
 
			
 
			
 
			 <div class="field">
 
			  <div class="label">
 
			      <label>${_('Description')}:</label>
 
			  </div>
 
			  <div class="input-short">
 
			      ${c.repo_info.dbrepo.description}
 
			  </div>
 
			 </div>
 
			
 
			
 
			 <div class="field">
 
			  <div class="label">
 
			      <label>${_('Contact')}:</label>
 
			  </div>
 
			  <div class="input-short">
 
			  	<div class="gravatar">
 
			  		<img alt="gravatar" src="${h.gravatar_url(c.repo_info.dbrepo.user.email)}"/>
 
			  	</div>
 
			  		${_('Username')}: ${c.repo_info.dbrepo.user.username}<br/>
 
			  		${_('Name')}: ${c.repo_info.dbrepo.user.name} ${c.repo_info.dbrepo.user.lastname}<br/>
 
			  		${_('Email')}: <a href="mailto:${c.repo_info.dbrepo.user.email}">${c.repo_info.dbrepo.user.email}</a>
 
			  </div>
 
			 </div>
 
			
 
			 <div class="field">
 
			  <div class="label">
 
			      <label>${_('Last change')}:</label>
 
			  </div>
 
			  <div class="input-short">
 
			      ${h.age(c.repo_info.last_change)} - ${c.repo_info.last_change} 
 
			      ${_('by')} ${h.get_changeset_safe(c.repo_info,'tip').author} 
 
			      
 
			  </div>
 
			 </div>
 
			
 
			 <div class="field">
 
			  <div class="label">
 
			      <label>${_('Clone url')}:</label>
 
			  </div>
 
			  <div class="input-short">
 
			      <input type="text" id="clone_url" readonly="readonly" value="hg clone ${c.clone_repo_url}" size="70"/>
 
			  </div>
 
			 </div>
 
			 
 
			 <div class="field">
 
			  <div class="label">
 
			      <label>${_('Trending source files')}:</label>
 
			  </div>
 
			  <div class="input-short">
 
			    <div id="lang_stats">
 
			    
 
			    </div> 			  
 
			  	<script type="text/javascript">
 
				  	YUE.onDOMReady(function(e){
 
				  	    id = 'clone_url';
 
				  	    YUE.on(id,'click',function(e){
 
				  	        YUD.get('clone_url').select();
 
				  	    })
 
				  	})
 
			  		var data = ${c.trending_languages|n};
 
			  		var total = 0;
 
			  		var no_data = true;
 
			  		for (k in data){
 
			  		    total += data[k];
 
			  		    no_data = false;
 
			  		} 
 
					var tbl = document.createElement('table');
 
					tbl.setAttribute('class','trending_language_tbl');
 
					var cnt =0;
 
			  		for (k in data){
 
			  			cnt+=1;
 
			  			var hide = cnt>2;
 
				  		var tr = document.createElement('tr');
 
				  		if (hide){
 
				  			tr.setAttribute('style','display:none');
 
				  			tr.setAttribute('class','stats_hidden');
 
				  		}
 
				  		var percentage = Math.round((data[k]/total*100),2);
 
						var value = data[k];
 
				  		var td1 = document.createElement('td');
 
				  		td1.width=150;
0 comments (0 inline, 0 general)