Changeset - 77e376fdc4c6
[Not reviewed]
rhodecode/config/routing.py
Show inline comments
 
@@ -372,96 +372,101 @@ def make_map(config):
 
    rmap.connect('reset_password_confirmation',
 
                 '%s/password_reset_confirmation' % ADMIN_PREFIX,
 
                 controller='login', action='password_reset_confirmation')
 

	
 
    #FEEDS
 
    rmap.connect('rss_feed_home', '/{repo_name:.*}/feed/rss',
 
                controller='feed', action='rss',
 
                conditions=dict(function=check_repo))
 

	
 
    rmap.connect('atom_feed_home', '/{repo_name:.*}/feed/atom',
 
                controller='feed', action='atom',
 
                conditions=dict(function=check_repo))
 

	
 
    #==========================================================================
 
    # REPOSITORY ROUTES
 
    #==========================================================================
 
    rmap.connect('summary_home', '/{repo_name:.*}',
 
                controller='summary',
 
                conditions=dict(function=check_repo))
 

	
 
    rmap.connect('repos_group_home', '/{group_name:.*}',
 
                controller='admin/repos_groups', action="show_by_name",
 
                conditions=dict(function=check_group))
 

	
 
    rmap.connect('changeset_home', '/{repo_name:.*}/changeset/{revision}',
 
                controller='changeset', revision='tip',
 
                conditions=dict(function=check_repo))
 

	
 
    rmap.connect('changeset_comment',
 
                 '/{repo_name:.*}/changeset/{revision}/comment',
 
                controller='changeset', revision='tip', action='comment',
 
                conditions=dict(function=check_repo))
 

	
 
    rmap.connect('changeset_comment_delete',
 
                 '/{repo_name:.*}/changeset/comment/{comment_id}/delete',
 
                controller='changeset', action='delete_comment',
 
                conditions=dict(function=check_repo, method=["DELETE"]))
 

	
 
    rmap.connect('raw_changeset_home',
 
                 '/{repo_name:.*}/raw-changeset/{revision}',
 
                 controller='changeset', action='raw_changeset',
 
                 revision='tip', conditions=dict(function=check_repo))
 

	
 
    rmap.connect('compare_home',
 
                 '/{repo_name:.*}/compare/{ref:.*}',
 
                 controller='compare', action='index',
 
                 conditions=dict(function=check_repo))
 

	
 
    rmap.connect('pullrequest_home',
 
                 '/{repo_name:.*}/pull-request/new',
 
                 controller='pullrequests', action='index',
 
                 conditions=dict(function=check_repo))
 

	
 
    rmap.connect('summary_home', '/{repo_name:.*}/summary',
 
                controller='summary', conditions=dict(function=check_repo))
 

	
 
    rmap.connect('shortlog_home', '/{repo_name:.*}/shortlog',
 
                controller='shortlog', conditions=dict(function=check_repo))
 

	
 
    rmap.connect('branches_home', '/{repo_name:.*}/branches',
 
                controller='branches', conditions=dict(function=check_repo))
 

	
 
    rmap.connect('tags_home', '/{repo_name:.*}/tags',
 
                controller='tags', conditions=dict(function=check_repo))
 

	
 
    rmap.connect('bookmarks_home', '/{repo_name:.*}/bookmarks',
 
                controller='bookmarks', conditions=dict(function=check_repo))
 

	
 
    rmap.connect('changelog_home', '/{repo_name:.*}/changelog',
 
                controller='changelog', conditions=dict(function=check_repo))
 

	
 
    rmap.connect('changelog_details', '/{repo_name:.*}/changelog_details/{cs}',
 
                controller='changelog', action='changelog_details',
 
                conditions=dict(function=check_repo))
 

	
 
    rmap.connect('files_home', '/{repo_name:.*}/files/{revision}/{f_path:.*}',
 
                controller='files', revision='tip', f_path='',
 
                conditions=dict(function=check_repo))
 

	
 
    rmap.connect('files_diff_home', '/{repo_name:.*}/diff/{f_path:.*}',
 
                controller='files', action='diff', revision='tip', f_path='',
 
                conditions=dict(function=check_repo))
 

	
 
    rmap.connect('files_rawfile_home',
 
                 '/{repo_name:.*}/rawfile/{revision}/{f_path:.*}',
 
                 controller='files', action='rawfile', revision='tip',
 
                 f_path='', conditions=dict(function=check_repo))
 

	
 
    rmap.connect('files_raw_home',
 
                 '/{repo_name:.*}/raw/{revision}/{f_path:.*}',
 
                 controller='files', action='raw', revision='tip', f_path='',
 
                 conditions=dict(function=check_repo))
 

	
 
    rmap.connect('files_annotate_home',
 
                 '/{repo_name:.*}/annotate/{revision}/{f_path:.*}',
 
                 controller='files', action='index', revision='tip',
 
                 f_path='', annotate=True, conditions=dict(function=check_repo))
 

	
 
    rmap.connect('files_edit_home',
 
                 '/{repo_name:.*}/edit/{revision}/{f_path:.*}',
 
                 controller='files', action='edit', revision='tip',
rhodecode/controllers/pullrequests.py
Show inline comments
 
new file 100644
 
# -*- coding: utf-8 -*-
 
"""
 
    rhodecode.controllers.pullrequests
 
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 

	
 
    pull requests controller for rhodecode for initializing pull requests
 

	
 
    :created_on: May 7, 2012
 
    :author: marcink
 
    :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
 
    :license: GPLv3, see COPYING for more details.
 
"""
 
# 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, either version 3 of the License, or
 
# (at your option) any later version.
 
#
 
# 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, see <http://www.gnu.org/licenses/>.
 
import logging
 
import traceback
 

	
 
from pylons import request, response, session, tmpl_context as c, url
 
from pylons.controllers.util import abort, redirect
 
from pylons.i18n.translation import _
 

	
 
from rhodecode.lib.base import BaseRepoController, render
 
from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
 
from webob.exc import HTTPNotFound
 

	
 
log = logging.getLogger(__name__)
 

	
 

	
 
class PullrequestsController(BaseRepoController):
 

	
 
    @LoginRequired()
 
    @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
 
                                   'repository.admin')
 
    def __before__(self):
 
        super(PullrequestsController, self).__before__()
 

	
 
    def _get_repo_refs(self,repo):
 
        hist_l = []
 

	
 
        branches_group = ([(k, k) for k in repo.branches.keys()], _("Branches"))
 
        bookmarks_group = ([(k, k) for k in repo.bookmarks.keys()], _("Bookmarks"))
 
        tags_group = ([(k, k) for k in repo.tags.keys()], _("Tags"))
 

	
 
        hist_l.append(bookmarks_group)
 
        hist_l.append(branches_group)
 
        hist_l.append(tags_group)
 

	
 
        return hist_l
 

	
 
    def index(self):
 
        c.org_refs = self._get_repo_refs(c.rhodecode_repo)
 
        c.sources = []
 
        c.sources.append('%s/%s' % (c.rhodecode_db_repo.user.username,
 
                                    c.repo_name))
 
        return render('/pullrequests/pullrequest.html')
