Changeset - f28dc032adf0
[Not reviewed]
beta
0 4 0
Marcin Kuzminski - 14 years ago 2011-12-03 20:00:36
marcin@python-works.com
Added handling of ignore whitespace flag in changesets
- fixed messages for FK validation
4 files changed with 25 insertions and 15 deletions:
0 comments (0 inline, 0 general)
rhodecode/controllers/admin/repos.py
Show inline comments
 
@@ -65,334 +65,334 @@ class ReposController(BaseController):
 

	
 
    def __load_defaults(self):
 
        c.repo_groups = RepoGroup.groups_choices()
 
        c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
 

	
 
        repo_model = RepoModel()
 
        c.users_array = repo_model.get_users_js()
 
        c.users_groups_array = repo_model.get_users_groups_js()
 

	
 
    def __load_data(self, repo_name=None):
 
        """
 
        Load defaults settings for edit, and update
 

	
 
        :param repo_name:
 
        """
 
        self.__load_defaults()
 

	
 
        c.repo_info = db_repo = Repository.get_by_repo_name(repo_name)
 
        repo = db_repo.scm_instance
 

	
 
        if c.repo_info is None:
 
            h.flash(_('%s repository is not mapped to db perhaps'
 
                      ' it was created or renamed from the filesystem'
 
                      ' please run the application again'
 
                      ' in order to rescan repositories') % repo_name,
 
                      category='error')
 

	
 
            return redirect(url('repos'))
 

	
 
        c.default_user_id = User.get_by_username('default').user_id
 
        c.in_public_journal = UserFollowing.query()\
 
            .filter(UserFollowing.user_id == c.default_user_id)\
 
            .filter(UserFollowing.follows_repository == c.repo_info).scalar()
 

	
 
        if c.repo_info.stats:
 
            last_rev = c.repo_info.stats.stat_on_revision
 
        else:
 
            last_rev = 0
 
        c.stats_revision = last_rev
 

	
 
        c.repo_last_rev = repo.count() - 1 if repo.revisions else 0
 

	
 
        if last_rev == 0 or c.repo_last_rev == 0:
 
            c.stats_percentage = 0
 
        else:
 
            c.stats_percentage = '%.2f' % ((float((last_rev)) /
 
                                            c.repo_last_rev) * 100)
 

	
 
        defaults = RepoModel()._get_defaults(repo_name)
 
        return defaults
 

	
 
    @HasPermissionAllDecorator('hg.admin')
 
    def index(self, format='html'):
 
        """GET /repos: All items in the collection"""
 
        # url('repos')
 

	
 
        c.repos_list = ScmModel().get_repos(Repository.query()
 
                                            .order_by(Repository.repo_name)
 
                                            .all(), sort_key='name_sort')
 
        return render('admin/repos/repos.html')
 

	
 
    @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
 
    def create(self):
 
        """
 
        POST /repos: Create a new item"""
 
        # url('repos')
 

	
 
        self.__load_defaults()
 
        form_result = {}
 
        try:
 
            form_result = RepoForm(repo_groups=c.repo_groups_choices)()\
 
                            .to_python(dict(request.POST))
 
            RepoModel().create(form_result, self.rhodecode_user)
 
            if form_result['clone_uri']:
 
                h.flash(_('created repository %s from %s') \
 
                    % (form_result['repo_name'], form_result['clone_uri']),
 
                    category='success')
 
            else:
 
                h.flash(_('created repository %s') % form_result['repo_name'],
 
                    category='success')
 

	
 
            if request.POST.get('user_created'):
 
                # created by regular non admin user
 
                action_logger(self.rhodecode_user, 'user_created_repo',
 
                              form_result['repo_name_full'], '', self.sa)
 
            else:
 
                action_logger(self.rhodecode_user, 'admin_created_repo',
 
                              form_result['repo_name_full'], '', self.sa)
 
            Session.commit()
 
        except formencode.Invalid, errors:
 

	
 
            c.new_repo = errors.value['repo_name']
 

	
 
            if request.POST.get('user_created'):
 
                r = render('admin/repos/repo_add_create_repository.html')
 
            else:
 
                r = render('admin/repos/repo_add.html')
 

	
 
            return htmlfill.render(
 
                r,
 
                defaults=errors.value,
 
                errors=errors.error_dict or {},
 
                prefix_error=False,
 
                encoding="UTF-8")
 

	
 
        except Exception:
 
            log.error(traceback.format_exc())
 
            msg = _('error occurred during creation of repository %s') \
 
                    % form_result.get('repo_name')
 
            h.flash(msg, category='error')
 
        if request.POST.get('user_created'):
 
            return redirect(url('home'))
 
        return redirect(url('repos'))
 

	
 
    @HasPermissionAllDecorator('hg.admin')
 
    def new(self, format='html'):
 
        """GET /repos/new: Form to create a new item"""
 
        new_repo = request.GET.get('repo', '')
 
        c.new_repo = repo_name_slug(new_repo)
 
        self.__load_defaults()
 
        return render('admin/repos/repo_add.html')
 

	
 
    @HasPermissionAllDecorator('hg.admin')
 
    def update(self, repo_name):
 
        """
 
        PUT /repos/repo_name: Update an existing item"""
 
        # Forms posted to this method should contain a hidden field:
 
        #    <input type="hidden" name="_method" value="PUT" />
 
        # Or using helpers:
 
        #    h.form(url('repo', repo_name=ID),
 
        #           method='put')
 
        # url('repo', repo_name=ID)
 
        self.__load_defaults()
 
        repo_model = RepoModel()
 
        changed_name = repo_name
 
        _form = RepoForm(edit=True, old_data={'repo_name': repo_name},
 
                         repo_groups=c.repo_groups_choices)()
 
        try:
 
            form_result = _form.to_python(dict(request.POST))
 
            repo = repo_model.update(repo_name, form_result)
 
            invalidate_cache('get_repo_cached_%s' % repo_name)
 
            h.flash(_('Repository %s updated successfully' % repo_name),
 
                    category='success')
 
            changed_name = repo.repo_name
 
            action_logger(self.rhodecode_user, 'admin_updated_repo',
 
                              changed_name, '', self.sa)
 
            Session.commit()
 
        except formencode.Invalid, errors:
 
            defaults = self.__load_data(repo_name)
 
            defaults.update(errors.value)
 
            return htmlfill.render(
 
                render('admin/repos/repo_edit.html'),
 
                defaults=defaults,
 
                errors=errors.error_dict or {},
 
                prefix_error=False,
 
                encoding="UTF-8")
 

	
 
        except Exception:
 
            log.error(traceback.format_exc())
 
            h.flash(_('error occurred during update of repository %s') \
 
                    % repo_name, category='error')
 
        return redirect(url('edit_repo', repo_name=changed_name))
 

	
 
    @HasPermissionAllDecorator('hg.admin')
 
    def delete(self, repo_name):
 
        """
 
        DELETE /repos/repo_name: Delete an existing item"""
 
        # Forms posted to this method should contain a hidden field:
 
        #    <input type="hidden" name="_method" value="DELETE" />
 
        # Or using helpers:
 
        #    h.form(url('repo', repo_name=ID),
 
        #           method='delete')
 
        # url('repo', repo_name=ID)
 

	
 
        repo_model = RepoModel()
 
        repo = repo_model.get_by_repo_name(repo_name)
 
        if not repo:
 
            h.flash(_('%s repository is not mapped to db perhaps'
 
                      ' it was moved or renamed  from the filesystem'
 
                      ' please run the application again'
 
                      ' in order to rescan repositories') % repo_name,
 
                      category='error')
 

	
 
            return redirect(url('repos'))
 
        try:
 
            action_logger(self.rhodecode_user, 'admin_deleted_repo',
 
                              repo_name, '', self.sa)
 
            repo_model.delete(repo)
 
            invalidate_cache('get_repo_cached_%s' % repo_name)
 
            h.flash(_('deleted repository %s') % repo_name, category='success')
 
            Session.commit()
 
        except IntegrityError, e:
 
            if e.message.find('repositories_fork_id_fkey'):
 
            if e.message.find('repositories_fork_id_fkey') != -1:
 
                log.error(traceback.format_exc())
 
                h.flash(_('Cannot delete %s it still contains attached '
 
                          'forks') % repo_name,
 
                        category='warning')
 
            else:
 
                log.error(traceback.format_exc())
 
                h.flash(_('An error occurred during '
 
                          'deletion of %s') % repo_name,
 
                        category='error')
 

	
 
        except Exception, e:
 
            log.error(traceback.format_exc())
 
            h.flash(_('An error occurred during deletion of %s') % repo_name,
 
                    category='error')
 

	
 
        return redirect(url('repos'))
 

	
 
    @HasPermissionAllDecorator('hg.admin')
 
    def delete_perm_user(self, repo_name):
 
        """
 
        DELETE an existing repository permission user
 

	
 
        :param repo_name:
 
        """
 

	
 
        try:
 
            repo_model = RepoModel()
 
            repo_model.delete_perm_user(request.POST, repo_name)
 
        except Exception, e:
 
            h.flash(_('An error occurred during deletion of repository user'),
 
                    category='error')
 
            raise HTTPInternalServerError()
 

	
 
    @HasPermissionAllDecorator('hg.admin')
 
    def delete_perm_users_group(self, repo_name):
 
        """
 
        DELETE an existing repository permission users group
 

	
 
        :param repo_name:
 
        """
 
        try:
 
            repo_model = RepoModel()
 
            repo_model.delete_perm_users_group(request.POST, repo_name)
 
        except Exception, e:
 
            h.flash(_('An error occurred during deletion of repository'
 
                      ' users groups'),
 
                    category='error')
 
            raise HTTPInternalServerError()
 

	
 
    @HasPermissionAllDecorator('hg.admin')
 
    def repo_stats(self, repo_name):
 
        """
 
        DELETE an existing repository statistics
 

	
 
        :param repo_name:
 
        """
 

	
 
        try:
 
            repo_model = RepoModel()
 
            repo_model.delete_stats(repo_name)
 
        except Exception, e:
 
            h.flash(_('An error occurred during deletion of repository stats'),
 
                    category='error')
 
        return redirect(url('edit_repo', repo_name=repo_name))
 

	
 
    @HasPermissionAllDecorator('hg.admin')
 
    def repo_cache(self, repo_name):
 
        """
 
        INVALIDATE existing repository cache
 

	
 
        :param repo_name:
 
        """
 

	
 
        try:
 
            ScmModel().mark_for_invalidation(repo_name)
 
        except Exception, e:
 
            h.flash(_('An error occurred during cache invalidation'),
 
                    category='error')
 
        return redirect(url('edit_repo', repo_name=repo_name))
 

	
 
    @HasPermissionAllDecorator('hg.admin')
 
    def repo_public_journal(self, repo_name):
 
        """
 
        Set's this repository to be visible in public journal,
 
        in other words assing default user to follow this repo
 

	
 
        :param repo_name:
 
        """
 

	
 
        cur_token = request.POST.get('auth_token')
 
        token = get_token()
 
        if cur_token == token:
 
            try:
 
                repo_id = Repository.get_by_repo_name(repo_name).repo_id
 
                user_id = User.get_by_username('default').user_id
 
                self.scm_model.toggle_following_repo(repo_id, user_id)
 
                h.flash(_('Updated repository visibility in public journal'),
 
                        category='success')
 
            except:
 
                h.flash(_('An error occurred during setting this'
 
                          ' repository in public journal'),
 
                        category='error')
 

	
 
        else:
 
            h.flash(_('Token mismatch'), category='error')
 
        return redirect(url('edit_repo', repo_name=repo_name))
 

	
 
    @HasPermissionAllDecorator('hg.admin')
 
    def repo_pull(self, repo_name):
 
        """
 
        Runs task to update given repository with remote changes,
 
        ie. make pull on remote location
 

	
 
        :param repo_name:
 
        """
 
        try:
 
            ScmModel().pull_changes(repo_name, self.rhodecode_user.username)
 
            h.flash(_('Pulled from remote location'), category='success')
 
        except Exception, e:
 
            h.flash(_('An error occurred during pull from remote location'),
 
                    category='error')
 

	
 
        return redirect(url('edit_repo', repo_name=repo_name))
 

	
 
    @HasPermissionAllDecorator('hg.admin')
 
    def show(self, repo_name, format='html'):
 
        """GET /repos/repo_name: Show a specific item"""
 
        # url('repo', repo_name=ID)
 

	
 
    @HasPermissionAllDecorator('hg.admin')
 
    def edit(self, repo_name, format='html'):
 
        """GET /repos/repo_name/edit: Form to edit an existing item"""
 
        # url('edit_repo', repo_name=ID)
 
        defaults = self.__load_data(repo_name)
 

	
 
        return htmlfill.render(
 
            render('admin/repos/repo_edit.html'),
 
            defaults=defaults,
 
            encoding="UTF-8",
 
            force_defaults=False
 
        )