rhodecode/model/db.py
Show inline comments
 
@@ -1236,96 +1236,97 @@ class ChangesetStatus(Base, BaseModel):
 

	
 
    STATUSES = [
 
        ('not_reviewed', _("Not Reviewed")),  # (no icon) and default
 
        ('approved', _("Approved")),
 
        ('rejected', _("Rejected")),
 
        ('under_review', _("Under Review")),
 
    ]
 
    DEFAULT = STATUSES[0][0]
 

	
 
    changeset_status_id = Column('changeset_status_id', Integer(), nullable=False, primary_key=True)
 
    repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
 
    user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
 
    revision = Column('revision', String(40), nullable=False)
 
    status = Column('status', String(128), nullable=False, default=DEFAULT)
 
    modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
 

	
 
    author = relationship('User', lazy='joined')
 
    repo = relationship('Repository')
 

	
 
    @property
 
    def status_lbl(self):
 
        return dict(self.STATUSES).get(self.status)
 

	
 

	
 
class ChangesetStatusHistory(Base, BaseModel):
 
    __tablename__ = 'changeset_statuses_history'
 
    __table_args__ = (
 
        {'extend_existing': True, 'mysql_engine': 'InnoDB',
 
         'mysql_charset': 'utf8'}
 
    )
 
    #TODO: check if sqla has a nice history table implementation
 
    changeset_status_id = Column('changeset_status_id', Integer(), ForeignKey('changeset_statuses.changeset_status_id'), nullable=False, primary_key=True)
 
    user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
 
    status = Column('status', String(128), nullable=False)
 
    modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
 

	
 

	
 
class Notification(Base, BaseModel):
 
    __tablename__ = 'notifications'
 
    __table_args__ = (
 
        {'extend_existing': True, 'mysql_engine': 'InnoDB',
 
         'mysql_charset': 'utf8'},
 
    )
 

	
 
    TYPE_CHANGESET_COMMENT = u'cs_comment'
 
    TYPE_MESSAGE = u'message'
 
    TYPE_MENTION = u'mention'
 
    TYPE_REGISTRATION = u'registration'
 
    TYPE_PULL_REQUEST = u'pull_request'
 

	
 
    notification_id = Column('notification_id', Integer(), nullable=False, primary_key=True)
 
    subject = Column('subject', Unicode(512), nullable=True)
 
    body = Column('body', Unicode(50000), nullable=True)
 
    created_by = Column("created_by", Integer(), ForeignKey('users.user_id'), nullable=True)
 
    created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
 
    type_ = Column('type', Unicode(256))
 

	
 
    created_by_user = relationship('User')
 
    notifications_to_users = relationship('UserNotification', lazy='joined',
 
                                          cascade="all, delete, delete-orphan")
 

	
 
    @property
 
    def recipients(self):
 
        return [x.user for x in UserNotification.query()\
 
                .filter(UserNotification.notification == self).all()]
 

	
 
    @classmethod
 
    def create(cls, created_by, subject, body, recipients, type_=None):
 
        if type_ is None:
 
            type_ = Notification.TYPE_MESSAGE
 

	
 
        notification = cls()
 
        notification.created_by_user = created_by
 
        notification.subject = subject
 
        notification.body = body
 
        notification.type_ = type_
 
        notification.created_on = datetime.datetime.now()
 

	
 
        for u in recipients:
 
            assoc = UserNotification()
 
            assoc.notification = notification
 
            u.notifications.append(assoc)
 
        Session.add(notification)
 
        return notification
 

	
 
    @property
 
    def description(self):
 
        from rhodecode.model.notification import NotificationModel
 
        return NotificationModel().make_description(self)
 

	
 

	
 