rhodecode/controllers/admin/repos_groups.py
Show inline comments
 
import logging
 
import traceback
 
import formencode
 

	
 
from formencode import htmlfill
 
from operator import itemgetter
 

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

	
 
from sqlalchemy.exc import IntegrityError
 

	
 
from rhodecode.lib import helpers as h
 
from rhodecode.lib.auth import LoginRequired, HasPermissionAnyDecorator
 
from rhodecode.lib.base import BaseController, render
 
from rhodecode.model.db import RepoGroup
 
from rhodecode.model.repos_group import ReposGroupModel
 
from rhodecode.model.forms import ReposGroupForm
 
from rhodecode.model.meta import Session
 

	
 
log = logging.getLogger(__name__)
 

	
 

	
 
class ReposGroupsController(BaseController):
 
    """REST Controller styled on the Atom Publishing Protocol"""
 
    # To properly map this controller, ensure your config/routing.py
 
    # file has a resource setup:
 
    #     map.resource('repos_group', 'repos_groups')
 

	
 
    @LoginRequired()
 
    def __before__(self):
 
        super(ReposGroupsController, self).__before__()
 

	
 
    def __load_defaults(self):
 
        c.repo_groups = RepoGroup.groups_choices()
 
        c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
 

	
 
    def __load_data(self, group_id):
 
        """
 
        Load defaults settings for edit, and update
 

	
 
        :param group_id:
 
        """
 
        self.__load_defaults()
 

	
 
        repo_group = RepoGroup.get(group_id)
 

	
 
        data = repo_group.get_dict()
 

	
 
        data['group_name'] = repo_group.name
 

	
 
        return data
 

	
 
    @HasPermissionAnyDecorator('hg.admin')
 
    def index(self, format='html'):
 
        """GET /repos_groups: All items in the collection"""
 
        # url('repos_groups')
 

	
 
        sk = lambda g:g.parents[0].group_name if g.parents else g.group_name
 
        c.groups = sorted(RepoGroup.query().all(), key=sk)
 
        return render('admin/repos_groups/repos_groups_show.html')
 

	
 
    @HasPermissionAnyDecorator('hg.admin')
 
    def create(self):
 
        """POST /repos_groups: Create a new item"""
 
        # url('repos_groups')
 
        self.__load_defaults()
 
        repos_group_form = ReposGroupForm(available_groups=
 
                                          c.repo_groups_choices)()
 
        try:
 
            form_result = repos_group_form.to_python(dict(request.POST))
 
            ReposGroupModel().create(form_result)
 
            Session.commit()
 
            h.flash(_('created repos group %s') \
 
                    % form_result['group_name'], category='success')
 
            #TODO: in futureaction_logger(, '', '', '', self.sa)
 
        except formencode.Invalid, errors:
 

	
 
            return htmlfill.render(
 
                render('admin/repos_groups/repos_groups_add.html'),
 
                defaults=errors.value,
 
                errors=errors.error_dict or {},
 
                prefix_error=False,
 
                encoding="UTF-8")
 
        except Exception:
 
            log.error(traceback.format_exc())
 
            h.flash(_('error occurred during creation of repos group %s') \
 
                    % request.POST.get('group_name'), category='error')
 

	
 
        return redirect(url('repos_groups'))
 

	
 

	
 
    @HasPermissionAnyDecorator('hg.admin')
 
    def new(self, format='html'):
 
        """GET /repos_groups/new: Form to create a new item"""
 
        # url('new_repos_group')
 
        self.__load_defaults()
 
        return render('admin/repos_groups/repos_groups_add.html')
 

	
 
    @HasPermissionAnyDecorator('hg.admin')
 
    def update(self, id):
 
        """PUT /repos_groups/id: Update an existing item"""
 
        # Forms posted to this method should contain a hidden field:
 
        #    <input type="hidden" name="_method" value="PUT" />
 
        # Or using helpers:
 
        #    h.form(url('repos_group', id=ID),
 
        #           method='put')
 
        # url('repos_group', id=ID)
 

	
 
        self.__load_defaults()
 
        c.repos_group = RepoGroup.get(id)
 

	
 
        repos_group_form = ReposGroupForm(edit=True,
 
                                          old_data=c.repos_group.get_dict(),
 
                                          available_groups=
 
                                            c.repo_groups_choices)()
 
        try:
 
            form_result = repos_group_form.to_python(dict(request.POST))
 
            ReposGroupModel().update(id, form_result)
 
            Session.commit()
 
            h.flash(_('updated repos group %s') \
 
                    % form_result['group_name'], category='success')
 
            #TODO: in futureaction_logger(, '', '', '', self.sa)
 
        except formencode.Invalid, errors:
 

	
 
            return htmlfill.render(
 
                render('admin/repos_groups/repos_groups_edit.html'),
 
                defaults=errors.value,
 
                errors=errors.error_dict or {},
 
                prefix_error=False,
 
                encoding="UTF-8")
 
        except Exception:
 
            log.error(traceback.format_exc())
 
            h.flash(_('error occurred during update of repos group %s') \
 
                    % request.POST.get('group_name'), category='error')
 

	
 
        return redirect(url('repos_groups'))
 

	
 

	
 
    @HasPermissionAnyDecorator('hg.admin')
 
    def delete(self, id):
 
        """DELETE /repos_groups/id: Delete an existing item"""
 
        # Forms posted to this method should contain a hidden field:
 
        #    <input type="hidden" name="_method" value="DELETE" />
 
        # Or using helpers:
 
        #    h.form(url('repos_group', id=ID),
 
        #           method='delete')
 
        # url('repos_group', id=ID)
 

	
 
        gr = RepoGroup.get(id)
 
        repos = gr.repositories.all()
 
        if repos:
 
            h.flash(_('This group contains %s repositores and cannot be '
 
                      'deleted' % len(repos)),
 
                    category='error')
 
            return redirect(url('repos_groups'))
 

	
 
        try:
 
            ReposGroupModel().delete(id)
 
            Session.commit()
 
            h.flash(_('removed repos group %s' % gr.group_name), category='success')
 
            #TODO: in future action_logger(, '', '', '', self.sa)
 
        except IntegrityError, e:
 
            if e.message.find('groups_group_parent_id_fkey'):
 
            if e.message.find('groups_group_parent_id_fkey') != -1:
 
                log.error(traceback.format_exc())
 
                h.flash(_('Cannot delete this group it still contains '
 
                          'subgroups'),
 
                        category='warning')
 
            else:
 
                log.error(traceback.format_exc())
 
                h.flash(_('error occurred during deletion of repos '
 
                          'group %s' % gr.group_name), category='error')
 

	
 
        except Exception:
 
            log.error(traceback.format_exc())
 
            h.flash(_('error occurred during deletion of repos '
 
                      'group %s' % gr.group_name), category='error')
 

	
 
        return redirect(url('repos_groups'))
 

	
 
    def show_by_name(self, group_name):
 
        id_ = RepoGroup.get_by_group_name(group_name).group_id
 
        return self.show(id_)
 

	
 
    def show(self, id, format='html'):
 
        """GET /repos_groups/id: Show a specific item"""
 
        # url('repos_group', id=ID)
 

	
 
        c.group = RepoGroup.get(id)
 

	
 
        if c.group:
 
            c.group_repos = c.group.repositories.all()
 
        else:
 
            return redirect(url('home'))
 

	
 
        #overwrite our cached list with current filter
 
        gr_filter = c.group_repos
 
        c.cached_repo_list = self.scm_model.get_repos(all_repos=gr_filter)
 

	
 
        c.repos_list = c.cached_repo_list
 

	
 
        c.repo_cnt = 0
 

	
 
        c.groups = self.sa.query(RepoGroup).order_by(RepoGroup.group_name)\
 
            .filter(RepoGroup.group_parent_id == id).all()
 

	
 
        return render('admin/repos_groups/repos_groups.html')
 

	
 
    @HasPermissionAnyDecorator('hg.admin')
 
    def edit(self, id, format='html'):
 
        """GET /repos_groups/id/edit: Form to edit an existing item"""
 
        # url('edit_repos_group', id=ID)
 

	
 
        id_ = int(id)
 

	
 
        c.repos_group = RepoGroup.get(id_)
 
        defaults = self.__load_data(id_)
 

	
 
        # we need to exclude this group from the group list for editing
 
        c.repo_groups = filter(lambda x:x[0] != id_, c.repo_groups)
 

	
 
        return htmlfill.render(
 
            render('admin/repos_groups/repos_groups_edit.html'),
 
            defaults=defaults,
 
            encoding="UTF-8",
 
            force_defaults=False
 
        )
 

	
 

	
rhodecode/controllers/changeset.py
Show inline comments
 
# -*- coding: utf-8 -*-
 
"""
 
    rhodecode.controllers.changeset
 
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 

	
 
    changeset controller for pylons showoing changes beetween
 
    revisions
 

	
 
    :created_on: Apr 25, 2010
 
    :author: marcink
 
    :copyright: (C) 2009-2011 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 tmpl_context as c, url, request, response
 
from pylons.i18n.translation import _
 
from pylons.controllers.util import redirect
 
from pylons.decorators import jsonify
 

	
 
import rhodecode.lib.helpers as h
 
from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
 
from rhodecode.lib.base import BaseRepoController, render
 
from rhodecode.lib.utils import EmptyChangeset
 
from rhodecode.lib.compat import OrderedDict
 
from rhodecode.model.db import ChangesetComment
 
from rhodecode.model.comment import ChangesetCommentsModel
 

	
 
from vcs.exceptions import RepositoryError, ChangesetError, \
 
    ChangesetDoesNotExistError
 
from vcs.nodes import FileNode
 
from vcs.utils import diffs as differ
 
from webob.exc import HTTPForbidden
 
from rhodecode.model.meta import Session
 

	
 
log = logging.getLogger(__name__)
 

	
 

	
 
class ChangesetController(BaseRepoController):
 

	
 
    @LoginRequired()
 
    @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
 
                                   'repository.admin')
 
    def __before__(self):
 
        super(ChangesetController, self).__before__()
 
        c.affected_files_cut_off = 60
 

	
 
    def index(self, revision):
 

	
 
        ignore_whitespace = request.GET.get('ignorews') == '1'
 
        def wrap_to_table(str):
 

	
 
            return '''<table class="code-difftable">
 
                        <tr class="line">
 
                        <td class="lineno new"></td>
 
                        <td class="code"><pre>%s</pre></td>
 
                        </tr>
 
                      </table>''' % str
 

	
 
        #get ranges of revisions if preset
 
        rev_range = revision.split('...')[:2]
 

	
 
        try:
 
            if len(rev_range) == 2:
 
                rev_start = rev_range[0]
 
                rev_end = rev_range[1]
 
                rev_ranges = c.rhodecode_repo.get_changesets(start=rev_start,
 
                                                            end=rev_end)
 
            else:
 
                rev_ranges = [c.rhodecode_repo.get_changeset(revision)]
 

	
 
            c.cs_ranges = list(rev_ranges)
 
            if not c.cs_ranges:
 
                raise RepositoryError('Changeset range returned empty result')
 

	
 
        except (RepositoryError, ChangesetDoesNotExistError, Exception), e:
 
            log.error(traceback.format_exc())
 
            h.flash(str(e), category='warning')
 
            return redirect(url('home'))
 

	
 
        c.changes = OrderedDict()
 
        c.sum_added = 0
 
        c.sum_removed = 0
 
        c.lines_added = 0
 
        c.lines_deleted = 0
 
        c.cut_off = False  # defines if cut off limit is reached
 

	
 
        c.comments = []
 
        c.inline_comments = []
 
        c.inline_cnt = 0
 
        # Iterate over ranges (default changeset view is always one changeset)
 
        for changeset in c.cs_ranges:
 
            c.comments.extend(ChangesetCommentsModel()\
 
                              .get_comments(c.rhodecode_db_repo.repo_id,
 
                                            changeset.raw_id))
 
            inlines = ChangesetCommentsModel()\
 
                        .get_inline_comments(c.rhodecode_db_repo.repo_id,
 
                                             changeset.raw_id)
 
            c.inline_comments.extend(inlines)
 
            c.changes[changeset.raw_id] = []
 
            try:
 
                changeset_parent = changeset.parents[0]
 
            except IndexError:
 
                changeset_parent = None
 

	
 
            #==================================================================
 
            # ADDED FILES
 
            #==================================================================
 
            for node in changeset.added:
 

	
 
                filenode_old = FileNode(node.path, '', EmptyChangeset())
 
                if filenode_old.is_binary or node.is_binary:
 
                    diff = wrap_to_table(_('binary file'))
 
                    st = (0, 0)
 
                else:
 
                    # in this case node.size is good parameter since those are
 
                    # added nodes and their size defines how many changes were
 
                    # made
 
                    c.sum_added += node.size
 
                    if c.sum_added < self.cut_off_limit:
 
                        f_gitdiff = differ.get_gitdiff(filenode_old, node)
 
                        f_gitdiff = differ.get_gitdiff(filenode_old, node,
 
                                           ignore_whitespace=ignore_whitespace)
 
                        d = differ.DiffProcessor(f_gitdiff, format='gitdiff')
 

	
 
                        st = d.stat()
 
                        diff = d.as_html()
 

	
 
                    else:
 
                        diff = wrap_to_table(_('Changeset is to big and '
 
                                               'was cut off, see raw '
 
                                               'changeset instead'))
 
                        c.cut_off = True
 
                        break
 

	
 
                cs1 = None
 
                cs2 = node.last_changeset.raw_id
 
                c.lines_added += st[0]
 
                c.lines_deleted += st[1]
 
                c.changes[changeset.raw_id].append(('added', node, diff,
 
                                                    cs1, cs2, st))
 

	
 
            #==================================================================
 
            # CHANGED FILES
 
            #==================================================================
 
            if not c.cut_off:
 
                for node in changeset.changed:
 
                    try:
 
                        filenode_old = changeset_parent.get_node(node.path)
 
                    except ChangesetError:
 
                        log.warning('Unable to fetch parent node for diff')
 
                        filenode_old = FileNode(node.path, '',
 
                                                EmptyChangeset())
 

	
 
                    if filenode_old.is_binary or node.is_binary:
 
                        diff = wrap_to_table(_('binary file'))
 
                        st = (0, 0)
 
                    else:
 

	
 
                        if c.sum_removed < self.cut_off_limit:
 
                            f_gitdiff = differ.get_gitdiff(filenode_old, node)
 
                            f_gitdiff = differ.get_gitdiff(filenode_old, node,
 
                                           ignore_whitespace=ignore_whitespace)
 
                            d = differ.DiffProcessor(f_gitdiff,
 
                                                     format='gitdiff')
 
                            st = d.stat()
 
                            if (st[0] + st[1]) * 256 > self.cut_off_limit:
 
                                diff = wrap_to_table(_('Diff is to big '
 
                                                       'and was cut off, see '
 
                                                       'raw diff instead'))
 
                            else:
 
                                diff = d.as_html()
 

	
 
                            if diff:
 
                                c.sum_removed += len(diff)
 
                        else:
 
                            diff = wrap_to_table(_('Changeset is to big and '
 
                                                   'was cut off, see raw '
 
                                                   'changeset instead'))
 
                            c.cut_off = True
 
                            break
 

	
 
                    cs1 = filenode_old.last_changeset.raw_id
 
                    cs2 = node.last_changeset.raw_id
 
                    c.lines_added += st[0]
 
                    c.lines_deleted += st[1]
 
                    c.changes[changeset.raw_id].append(('changed', node, diff,
 
                                                        cs1, cs2, st))
 

	
 
            #==================================================================
 
            # REMOVED FILES
 
            #==================================================================
 
            if not c.cut_off:
 
                for node in changeset.removed:
 
                    c.changes[changeset.raw_id].append(('removed', node, None,
 
                                                        None, None, (0, 0)))
 

	
 
        # count inline comments
 
        for path, lines in c.inline_comments:
 
            for comments in lines.values():
 
                c.inline_cnt += len(comments)
 

	
 
        if len(c.cs_ranges) == 1:
 
            c.changeset = c.cs_ranges[0]
 
            c.changes = c.changes[c.changeset.raw_id]
 

	
 
            return render('changeset/changeset.html')
 
        else:
 
            return render('changeset/changeset_range.html')
 

	
 
    def raw_changeset(self, revision):
 

	
 
        method = request.GET.get('diff', 'show')
 
        ignore_whitespace = request.GET.get('ignorews') == '1'
 
        try:
 
            c.scm_type = c.rhodecode_repo.alias
 
            c.changeset = c.rhodecode_repo.get_changeset(revision)
 
        except RepositoryError:
 
            log.error(traceback.format_exc())
 
            return redirect(url('home'))
 
        else:
 
            try:
 
                c.changeset_parent = c.changeset.parents[0]
 
            except IndexError:
 
                c.changeset_parent = None
 
            c.changes = []
 

	
 
            for node in c.changeset.added:
 
                filenode_old = FileNode(node.path, '')
 
                if filenode_old.is_binary or node.is_binary:
 
                    diff = _('binary file') + '\n'
 
                else:
 
                    f_gitdiff = differ.get_gitdiff(filenode_old, node)
 
                    f_gitdiff = differ.get_gitdiff(filenode_old, node,
 
                                           ignore_whitespace=ignore_whitespace)
 
                    diff = differ.DiffProcessor(f_gitdiff,
 
                                                format='gitdiff').raw_diff()
 

	
 
                cs1 = None
 
                cs2 = node.last_changeset.raw_id
 
                c.changes.append(('added', node, diff, cs1, cs2))
 

	
 
            for node in c.changeset.changed:
 
                filenode_old = c.changeset_parent.get_node(node.path)
 
                if filenode_old.is_binary or node.is_binary:
 
                    diff = _('binary file')
 
                else:
 
                    f_gitdiff = differ.get_gitdiff(filenode_old, node)
 
                    f_gitdiff = differ.get_gitdiff(filenode_old, node,
 
                                           ignore_whitespace=ignore_whitespace)
 
                    diff = differ.DiffProcessor(f_gitdiff,
 
                                                format='gitdiff').raw_diff()
 

	
 
                cs1 = filenode_old.last_changeset.raw_id
 
                cs2 = node.last_changeset.raw_id
 
                c.changes.append(('changed', node, diff, cs1, cs2))
 

	
 
        response.content_type = 'text/plain'
 

	
 
        if method == 'download':
 
            response.content_disposition = 'attachment; filename=%s.patch' \
 
                                            % revision
 

	
 
        c.parent_tmpl = ''.join(['# Parent  %s\n' % x.raw_id for x in
 
                                                 c.changeset.parents])
 

	
 
        c.diffs = ''
 
        for x in c.changes:
 
            c.diffs += x[2]
 

	
 
        return render('changeset/raw_changeset.html')
 

	
 
    def comment(self, repo_name, revision):
 
        ChangesetCommentsModel().create(text=request.POST.get('text'),
 
                                        repo_id=c.rhodecode_db_repo.repo_id,
 
                                        user_id=c.rhodecode_user.user_id,
 
                                        revision=revision,
 
                                        f_path=request.POST.get('f_path'),
 
                                        line_no=request.POST.get('line'))
 
        Session.commit()
 
        return redirect(h.url('changeset_home', repo_name=repo_name,
 
                              revision=revision))
 

	
 
    @jsonify
 
    def delete_comment(self, repo_name, comment_id):
 
        co = ChangesetComment.get(comment_id)
 
        owner = lambda : co.author.user_id == c.rhodecode_user.user_id
 
        if h.HasPermissionAny('hg.admin', 'repository.admin')() or owner:
 
            ChangesetCommentsModel().delete(comment=co)
 
            Session.commit()
 
            return True
 
        else:
 
            raise HTTPForbidden()
 

	
rhodecode/controllers/files.py
Show inline comments
 
@@ -215,298 +215,303 @@ class FilesController(BaseRepoController
 
            if file_node.is_binary:
 
                # do same as download raw for binary files
 
                mimetype, dispo = 'application/octet-stream', 'attachment'
 
            else:
 
                # do not just use the original mimetype, but force text/plain,
 
                # otherwise it would serve text/html and that might be unsafe.
 
                # Note: underlying vcs library fakes text/plain mimetype if the
 
                # mimetype can not be determined and it thinks it is not
 
                # binary.This might lead to erroneous text display in some
 
                # cases, but helps in other cases, like with text files
 
                # without extension.
 
                mimetype, dispo = 'text/plain', 'inline'
 

	
 
        if dispo == 'attachment':
 
            dispo = 'attachment; filename=%s' % \
 
                        safe_str(f_path.split(os.sep)[-1])
 

	
 
        response.content_disposition = dispo
 
        response.content_type = mimetype
 
        return file_node.content
 

	
 
    @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
 
                                   'repository.admin')
 
    def annotate(self, repo_name, revision, f_path):
 
        c.cs = self.__get_cs_or_redirect(revision, repo_name)
 
        c.file = self.__get_filenode_or_redirect(repo_name, c.cs, f_path)
 

	
 
        c.file_history = self._get_node_history(c.cs, f_path)
 
        c.f_path = f_path
 
        return render('files/files_annotate.html')
 

	
 
    @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
 
    def edit(self, repo_name, revision, f_path):
 
        r_post = request.POST
 

	
 
        c.cs = self.__get_cs_or_redirect(revision, repo_name)
 
        c.file = self.__get_filenode_or_redirect(repo_name, c.cs, f_path)
 

	
 
        if c.file.is_binary:
 
            return redirect(url('files_home', repo_name=c.repo_name,
 
                         revision=c.cs.raw_id, f_path=f_path))
 

	
 
        c.f_path = f_path
 

	
 
        if r_post:
 

	
 
            old_content = c.file.content
 
            sl = old_content.splitlines(1)
 
            first_line = sl[0] if sl else ''
 
            # modes:  0 - Unix, 1 - Mac, 2 - DOS
 
            mode = detect_mode(first_line, 0)
 
            content = convert_line_endings(r_post.get('content'), mode)
 

	
 
            message = r_post.get('message') or (_('Edited %s via RhodeCode')
 
                                                % (f_path))
 
            author = self.rhodecode_user.full_contact
 

	
 
            if content == old_content:
 
                h.flash(_('No changes'),
 
                    category='warning')
 
                return redirect(url('changeset_home', repo_name=c.repo_name,
 
                                    revision='tip'))
 

	
 
            try:
 
                self.scm_model.commit_change(repo=c.rhodecode_repo,
 
                                             repo_name=repo_name, cs=c.cs,
 
                                             user=self.rhodecode_user,
 
                                             author=author, message=message,
 
                                             content=content, f_path=f_path)
 
                h.flash(_('Successfully committed to %s' % f_path),
 
                        category='success')
 

	
 
            except Exception:
 
                log.error(traceback.format_exc())
 
                h.flash(_('Error occurred during commit'), category='error')
 
            return redirect(url('changeset_home',
 
                                repo_name=c.repo_name, revision='tip'))
 

	
 
        return render('files/files_edit.html')
 

	
 
    @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
 
    def add(self, repo_name, revision, f_path):
 
        r_post = request.POST
 
        c.cs = self.__get_cs_or_redirect(revision, repo_name,
 
                                         redirect_after=False)
 
        if c.cs is None:
 
            c.cs = EmptyChangeset(alias=c.rhodecode_repo.alias)
 

	
 
        c.f_path = f_path
 

	
 
        if r_post:
 
            unix_mode = 0
 
            content = convert_line_endings(r_post.get('content'), unix_mode)
 

	
 
            message = r_post.get('message') or (_('Added %s via RhodeCode')
 
                                                % (f_path))
 
            location = r_post.get('location')
 
            filename = r_post.get('filename')
 
            file_obj = r_post.get('upload_file', None)
 

	
 
            if file_obj is not None and hasattr(file_obj, 'filename'):
 
                filename = file_obj.filename
 
                content = file_obj.file
 

	
 
            node_path = os.path.join(location, filename)
 
            author = self.rhodecode_user.full_contact
 

	
 
            if not content:
 
                h.flash(_('No content'), category='warning')
 
                return redirect(url('changeset_home', repo_name=c.repo_name,
 
                                    revision='tip'))
 
            if not filename:
 
                h.flash(_('No filename'), category='warning')
 
                return redirect(url('changeset_home', repo_name=c.repo_name,
 
                                    revision='tip'))
 

	
 
            try:
 
                self.scm_model.create_node(repo=c.rhodecode_repo,
 
                                             repo_name=repo_name, cs=c.cs,
 
                                             user=self.rhodecode_user,
 
                                             author=author, message=message,
 
                                             content=content, f_path=node_path)
 
                h.flash(_('Successfully committed to %s' % node_path),
 
                        category='success')
 
            except NodeAlreadyExistsError, e:
 
                h.flash(_(e), category='error')
 
            except Exception:
 
                log.error(traceback.format_exc())
 
                h.flash(_('Error occurred during commit'), category='error')
 
            return redirect(url('changeset_home',
 
                                repo_name=c.repo_name, revision='tip'))
 

	
 
        return render('files/files_add.html')
 

	
 
    @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
 
                                   'repository.admin')
 
    def archivefile(self, repo_name, fname):
 

	
 
        fileformat = None
 
        revision = None
 
        ext = None
 
        subrepos = request.GET.get('subrepos') == 'true'
 

	
 
        for a_type, ext_data in settings.ARCHIVE_SPECS.items():
 
            archive_spec = fname.split(ext_data[1])
 
            if len(archive_spec) == 2 and archive_spec[1] == '':
 
                fileformat = a_type or ext_data[1]
 
                revision = archive_spec[0]
 
                ext = ext_data[1]
 

	
 
        try:
 
            dbrepo = RepoModel().get_by_repo_name(repo_name)
 
            if dbrepo.enable_downloads is False:
 
                return _('downloads disabled')
 

	
 
            # patch and reset hooks section of UI config to not run any 
 
            # hooks on fetching archives with subrepos
 
            for k, v in c.rhodecode_repo._repo.ui.configitems('hooks'):
 
                c.rhodecode_repo._repo.ui.setconfig('hooks', k, None)
 

	
 
            cs = c.rhodecode_repo.get_changeset(revision)
 
            content_type = settings.ARCHIVE_SPECS[fileformat][0]
 
        except ChangesetDoesNotExistError:
 
            return _('Unknown revision %s') % revision
 
        except EmptyRepositoryError:
 
            return _('Empty repository')
 
        except (ImproperArchiveTypeError, KeyError):
 
            return _('Unknown archive type')
 

	
 
        response.content_type = content_type
 
        response.content_disposition = 'attachment; filename=%s-%s%s' \
 
            % (repo_name, revision, ext)
 

	
 
        import tempfile
 
        archive = tempfile.mkstemp()[1]
 
        t = open(archive, 'wb')
 
        cs.fill_archive(stream=t, kind=fileformat, subrepos=subrepos)
 

	
 
        def get_chunked_archive(archive):
 
            stream = open(archive, 'rb')
 
            while True:
 
                data = stream.read(4096)
 
                if not data:
 
                    os.remove(archive)
 
                    break
 
                yield data
 

	
 
        return get_chunked_archive(archive)
 

	
 
    @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
 
                                   'repository.admin')
 
    def diff(self, repo_name, f_path):
 
        ignore_whitespace = request.GET.get('ignorews') == '1'
 
        diff1 = request.GET.get('diff1')
 
        diff2 = request.GET.get('diff2')
 
        c.action = request.GET.get('diff')
 
        c.no_changes = diff1 == diff2
 
        c.f_path = f_path
 
        c.big_diff = False
 

	
 
        try:
 
            if diff1 not in ['', None, 'None', '0' * 12, '0' * 40]:
 
                c.changeset_1 = c.rhodecode_repo.get_changeset(diff1)
 
                node1 = c.changeset_1.get_node(f_path)
 
            else:
 
                c.changeset_1 = EmptyChangeset(repo=c.rhodecode_repo)
 
                node1 = FileNode('.', '', changeset=c.changeset_1)
 

	
 
            if diff2 not in ['', None, 'None', '0' * 12, '0' * 40]:
 
                c.changeset_2 = c.rhodecode_repo.get_changeset(diff2)
 
                node2 = c.changeset_2.get_node(f_path)
 
            else:
 
                c.changeset_2 = EmptyChangeset(repo=c.rhodecode_repo)
 
                node2 = FileNode('.', '', changeset=c.changeset_2)
 
        except RepositoryError:
 
            return redirect(url('files_home',
 
                                repo_name=c.repo_name, f_path=f_path))
 

	
 
        if c.action == 'download':
 
            diff = differ.DiffProcessor(differ.get_gitdiff(node1, node2),
 
                                        format='gitdiff')
 
            _diff = differ.get_gitdiff(node1, node2,
 
                                       ignore_whitespace=ignore_whitespace)
 
            diff = differ.DiffProcessor(_diff,format='gitdiff')
 

	
 
            diff_name = '%s_vs_%s.diff' % (diff1, diff2)
 
            response.content_type = 'text/plain'
 
            response.content_disposition = 'attachment; filename=%s' \
 
                                                    % diff_name
 
            return diff.raw_diff()
 

	
 
        elif c.action == 'raw':
 
            diff = differ.DiffProcessor(differ.get_gitdiff(node1, node2),
 
                                        format='gitdiff')
 
            _diff = differ.get_gitdiff(node1, node2,
 
                                       ignore_whitespace=ignore_whitespace)
 
            diff = differ.DiffProcessor(_diff,format='gitdiff')
 
            response.content_type = 'text/plain'
 
            return diff.raw_diff()
 

	
 
        elif c.action == 'diff':
 
            if node1.is_binary or node2.is_binary:
 
                c.cur_diff = _('Binary file')
 
            elif node1.size > self.cut_off_limit or \
 
                    node2.size > self.cut_off_limit:
 
                c.cur_diff = ''
 
                c.big_diff = True
 
            else:
 
                diff = differ.DiffProcessor(differ.get_gitdiff(node1, node2),
 
                                        format='gitdiff')
 
                _diff = differ.get_gitdiff(node1, node2,
 
                                           ignore_whitespace=ignore_whitespace)
 
                diff = differ.DiffProcessor(_diff,format='gitdiff')
 
                c.cur_diff = diff.as_html()
 
        else:
 

	
 
            #default option
 
            if node1.is_binary or node2.is_binary:
 
                c.cur_diff = _('Binary file')
 
            elif node1.size > self.cut_off_limit or \
 
                    node2.size > self.cut_off_limit:
 
                c.cur_diff = ''
 
                c.big_diff = True
 

	
 
            else:
 
                diff = differ.DiffProcessor(differ.get_gitdiff(node1, node2),
 
                                        format='gitdiff')
 
                _diff = differ.get_gitdiff(node1, node2,
 
                                           ignore_whitespace=ignore_whitespace)
 
                diff = differ.DiffProcessor(_diff,format='gitdiff')
 
                c.cur_diff = diff.as_html()
 

	
 
        if not c.cur_diff and not c.big_diff:
 
            c.no_changes = True
 
        return render('files/file_diff.html')
 

	
 
    def _get_node_history(self, cs, f_path):
 
        changesets = cs.get_file_history(f_path)
 
        hist_l = []
 

	
 
        changesets_group = ([], _("Changesets"))
 
        branches_group = ([], _("Branches"))
 
        tags_group = ([], _("Tags"))
 

	
 
        for chs in changesets:
 
            n_desc = 'r%s:%s' % (chs.revision, chs.short_id)
 
            changesets_group[0].append((chs.raw_id, n_desc,))
 

	
 
        hist_l.append(changesets_group)
 

	
 
        for name, chs in c.rhodecode_repo.branches.items():
 
            #chs = chs.split(':')[-1]
 
            branches_group[0].append((chs, name),)
 
        hist_l.append(branches_group)
 

	
 
        for name, chs in c.rhodecode_repo.tags.items():
 
            #chs = chs.split(':')[-1]
 
            tags_group[0].append((chs, name),)
 
        hist_l.append(tags_group)
 

	
 
        return hist_l
 

	
 
    @jsonify
 
    @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
 
                                   'repository.admin')
 
    def nodelist(self, repo_name, revision, f_path):
 
        if request.environ.get('HTTP_X_PARTIAL_XHR'):
 
            cs = self.__get_cs_or_redirect(revision, repo_name)
 
            _d, _f = self.__get_paths(cs, f_path)
 
            return _d + _f
 

	
0 comments (0 inline, 0 general)