class UserNotification(Base, BaseModel):
 
    __tablename__ = 'user_to_notification'
 
    __table_args__ = (
 
        UniqueConstraint('user_id', 'notification_id'),
 
        {'extend_existing': True, 'mysql_engine': 'InnoDB',
 
         'mysql_charset': 'utf8'}
rhodecode/templates/admin/notifications/notifications.html
Show inline comments
 
## -*- coding: utf-8 -*-
 
<%inherit file="/base/base.html"/>
 

	
 
<%def name="title()">
 
    ${_('My Notifications')} ${c.rhodecode_user.username} - ${c.rhodecode_name}
 
</%def>
 

	
 
<%def name="breadcrumbs_links()">
 
    ${_('My Notifications')}
 
</%def>
 

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

	
 
<%def name="main()">
 
<div class="box">
 
    <!-- box / title -->
 
    <div class="title">
 
        ${self.breadcrumbs()}
 
        ##<ul class="links">
 
        ##    <li>
 
        ##      <span style="text-transform: uppercase;"><a href="#">${_('Compose message')}</a></span>
 
        ##    </li>
 
        ##</ul>
 
    </div>
 
    %if c.notifications:
 
      <div style="padding:14px 18px;text-align: right;float:left">
 
      <span id='all' class="ui-btn">${_('All')}</span>
 
      <span id='pull_request' class="ui-btn">${_('Pull requests')}</span>
 
      </div>
 
      <div style="padding:14px 18px;text-align: right;float:right">
 
      <span id='mark_all_read' class="ui-btn">${_('Mark all read')}</span>
 
      </div>
 
    %endif
 
  <div id='notification_data'>
 
    <%include file='notifications_data.html'/>
 
  </div>
 
</div>
 
<script type="text/javascript">
 
var url_del = "${url('notification', notification_id='__NOTIFICATION_ID__')}";
 
YUE.on(YUQ('.delete-notification'),'click',function(e){
 
 var notification_id = e.currentTarget.id;
 
 deleteNotification(url_del,notification_id)
 
})
 
YUE.on('mark_all_read','click',function(e){
 
    var url = "${h.url('notifications_mark_all_read')}";
 
    ypjax(url,'notification_data',function(){
 
    	var notification_counter = YUD.get('notification_counter');
 
    	if(notification_counter){
 
    		notification_counter.innerHTML=0;
 
    	}
 
    	YUE.on(YUQ('.delete-notification'),'click',function(e){
 
    		 var notification_id = e.currentTarget.id;
 
    		 deleteNotification(url_del,notification_id)
 
    	})
 
    });
 
})
 
</script>
 
</%def>
rhodecode/templates/changelog/changelog.html
Show inline comments
 
## -*- coding: utf-8 -*-
 

	
 
<%inherit file="/base/base.html"/>
 

	
 
<%def name="title()">
 
${c.repo_name} ${_('Changelog')} - ${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;
 
    ${_('Changelog')} - ${_('showing ')} ${c.size if c.size <= c.total_cs else c.total_cs} ${_('out of')} ${c.total_cs} ${_('revisions')}
 
</%def>
 

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

	
 
<%def name="main()">
 
<div class="box">
 
    <!-- box / title -->
 
    <div class="title">
 
        ${self.breadcrumbs()}
 
    </div>
 
    <div class="table">
 
		% if c.pagination:
 
			<div id="graph">
 
				<div id="graph_nodes">
 
					<canvas id="graph_canvas"></canvas>
 
				</div>
 
				<div id="graph_content">
 
                    <div class="info_box" style="clear: both;padding: 10px 6px;vertical-align: right;text-align: right;"><a href="${h.url('pullrequest_home',repo_name=c.repo_name)}" class="ui-btn small">${_('Open new pull request')}</a></div>
 
					<div class="container_header">
 
				        ${h.form(h.url.current(),method='get')}
 
				        <div class="info_box" style="float:left">
 
				          ${h.submit('set',_('Show'),class_="ui-btn")}
 
				          ${h.text('size',size=1,value=c.size)}
 
				          ${_('revisions')}
 
				        </div>
 
				        ${h.end_form()}
 
					<div id="rev_range_container" style="display:none"></div>
 
                    <div style="float:right">${h.select('branch_filter',c.branch_name,c.branch_filters)}</div>
 
					</div>
 

	
 
				%for cnt,cs in enumerate(c.pagination):
 
					<div id="chg_${cnt+1}" class="container ${'tablerow%s' % (cnt%2)}">
 
						<div class="left">
 
							<div>
 
							${h.checkbox(cs.short_id,class_="changeset_range")}
 
							<span class="tooltip" title="${h.age(cs.date)}"><a href="${h.url('changeset_home',repo_name=c.repo_name,revision=cs.raw_id)}"><span class="changeset_id">${cs.revision}:<span class="changeset_hash">${h.short_id(cs.raw_id)}</span></span></a></span>
 
							</div>
 
							<div class="author">
 
								<div class="gravatar">
 
									<img alt="gravatar" src="${h.gravatar_url(h.email(cs.author),16)}"/>
 
								</div>
 
								<div title="${cs.author}" class="user">${h.person(cs.author)}</div>
 
							</div>
 
                            <div class="date">${cs.date}</div>
 
						</div>
 
						<div class="mid">
 
                            <div class="message">${h.urlify_commit(h.wrap_paragraphs(cs.message),c.repo_name,h.url('changeset_home',repo_name=c.repo_name,revision=cs.raw_id))}</div>
 
                            <div class="expand"><span class="expandtext">&darr; ${_('show more')} &darr;</span></div>
 
						</div>
 
						<div class="right">
 
									<div class="changes">
 
                                        <div id="${cs.raw_id}"  style="float:right;" class="changed_total tooltip" title="${_('Affected number of files, click to show more details')}">${len(cs.affected_files)}</div>
 
                                        <div class="comments-container">
 
                                        %if len(c.comments.get(cs.raw_id,[])) > 0:
 
                                            <div class="comments-cnt" title="${('comments')}">
 
                                              <a href="${h.url('changeset_home',repo_name=c.repo_name,revision=cs.raw_id,anchor='comment-%s' % c.comments[cs.raw_id][0].comment_id)}">
 
                                               <div class="comments-cnt">${len(c.comments[cs.raw_id])}</div>
 
                                               <img src="${h.url('/images/icons/comments.png')}">
 
                                              </a>
 
                                            </div>
 
                                        %endif
 
                                        </div>
 
                                        <div class="changeset-status-container">
 
                                            %if c.statuses.get(cs.raw_id):
 
                                              <div title="${_('Changeset status')}" class="changeset-status-lbl">${c.statuses.get(cs.raw_id)[1]}</div>
 
                                              <div class="changeset-status-ico"><img src="${h.url('/images/icons/flag_status_%s.png' % c.statuses.get(cs.raw_id)[0])}" /></div>
rhodecode/templates/pullrequests/pullrequest.html
Show inline comments
 
new file 100644
 
<%inherit file="/base/base.html"/>
 

	
 
<%def name="title()">
 
    ${c.repo_name} ${_('Pull request')}
 
</%def>
 

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

	
 
<%def name="main()">
 

	
 
<div class="box">
 
    <!-- box / title -->
 
    <div class="title">
 
        ${self.breadcrumbs()}
 
    </div>
 
    <div style="padding:30px">
 
        ##ORG
 
        <div style="float:left">
 
            <div class="fork_user">
 
                <div class="gravatar">
 
                    <img alt="gravatar" src="${h.gravatar_url(c.rhodecode_db_repo.user.email,24)}"/>
 
                </div>
 
                <span style="font-size: 20px">
 
                ${h.select('other','',['%s/%s' % (c.rhodecode_db_repo.user.username,c.repo_name)])}:${h.select('other_ref','',c.org_refs)}
 
                </span>
 
                 <div style="padding:5px 3px 3px 42px;">${c.rhodecode_db_repo.description}</div>
 
            </div>
 
            <div style="clear:both;padding-top: 10px"></div>
 
        </div>
 
          <div style="float:left;font-size:24px;padding:0px 20px">
 
          <img src="${h.url('/images/arrow_right_64.png')}"/>
 
          </div>
 
        
 
        ##OTHER, most Probably the PARENT OF THIS FORK
 
        <div style="float:left">
 
            <div class="fork_user">
 
                <div class="gravatar">
 
                    <img alt="gravatar" src="${h.gravatar_url(c.rhodecode_db_repo.user.email,24)}"/>
 
                </div>
 
                <span style="font-size: 20px">
 
                ${h.select('orther','',c.sources)}:${h.select('other_ref','',c.org_refs)}
 
                </span>
 
                 <div style="padding:5px 3px 3px 42px;">${c.rhodecode_db_repo.description}</div>
 
            </div>
 
            <div style="clear:both;padding-top: 10px"></div>
 
        </div>
 
     </div>
 
     
 
    <h3>${_('New pull request')} from USER:REF into PARENT:REF</h3>
 
    ${h.form(url('#'),method='put')}
 
    <div class="form">
 
        <!-- fields -->
 

	
 
        <div class="fields">
 

	
 
             <div class="field">
 
                <div class="label">
 
                    <label for="pullrequest_title">${_('Title')}:</label>
 
                </div>
 
                <div class="input">
 
                    ${h.text('pullrequest_title',size=30)}
 
                </div>
 
             </div>
 

	
 
            <div class="field">
 
                <div class="label label-textarea">
 
                    <label for="pullrequest_desc">${_('description')}:</label>
 
                </div>
 
                <div class="textarea text-area editor">
 
                    ${h.textarea('pullrequest_desc',size=30)}
 
                </div>
 
            </div>
 

	
 
            <div class="buttons">
 
                ${h.submit('save',_('Send pull request'),class_="ui-button")}
 
                ${h.reset('reset',_('Reset'),class_="ui-button")}
 
           </div>
 
        </div>
 
    </div>
 
    ${h.end_form()}     
 
     
 
     
 
</div>
 

	
 
</%def>
rhodecode/templates/repo_switcher_list.html
Show inline comments
 
## -*- coding: utf-8 -*-
 

	
 
<li class="qfilter_rs">
 
    <input type="text" style="border:0" value="quick filter..." name="filter" size="15" id="q_filter_rs" />
 
    <input type="text" style="border:0" value="quick filter..." name="filter" size="25" id="q_filter_rs" />
 
</li>
 

	
 
%for repo in c.repos_list:
 

	
 
      %if repo['dbrepo']['private']:
 
         <li>
 
             <img src="${h.url('/images/icons/lock.png')}" alt="${_('Private repository')}" class="repo_switcher_type"/>
 
             ${h.link_to(repo['name'],h.url('summary_home',repo_name=repo['name']),class_="repo_name %s" % repo['dbrepo']['repo_type'])}
 
          </li>
 
      %else:
 
         <li>
 
             <img src="${h.url('/images/icons/lock_open.png')}" alt="${_('Public repository')}" class="repo_switcher_type" />
 
             ${h.link_to(repo['name'],h.url('summary_home',repo_name=repo['name']),class_="repo_name %s" % repo['dbrepo']['repo_type'])}
 
         </li>
 
      %endif
 
%endfor
rhodecode/tests/functional/test_pullrequests.py
Show inline comments
 
new file 100644
 
from rhodecode.tests import *
 

	
 
class TestPullrequestsController(TestController):
 

	
 
    def test_index(self):
 
        response = self.app.get(url(controller='pullrequests', action='index'))
 
        # Test response...
0 comments (0 inline, 0 general)