Changeset - a437a986d399
[Not reviewed]
Merge default
1 65 2
Marcin Kuzminski - 14 years ago 2012-05-10 20:27:45
marcin@python-works.com
merged beta into stable
58 files changed:
Changeset was too big and was cut off... Show full diff anyway
0 comments (0 inline, 0 general)
docs/changelog.rst
Show inline comments
 
modified file chmod 100644 => 100755
 
.. _changelog:
 

	
 
=========
 
Changelog
 
=========
 

	
 
1.3.5 (**2012-05-10**)
 
----------------------
 

	
 
news
 
++++
 

	
 
- use ext_json for json module
 
- unified annotation view with file source view
 
- notification improvements, better inbox + css
 
- #419 don't strip passwords for login forms, make rhodecode 
 
  more compatible with LDAP servers
 
- Added HTTP_X_FORWARDED_FOR as another method of extracting 
 
  IP for pull/push logs. - moved all to base controller  
 
- #415: Adding comment to changeset causes reload. 
 
  Comments are now added via ajax and doesn't reload the page
 
- #374 LDAP config is discarded when LDAP can't be activated
 
- limited push/pull operations are now logged for git in the journal
 
- bumped mercurial to 2.2.X series
 
- added support for displaying submodules in file-browser
 
- #421 added bookmarks in changelog view
 

	
 
fixes
 
+++++
 

	
 
- fixed dev-version marker for stable when served from source codes
 
- fixed missing permission checks on show forks page
 
- #418 cast to unicode fixes in notification objects
 
- #426 fixed mention extracting regex
 
- fixed remote-pulling for git remotes remopositories
 
- fixed #434: Error when accessing files or changesets of a git repository 
 
  with submodules
 
- fixed issue with empty APIKEYS for users after registration ref. #438
 
- fixed issue with getting README files from git repositories
 

	
 
1.3.4 (**2012-03-28**)
 
----------------------
 

	
 
news
 
++++
 

	
 
- Whoosh logging is now controlled by the .ini files logging setup
 
- added clone-url into edit form on /settings page
 
- added help text into repo add/edit forms
 
- created rcextensions module with additional mappings (ref #322) and
 
  post push/pull/create repo hooks callbacks
 
- implemented #377 Users view for his own permissions on account page
 
- #399 added inheritance of permissions for users group on repos groups
 
- #401 repository group is automatically pre-selected when adding repos 
 
  inside a repository group
 
- added alternative HTTP 403 response when client failed to authenticate. Helps 
 
  solving issues with Mercurial and LDAP
 
- #402 removed group prefix from repository name when listing repositories 
 
  inside a group
 
- added gravatars into permission view and permissions autocomplete
 
- #347 when running multiple RhodeCode instances, properly invalidates cache 
 
  for all registered servers
 

	
docs/conf.py
Show inline comments
 
# -*- coding: utf-8 -*-
 
#
 
# RhodeCode documentation build configuration file, created by
 
# sphinx-quickstart on Sun Oct 10 16:46:37 2010.
 
#
 
# This file is execfile()d with the current directory set to its containing dir.
 
#
 
# Note that not all possible configuration values are present in this
 
# autogenerated file.
 
#
 
# All configuration values have a default; values that are commented out
 
# serve to show the default.
 

	
 
import sys, os
 
import sys
 
import os
 
import datetime
 

	
 
# If extensions (or modules to document with autodoc) are in another directory,
 
# add these directories to sys.path here. If the directory is relative to the
 
# documentation root, use os.path.abspath to make it absolute, like shown here.
 
sys.path.insert(0, os.path.abspath('..'))
 

	
 
# -- General configuration -----------------------------------------------------
 

	
 
# If your documentation needs a minimal Sphinx version, state it here.
 
#needs_sphinx = '1.0'
 

	
 
# Add any Sphinx extension module names here, as strings. They can be extensions
 
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
 
extensions = ['sphinx.ext.autodoc', 'sphinx.ext.doctest', 'sphinx.ext.intersphinx', 'sphinx.ext.todo', 'sphinx.ext.viewcode']
 
extensions = ['sphinx.ext.autodoc', 'sphinx.ext.doctest',
 
              'sphinx.ext.intersphinx', 'sphinx.ext.todo',
 
              'sphinx.ext.viewcode']
 

	
 
# Add any paths that contain templates here, relative to this directory.
 
templates_path = ['_templates']
 

	
 
# The suffix of source filenames.
 
source_suffix = '.rst'
 

	
 
# The encoding of source files.
 
#source_encoding = 'utf-8-sig'
 

	
 
# The master toctree document.
 
master_doc = 'index'
 

	
 
# General information about the project.
 
project = u'RhodeCode'
 
copyright = u'2010, Marcin Kuzminski'
 
copyright = u'%s, Marcin Kuzminski' % (datetime.datetime.now().year)
 

	
 
# The version info for the project you're documenting, acts as replacement for
 
# |version| and |release|, also used in various other places throughout the
 
# built documents.
 
#
 
# The short X.Y version.
 
root = os.path.dirname(os.path.dirname(__file__))
 
sys.path.append(root)
 
from rhodecode import get_version, __version__
 
version = get_version()
 
# The full version, including alpha/beta/rc tags.
 
release = __version__
 

	
 
# The language for content autogenerated by Sphinx. Refer to documentation
 
# for a list of supported languages.
 
#language = None
 

	
 
# There are two options for replacing |today|: either, you set today to some
 
# non-false value, then it is used:
 
#today = ''
 
# Else, today_fmt is used as the format for a strftime call.
 
#today_fmt = '%B %d, %Y'
 

	
 
# List of patterns, relative to source directory, that match files and
rhodecode/__init__.py
Show inline comments
 
@@ -5,90 +5,93 @@
 

	
 
    RhodeCode, a web based repository management based on pylons
 
    versioning implementation: http://www.python.org/dev/peps/pep-0386/
 

	
 
    :created_on: Apr 9, 2010
 
    :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 sys
 
import platform
 

	
 
VERSION = (1, 3, 4)
 
VERSION = (1, 3, 5)
 

	
 
try:
 
    from rhodecode.lib import get_current_revision
 
    _rev = get_current_revision()
 
    if _rev and len(VERSION) > 3:
 
        VERSION += ('dev%s' % _rev[0],)
 
except ImportError:
 
    pass
 

	
 
__version__ = ('.'.join((str(each) for each in VERSION[:3])) +
 
               '.'.join(VERSION[3:]))
 
__dbversion__ = 5  # defines current db version for migrations
 
__platform__ = platform.system()
 
__license__ = 'GPLv3'
 
__py_version__ = sys.version_info
 

	
 
PLATFORM_WIN = ('Windows')
 
PLATFORM_OTHERS = ('Linux', 'Darwin', 'FreeBSD', 'OpenBSD', 'SunOS')
 

	
 
is_windows = __platform__ in PLATFORM_WIN
 
is_unix = __platform__ in PLATFORM_OTHERS
 

	
 
requirements = [
 
    "Pylons==1.0.0",
 
    "Beaker==1.6.3",
 
    "WebHelpers==1.3",
 
    "formencode==1.2.4",
 
    "SQLAlchemy==0.7.6",
 
    "Mako==0.6.2",
 
    "Mako==0.7.0",
 
    "pygments>=1.4",
 
    "whoosh>=2.3.0,<2.4",
 
    "whoosh>=2.4.0,<2.5",
 
    "celery>=2.2.5,<2.3",
 
    "babel",
 
    "python-dateutil>=1.5.0,<2.0.0",
 
    "dulwich>=0.8.4,<0.9.0",
 
    "dulwich>=0.8.5,<0.9.0",
 
    "webob==1.0.8",
 
    "markdown==2.1.1",
 
    "docutils==0.8.1",
 
]
 

	
 
if __py_version__ < (2, 6):
 
    requirements.append("simplejson")
 
    requirements.append("pysqlite")
 

	
 
if __platform__ in PLATFORM_WIN:
 
    requirements.append("mercurial>=2.1,<2.2")
 
if is_windows:
 
    requirements.append("mercurial>=2.2.1,<2.3")
 
else:
 
    requirements.append("py-bcrypt")
 
    requirements.append("mercurial>=2.1,<2.2")
 
    requirements.append("mercurial>=2.2.1,<2.3")
 

	
 

	
 
def get_version():
 
    """Returns shorter version (digit parts only) as string."""
 

	
 
    return '.'.join((str(each) for each in VERSION[:3]))
 

	
 
BACKENDS = {
 
    'hg': 'Mercurial repository',
 
    'git': 'Git repository',
 
}
 

	
 
CELERY_ON = False
 

	
 
# link to config for pylons
 
CONFIG = {}
 

	
 
# Linked module for extensions
 
EXTENSIONS = {}
rhodecode/config/routing.py
Show inline comments
 
@@ -433,50 +433,50 @@ def make_map(config):
 
    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='annotate', revision='tip',
 
                 f_path='', conditions=dict(function=check_repo))
 
                 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',
 
                 f_path='', conditions=dict(function=check_repo))
 

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

	
 
    rmap.connect('files_archive_home', '/{repo_name:.*}/archive/{fname}',
 
                controller='files', action='archivefile',
 
                conditions=dict(function=check_repo))
 

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

	
 
    rmap.connect('repo_settings_delete', '/{repo_name:.*}/settings',
 
                controller='settings', action="delete",
 
                conditions=dict(method=["DELETE"], function=check_repo))
 

	
rhodecode/controllers/admin/ldap_settings.py
Show inline comments
 
@@ -79,59 +79,71 @@ class LdapSettingsController(BaseControl
 
        c.search_scope_cur = self.search_scope_default
 
        c.tls_reqcert_cur = self.tls_reqcert_default
 
        c.tls_kind_cur = self.tls_kind_default
 

	
 
        super(LdapSettingsController, self).__before__()
 

	
 
    def index(self):
 
        defaults = RhodeCodeSetting.get_ldap_settings()
 
        c.search_scope_cur = defaults.get('ldap_search_scope')
 
        c.tls_reqcert_cur = defaults.get('ldap_tls_reqcert')
 
        c.tls_kind_cur = defaults.get('ldap_tls_kind')
 

	
 
        return htmlfill.render(
 
                    render('admin/ldap/ldap.html'),
 
                    defaults=defaults,
 
                    encoding="UTF-8",
 
                    force_defaults=True,)
 

	
 
    def ldap_settings(self):
 
        """POST ldap create and store ldap settings"""
 

	
 
        _form = LdapSettingsForm([x[0] for x in self.tls_reqcert_choices],
 
                                 [x[0] for x in self.search_scope_choices],
 
                                 [x[0] for x in self.tls_kind_choices])()
 
        # check the ldap lib
 
        ldap_active = False
 
        try:
 
            import ldap
 
            ldap_active = True
 
        except ImportError:
 
            pass
 

	
 
        try:
 
            form_result = _form.to_python(dict(request.POST))
 

	
 
            try:
 

	
 
                for k, v in form_result.items():
 
                    if k.startswith('ldap_'):
 
                        if k == 'ldap_active':
 
                            v = ldap_active
 
                        setting = RhodeCodeSetting.get_by_name(k)
 
                        setting.app_settings_value = v
 
                        self.sa.add(setting)
 

	
 
                self.sa.commit()
 
                h.flash(_('Ldap settings updated successfully'),
 
                    category='success')
 
                if not ldap_active:
 
                    #if ldap is missing send an info to user
 
                    h.flash(_('Unable to activate ldap. The "python-ldap" library '
 
                              'is missing.'), category='warning')
 

	
 
            except (DatabaseError,):
 
                raise
 
        except LdapImportError:
 
            h.flash(_('Unable to activate ldap. The "python-ldap" library '
 
                      'is missing.'), category='warning')
 

	
 
        except formencode.Invalid, errors:
 
            e = errors.error_dict or {}
 

	
 
            return htmlfill.render(
 
                render('admin/ldap/ldap.html'),
 
                defaults=errors.value,
 
                errors=e,
 
                prefix_error=False,
 
                encoding="UTF-8")
 
        except Exception:
 
            log.error(traceback.format_exc())
 
            h.flash(_('error occurred during update of ldap settings'),
 
                    category='error')
 

	
 
        return redirect(url('ldap_home'))
rhodecode/controllers/admin/notifications.py
Show inline comments
 
@@ -9,88 +9,92 @@
 
    :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
 
from pylons import tmpl_context as c, url
 
from pylons.controllers.util import redirect
 

	
 
from webhelpers.paginate import Page
 

	
 
from rhodecode.lib.base import BaseController, render
 
from rhodecode.model.db import Notification
 

	
 
from rhodecode.model.notification import NotificationModel
 
from rhodecode.lib.auth import LoginRequired, NotAnonymous
 
from rhodecode.lib import helpers as h
 
from rhodecode.model.meta import Session
 

	
 

	
 
log = logging.getLogger(__name__)
 

	
 

	
 
class NotificationsController(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('notification', 'notifications', controller='_admin/notifications',
 
    #         path_prefix='/_admin', name_prefix='_admin_')
 

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

	
 
    def index(self, format='html'):
 
        """GET /_admin/notifications: All items in the collection"""
 
        # url('notifications')
 
        c.user = self.rhodecode_user
 
        c.notifications = NotificationModel()\
 
                            .get_for_user(self.rhodecode_user.user_id)
 
        notif = NotificationModel().get_for_user(self.rhodecode_user.user_id)
 
        p = int(request.params.get('page', 1))
 
        c.notifications = Page(notif, page=p, items_per_page=10)
 
        return render('admin/notifications/notifications.html')
 

	
 
    def mark_all_read(self):
 
        if request.environ.get('HTTP_X_PARTIAL_XHR'):
 
            nm = NotificationModel()
 
            # mark all read
 
            nm.mark_all_read_for_user(self.rhodecode_user.user_id)
 
            Session.commit()
 
            c.user = self.rhodecode_user
 
            c.notifications = nm.get_for_user(self.rhodecode_user.user_id)
 
            notif = nm.get_for_user(self.rhodecode_user.user_id)
 
            c.notifications = Page(notif, page=1, items_per_page=10)
 
            return render('admin/notifications/notifications_data.html')
 

	
 
    def create(self):
 
        """POST /_admin/notifications: Create a new item"""
 
        # url('notifications')
 

	
 
    def new(self, format='html'):
 
        """GET /_admin/notifications/new: Form to create a new item"""
 
        # url('new_notification')
 

	
 
    def update(self, notification_id):
 
        """PUT /_admin/notifications/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('notification', notification_id=ID),
 
        #           method='put')
 
        # url('notification', notification_id=ID)
 

	
 
    def delete(self, notification_id):
 
        """DELETE /_admin/notifications/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:
rhodecode/controllers/admin/settings.py
Show inline comments
 
@@ -5,95 +5,103 @@
 

	
 
    settings controller for rhodecode admin
 

	
 
    :created_on: Jul 14, 2010
 
    :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
 
import formencode
 
import pkg_resources
 
import platform
 

	
 
from sqlalchemy import func
 
from formencode import htmlfill
 
from pylons import request, session, tmpl_context as c, url, config
 
from pylons.controllers.util import abort, redirect
 
from pylons.i18n.translation import _
 

	
 
from rhodecode.lib import helpers as h
 
from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator, \
 
    HasPermissionAnyDecorator, NotAnonymous
 
from rhodecode.lib.base import BaseController, render
 
from rhodecode.lib.celerylib import tasks, run_task
 
from rhodecode.lib.utils import repo2db_mapper, invalidate_cache, \
 
    set_rhodecode_config, repo_name_slug
 
from rhodecode.model.db import RhodeCodeUi, Repository, RepoGroup, \
 
    RhodeCodeSetting
 
from rhodecode.model.forms import UserForm, ApplicationSettingsForm, \
 
    ApplicationUiSettingsForm
 
from rhodecode.model.scm import ScmModel
 
from rhodecode.model.user import UserModel
 
from rhodecode.model.db import User
 
from rhodecode.model.notification import EmailNotificationModel
 
from rhodecode.model.meta import Session
 

	
 
log = logging.getLogger(__name__)
 

	
 

	
 
class SettingsController(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('setting', 'settings', controller='admin/settings',
 
    #         path_prefix='/admin', name_prefix='admin_')
 

	
 
    @LoginRequired()
 
    def __before__(self):
 
        c.admin_user = session.get('admin_user')
 
        c.admin_username = session.get('admin_username')
 
        c.modules = sorted([(p.project_name, p.version)
 
                            for p in pkg_resources.working_set],
 
                           key=lambda k: k[0].lower())
 
        c.py_version = platform.python_version()
 
        c.platform = platform.platform()
 
        super(SettingsController, self).__before__()
 

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

	
 
        defaults = RhodeCodeSetting.get_app_settings()
 
        defaults.update(self.get_hg_ui_settings())
 

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

	
 
    @HasPermissionAllDecorator('hg.admin')
 
    def create(self):
 
        """POST /admin/settings: Create a new item"""
 
        # url('admin_settings')
 

	
 
    @HasPermissionAllDecorator('hg.admin')
 
    def new(self, format='html'):
 
        """GET /admin/settings/new: Form to create a new item"""
 
        # url('admin_new_setting')
 

	
 
    @HasPermissionAllDecorator('hg.admin')
 
    def update(self, setting_id):
 
        """PUT /admin/settings/setting_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('admin_setting', setting_id=ID),
rhodecode/controllers/changelog.py
Show inline comments
 
@@ -104,31 +104,32 @@ class ChangelogController(BaseRepoContro
 
            return render('changelog/changelog_details.html')
 

	
 
    def _graph(self, repo, collection, repo_size, size, p):
 
        """
 
        Generates a DAG graph for mercurial
 

	
 
        :param repo: repo instance
 
        :param size: number of commits to show
 
        :param p: page number
 
        """
 
        if not collection:
 
            c.jsdata = json.dumps([])
 
            return
 

	
 
        data = []
 
        revs = [x.revision for x in collection]
 

	
 
        if repo.alias == 'git':
 
            for _ in revs:
 
                vtx = [0, 1]
 
                edges = [[0, 0, 1]]
 
                data.append(['', vtx, edges])
 

	
 
        elif repo.alias == 'hg':
 
            c.dag = graphmod.colored(graphmod.dagwalker(repo._repo, revs))
 
            dag = graphmod.dagwalker(repo._repo, revs)
 
            c.dag = graphmod.colored(dag, repo._repo)
 
            for (id, type, ctx, vtx, edges) in c.dag:
 
                if type != graphmod.CHANGESET:
 
                    continue
 
                data.append(['', vtx, edges])
 

	
 
        c.jsdata = json.dumps(data)
rhodecode/controllers/changeset.py
Show inline comments
 
@@ -338,45 +338,60 @@ class ChangesetController(BaseRepoContro
 
                                           ignore_whitespace=ignore_whitespace,
 
                                           context=line_context)
 
                    diff = diffs.DiffProcessor(f_gitdiff,
 
                                                format='gitdiff').raw_diff()
 

	
 
                cs1 = filenode_old.changeset.raw_id
 
                cs2 = node.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')
 

	
 
    @jsonify
 
    def comment(self, repo_name, revision):
 
        ChangesetCommentsModel().create(text=request.POST.get('text'),
 
        comm = 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'))
 
            line_no=request.POST.get('line')
 
        )
 
        Session.commit()
 
        if not request.environ.get('HTTP_X_PARTIAL_XHR'):
 
        return redirect(h.url('changeset_home', repo_name=repo_name,
 
                              revision=revision))
 

	
 
        data = {
 
           'target_id': h.safeid(h.safe_unicode(request.POST.get('f_path'))),
 
        }
 
        if comm:
 
            c.co = comm
 
            data.update(comm.get_dict())
 
            data.update({'rendered_text':
 
                         render('changeset/changeset_comment_block.html')})
 

	
 
        return data
 

	
 
    @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
 
@@ -27,48 +27,49 @@ import os
 
import logging
 
import traceback
 

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

	
 
from rhodecode.lib import diffs
 
from rhodecode.lib import helpers as h
 

	
 
from rhodecode.lib.compat import OrderedDict
 
from rhodecode.lib.utils2 import convert_line_endings, detect_mode, safe_str
 
from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
 
from rhodecode.lib.base import BaseRepoController, render
 
from rhodecode.lib.utils import EmptyChangeset
 
from rhodecode.lib.vcs.conf import settings
 
from rhodecode.lib.vcs.exceptions import RepositoryError, \
 
    ChangesetDoesNotExistError, EmptyRepositoryError, \
 
    ImproperArchiveTypeError, VCSError, NodeAlreadyExistsError
 
from rhodecode.lib.vcs.nodes import FileNode
 

	
 
from rhodecode.model.repo import RepoModel
 
from rhodecode.model.scm import ScmModel
 
from rhodecode.model.db import Repository
 

	
 
from rhodecode.controllers.changeset import anchor_url, _ignorews_url,\
 
    _context_url, get_line_ctx, get_ignore_ws
 

	
 

	
 
log = logging.getLogger(__name__)
 

	
 

	
 
class FilesController(BaseRepoController):
 

	
 
    @LoginRequired()
 
    def __before__(self):
 
        super(FilesController, self).__before__()
 
        c.cut_off_limit = self.cut_off_limit
 

	
 
    def __get_cs_or_redirect(self, rev, repo_name, redirect_after=True):
 
        """
 
        Safe way to get changeset if error occur it redirects to tip with
 
        proper message
 

	
 
        :param rev: revision to fetch
 
        :param repo_name: repo name to redirect after
 
        """
 

	
 
@@ -91,105 +92,105 @@ class FilesController(BaseRepoController
 

	
 
    def __get_filenode_or_redirect(self, repo_name, cs, path):
 
        """
 
        Returns file_node, if error occurs or given path is directory,
 
        it'll redirect to top level path
 

	
 
        :param repo_name: repo_name
 
        :param cs: given changeset
 
        :param path: path to lookup
 
        """
 

	
 
        try:
 
            file_node = cs.get_node(path)
 
            if file_node.is_dir():
 
                raise RepositoryError('given path is a directory')
 
        except RepositoryError, e:
 
            h.flash(str(e), category='warning')
 
            redirect(h.url('files_home', repo_name=repo_name,
 
                           revision=cs.raw_id))
 

	
 
        return file_node
 

	
 
    @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
 
                                   'repository.admin')
 
    def index(self, repo_name, revision, f_path):
 
    def index(self, repo_name, revision, f_path, annotate=False):
 
        # redirect to given revision from form if given
 
        post_revision = request.POST.get('at_rev', None)
 
        if post_revision:
 
            cs = self.__get_cs_or_redirect(post_revision, repo_name)
 
            redirect(url('files_home', repo_name=c.repo_name,
 
                         revision=cs.raw_id, f_path=f_path))
 

	
 
        c.changeset = self.__get_cs_or_redirect(revision, repo_name)
 
        c.branch = request.GET.get('branch', None)
 
        c.f_path = f_path
 

	
 
        c.annotate = annotate
 
        cur_rev = c.changeset.revision
 

	
 
        # prev link
 
        try:
 
            prev_rev = c.rhodecode_repo.get_changeset(cur_rev).prev(c.branch)
 
            c.url_prev = url('files_home', repo_name=c.repo_name,
 
                         revision=prev_rev.raw_id, f_path=f_path)
 
            if c.branch:
 
                c.url_prev += '?branch=%s' % c.branch
 
        except (ChangesetDoesNotExistError, VCSError):
 
            c.url_prev = '#'
 

	
 
        # next link
 
        try:
 
            next_rev = c.rhodecode_repo.get_changeset(cur_rev).next(c.branch)
 
            c.url_next = url('files_home', repo_name=c.repo_name,
 
                     revision=next_rev.raw_id, f_path=f_path)
 
            if c.branch:
 
                c.url_next += '?branch=%s' % c.branch
 
        except (ChangesetDoesNotExistError, VCSError):
 
            c.url_next = '#'
 

	
 
        # files or dirs
 
        try:
 
            c.file = c.changeset.get_node(f_path)
 

	
 
            if c.file.is_file():
 
                c.file_history = self._get_node_history(c.changeset, f_path)
 
            else:
 
                c.file_history = []
 
        except RepositoryError, e:
 
            h.flash(str(e), category='warning')
 
            redirect(h.url('files_home', repo_name=repo_name,
 
                           revision=revision))
 

	
 
        return render('files/files.html')
 

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

	
 
        response.content_disposition = 'attachment; filename=%s' % \
 
            safe_str(f_path.split(os.sep)[-1])
 
            safe_str(f_path.split(Repository.url_sep())[-1])
 

	
 
        response.content_type = file_node.mimetype
 
        return file_node.content
 

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

	
 
        raw_mimetype_mapping = {
 
            # map original mimetype to a mimetype used for "show as raw"
 
            # you can also provide a content-disposition to override the
 
            # default "attachment" disposition.
 
            # orig_type: (new_type, new_dispo)
 

	
 
            # show images inline:
 
            'image/x-icon': ('image/x-icon', 'inline'),
 
            'image/png': ('image/png', 'inline'),
 
            'image/gif': ('image/gif', 'inline'),
 
            'image/jpeg': ('image/jpeg', 'inline'),
 
            'image/svg+xml': ('image/svg+xml', 'inline'),
 
        }
 

	
 
@@ -198,58 +199,48 @@ class FilesController(BaseRepoController
 
            mimetype, dispo = raw_mimetype_mapping[mimetype]
 
        except KeyError:
 
            # we don't know anything special about this, handle it safely
 
            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))
rhodecode/controllers/forks.py
Show inline comments
 
@@ -14,49 +14,49 @@
 
# 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 formencode
 
import traceback
 
from formencode import htmlfill
 

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

	
 
import rhodecode.lib.helpers as h
 

	
 
from rhodecode.lib.helpers import Page
 
from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator, \
 
    NotAnonymous
 
    NotAnonymous, HasRepoPermissionAny
 
from rhodecode.lib.base import BaseRepoController, render
 
from rhodecode.model.db import Repository, RepoGroup, UserFollowing, User
 
from rhodecode.model.repo import RepoModel
 
from rhodecode.model.forms import RepoForkForm
 

	
 
log = logging.getLogger(__name__)
 

	
 

	
 
class ForksController(BaseRepoController):
 

	
 
    @LoginRequired()
 
    def __before__(self):
 
        super(ForksController, 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, repo_name=None):
 
        """
 
        Load defaults settings for edit, and update
 

	
 
        :param repo_name:
 
        """
 
@@ -82,49 +82,55 @@ class ForksController(BaseRepoController
 
        if c.repo_info.stats:
 
            last_rev = c.repo_info.stats.stat_on_revision+1
 
        else:
 
            last_rev = 0
 
        c.stats_revision = last_rev
 

	
 
        c.repo_last_rev = repo.count() 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)
 
        # add prefix to fork
 
        defaults['repo_name'] = 'fork-' + defaults['repo_name']
 
        return defaults
 

	
 
    @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
 
                                   'repository.admin')
 
    def forks(self, repo_name):
 
        p = int(request.params.get('page', 1))
 
        repo_id = c.rhodecode_db_repo.repo_id
 
        d = Repository.get_repo_forks(repo_id)
 
        d = []
 
        for r in Repository.get_repo_forks(repo_id):
 
            if not HasRepoPermissionAny(
 
                'repository.read', 'repository.write', 'repository.admin'
 
            )(r.repo_name, 'get forks check'):
 
                continue
 
            d.append(r)
 
        c.forks_pager = Page(d, page=p, items_per_page=20)
 

	
 
        c.forks_data = render('/forks/forks_data.html')
 

	
 
        if request.environ.get('HTTP_X_PARTIAL_XHR'):
 
            return c.forks_data
 

	
 
        return render('/forks/forks.html')
 

	
 
    @NotAnonymous()
 
    @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
 
                                   'repository.admin')
 
    def fork(self, repo_name):
 
        c.repo_info = Repository.get_by_repo_name(repo_name)
 
        if not c.repo_info:
 
            h.flash(_('%s repository is not mapped to db perhaps'
 
                      ' it was created or renamed from the file system'
 
                      ' please run the application again'
 
                      ' in order to rescan repositories') % repo_name,
 
                      category='error')
 

	
 
            return redirect(url('home'))
 

	
 
        defaults = self.__load_data(repo_name)
rhodecode/controllers/summary.py
Show inline comments
 
@@ -158,80 +158,83 @@ class SummaryController(BaseRepoControll
 
                               "desc": LANGUAGES_EXTENSIONS_MAP.get(x)})
 
                          for x, y in lang_stats_d.items())
 

	
 
            c.trending_languages = json.dumps(
 
                sorted(lang_stats, reverse=True, key=lambda k: k[1])[:10]
 
            )
 
            last_rev = stats.stat_on_revision + 1
 
            c.repo_last_rev = c.rhodecode_repo.count()\
 
                if c.rhodecode_repo.revisions else 0
 
            if last_rev == 0 or c.repo_last_rev == 0:
 
                pass
 
            else:
 
                c.stats_percentage = '%.2f' % ((float((last_rev)) /
 
                                                c.repo_last_rev) * 100)
 
        else:
 
            c.commit_data = json.dumps({})
 
            c.overview_data = json.dumps([[ts_min_y, 0], [ts_max_y, 10]])
 
            c.trending_languages = json.dumps({})
 
            c.no_data = True
 

	
 
        c.enable_downloads = dbrepo.enable_downloads
 
        if c.enable_downloads:
 
            c.download_options = self._get_download_links(c.rhodecode_repo)
 

	
 
        c.readme_data, c.readme_file = self.__get_readme_data(c.rhodecode_db_repo)
 
        c.readme_data, c.readme_file = self.__get_readme_data(
 
            c.rhodecode_db_repo.repo_name, c.rhodecode_repo
 
        )
 
        return render('summary/summary.html')
 

	
 
    def __get_readme_data(self, repo):
 
    def __get_readme_data(self, repo_name, repo):
 

	
 
        @cache_region('long_term')
 
        def _get_readme_from_cache(key):
 
            readme_data = None
 
            readme_file = None
 
            log.debug('Fetching readme file')
 
            try:
 
                cs = repo.get_changeset('tip')
 
                cs = repo.get_changeset()  # fetches TIP
 
                renderer = MarkupRenderer()
 
                for f in README_FILES:
 
                    try:
 
                        readme = cs.get_node(f)
 
                        readme_file = f
 
                        readme_data = renderer.render(readme.content, f)
 
                        log.debug('Found readme %s' % readme_file)
 
                        break
 
                    except NodeDoesNotExistError:
 
                        continue
 
            except ChangesetError:
 
                log.error(traceback.format_exc())
 
                pass
 
            except EmptyRepositoryError:
 
                pass
 
            except Exception:
 
                log.error(traceback.format_exc())
 

	
 
            return readme_data, readme_file
 

	
 
        key = repo.repo_name + '_README'
 
        key = repo_name + '_README'
 
        inv = CacheInvalidation.invalidate(key)
 
        if inv is not None:
 
            region_invalidate(_get_readme_from_cache, None, key)
 
            CacheInvalidation.set_valid(inv.cache_key)
 
        return _get_readme_from_cache(key)
 

	
 
    def _get_download_links(self, repo):
 

	
 
        download_l = []
 

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

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

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

	
 
        return download_l
rhodecode/lib/base.py
Show inline comments
 
@@ -95,48 +95,59 @@ class BaseVCSController(object):
 
        """
 
        Checks permissions using action (push/pull) user and repository
 
        name
 

	
 
        :param action: push or pull action
 
        :param user: user instance
 
        :param repo_name: repository name
 
        """
 
        if action == 'push':
 
            if not HasPermissionAnyMiddleware('repository.write',
 
                                              'repository.admin')(user,
 
                                                                  repo_name):
 
                return False
 

	
 
        else:
 
            #any other action need at least read permission
 
            if not HasPermissionAnyMiddleware('repository.read',
 
                                              'repository.write',
 
                                              'repository.admin')(user,
 
                                                                  repo_name):
 
                return False
 

	
 
        return True
 

	
 
    def _get_ip_addr(self, environ):
 
        proxy_key = 'HTTP_X_REAL_IP'
 
        proxy_key2 = 'HTTP_X_FORWARDED_FOR'
 
        def_key = 'REMOTE_ADDR'
 

	
 
        return environ.get(proxy_key2,
 
                           environ.get(proxy_key,
 
                                       environ.get(def_key, '0.0.0.0')
 
                            )
 
                        )
 

	
 
    def __call__(self, environ, start_response):
 
        start = time.time()
 
        try:
 
            return self._handle_request(environ, start_response)
 
        finally:
 
            log = logging.getLogger('rhodecode.' + self.__class__.__name__)
 
            log.debug('Request time: %.3fs' % (time.time() - start))
 
            meta.Session.remove()
 

	
 

	
 
class BaseController(WSGIController):
 

	
 
    def __before__(self):
 
        c.rhodecode_version = __version__
 
        c.rhodecode_instanceid = config.get('instance_id')
 
        c.rhodecode_name = config.get('rhodecode_title')
 
        c.use_gravatar = str2bool(config.get('use_gravatar'))
 
        c.ga_code = config.get('rhodecode_ga_code')
 
        c.repo_name = get_repo_slug(request)
 
        c.backends = BACKENDS.keys()
 
        c.unread_notifications = NotificationModel()\
 
                        .get_unread_cnt_for_user(c.rhodecode_user.user_id)
 
        self.cut_off_limit = int(config.get('cut_off_limit'))
 

	
rhodecode/lib/celerylib/tasks.py
Show inline comments
 
@@ -26,48 +26,49 @@
 
from celery.decorators import task
 

	
 
import os
 
import traceback
 
import logging
 
from os.path import join as jn
 

	
 
from time import mktime
 
from operator import itemgetter
 
from string import lower
 

	
 
from pylons import config, url
 
from pylons.i18n.translation import _
 

	
 
from rhodecode.lib.vcs import get_backend
 

	
 
from rhodecode import CELERY_ON
 
from rhodecode.lib.utils2 import safe_str
 
from rhodecode.lib.celerylib import run_task, locked_task, dbsession, \
 
    str2bool, __get_lockkey, LockHeld, DaemonLock, get_session
 
from rhodecode.lib.helpers import person
 
from rhodecode.lib.rcmail.smtp_mailer import SmtpMailer
 
from rhodecode.lib.utils import add_cache, action_logger
 
from rhodecode.lib.compat import json, OrderedDict
 
from rhodecode.lib.hooks import log_create_repository
 

	
 
from rhodecode.model.db import Statistics, Repository, User
 

	
 

	
 
add_cache(config)
 

	
 
__all__ = ['whoosh_index', 'get_commits_stats',
 
           'reset_user_password', 'send_email']
 

	
 

	
 
def get_logger(cls):
 
    if CELERY_ON:
 
        try:
 
            log = cls.get_logger()
 
        except:
 
            log = logging.getLogger(__name__)
 
    else:
 
        log = logging.getLogger(__name__)
 

	
 
    return log
 

	
 

	
 
@task(ignore_result=True)
 
@locked_task
 
@@ -351,66 +352,70 @@ def send_email(recipients, subject, body
 
        m.send(recipients, subject, body, html_body)
 
    except:
 
        log.error('Mail sending failed')
 
        log.error(traceback.format_exc())
 
        return False
 
    return True
 

	
 

	
 
@task(ignore_result=True)
 
@dbsession
 
def create_repo_fork(form_data, cur_user):
 
    """
 
    Creates a fork of repository using interval VCS methods
 

	
 
    :param form_data:
 
    :param cur_user:
 
    """
 
    from rhodecode.model.repo import RepoModel
 

	
 
    log = get_logger(create_repo_fork)
 
    DBS = get_session()
 

	
 
    base_path = Repository.base_path()
 

	
 
    RepoModel(DBS).create(form_data, cur_user, just_db=True, fork=True)
 
    fork_repo = RepoModel(DBS).create(form_data, cur_user,
 
                                      just_db=True, fork=True)
 

	
 
    alias = form_data['repo_type']
 
    org_repo_name = form_data['org_path']
 
    fork_name = form_data['repo_name_full']
 
    update_after_clone = form_data['update_after_clone']
 
    source_repo_path = os.path.join(base_path, org_repo_name)
 
    destination_fork_path = os.path.join(base_path, fork_name)
 

	
 
    log.info('creating fork of %s as %s', source_repo_path,
 
             destination_fork_path)
 
    backend = get_backend(alias)
 
    backend(safe_str(destination_fork_path), create=True,
 
            src_url=safe_str(source_repo_path),
 
            update_after_clone=update_after_clone)
 
    log_create_repository(fork_repo.get_dict(), created_by=cur_user.username)
 

	
 
    action_logger(cur_user, 'user_forked_repo:%s' % fork_name,
 
                   org_repo_name, '', DBS)
 

	
 
    action_logger(cur_user, 'user_created_fork:%s' % fork_name,
 
                   fork_name, '', DBS)
 
    # finally commit at latest possible stage
 
    DBS.commit()
 

	
 

	
 
def __get_codes_stats(repo_name):
 
    from rhodecode.config.conf import  LANGUAGES_EXTENSIONS_MAP
 
    repo = Repository.get_by_repo_name(repo_name).scm_instance
 

	
 
    tip = repo.get_changeset()
 
    code_stats = {}
 

	
 
    def aggregate(cs):
 
        for f in cs[2]:
 
            ext = lower(f.extension)
 
            if ext in LANGUAGES_EXTENSIONS_MAP.keys() and not f.is_binary:
 
                if ext in code_stats:
 
                    code_stats[ext] += 1
 
                else:
 
                    code_stats[ext] = 1
 

	
 
    map(aggregate, tip.walk('/'))
 

	
 
    return code_stats or {}
rhodecode/lib/compat.py
Show inline comments
 
@@ -4,134 +4,54 @@
 
    ~~~~~~~~~~~~~~~~~~~~
 

	
 
    Python backward compatibility functions and common libs
 

	
 

	
 
    :created_on: Oct 7, 2011
 
    :author: marcink
 
    :copyright: (C) 2010-2010 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 os
 
import datetime
 
import functools
 
import decimal
 
from rhodecode import __platform__, PLATFORM_WIN
 

	
 
#==============================================================================
 
# json
 
#==============================================================================
 

	
 

	
 
def _is_aware(value):
 
    """
 
    Determines if a given datetime.time is aware.
 

	
 
    The logic is described in Python's docs:
 
    http://docs.python.org/library/datetime.html#datetime.tzinfo
 
    """
 
    return (value.tzinfo is not None
 
            and value.tzinfo.utcoffset(value) is not None)
 

	
 

	
 
def _obj_dump(obj):
 
    """
 
    Custom function for dumping objects to JSON, if obj has __json__ attribute
 
    or method defined it will be used for serialization
 

	
 
    :param obj:
 
    """
 

	
 
    if isinstance(obj, complex):
 
        return [obj.real, obj.imag]
 
    # See "Date Time String Format" in the ECMA-262 specification.
 
    # some code borrowed from django 1.4
 
    elif isinstance(obj, datetime.datetime):
 
        r = obj.isoformat()
 
        if obj.microsecond:
 
            r = r[:23] + r[26:]
 
        if r.endswith('+00:00'):
 
            r = r[:-6] + 'Z'
 
        return r
 
    elif isinstance(obj, datetime.date):
 
        return obj.isoformat()
 
    elif isinstance(obj, decimal.Decimal):
 
        return str(obj)
 
    elif isinstance(obj, datetime.time):
 
        if _is_aware(obj):
 
            raise ValueError("JSON can't represent timezone-aware times.")
 
        r = obj.isoformat()
 
        if obj.microsecond:
 
            r = r[:12]
 
        return r
 
    elif isinstance(obj, set):
 
        return list(obj)
 
    elif isinstance(obj, OrderedDict):
 
        return obj.as_dict()
 
    elif hasattr(obj, '__json__'):
 
        if callable(obj.__json__):
 
            return obj.__json__()
 
        else:
 
            return obj.__json__
 
    else:
 
        raise NotImplementedError
 

	
 
try:
 
    import json
 

	
 
    # extended JSON encoder for json
 
    class ExtendedEncoder(json.JSONEncoder):
 
        def default(self, obj):
 
            try:
 
                return _obj_dump(obj)
 
            except NotImplementedError:
 
                pass
 
            return json.JSONEncoder.default(self, obj)
 
    # monkey-patch JSON encoder to use extended version
 
    json.dumps = functools.partial(json.dumps, cls=ExtendedEncoder)
 
except ImportError:
 
    import simplejson as json
 

	
 
    def extended_encode(obj):
 
        try:
 
            return _obj_dump(obj)
 
        except NotImplementedError:
 
            pass
 
        raise TypeError("%r is not JSON serializable" % (obj,))
 
    json.dumps = functools.partial(json.dumps, default=extended_encode)
 
from rhodecode.lib.ext_json import json
 

	
 

	
 
#==============================================================================
 
# izip_longest
 
#==============================================================================
 
try:
 
    from itertools import izip_longest
 
except ImportError:
 
    import itertools
 

	
 
    def izip_longest(*args, **kwds):
 
        fillvalue = kwds.get("fillvalue")
 

	
 
        def sentinel(counter=([fillvalue] * (len(args) - 1)).pop):
 
            yield counter()  # yields the fillvalue, or raises IndexError
 

	
 
        fillers = itertools.repeat(fillvalue)
 
        iters = [itertools.chain(it, sentinel(), fillers)
 
                    for it in args]
 
        try:
 
            for tup in itertools.izip(*iters):
 
                yield tup
 
        except IndexError:
 
            pass
rhodecode/lib/dbmigrate/versions/003_version_1_2_0.py
Show inline comments
 
@@ -50,71 +50,69 @@ def upgrade(migrate_engine):
 
    UsersGroupRepoToPerm().__table__.create()
 

	
 
    #==========================================================================
 
    # Add table `users_group_to_perm`
 
    #==========================================================================
 
    from rhodecode.lib.dbmigrate.schema.db_1_2_0 import UsersGroupToPerm
 
    UsersGroupToPerm().__table__.create()
 

	
 
    #==========================================================================
 
    # Upgrade of `users` table
 
    #==========================================================================
 
    from rhodecode.lib.dbmigrate.schema.db_1_2_0 import User
 

	
 
    #add column
 
    ldap_dn = Column("ldap_dn", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
 
    ldap_dn.create(User().__table__)
 

	
 
    api_key = Column("api_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
 
    api_key.create(User().__table__)
 

	
 
    #remove old column
 
    is_ldap = Column("is_ldap", Boolean(), nullable=False, unique=None, default=False)
 
    is_ldap.drop(User().__table__)
 

	
 

	
 
    #==========================================================================
 
    # Upgrade of `repositories` table
 
    #==========================================================================
 
    from rhodecode.lib.dbmigrate.schema.db_1_2_0 import Repository
 

	
 
    #ADD clone_uri column#
 

	
 
    clone_uri = Column("clone_uri", String(length=255, convert_unicode=False,
 
                                           assert_unicode=None),
 
                        nullable=True, unique=False, default=None)
 

	
 
    clone_uri.create(Repository().__table__)
 

	
 
    #ADD downloads column#
 
    enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
 
    enable_downloads.create(Repository().__table__)
 

	
 
    #ADD column created_on
 
    created_on = Column('created_on', DateTime(timezone=False), nullable=True,
 
                        unique=None, default=datetime.datetime.now)
 
    created_on.create(Repository().__table__)
 

	
 
    #ADD group_id column#
 
    group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'),
 
                  nullable=True, unique=False, default=None)
 

	
 
    group_id.create(Repository().__table__)
 

	
 

	
 
    #==========================================================================
 
    # Upgrade of `user_followings` table
 
    #==========================================================================
 

	
 
    from rhodecode.lib.dbmigrate.schema.db_1_2_0 import UserFollowing
 

	
 
    follows_from = Column('follows_from', DateTime(timezone=False),
 
                          nullable=True, unique=None,
 
                          default=datetime.datetime.now)
 
    follows_from.create(UserFollowing().__table__)
 

	
 
    return
 

	
 

	
 
def downgrade(migrate_engine):
 
    meta = MetaData()
 
    meta.bind = migrate_engine
rhodecode/lib/diffs.py
Show inline comments
 
@@ -12,125 +12,132 @@
 
    :original copyright: 2007-2008 by Armin Ronacher
 
    :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 re
 
import difflib
 
import markupsafe
 
from itertools import tee, imap
 

	
 
from pylons.i18n.translation import _
 

	
 
from rhodecode.lib.vcs.exceptions import VCSError
 
from rhodecode.lib.vcs.nodes import FileNode
 

	
 
from rhodecode.lib.vcs.nodes import FileNode, SubModuleNode
 
from rhodecode.lib.helpers import escape
 
from rhodecode.lib.utils import EmptyChangeset
 

	
 

	
 
def wrap_to_table(str_):
 
    return '''<table class="code-difftable">
 
                <tr class="line no-comment">
 
                <td class="lineno new"></td>
 
                <td class="code no-comment"><pre>%s</pre></td>
 
                </tr>
 
              </table>''' % str_
 

	
 

	
 
def wrapped_diff(filenode_old, filenode_new, cut_off_limit=None,
 
                ignore_whitespace=True, line_context=3,
 
                enable_comments=False):
 
    """
 
    returns a wrapped diff into a table, checks for cut_off_limit and presents
 
    proper message
 
    """
 

	
 
    if filenode_old is None:
 
        filenode_old = FileNode(filenode_new.path, '', EmptyChangeset())
 

	
 
    if filenode_old.is_binary or filenode_new.is_binary:
 
        diff = wrap_to_table(_('binary file'))
 
        stats = (0, 0)
 
        size = 0
 

	
 
    elif cut_off_limit != -1 and (cut_off_limit is None or
 
    (filenode_old.size < cut_off_limit and filenode_new.size < cut_off_limit)):
 

	
 
        f_gitdiff = get_gitdiff(filenode_old, filenode_new,
 
                                ignore_whitespace=ignore_whitespace,
 
                                context=line_context)
 
        diff_processor = DiffProcessor(f_gitdiff, format='gitdiff')
 

	
 
        diff = diff_processor.as_html(enable_comments=enable_comments)
 
        stats = diff_processor.stat()
 
        size = len(diff or '')
 
    else:
 
        diff = wrap_to_table(_('Changeset was to big and was cut off, use '
 
                               'diff menu to display this diff'))
 
        stats = (0, 0)
 
        size = 0
 

	
 
    if not diff:
 
        submodules = filter(lambda o: isinstance(o, SubModuleNode),
 
                            [filenode_new, filenode_old])
 
        if submodules:
 
            diff = wrap_to_table(escape('Submodule %r' % submodules[0]))
 
        else:
 
        diff = wrap_to_table(_('No changes detected'))
 

	
 
    cs1 = filenode_old.changeset.raw_id
 
    cs2 = filenode_new.changeset.raw_id
 

	
 
    return size, cs1, cs2, diff, stats
 

	
 

	
 
def get_gitdiff(filenode_old, filenode_new, ignore_whitespace=True, context=3):
 
    """
 
    Returns git style diff between given ``filenode_old`` and ``filenode_new``.
 

	
 
    :param ignore_whitespace: ignore whitespaces in diff
 
    """
 
    # make sure we pass in default context
 
    context = context or 3
 
    submodules = filter(lambda o: isinstance(o, SubModuleNode),
 
                        [filenode_new, filenode_old])
 
    if submodules:
 
        return ''
 

	
 
    for filenode in (filenode_old, filenode_new):
 
        if not isinstance(filenode, FileNode):
 
            raise VCSError("Given object should be FileNode object, not %s"
 
                % filenode.__class__)
 

	
 
    repo = filenode_new.changeset.repository
 
    old_raw_id = getattr(filenode_old.changeset, 'raw_id', repo.EMPTY_CHANGESET)
 
    new_raw_id = getattr(filenode_new.changeset, 'raw_id', repo.EMPTY_CHANGESET)
 

	
 
    vcs_gitdiff = repo.get_diff(old_raw_id, new_raw_id, filenode_new.path,
 
                                 ignore_whitespace, context)
 

	
 
    return vcs_gitdiff
 

	
 

	
 
class DiffProcessor(object):
 
    """
 
    Give it a unified diff and it returns a list of the files that were
 
    mentioned in the diff together with a dict of meta information that
 
    can be used to render it in a HTML template.
 
    """
 
    _chunk_re = re.compile(r'@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@(.*)')
 

	
 
    def __init__(self, diff, differ='diff', format='udiff'):
 
        """
 
        :param diff:   a text in diff format or generator
 
        :param format: format of diff passed, `udiff` or `gitdiff`
 
        """
 
        if isinstance(diff, basestring):
 
            diff = [diff]
 

	
 
        self.__udiff = diff
 
        self.__format = format
 
        self.adds = 0
 
        self.removes = 0
 

	
rhodecode/lib/ext_json.py
Show inline comments
 
new file 100644
 
import datetime
 
import functools
 
import decimal
 

	
 
__all__ = ['json', 'simplejson', 'stdjson']
 

	
 

	
 
def _is_aware(value):
 
    """
 
    Determines if a given datetime.time is aware.
 

	
 
    The logic is described in Python's docs:
 
    http://docs.python.org/library/datetime.html#datetime.tzinfo
 
    """
 
    return (value.tzinfo is not None
 
            and value.tzinfo.utcoffset(value) is not None)
 

	
 

	
 
def _obj_dump(obj):
 
    """
 
    Custom function for dumping objects to JSON, if obj has __json__ attribute
 
    or method defined it will be used for serialization
 

	
 
    :param obj:
 
    """
 

	
 
    if isinstance(obj, complex):
 
        return [obj.real, obj.imag]
 
    # See "Date Time String Format" in the ECMA-262 specification.
 
    # some code borrowed from django 1.4
 
    elif isinstance(obj, datetime.datetime):
 
        r = obj.isoformat()
 
        if obj.microsecond:
 
            r = r[:23] + r[26:]
 
        if r.endswith('+00:00'):
 
            r = r[:-6] + 'Z'
 
        return r
 
    elif isinstance(obj, datetime.date):
 
        return obj.isoformat()
 
    elif isinstance(obj, decimal.Decimal):
 
        return str(obj)
 
    elif isinstance(obj, datetime.time):
 
        if _is_aware(obj):
 
            raise ValueError("JSON can't represent timezone-aware times.")
 
        r = obj.isoformat()
 
        if obj.microsecond:
 
            r = r[:12]
 
        return r
 
    elif isinstance(obj, set):
 
        return list(obj)
 
    elif hasattr(obj, '__json__'):
 
        if callable(obj.__json__):
 
            return obj.__json__()
 
        else:
 
            return obj.__json__
 
    else:
 
        raise NotImplementedError
 

	
 

	
 
# Import simplejson
 
try:
 
    # import simplejson initially
 
    import simplejson as _sj
 

	
 
    def extended_encode(obj):
 
        try:
 
            return _obj_dump(obj)
 
        except NotImplementedError:
 
            pass
 
        raise TypeError("%r is not JSON serializable" % (obj,))
 
    # we handle decimals our own it makes unified behavior of json vs
 
    # simplejson
 
    _sj.dumps = functools.partial(_sj.dumps, default=extended_encode,
 
                                  use_decimal=False)
 
    _sj.dump = functools.partial(_sj.dump, default=extended_encode,
 
                                 use_decimal=False)
 
    simplejson = _sj
 

	
 
except ImportError:
 
    # no simplejson set it to None
 
    _sj = None
 

	
 

	
 
# simplejson not found try out regular json module
 
import json as _json
 

	
 

	
 
# extended JSON encoder for json
 
class ExtendedEncoder(_json.JSONEncoder):
 
    def default(self, obj):
 
        try:
 
            return _obj_dump(obj)
 
        except NotImplementedError:
 
            pass
 
        return _json.JSONEncoder.default(self, obj)
 
# monkey-patch JSON encoder to use extended version
 
_json.dumps = functools.partial(_json.dumps, cls=ExtendedEncoder)
 
_json.dump = functools.partial(_json.dump, cls=ExtendedEncoder)
 
stdlib = _json
 

	
 
# set all available json modules
 
simplejson = _sj
 
stdjson = _json
 
json = _sj if _sj else _json
rhodecode/lib/helpers.py
Show inline comments
 
@@ -433,48 +433,51 @@ def action_parser(user_log, feed=False):
 
    x = action.split(':')
 

	
 
    if len(x) > 1:
 
        action, action_params = x
 

	
 
    def get_cs_links():
 
        revs_limit = 3  # display this amount always
 
        revs_top_limit = 50  # show upto this amount of changesets hidden
 
        revs_ids = action_params.split(',')
 
        deleted = user_log.repository is None
 
        if deleted:
 
            return ','.join(revs_ids)
 

	
 
        repo_name = user_log.repository.repo_name
 

	
 
        repo = user_log.repository.scm_instance
 

	
 
        message = lambda rev: rev.message
 
        lnk = lambda rev, repo_name: (
 
            link_to('r%s:%s' % (rev.revision, rev.short_id),
 
                    url('changeset_home', repo_name=repo_name,
 
                        revision=rev.raw_id),
 
                    title=tooltip(message(rev)), class_='tooltip')
 
        )
 

	
 
        revs = []
 
        if len(filter(lambda v: v != '', revs_ids)) > 0:
 
        # get only max revs_top_limit of changeset for performance/ui reasons
 
        revs = [
 
            x for x in repo.get_changesets(revs_ids[0],
 
                                           revs_ids[:revs_top_limit][-1])
 
        ]
 

	
 
        cs_links = []
 
        cs_links.append(" " + ', '.join(
 
            [lnk(rev, repo_name) for rev in revs[:revs_limit]]
 
            )
 
        )
 

	
 
        compare_view = (
 
            ' <div class="compare_view tooltip" title="%s">'
 
            '<a href="%s">%s</a> </div>' % (
 
                _('Show all combined changesets %s->%s') % (
 
                    revs_ids[0], revs_ids[-1]
 
                ),
 
                url('changeset_home', repo_name=repo_name,
 
                    revision='%s...%s' % (revs_ids[0], revs_ids[-1])
 
                ),
 
                _('compare view')
 
            )
 
        )
rhodecode/lib/hooks.py
Show inline comments
 
@@ -12,137 +12,153 @@
 
"""
 
# 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 os
 
import sys
 

	
 
from mercurial.scmutil import revrange
 
from mercurial.node import nullrev
 
from rhodecode import EXTENSIONS
 
from rhodecode.lib import helpers as h
 
from rhodecode.lib.utils import action_logger
 
from inspect import isfunction
 

	
 

	
 
def _get_scm_size(alias, root_path):
 

	
 
    if not alias.startswith('.'):
 
        alias += '.'
 

	
 
    size_scm, size_root = 0, 0
 
    for path, dirs, files in os.walk(root_path):
 
        if path.find(alias) != -1:
 
            for f in files:
 
                try:
 
                    size_scm += os.path.getsize(os.path.join(path, f))
 
                except OSError:
 
                    pass
 
        else:
 
            for f in files:
 
                try:
 
                    size_root += os.path.getsize(os.path.join(path, f))
 
                except OSError:
 
                    pass
 

	
 
    size_scm_f = h.format_byte_size(size_scm)
 
    size_root_f = h.format_byte_size(size_root)
 
    size_total_f = h.format_byte_size(size_root + size_scm)
 

	
 
    return size_scm_f, size_root_f, size_total_f
 

	
 

	
 
def repo_size(ui, repo, hooktype=None, **kwargs):
 
    """
 
    Presents size of repository after push
 

	
 
    :param ui:
 
    :param repo:
 
    :param hooktype:
 
    """
 

	
 
    size_hg, size_root = 0, 0
 
    for path, dirs, files in os.walk(repo.root):
 
        if path.find('.hg') != -1:
 
            for f in files:
 
                try:
 
                    size_hg += os.path.getsize(os.path.join(path, f))
 
                except OSError:
 
                    pass
 
        else:
 
            for f in files:
 
                try:
 
                    size_root += os.path.getsize(os.path.join(path, f))
 
                except OSError:
 
                    pass
 

	
 
    size_hg_f = h.format_byte_size(size_hg)
 
    size_root_f = h.format_byte_size(size_root)
 
    size_total_f = h.format_byte_size(size_root + size_hg)
 
    size_hg_f, size_root_f, size_total_f = _get_scm_size('.hg', repo.root)
 

	
 
    last_cs = repo[len(repo) - 1]
 

	
 
    msg = ('Repository size .hg:%s repo:%s total:%s\n'
 
           'Last revision is now r%s:%s\n') % (
 
        size_hg_f, size_root_f, size_total_f, last_cs.rev(), last_cs.hex()[:12]
 
    )
 

	
 
    sys.stdout.write(msg)
 

	
 

	
 
def log_pull_action(ui, repo, **kwargs):
 
    """
 
    Logs user last pull action
 

	
 
    :param ui:
 
    :param repo:
 
    """
 

	
 
    extras = dict(repo.ui.configitems('rhodecode_extras'))
 
    username = extras['username']
 
    repository = extras['repository']
 
    scm = extras['scm']
 
    action = 'pull'
 

	
 
    action_logger(username, action, repository, extras['ip'], commit=True)
 
    # extension hook call
 
    callback = getattr(EXTENSIONS, 'PULL_HOOK', None)
 

	
 
    if isfunction(callback):
 
        kw = {}
 
        kw.update(extras)
 
        callback(**kw)
 
    return 0
 

	
 

	
 
def log_push_action(ui, repo, **kwargs):
 
    """
 
    Maps user last push action to new changeset id, from mercurial
 

	
 
    :param ui:
 
    :param repo:
 
    :param repo: repo object containing the `ui` object
 
    """
 

	
 
    extras = dict(repo.ui.configitems('rhodecode_extras'))
 
    username = extras['username']
 
    repository = extras['repository']
 
    action = extras['action'] + ':%s'
 
    scm = extras['scm']
 

	
 
    if scm == 'hg':
 
    node = kwargs['node']
 

	
 
    def get_revs(repo, rev_opt):
 
        if rev_opt:
 
            revs = revrange(repo, rev_opt)
 

	
 
            if len(revs) == 0:
 
                return (nullrev, nullrev)
 
            return (max(revs), min(revs))
 
        else:
 
            return (len(repo) - 1, 0)
 

	
 
    stop, start = get_revs(repo, [node + ':'])
 

	
 
    revs = (str(repo[r]) for r in xrange(start, stop + 1))
 
    elif scm == 'git':
 
        revs = []
 

	
 
    action = action % ','.join(revs)
 

	
 
    action_logger(username, action, repository, extras['ip'], commit=True)
 

	
 
    # extension hook call
 
    callback = getattr(EXTENSIONS, 'PUSH_HOOK', None)
 
    if isfunction(callback):
 
        kw = {'pushed_revs': revs}
 
        kw.update(extras)
 
        callback(**kw)
 
    return 0
 

	
 

	
 
def log_create_repository(repository_dict, created_by, **kwargs):
 
    """
 
    Post create repository Hook. This is a dummy function for admins to re-use
 
    if needed. It's taken from rhodecode-extensions module and executed
 
    if present
 

	
 
    :param repository: dict dump of repository object
 
    :param created_by: username who created repository
 
    :param created_date: date of creation
 

	
rhodecode/lib/markup_renderer.py
Show inline comments
 
@@ -6,49 +6,49 @@
 

	
 
    Renderer for markup languages with ability to parse using rst or markdown
 

	
 
    :created_on: Oct 27, 2011
 
    :author: marcink
 
    :copyright: (C) 2011-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 re
 
import logging
 

	
 
from rhodecode.lib.utils2 import safe_unicode
 
from rhodecode.lib.utils2 import safe_unicode, MENTIONS_REGEX
 

	
 
log = logging.getLogger(__name__)
 

	
 

	
 
class MarkupRenderer(object):
 
    RESTRUCTUREDTEXT_DISALLOWED_DIRECTIVES = ['include', 'meta', 'raw']
 

	
 
    MARKDOWN_PAT = re.compile(r'md|mkdn?|mdown|markdown', re.IGNORECASE)
 
    RST_PAT = re.compile(r're?st', re.IGNORECASE)
 
    PLAIN_PAT = re.compile(r'readme', re.IGNORECASE)
 

	
 
    def __detect_renderer(self, source, filename=None):
 
        """
 
        runs detection of what renderer should be used for generating html
 
        from a markup language
 

	
 
        filename can be also explicitly a renderer name
 

	
 
        :param source:
 
        :param filename:
 
        """
 

	
 
        if MarkupRenderer.MARKDOWN_PAT.findall(filename):
 
            detected_renderer = 'markdown'
 
@@ -107,31 +107,31 @@ class MarkupRenderer(object):
 
        source = safe_unicode(source)
 
        try:
 
            from docutils.core import publish_parts
 
            from docutils.parsers.rst import directives
 
            docutils_settings = dict([(alias, None) for alias in
 
                                cls.RESTRUCTUREDTEXT_DISALLOWED_DIRECTIVES])
 

	
 
            docutils_settings.update({'input_encoding': 'unicode',
 
                                      'report_level': 4})
 

	
 
            for k, v in docutils_settings.iteritems():
 
                directives.register_directive(k, v)
 

	
 
            parts = publish_parts(source=source,
 
                                  writer_name="html4css1",
 
                                  settings_overrides=docutils_settings)
 

	
 
            return parts['html_title'] + parts["fragment"]
 
        except ImportError:
 
            log.warning('Install docutils to use this function')
 
            return cls.plain(source)
 

	
 
    @classmethod
 
    def rst_with_mentions(cls, source):
 
        mention_pat = re.compile(r'(?:^@|\s@)(\w+)')
 
        mention_pat = re.compile(MENTIONS_REGEX)
 

	
 
        def wrapp(match_obj):
 
            uname = match_obj.groups()[0]
 
            return ' **@%(uname)s** ' % {'uname':uname}
 
        mention_hl = mention_pat.sub(wrapp, source).strip()
 
        return cls.rst(mention_hl)
rhodecode/lib/middleware/simplegit.py
Show inline comments
 
@@ -23,107 +23,108 @@
 
#
 
# 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 os
 
import re
 
import logging
 
import traceback
 

	
 
from dulwich import server as dulserver
 

	
 

	
 
class SimpleGitUploadPackHandler(dulserver.UploadPackHandler):
 

	
 
    def handle(self):
 
        write = lambda x: self.proto.write_sideband(1, x)
 

	
 
        graph_walker = dulserver.ProtocolGraphWalker(self,
 
                                                     self.repo.object_store,
 
                                                     self.repo.get_peeled)
 
        objects_iter = self.repo.fetch_objects(
 
          graph_walker.determine_wants, graph_walker, self.progress,
 
          get_tagged=self.get_tagged)
 

	
 
        # Do they want any objects?
 
        if objects_iter is None or len(objects_iter) == 0:
 
        # Did the process short-circuit (e.g. in a stateless RPC call)? Note
 
        # that the client still expects a 0-object pack in most cases.
 
        if objects_iter is None:
 
            return
 

	
 
        self.progress("counting objects: %d, done.\n" % len(objects_iter))
 
        dulserver.write_pack_objects(dulserver.ProtocolFile(None, write),
 
                                  objects_iter, len(objects_iter))
 
                                     objects_iter)
 
        messages = []
 
        messages.append('thank you for using rhodecode')
 

	
 
        for msg in messages:
 
            self.progress(msg + "\n")
 
        # we are done
 
        self.proto.write("0000")
 

	
 

	
 
dulserver.DEFAULT_HANDLERS = {
 
  'git-upload-pack': SimpleGitUploadPackHandler,
 
  'git-receive-pack': dulserver.ReceivePackHandler,
 
}
 

	
 
from dulwich.repo import Repo
 
from dulwich.web import make_wsgi_chain
 

	
 
from paste.httpheaders import REMOTE_USER, AUTH_TYPE
 

	
 
from rhodecode.lib.utils2 import safe_str
 
from rhodecode.lib.base import BaseVCSController
 
from rhodecode.lib.auth import get_container_username
 
from rhodecode.lib.utils import is_valid_repo
 
from rhodecode.lib.utils import is_valid_repo, make_ui
 
from rhodecode.model.db import User
 

	
 
from webob.exc import HTTPNotFound, HTTPForbidden, HTTPInternalServerError
 

	
 
log = logging.getLogger(__name__)
 

	
 

	
 
GIT_PROTO_PAT = re.compile(r'^/(.+)/(info/refs|git-upload-pack|git-receive-pack)')
 

	
 

	
 
def is_git(environ):
 
    path_info = environ['PATH_INFO']
 
    isgit_path = GIT_PROTO_PAT.match(path_info)
 
    log.debug('pathinfo: %s detected as GIT %s' % (
 
        path_info, isgit_path != None)
 
    )
 
    return isgit_path
 

	
 

	
 
class SimpleGit(BaseVCSController):
 

	
 
    def _handle_request(self, environ, start_response):
 

	
 
        if not is_git(environ):
 
            return self.application(environ, start_response)
 

	
 
        proxy_key = 'HTTP_X_REAL_IP'
 
        def_key = 'REMOTE_ADDR'
 
        ipaddr = environ.get(proxy_key, environ.get(def_key, '0.0.0.0'))
 
        ipaddr = self._get_ip_addr(environ)
 
        username = None
 
        self._git_first_op = False
 
        # skip passing error to error controller
 
        environ['pylons.status_code_redirect'] = True
 

	
 
        #======================================================================
 
        # EXTRACT REPOSITORY NAME FROM ENV
 
        #======================================================================
 
        try:
 
            repo_name = self.__get_repository(environ)
 
            log.debug('Extracted repo name is %s' % repo_name)
 
        except:
 
            return HTTPInternalServerError()(environ, start_response)
 

	
 
        # quick check if that dir exists...
 
        if is_valid_repo(repo_name, self.basepath) is False:
 
            return HTTPNotFound()(environ, start_response)
 

	
 
        #======================================================================
 
        # GET ACTION PULL or PUSH
 
        #======================================================================
 
        action = self.__get_action(environ)
 

	
 
        #======================================================================
 
        # CHECK ANONYMOUS PERMISSION
 
        #======================================================================
 
@@ -157,59 +158,72 @@ class SimpleGit(BaseVCSController):
 
                        AUTH_TYPE.update(environ, 'basic')
 
                        REMOTE_USER.update(environ, result)
 
                        username = result
 
                    else:
 
                        return result.wsgi_application(environ, start_response)
 

	
 
                #==============================================================
 
                # CHECK PERMISSIONS FOR THIS REQUEST USING GIVEN USERNAME
 
                #==============================================================
 
                if action in ['pull', 'push']:
 
                    try:
 
                        user = self.__get_user(username)
 
                        if user is None or not user.active:
 
                            return HTTPForbidden()(environ, start_response)
 
                        username = user.username
 
                    except:
 
                        log.error(traceback.format_exc())
 
                        return HTTPInternalServerError()(environ,
 
                                                         start_response)
 

	
 
                    #check permissions for this repository
 
                    perm = self._check_permission(action, user, repo_name)
 
                    if perm is not True:
 
                        return HTTPForbidden()(environ, start_response)
 
        extras = {
 
            'ip': ipaddr,
 
            'username': username,
 
            'action': action,
 
            'repository': repo_name,
 
            'scm': 'git',
 
        }
 

	
 
        #===================================================================
 
        # GIT REQUEST HANDLING
 
        #===================================================================
 
        repo_path = os.path.join(safe_str(self.basepath), safe_str(repo_name))
 
        log.debug('Repository path is %s' % repo_path)
 

	
 
        baseui = make_ui('db')
 
        self.__inject_extras(repo_path, baseui, extras)
 

	
 

	
 
        try:
 
            #invalidate cache on push
 
            if action == 'push':
 
                self._invalidate_cache(repo_name)
 
            self._handle_githooks(repo_name, action, baseui, environ)
 

	
 
            log.info('%s action on GIT repo "%s"' % (action, repo_name))
 
            app = self.__make_app(repo_name, repo_path)
 
            return app(environ, start_response)
 
        except Exception:
 
            log.error(traceback.format_exc())
 
            return HTTPInternalServerError()(environ, start_response)
 

	
 
    def __make_app(self, repo_name, repo_path):
 
        """
 
        Make an wsgi application using dulserver
 

	
 
        :param repo_name: name of the repository
 
        :param repo_path: full path to the repository
 
        """
 
        _d = {'/' + repo_name: Repo(repo_path)}
 
        backend = dulserver.DictBackend(_d)
 
        gitserve = make_wsgi_chain(backend)
 

	
 
        return gitserve
 

	
 
    def __get_repository(self, environ):
 
        """
 
        Get's repository name out of PATH_INFO header
 

	
 
@@ -228,24 +242,59 @@ class SimpleGit(BaseVCSController):
 
        return User.get_by_username(username)
 

	
 
    def __get_action(self, environ):
 
        """
 
        Maps git request commands into a pull or push command.
 

	
 
        :param environ:
 
        """
 
        service = environ['QUERY_STRING'].split('=')
 

	
 
        if len(service) > 1:
 
            service_cmd = service[1]
 
            mapping = {
 
                'git-receive-pack': 'push',
 
                'git-upload-pack': 'pull',
 
            }
 
            op = mapping[service_cmd]
 
            self._git_stored_op = op
 
            return op
 
        else:
 
            # try to fallback to stored variable as we don't know if the last
 
            # operation is pull/push
 
            op = getattr(self, '_git_stored_op', 'pull')
 
        return op
 

	
 
    def _handle_githooks(self, repo_name, action, baseui, environ):
 
        from rhodecode.lib.hooks import log_pull_action, log_push_action
 
        service = environ['QUERY_STRING'].split('=')
 
        if len(service) < 2:
 
            return
 

	
 
        from rhodecode.model.db import Repository
 
        _repo = Repository.get_by_repo_name(repo_name)
 
        _repo = _repo.scm_instance
 
        _repo._repo.ui = baseui
 

	
 
        push_hook = 'pretxnchangegroup.push_logger'
 
        pull_hook = 'preoutgoing.pull_logger'
 
        _hooks = dict(baseui.configitems('hooks')) or {}
 
        if action == 'push' and _hooks.get(push_hook):
 
            log_push_action(ui=baseui, repo=_repo._repo)
 
        elif action == 'pull' and _hooks.get(pull_hook):
 
            log_pull_action(ui=baseui, repo=_repo._repo)
 

	
 
    def __inject_extras(self, repo_path, baseui, extras={}):
 
        """
 
        Injects some extra params into baseui instance
 

	
 
        :param baseui: baseui instance
 
        :param extras: dict with extra params to put into baseui
 
        """
 

	
 
        # make our hgweb quiet so it doesn't print output
 
        baseui.setconfig('ui', 'quiet', 'true')
 

	
 
        #inject some additional parameters that will be available in ui
 
        #for hooks
 
        for k, v in extras.items():
 
            baseui.setconfig('rhodecode_extras', k, v)
rhodecode/lib/middleware/simplehg.py
Show inline comments
 
@@ -48,51 +48,49 @@ log = logging.getLogger(__name__)
 
def is_mercurial(environ):
 
    """
 
    Returns True if request's target is mercurial server - header
 
    ``HTTP_ACCEPT`` of such request would start with ``application/mercurial``.
 
    """
 
    http_accept = environ.get('HTTP_ACCEPT')
 
    path_info = environ['PATH_INFO']
 
    if http_accept and http_accept.startswith('application/mercurial'):
 
        ishg_path = True
 
    else:
 
        ishg_path = False
 

	
 
    log.debug('pathinfo: %s detected as HG %s' % (
 
        path_info, ishg_path)
 
    )
 
    return ishg_path
 

	
 

	
 
class SimpleHg(BaseVCSController):
 

	
 
    def _handle_request(self, environ, start_response):
 
        if not is_mercurial(environ):
 
            return self.application(environ, start_response)
 

	
 
        proxy_key = 'HTTP_X_REAL_IP'
 
        def_key = 'REMOTE_ADDR'
 
        ipaddr = environ.get(proxy_key, environ.get(def_key, '0.0.0.0'))
 
        ipaddr = self._get_ip_addr(environ)
 

	
 
        # skip passing error to error controller
 
        environ['pylons.status_code_redirect'] = True
 

	
 
        #======================================================================
 
        # EXTRACT REPOSITORY NAME FROM ENV
 
        #======================================================================
 
        try:
 
            repo_name = environ['REPO_NAME'] = self.__get_repository(environ)
 
            log.debug('Extracted repo name is %s' % repo_name)
 
        except:
 
            return HTTPInternalServerError()(environ, start_response)
 

	
 
        # quick check if that dir exists...
 
        if is_valid_repo(repo_name, self.basepath) is False:
 
            return HTTPNotFound()(environ, start_response)
 

	
 
        #======================================================================
 
        # GET ACTION PULL or PUSH
 
        #======================================================================
 
        action = self.__get_action(environ)
 

	
 
        #======================================================================
 
        # CHECK ANONYMOUS PERMISSION
 
@@ -134,49 +132,50 @@ class SimpleHg(BaseVCSController):
 
                # CHECK PERMISSIONS FOR THIS REQUEST USING GIVEN USERNAME
 
                #==============================================================
 
                if action in ['pull', 'push']:
 
                    try:
 
                        user = self.__get_user(username)
 
                        if user is None or not user.active:
 
                            return HTTPForbidden()(environ, start_response)
 
                        username = user.username
 
                    except:
 
                        log.error(traceback.format_exc())
 
                        return HTTPInternalServerError()(environ,
 
                                                         start_response)
 

	
 
                    #check permissions for this repository
 
                    perm = self._check_permission(action, user, repo_name)
 
                    if perm is not True:
 
                        return HTTPForbidden()(environ, start_response)
 

	
 
        # extras are injected into mercurial UI object and later available
 
        # in hg hooks executed by rhodecode
 
        extras = {
 
            'ip': ipaddr,
 
            'username': username,
 
            'action': action,
 
            'repository': repo_name
 
            'repository': repo_name,
 
            'scm': 'hg',
 
        }
 

	
 
        #======================================================================
 
        # MERCURIAL REQUEST HANDLING
 
        #======================================================================
 
        repo_path = os.path.join(safe_str(self.basepath), safe_str(repo_name))
 
        log.debug('Repository path is %s' % repo_path)
 

	
 
        baseui = make_ui('db')
 
        self.__inject_extras(repo_path, baseui, extras)
 

	
 
        try:
 
            # invalidate cache on push
 
            if action == 'push':
 
                self._invalidate_cache(repo_name)
 
            log.info('%s action on HG repo "%s"' % (action, repo_name))
 
            app = self.__make_app(repo_path, baseui, extras)
 
            return app(environ, start_response)
 
        except RepoError, e:
 
            if str(e).find('not found') != -1:
 
                return HTTPNotFound()(environ, start_response)
 
        except Exception:
 
            log.error(traceback.format_exc())
 
            return HTTPInternalServerError()(environ, start_response)
rhodecode/lib/utils.py
Show inline comments
 
@@ -129,49 +129,49 @@ def action_logger(user, action, repo, ip
 
    """
 

	
 
    if not sa:
 
        sa = meta.Session
 

	
 
    try:
 
        if hasattr(user, 'user_id'):
 
            user_obj = user
 
        elif isinstance(user, basestring):
 
            user_obj = User.get_by_username(user)
 
        else:
 
            raise Exception('You have to provide user object or username')
 

	
 
        if hasattr(repo, 'repo_id'):
 
            repo_obj = Repository.get(repo.repo_id)
 
            repo_name = repo_obj.repo_name
 
        elif  isinstance(repo, basestring):
 
            repo_name = repo.lstrip('/')
 
            repo_obj = Repository.get_by_repo_name(repo_name)
 
        else:
 
            raise Exception('You have to provide repository to action logger')
 

	
 
        user_log = UserLog()
 
        user_log.user_id = user_obj.user_id
 
        user_log.action = action
 
        user_log.action = safe_unicode(action)
 

	
 
        user_log.repository_id = repo_obj.repo_id
 
        user_log.repository_name = repo_name
 

	
 
        user_log.action_date = datetime.datetime.now()
 
        user_log.user_ip = ipaddr
 
        sa.add(user_log)
 

	
 
        log.info(
 
            'Adding user %s, action %s on %s' % (user_obj, action,
 
                                                 safe_unicode(repo))
 
        )
 
        if commit:
 
            sa.commit()
 
    except:
 
        log.error(traceback.format_exc())
 
        raise
 

	
 

	
 
def get_repos(path, recursive=False):
 
    """
 
    Scans given path for repos and return (name,(type,path)) tuple
 

	
 
    :param path: path to scan for repositories
rhodecode/lib/utils2.py
Show inline comments
 
@@ -371,35 +371,38 @@ def credentials_filter(uri):
 

	
 

	
 
def get_changeset_safe(repo, rev):
 
    """
 
    Safe version of get_changeset if this changeset doesn't exists for a
 
    repo it returns a Dummy one instead
 

	
 
    :param repo:
 
    :param rev:
 
    """
 
    from rhodecode.lib.vcs.backends.base import BaseRepository
 
    from rhodecode.lib.vcs.exceptions import RepositoryError
 
    if not isinstance(repo, BaseRepository):
 
        raise Exception('You must pass an Repository '
 
                        'object as first argument got %s', type(repo))
 

	
 
    try:
 
        cs = repo.get_changeset(rev)
 
    except RepositoryError:
 
        from rhodecode.lib.utils import EmptyChangeset
 
        cs = EmptyChangeset(requested_revision=rev)
 
    return cs
 

	
 

	
 
MENTIONS_REGEX = r'(?:^@|\s@)([a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]+)(?:\s{1})'
 

	
 

	
 
def extract_mentioned_users(s):
 
    """
 
    Returns unique usernames from given string s that have @mention
 

	
 
    :param s: string to get mentions
 
    """
 
    usrs = {}
 
    for username in re.findall(r'(?:^@|\s@)(\w+)', s):
 
        usrs[username] = username
 
    usrs = set()
 
    for username in re.findall(MENTIONS_REGEX, s):
 
        usrs.add(username)
 

	
 
    return sorted(usrs.keys())
 
    return sorted(list(usrs), key=lambda k: k.lower())
rhodecode/lib/vcs/backends/base.py
Show inline comments
 
@@ -888,24 +888,69 @@ class BaseInMemoryChangeset(object):
 

	
 
    def commit(self, message, author, parents=None, branch=None, date=None,
 
            **kwargs):
 
        """
 
        Performs in-memory commit (doesn't check workdir in any way) and
 
        returns newly created ``Changeset``. Updates repository's
 
        ``revisions``.
 

	
 
        .. note::
 
            While overriding this method each backend's should call
 
            ``self.check_integrity(parents)`` in the first place.
 

	
 
        :param message: message of the commit
 
        :param author: full username, i.e. "Joe Doe <joe.doe@example.com>"
 
        :param parents: single parent or sequence of parents from which commit
 
          would be derieved
 
        :param date: ``datetime.datetime`` instance. Defaults to
 
          ``datetime.datetime.now()``.
 
        :param branch: branch name, as string. If none given, default backend's
 
          branch would be used.
 

	
 
        :raises ``CommitError``: if any error occurs while committing
 
        """
 
        raise NotImplementedError
 

	
 

	
 
class EmptyChangeset(BaseChangeset):
 
    """
 
    An dummy empty changeset. It's possible to pass hash when creating
 
    an EmptyChangeset
 
    """
 

	
 
    def __init__(self, cs='0' * 40, repo=None, requested_revision=None,
 
                 alias=None):
 
        self._empty_cs = cs
 
        self.revision = -1
 
        self.message = ''
 
        self.author = ''
 
        self.date = ''
 
        self.repository = repo
 
        self.requested_revision = requested_revision
 
        self.alias = alias
 

	
 
    @LazyProperty
 
    def raw_id(self):
 
        """
 
        Returns raw string identifying this changeset, useful for web
 
        representation.
 
        """
 

	
 
        return self._empty_cs
 

	
 
    @LazyProperty
 
    def branch(self):
 
        from rhodecode.lib.vcs.backends import get_backend
 
        return get_backend(self.alias).DEFAULT_BRANCH_NAME
 

	
 
    @LazyProperty
 
    def short_id(self):
 
        return self.raw_id[:12]
 

	
 
    def get_file_changeset(self, path):
 
        return self
 

	
 
    def get_file_content(self, path):
 
        return u''
 

	
 
    def get_file_size(self, path):
 
        return 0
rhodecode/lib/vcs/backends/git/changeset.py
Show inline comments
 
import re
 
from itertools import chain
 
from dulwich import objects
 
from subprocess import Popen, PIPE
 
from rhodecode.lib.vcs.conf import settings
 
from rhodecode.lib.vcs.exceptions import RepositoryError
 
from rhodecode.lib.vcs.exceptions import ChangesetError
 
from rhodecode.lib.vcs.exceptions import NodeDoesNotExistError
 
from rhodecode.lib.vcs.exceptions import VCSError
 
from rhodecode.lib.vcs.exceptions import ChangesetDoesNotExistError
 
from rhodecode.lib.vcs.exceptions import ImproperArchiveTypeError
 
from rhodecode.lib.vcs.backends.base import BaseChangeset
 
from rhodecode.lib.vcs.nodes import FileNode, DirNode, NodeKind, RootNode, RemovedFileNode
 
from rhodecode.lib.vcs.nodes import FileNode, DirNode, NodeKind, RootNode, \
 
    RemovedFileNode, SubModuleNode
 
from rhodecode.lib.vcs.utils import safe_unicode
 
from rhodecode.lib.vcs.utils import date_fromtimestamp
 
from rhodecode.lib.vcs.utils.lazy import LazyProperty
 

	
 

	
 
class GitChangeset(BaseChangeset):
 
    """
 
    Represents state of the repository at single revision.
 
    """
 

	
 
    def __init__(self, repository, revision):
 
        self._stat_modes = {}
 
        self.repository = repository
 
        self.raw_id = revision
 
        self.revision = repository.revisions.index(revision)
 

	
 
        self.short_id = self.raw_id[:12]
 
        self.id = self.raw_id
 
        try:
 
            commit = self.repository._repo.get_object(self.raw_id)
 
        except KeyError:
 
            raise RepositoryError("Cannot get object with id %s" % self.raw_id)
 
        self._commit = commit
 
        self._tree_id = commit.tree
 
@@ -45,68 +46,54 @@ class GitChangeset(BaseChangeset):
 
        #self.branch = None
 
        self.tags = []
 
        #tree = self.repository.get_object(self._tree_id)
 
        self.nodes = {}
 
        self._paths = {}
 

	
 
    @LazyProperty
 
    def author(self):
 
        return safe_unicode(self._commit.committer)
 

	
 
    @LazyProperty
 
    def date(self):
 
        return date_fromtimestamp(self._commit.commit_time,
 
                                  self._commit.commit_timezone)
 

	
 
    @LazyProperty
 
    def status(self):
 
        """
 
        Returns modified, added, removed, deleted files for current changeset
 
        """
 
        return self.changed, self.added, self.removed
 

	
 
    @LazyProperty
 
    def branch(self):
 
        # TODO: Cache as we walk (id <-> branch name mapping)
 
        refs = self.repository._repo.get_refs()
 
        heads = {}
 
        for key, val in refs.items():
 
            for ref_key in ['refs/heads/', 'refs/remotes/origin/']:
 
                if key.startswith(ref_key):
 
                    n = key[len(ref_key):]
 
                    if n not in ['HEAD']:
 
                        heads[n] = val
 

	
 
        heads = self.repository._heads(reverse=False)
 

	
 
        for name, id in heads.iteritems():
 
            walker = self.repository._repo.object_store.get_graph_walker([id])
 
            while True:
 
                id_ = walker.next()
 
                if not id_:
 
                    break
 
                if id_ == self.id:
 
                    return safe_unicode(name)
 
        raise ChangesetError("This should not happen... Have you manually "
 
                             "change id of the changeset?")
 
        ref = heads.get(self.raw_id)
 
        if ref:
 
            return safe_unicode(ref)
 

	
 
    def _fix_path(self, path):
 
        """
 
        Paths are stored without trailing slash so we need to get rid off it if
 
        needed.
 
        """
 
        if path.endswith('/'):
 
            path = path.rstrip('/')
 
        return path
 

	
 
    def _get_id_for_path(self, path):
 

	
 
        # FIXME: Please, spare a couple of minutes and make those codes cleaner;
 
        if not path in self._paths:
 
            path = path.strip('/')
 
            # set root tree
 
            tree = self.repository._repo[self._commit.tree]
 
            if path == '':
 
                self._paths[''] = tree.id
 
                return tree.id
 
            splitted = path.split('/')
 
            dirs, name = splitted[:-1], splitted[-1]
 
            curdir = ''
 

	
 
@@ -123,49 +110,48 @@ class GitChangeset(BaseChangeset):
 
                if curdir:
 
                    curdir = '/'.join((curdir, dir))
 
                else:
 
                    curdir = dir
 
                dir_id = None
 
                for item, stat, id in tree.iteritems():
 
                    if dir == item:
 
                        dir_id = id
 
                if dir_id:
 
                    # Update tree
 
                    tree = self.repository._repo[dir_id]
 
                    if not isinstance(tree, objects.Tree):
 
                        raise ChangesetError('%s is not a directory' % curdir)
 
                else:
 
                    raise ChangesetError('%s have not been found' % curdir)
 

	
 
                # cache all items from the given traversed tree
 
                for item, stat, id in tree.iteritems():
 
                    if curdir:
 
                        name = '/'.join((curdir, item))
 
                    else:
 
                        name = item
 
                    self._paths[name] = id
 
                    self._stat_modes[name] = stat
 

	
 
            if not path in self._paths:
 
                raise NodeDoesNotExistError("There is no file nor directory "
 
                    "at the given path %r at revision %r"
 
                    % (path, self.short_id))
 
        return self._paths[path]
 

	
 
    def _get_kind(self, path):
 
        id = self._get_id_for_path(path)
 
        obj = self.repository._repo[id]
 
        if isinstance(obj, objects.Blob):
 
            return NodeKind.FILE
 
        elif isinstance(obj, objects.Tree):
 
            return NodeKind.DIR
 

	
 
    def _get_file_nodes(self):
 
        return chain(*(t[2] for t in self.walk()))
 

	
 
    @LazyProperty
 
    def parents(self):
 
        """
 
        Returns list of parents changesets.
 
        """
 
        return [self.repository.get_changeset(parent)
 
            for parent in self._commit.parents]
 
@@ -323,133 +309,148 @@ class GitChangeset(BaseChangeset):
 

	
 
        if stream is None:
 
            raise VCSError('You need to pass in a valid stream for filling'
 
                           ' with archival data')
 
        popen = Popen(cmd, stdout=PIPE, stderr=PIPE, shell=True,
 
            cwd=self.repository.path)
 

	
 
        buffer_size = 1024 * 8
 
        chunk = popen.stdout.read(buffer_size)
 
        while chunk:
 
            stream.write(chunk)
 
            chunk = popen.stdout.read(buffer_size)
 
        # Make sure all descriptors would be read
 
        popen.communicate()
 

	
 
    def get_nodes(self, path):
 
        if self._get_kind(path) != NodeKind.DIR:
 
            raise ChangesetError("Directory does not exist for revision %r at "
 
                " %r" % (self.revision, path))
 
        path = self._fix_path(path)
 
        id = self._get_id_for_path(path)
 
        tree = self.repository._repo[id]
 
        dirnodes = []
 
        filenodes = []
 
        als = self.repository.alias
 
        for name, stat, id in tree.iteritems():
 
            if objects.S_ISGITLINK(stat):
 
                dirnodes.append(SubModuleNode(name, url=None, changeset=id,
 
                                              alias=als))
 
                continue
 

	
 
            obj = self.repository._repo.get_object(id)
 
            if path != '':
 
                obj_path = '/'.join((path, name))
 
            else:
 
                obj_path = name
 
            if obj_path not in self._stat_modes:
 
                self._stat_modes[obj_path] = stat
 
            if isinstance(obj, objects.Tree):
 
                dirnodes.append(DirNode(obj_path, changeset=self))
 
            elif isinstance(obj, objects.Blob):
 
                filenodes.append(FileNode(obj_path, changeset=self, mode=stat))
 
            else:
 
                raise ChangesetError("Requested object should be Tree "
 
                                     "or Blob, is %r" % type(obj))
 
        nodes = dirnodes + filenodes
 
        for node in nodes:
 
            if not node.path in self.nodes:
 
                self.nodes[node.path] = node
 
        nodes.sort()
 
        return nodes
 

	
 
    def get_node(self, path):
 
        if isinstance(path, unicode):
 
            path = path.encode('utf-8')
 
        path = self._fix_path(path)
 
        if not path in self.nodes:
 
            try:
 
                id = self._get_id_for_path(path)
 
                id_ = self._get_id_for_path(path)
 
            except ChangesetError:
 
                raise NodeDoesNotExistError("Cannot find one of parents' "
 
                    "directories for a given path: %s" % path)
 
            obj = self.repository._repo.get_object(id)
 

	
 
            als = self.repository.alias
 
            _GL = lambda m: m and objects.S_ISGITLINK(m)
 
            if _GL(self._stat_modes.get(path)):
 
                node = SubModuleNode(path, url=None, changeset=id_, alias=als)
 
            else:
 
                obj = self.repository._repo.get_object(id_)
 

	
 
            if isinstance(obj, objects.Tree):
 
                if path == '':
 
                    node = RootNode(changeset=self)
 
                else:
 
                    node = DirNode(path, changeset=self)
 
                node._tree = obj
 
            elif isinstance(obj, objects.Blob):
 
                node = FileNode(path, changeset=self)
 
                node._blob = obj
 
            else:
 
                raise NodeDoesNotExistError("There is no file nor directory "
 
                    "at the given path %r at revision %r"
 
                    % (path, self.short_id))
 
            # cache node
 
            self.nodes[path] = node
 
        return self.nodes[path]
 

	
 
    @LazyProperty
 
    def affected_files(self):
 
        """
 
        Get's a fast accessible file changes for given changeset
 
        """
 

	
 
        return self.added + self.changed
 

	
 
    @LazyProperty
 
    def _diff_name_status(self):
 
        output = []
 
        for parent in self.parents:
 
            cmd = 'diff --name-status %s %s' % (parent.raw_id, self.raw_id)
 
            cmd = 'diff --name-status %s %s --encoding=utf8' % (parent.raw_id, self.raw_id)
 
            so, se = self.repository.run_git_command(cmd)
 
            output.append(so.strip())
 
        return '\n'.join(output)
 

	
 
    def _get_paths_for_status(self, status):
 
        """
 
        Returns sorted list of paths for given ``status``.
 

	
 
        :param status: one of: *added*, *modified* or *deleted*
 
        """
 
        paths = set()
 
        char = status[0].upper()
 
        for line in self._diff_name_status.splitlines():
 
            if not line:
 
                continue
 

	
 
            if line.startswith(char):
 
                splitted = line.split(char,1)
 
                if not len(splitted) == 2:
 
                    raise VCSError("Couldn't parse diff result:\n%s\n\n and "
 
                        "particularly that line: %s" % (self._diff_name_status,
 
                        line))
 
                paths.add(splitted[1].strip())
 
                _path = splitted[1].strip()
 
                paths.add(_path)
 
        return sorted(paths)
 

	
 
    @LazyProperty
 
    def added(self):
 
        """
 
        Returns list of added ``FileNode`` objects.
 
        """
 
        if not self.parents:
 
            return list(self._get_file_nodes())
 
        return [self.get_node(path) for path in self._get_paths_for_status('added')]
 

	
 
    @LazyProperty
 
    def changed(self):
 
        """
 
        Returns list of modified ``FileNode`` objects.
 
        """
 
        if not self.parents:
 
            return []
 
        return [self.get_node(path) for path in self._get_paths_for_status('modified')]
 

	
 
    @LazyProperty
 
    def removed(self):
 
        """
 
        Returns list of removed ``FileNode`` objects.
rhodecode/lib/vcs/backends/git/inmemory.py
Show inline comments
 
import time
 
import datetime
 
import posixpath
 
from dulwich import objects
 
from dulwich.repo import Repo
 
from rhodecode.lib.vcs.backends.base import BaseInMemoryChangeset
 
from rhodecode.lib.vcs.exceptions import RepositoryError
 
from rhodecode.lib.vcs.utils import safe_str
 

	
 

	
 
class GitInMemoryChangeset(BaseInMemoryChangeset):
 

	
 
    def commit(self, message, author, parents=None, branch=None, date=None,
 
            **kwargs):
 
        """
 
        Performs in-memory commit (doesn't check workdir in any way) and
 
        returns newly created ``Changeset``. Updates repository's
 
        ``revisions``.
 

	
 
        :param message: message of the commit
 
        :param author: full username, i.e. "Joe Doe <joe.doe@example.com>"
 
        :param parents: single parent or sequence of parents from which commit
 
          would be derieved
 
        :param date: ``datetime.datetime`` instance. Defaults to
 
          ``datetime.datetime.now()``.
 
        :param branch: branch name, as string. If none given, default backend's
 
          branch would be used.
 

	
 
        :raises ``CommitError``: if any error occurs while committing
 
        """
 
        self.check_integrity(parents)
 

	
 
@@ -99,51 +100,51 @@ class GitInMemoryChangeset(BaseInMemoryC
 
            trees = [tree]
 
            # Traverse deep into the forest...
 
            for path in paths:
 
                try:
 
                    obj = self.repository._repo[tree[path][1]]
 
                    if isinstance(obj, objects.Tree):
 
                        trees.append(obj)
 
                        tree = obj
 
                except KeyError:
 
                    break
 
            # Cut down the blob and all rotten trees on the way back...
 
            for path, tree in reversed(zip(paths, trees)):
 
                del tree[path]
 
                if tree:
 
                    # This tree still has elements - don't remove it or any
 
                    # of it's parents
 
                    break
 

	
 
        object_store.add_object(commit_tree)
 

	
 
        # Create commit
 
        commit = objects.Commit()
 
        commit.tree = commit_tree.id
 
        commit.parents = [p._commit.id for p in self.parents if p]
 
        commit.author = commit.committer = author
 
        commit.author = commit.committer = safe_str(author)
 
        commit.encoding = ENCODING
 
        commit.message = message + ' '
 
        commit.message = safe_str(message) + ' '
 

	
 
        # Compute date
 
        if date is None:
 
            date = time.time()
 
        elif isinstance(date, datetime.datetime):
 
            date = time.mktime(date.timetuple())
 

	
 
        author_time = kwargs.pop('author_time', date)
 
        commit.commit_time = int(date)
 
        commit.author_time = int(author_time)
 
        tz = time.timezone
 
        author_tz = kwargs.pop('author_timezone', tz)
 
        commit.commit_timezone = tz
 
        commit.author_timezone = author_tz
 

	
 
        object_store.add_object(commit)
 

	
 
        ref = 'refs/heads/%s' % branch
 
        repo.refs[ref] = commit.id
 
        repo.refs.set_symbolic_ref('HEAD', ref)
 

	
 
        # Update vcs repository object & recreate dulwich repo
 
        self.repository.revisions.append(commit.id)
 
        self.repository._repo = Repo(self.repository.path)
rhodecode/lib/vcs/backends/git/repository.py
Show inline comments
 
@@ -26,84 +26,98 @@ from rhodecode.lib.vcs.exceptions import
 
from rhodecode.lib.vcs.exceptions import TagDoesNotExistError
 
from rhodecode.lib.vcs.utils import safe_unicode, makedate, date_fromtimestamp
 
from rhodecode.lib.vcs.utils.lazy import LazyProperty
 
from rhodecode.lib.vcs.utils.ordered_dict import OrderedDict
 
from rhodecode.lib.vcs.utils.paths import abspath
 
from rhodecode.lib.vcs.utils.paths import get_user_home
 
from .workdir import GitWorkdir
 
from .changeset import GitChangeset
 
from .inmemory import GitInMemoryChangeset
 
from .config import ConfigFile
 

	
 

	
 
class GitRepository(BaseRepository):
 
    """
 
    Git repository backend.
 
    """
 
    DEFAULT_BRANCH_NAME = 'master'
 
    scm = 'git'
 

	
 
    def __init__(self, repo_path, create=False, src_url=None,
 
                 update_after_clone=False, bare=False):
 

	
 
        self.path = abspath(repo_path)
 
        self._repo = self._get_repo(create, src_url, update_after_clone, bare)
 
        #temporary set that to now at later we will move it to constructor
 
        baseui = None
 
        if baseui is None:
 
            from mercurial.ui import ui
 
            baseui = ui()
 
        # patch the instance of GitRepo with an "FAKE" ui object to add
 
        # compatibility layer with Mercurial
 
        setattr(self._repo, 'ui', baseui)
 

	
 
        try:
 
            self.head = self._repo.head()
 
        except KeyError:
 
            self.head = None
 

	
 
        self._config_files = [
 
            bare and abspath(self.path, 'config') or abspath(self.path, '.git',
 
                'config'),
 
            abspath(get_user_home(), '.gitconfig'),
 
        ]
 

	
 
    @LazyProperty
 
    def revisions(self):
 
        """
 
        Returns list of revisions' ids, in ascending order.  Being lazy
 
        attribute allows external tools to inject shas from cache.
 
        """
 
        return self._get_all_revisions()
 

	
 
    def run_git_command(self, cmd):
 
        """
 
        Runs given ``cmd`` as git command and returns tuple
 
        (returncode, stdout, stderr).
 

	
 
        .. note::
 
           This method exists only until log/blame functionality is implemented
 
           at Dulwich (see https://bugs.launchpad.net/bugs/645142). Parsing
 
           os command's output is road to hell...
 

	
 
        :param cmd: git command to be executed
 
        """
 
        #cmd = '(cd %s && git %s)' % (self.path, cmd)
 

	
 
        _copts = ['-c', 'core.quotepath=false', ]
 
        _str_cmd = False
 
        if isinstance(cmd, basestring):
 
            cmd = 'git %s' % cmd
 
        else:
 
            cmd = ['git'] + cmd
 
            cmd = [cmd]
 
            _str_cmd = True
 

	
 
        cmd = ['GIT_CONFIG_NOGLOBAL=1', 'git'] + _copts + cmd
 
        if _str_cmd:
 
            cmd = ' '.join(cmd)
 
        try:
 
            opts = dict(
 
                shell=isinstance(cmd, basestring),
 
                stdout=PIPE,
 
                stderr=PIPE)
 
            if os.path.isdir(self.path):
 
                opts['cwd'] = self.path
 
            p = Popen(cmd, **opts)
 
        except OSError, err:
 
            raise RepositoryError("Couldn't run git command (%s).\n"
 
                "Original error was:%s" % (cmd, err))
 
        so, se = p.communicate()
 
        if not se.startswith("fatal: bad default revision 'HEAD'") and \
 
            p.returncode != 0:
 
            raise RepositoryError("Couldn't run git command (%s).\n"
 
                "stderr:\n%s" % (cmd, se))
 
        return so, se
 

	
 
    def _check_url(self, url):
 
        """
 
        Functon will check given url and try to verify if it's a valid
 
        link. Sometimes it may happened that mercurial will issue basic
 
        auth request that can cause whole API to hang when used from python
 
        or other external calls.
 
@@ -224,48 +238,61 @@ class GitRepository(BaseRepository):
 
    def description(self):
 
        undefined_description = u'unknown'
 
        description_path = os.path.join(self.path, '.git', 'description')
 
        if os.path.isfile(description_path):
 
            return safe_unicode(open(description_path).read())
 
        else:
 
            return undefined_description
 

	
 
    @LazyProperty
 
    def contact(self):
 
        undefined_contact = u'Unknown'
 
        return undefined_contact
 

	
 
    @property
 
    def branches(self):
 
        if not self.revisions:
 
            return {}
 
        refs = self._repo.refs.as_dict()
 
        sortkey = lambda ctx: ctx[0]
 
        _branches = [('/'.join(ref.split('/')[2:]), head)
 
            for ref, head in refs.items()
 
            if ref.startswith('refs/heads/') and not ref.endswith('/HEAD')]
 
        return OrderedDict(sorted(_branches, key=sortkey, reverse=False))
 

	
 
    def _heads(self, reverse=False):
 
        refs = self._repo.get_refs()
 
        heads = {}
 

	
 
        for key, val in refs.items():
 
            for ref_key in ['refs/heads/', 'refs/remotes/origin/']:
 
                if key.startswith(ref_key):
 
                    n = key[len(ref_key):]
 
                    if n not in ['HEAD']:
 
                        heads[n] = val
 

	
 
        return heads if reverse else dict((y,x) for x,y in heads.iteritems())
 

	
 
    def _get_tags(self):
 
        if not self.revisions:
 
            return {}
 
        sortkey = lambda ctx: ctx[0]
 
        _tags = [('/'.join(ref.split('/')[2:]), head) for ref, head in
 
            self._repo.get_refs().items() if ref.startswith('refs/tags/')]
 
        return OrderedDict(sorted(_tags, key=sortkey, reverse=True))
 

	
 
    @LazyProperty
 
    def tags(self):
 
        return self._get_tags()
 

	
 
    def tag(self, name, user, revision=None, message=None, date=None,
 
            **kwargs):
 
        """
 
        Creates and returns a tag for the given ``revision``.
 

	
 
        :param name: name for new tag
 
        :param user: full username, i.e.: "Joe Doe <joe.doe@example.com>"
 
        :param revision: changeset id for which new tag would be created
 
        :param message: message of the tag's commit
 
        :param date: date of tag's commit
 

	
 
        :raises TagAlreadyExistError: if tag with same name already exists
 
@@ -432,48 +459,60 @@ class GitRepository(BaseRepository):
 
        Returns ``GitInMemoryChangeset`` object for this repository.
 
        """
 
        return GitInMemoryChangeset(self)
 

	
 
    def clone(self, url, update_after_clone=True, bare=False):
 
        """
 
        Tries to clone changes from external location.
 

	
 
        :param update_after_clone: If set to ``False``, git won't checkout
 
          working directory
 
        :param bare: If set to ``True``, repository would be cloned into
 
          *bare* git repository (no working directory at all).
 
        """
 
        url = self._get_url(url)
 
        cmd = ['clone']
 
        if bare:
 
            cmd.append('--bare')
 
        elif not update_after_clone:
 
            cmd.append('--no-checkout')
 
        cmd += ['--', '"%s"' % url, '"%s"' % self.path]
 
        cmd = ' '.join(cmd)
 
        # If error occurs run_git_command raises RepositoryError already
 
        self.run_git_command(cmd)
 

	
 
    def pull(self, url):
 
        """
 
        Tries to pull changes from external location.
 
        """
 
        url = self._get_url(url)
 
        cmd = ['pull']
 
        cmd.append("--ff-only")
 
        cmd.append(url)
 
        cmd = ' '.join(cmd)
 
        # If error occurs run_git_command raises RepositoryError already
 
        self.run_git_command(cmd)
 

	
 
    @LazyProperty
 
    def workdir(self):
 
        """
 
        Returns ``Workdir`` instance for this repository.
 
        """
 
        return GitWorkdir(self)
 

	
 
    def get_config_value(self, section, name, config_file=None):
 
        """
 
        Returns configuration value for a given [``section``] and ``name``.
 

	
 
        :param section: Section we want to retrieve value from
 
        :param name: Name of configuration we want to retrieve
 
        :param config_file: A path to file which should be used to retrieve
 
          configuration from (might also be a list of file paths)
 
        """
 
        if config_file is None:
 
            config_file = []
 
        elif isinstance(config_file, basestring):
 
            config_file = [config_file]
 

	
 
        def gen_configs():
 
            for path in config_file + self._config_files:
 
                try:
rhodecode/lib/vcs/backends/hg/changeset.py
Show inline comments
 
import os
 
import posixpath
 

	
 
from rhodecode.lib.vcs.backends.base import BaseChangeset
 
from rhodecode.lib.vcs.conf import settings
 
from rhodecode.lib.vcs.exceptions import  ChangesetDoesNotExistError, \
 
    ChangesetError, ImproperArchiveTypeError, NodeDoesNotExistError, VCSError
 
from rhodecode.lib.vcs.nodes import AddedFileNodesGenerator, ChangedFileNodesGenerator, \
 
    DirNode, FileNode, NodeKind, RemovedFileNodesGenerator, RootNode
 
from rhodecode.lib.vcs.nodes import AddedFileNodesGenerator, \
 
    ChangedFileNodesGenerator, DirNode, FileNode, NodeKind, \
 
    RemovedFileNodesGenerator, RootNode, SubModuleNode
 

	
 
from rhodecode.lib.vcs.utils import safe_str, safe_unicode, date_fromtimestamp
 
from rhodecode.lib.vcs.utils.lazy import LazyProperty
 
from rhodecode.lib.vcs.utils.paths import get_dirs_for_path
 

	
 
from ...utils.hgcompat import archival, hex
 

	
 

	
 
class MercurialChangeset(BaseChangeset):
 
    """
 
    Represents state of the repository at the single revision.
 
    """
 

	
 
    def __init__(self, repository, revision):
 
        self.repository = repository
 
        self.raw_id = revision
 
        self._ctx = repository._repo[revision]
 
        self.revision = self._ctx._rev
 
        self.nodes = {}
 

	
 
    @LazyProperty
 
    def tags(self):
 
        return map(safe_unicode, self._ctx.tags())
 

	
 
    @LazyProperty
 
    def branch(self):
 
        return  safe_unicode(self._ctx.branch())
 

	
 
    @LazyProperty
 
    def bookmarks(self):
 
        return map(safe_unicode, self._ctx.bookmarks())
 

	
 
    @LazyProperty
 
    def message(self):
 
        return safe_unicode(self._ctx.description())
 

	
 
    @LazyProperty
 
    def author(self):
 
        return safe_unicode(self._ctx.user())
 

	
 
    @LazyProperty
 
    def date(self):
 
        return date_fromtimestamp(*self._ctx.date())
 

	
 
    @LazyProperty
 
    def status(self):
 
        """
 
        Returns modified, added, removed, deleted files for current changeset
 
        """
 
        return self.repository._repo.status(self._ctx.p1().node(),
 
                                            self._ctx.node())
 

	
 
    @LazyProperty
 
    def _file_paths(self):
 
        return list(self._ctx)
 

	
 
    @LazyProperty
 
@@ -138,48 +143,55 @@ class MercurialChangeset(BaseChangeset):
 
        from unicode to str
 
        """
 
        if path.endswith('/'):
 
            path = path.rstrip('/')
 

	
 
        return safe_str(path)
 

	
 
    def _get_kind(self, path):
 
        path = self._fix_path(path)
 
        if path in self._file_paths:
 
            return NodeKind.FILE
 
        elif path in self._dir_paths:
 
            return NodeKind.DIR
 
        else:
 
            raise ChangesetError("Node does not exist at the given path %r"
 
                % (path))
 

	
 
    def _get_filectx(self, path):
 
        path = self._fix_path(path)
 
        if self._get_kind(path) != NodeKind.FILE:
 
            raise ChangesetError("File does not exist for revision %r at "
 
                " %r" % (self.revision, path))
 
        return self._ctx.filectx(path)
 

	
 
    def _extract_submodules(self):
 
        """
 
        returns a dictionary with submodule information from substate file
 
        of hg repository
 
        """
 
        return self._ctx.substate
 

	
 
    def get_file_mode(self, path):
 
        """
 
        Returns stat mode of the file at the given ``path``.
 
        """
 
        fctx = self._get_filectx(path)
 
        if 'x' in fctx.flags():
 
            return 0100755
 
        else:
 
            return 0100644
 

	
 
    def get_file_content(self, path):
 
        """
 
        Returns content of the file at given ``path``.
 
        """
 
        fctx = self._get_filectx(path)
 
        return fctx.data()
 

	
 
    def get_file_size(self, path):
 
        """
 
        Returns size of the file at given ``path``.
 
        """
 
        fctx = self._get_filectx(path)
 
        return fctx.size()
 

	
 
@@ -250,59 +262,69 @@ class MercurialChangeset(BaseChangeset):
 

	
 
        archival.archive(self.repository._repo, stream, self.raw_id,
 
                         kind, prefix=prefix, subrepos=subrepos)
 

	
 
        #stream.close()
 

	
 
        if stream.closed and hasattr(stream, 'name'):
 
            stream = open(stream.name, 'rb')
 
        elif hasattr(stream, 'mode') and 'r' not in stream.mode:
 
            stream = open(stream.name, 'rb')
 
        else:
 
            stream.seek(0)
 

	
 
    def get_nodes(self, path):
 
        """
 
        Returns combined ``DirNode`` and ``FileNode`` objects list representing
 
        state of changeset at the given ``path``. If node at the given ``path``
 
        is not instance of ``DirNode``, ChangesetError would be raised.
 
        """
 

	
 
        if self._get_kind(path) != NodeKind.DIR:
 
            raise ChangesetError("Directory does not exist for revision %r at "
 
                " %r" % (self.revision, path))
 
        path = self._fix_path(path)
 

	
 
        filenodes = [FileNode(f, changeset=self) for f in self._file_paths
 
            if os.path.dirname(f) == path]
 
        dirs = path == '' and '' or [d for d in self._dir_paths
 
            if d and posixpath.dirname(d) == path]
 
        dirnodes = [DirNode(d, changeset=self) for d in dirs
 
            if os.path.dirname(d) == path]
 

	
 
        als = self.repository.alias
 
        for k, vals in self._extract_submodules().iteritems():
 
            #vals = url,rev,type
 
            loc = vals[0]
 
            cs = vals[1]
 
            dirnodes.append(SubModuleNode(k, url=loc, changeset=cs,
 
                                          alias=als))
 
        nodes = dirnodes + filenodes
 
        # cache nodes
 
        for node in nodes:
 
            self.nodes[node.path] = node
 
        nodes.sort()
 

	
 
        return nodes
 

	
 
    def get_node(self, path):
 
        """
 
        Returns ``Node`` object from the given ``path``. If there is no node at
 
        the given ``path``, ``ChangesetError`` would be raised.
 
        """
 

	
 
        path = self._fix_path(path)
 

	
 
        if not path in self.nodes:
 
            if path in self._file_paths:
 
                node = FileNode(path, changeset=self)
 
            elif path in self._dir_paths or path in self._dir_paths:
 
                if path == '':
 
                    node = RootNode(changeset=self)
 
                else:
 
                    node = DirNode(path, changeset=self)
 
            else:
 
                raise NodeDoesNotExistError("There is no file nor directory "
 
                    "at the given path: %r at revision %r"
 
                    % (path, self.short_id))
 
            # cache node
 
            self.nodes[path] = node
rhodecode/lib/vcs/backends/hg/inmemory.py
Show inline comments
 
import datetime
 
import errno
 

	
 
from rhodecode.lib.vcs.backends.base import BaseInMemoryChangeset
 
from rhodecode.lib.vcs.exceptions import RepositoryError
 

	
 
from ...utils.hgcompat import memfilectx, memctx, hex
 
from ...utils.hgcompat import memfilectx, memctx, hex, tolocal
 

	
 

	
 
class MercurialInMemoryChangeset(BaseInMemoryChangeset):
 

	
 
    def commit(self, message, author, parents=None, branch=None, date=None,
 
            **kwargs):
 
        """
 
        Performs in-memory commit (doesn't check workdir in any way) and
 
        returns newly created ``Changeset``. Updates repository's
 
        ``revisions``.
 

	
 
        :param message: message of the commit
 
        :param author: full username, i.e. "Joe Doe <joe.doe@example.com>"
 
        :param parents: single parent or sequence of parents from which commit
 
          would be derieved
 
        :param date: ``datetime.datetime`` instance. Defaults to
 
          ``datetime.datetime.now()``.
 
        :param branch: branch name, as string. If none given, default backend's
 
          branch would be used.
 

	
 
        :raises ``CommitError``: if any error occurs while committing
 
        """
 
        self.check_integrity(parents)
 

	
 
        from .repository import MercurialRepository
 
        if not isinstance(message, str) or not isinstance(author, str):
 
        if not isinstance(message, unicode) or not isinstance(author, unicode):
 
            raise RepositoryError('Given message and author needs to be '
 
                                  'an <str> instance')
 
                                  'an <unicode> instance')
 

	
 
        if branch is None:
 
            branch = MercurialRepository.DEFAULT_BRANCH_NAME
 
        kwargs['branch'] = branch
 

	
 
        def filectxfn(_repo, memctx, path):
 
            """
 
            Marks given path as added/changed/removed in a given _repo. This is
 
            for internal mercurial commit function.
 
            """
 

	
 
            # check if this path is removed
 
            if path in (node.path for node in self.removed):
 
                # Raising exception is a way to mark node for removal
 
                raise IOError(errno.ENOENT, '%s is deleted' % path)
 

	
 
            # check if this path is added
 
            for node in self.added:
 
                if node.path == path:
 
                    return memfilectx(path=node.path,
 
                        data=(node.content.encode('utf8')
 
                              if not node.is_binary else node.content),
 
                        islink=False,
 
                        isexec=node.is_executable,
 
@@ -68,43 +68,45 @@ class MercurialInMemoryChangeset(BaseInM
 
                        islink=False,
 
                        isexec=node.is_executable,
 
                        copied=False)
 

	
 
            raise RepositoryError("Given path haven't been marked as added,"
 
                "changed or removed (%s)" % path)
 

	
 
        parents = [None, None]
 
        for i, parent in enumerate(self.parents):
 
            if parent is not None:
 
                parents[i] = parent._ctx.node()
 

	
 
        if date and isinstance(date, datetime.datetime):
 
            date = date.ctime()
 

	
 
        commit_ctx = memctx(repo=self.repository._repo,
 
            parents=parents,
 
            text='',
 
            files=self.get_paths(),
 
            filectxfn=filectxfn,
 
            user=author,
 
            date=date,
 
            extra=kwargs)
 

	
 
        loc = lambda u: tolocal(u.encode('utf-8'))
 

	
 
        # injecting given _repo params
 
        commit_ctx._text = message
 
        commit_ctx._user = author
 
        commit_ctx._text = loc(message)
 
        commit_ctx._user = loc(author)
 
        commit_ctx._date = date
 

	
 
        # TODO: Catch exceptions!
 
        n = self.repository._repo.commitctx(commit_ctx)
 
        # Returns mercurial node
 
        self._commit_ctx = commit_ctx  # For reference
 
        # Update vcs repository object & recreate mercurial _repo
 
        # new_ctx = self.repository._repo[node]
 
        # new_tip = self.repository.get_changeset(new_ctx.hex())
 
        new_id = hex(n)
 
        self.repository.revisions.append(new_id)
 
        self._repo = self.repository._get_repo(create=False)
 
        self.repository.branches = self.repository._get_branches()
 
        tip = self.repository.get_changeset()
 
        self.reset()
 
        return tip
rhodecode/lib/vcs/nodes.py
Show inline comments
 
# -*- coding: utf-8 -*-
 
"""
 
    vcs.nodes
 
    ~~~~~~~~~
 

	
 
    Module holding everything related to vcs nodes.
 

	
 
    :created_on: Apr 8, 2010
 
    :copyright: (c) 2010-2011 by Marcin Kuzminski, Lukasz Balcerzak.
 
"""
 
import os
 
import stat
 
import posixpath
 
import mimetypes
 

	
 
from pygments import lexers
 

	
 
from rhodecode.lib.vcs.utils.lazy import LazyProperty
 
from rhodecode.lib.vcs.utils import safe_unicode
 
from rhodecode.lib.vcs.utils import safe_unicode, safe_str
 
from rhodecode.lib.vcs.exceptions import NodeError
 
from rhodecode.lib.vcs.exceptions import RemovedFileNodeError
 

	
 
from pygments import lexers
 
from rhodecode.lib.vcs.backends.base import EmptyChangeset
 

	
 

	
 
class NodeKind:
 
    SUBMODULE = -1
 
    DIR = 1
 
    FILE = 2
 

	
 

	
 
class NodeState:
 
    ADDED = u'added'
 
    CHANGED = u'changed'
 
    NOT_CHANGED = u'not changed'
 
    REMOVED = u'removed'
 

	
 

	
 
class NodeGeneratorBase(object):
 
    """
 
    Base class for removed added and changed filenodes, it's a lazy generator
 
    class that will create filenodes only on iteration or call
 

	
 
    The len method doesn't need to create filenodes at all
 
    """
 

	
 
    def __init__(self, current_paths, cs):
 
        self.cs = cs
 
        self.current_paths = current_paths
 

	
 
    def __call__(self):
 
@@ -99,48 +102,52 @@ class Node(object):
 

	
 
    def __init__(self, path, kind):
 
        if path.startswith('/'):
 
            raise NodeError("Cannot initialize Node objects with slash at "
 
                "the beginning as only relative paths are supported")
 
        self.path = path.rstrip('/')
 
        if path == '' and kind != NodeKind.DIR:
 
            raise NodeError("Only DirNode and its subclasses may be "
 
                            "initialized with empty path")
 
        self.kind = kind
 
        #self.dirs, self.files = [], []
 
        if self.is_root() and not self.is_dir():
 
            raise NodeError("Root node cannot be FILE kind")
 

	
 
    @LazyProperty
 
    def parent(self):
 
        parent_path = self.get_parent_path()
 
        if parent_path:
 
            if self.changeset:
 
                return self.changeset.get_node(parent_path)
 
            return DirNode(parent_path)
 
        return None
 

	
 
    @LazyProperty
 
    def unicode_path(self):
 
        return safe_unicode(self.path)
 

	
 
    @LazyProperty
 
    def name(self):
 
        """
 
        Returns name of the node so if its path
 
        then only last part is returned.
 
        """
 
        return safe_unicode(self.path.rstrip('/').split('/')[-1])
 

	
 
    def _get_kind(self):
 
        return self._kind
 

	
 
    def _set_kind(self, kind):
 
        if hasattr(self, '_kind'):
 
            raise NodeError("Cannot change node's kind")
 
        else:
 
            self._kind = kind
 
            # Post setter check (path's trailing slash)
 
            if self.path.endswith('/'):
 
                raise NodeError("Node's path cannot end with slash")
 

	
 
    kind = property(_get_kind, _set_kind)
 

	
 
    def __cmp__(self, other):
 
        """
 
        Comparator using name of the node, needed for quick list sorting.
 
@@ -184,48 +191,55 @@ class Node(object):
 
        if self.is_root():
 
            return ''
 
        return posixpath.dirname(self.path.rstrip('/')) + '/'
 

	
 
    def is_file(self):
 
        """
 
        Returns ``True`` if node's kind is ``NodeKind.FILE``, ``False``
 
        otherwise.
 
        """
 
        return self.kind == NodeKind.FILE
 

	
 
    def is_dir(self):
 
        """
 
        Returns ``True`` if node's kind is ``NodeKind.DIR``, ``False``
 
        otherwise.
 
        """
 
        return self.kind == NodeKind.DIR
 

	
 
    def is_root(self):
 
        """
 
        Returns ``True`` if node is a root node and ``False`` otherwise.
 
        """
 
        return self.kind == NodeKind.DIR and self.path == ''
 

	
 
    def is_submodule(self):
 
        """
 
        Returns ``True`` if node's kind is ``NodeKind.SUBMODULE``, ``False``
 
        otherwise.
 
        """
 
        return self.kind == NodeKind.SUBMODULE
 

	
 
    @LazyProperty
 
    def added(self):
 
        return self.state is NodeState.ADDED
 

	
 
    @LazyProperty
 
    def changed(self):
 
        return self.state is NodeState.CHANGED
 

	
 
    @LazyProperty
 
    def not_changed(self):
 
        return self.state is NodeState.NOT_CHANGED
 

	
 
    @LazyProperty
 
    def removed(self):
 
        return self.state is NodeState.REMOVED
 

	
 

	
 
class FileNode(Node):
 
    """
 
    Class representing file nodes.
 

	
 
    :attribute: path: path to the node, relative to repostiory's root
 
    :attribute: content: if given arbitrary sets content of the file
 
    :attribute: changeset: if given, first time content is accessed, callback
 
@@ -536,24 +550,62 @@ class DirNode(Node):
 
    def size(self):
 
        size = 0
 
        for root, dirs, files in self.changeset.walk(self.path):
 
            for f in files:
 
                size += f.size
 

	
 
        return size
 

	
 
    def __repr__(self):
 
        return '<%s %r @ %s>' % (self.__class__.__name__, self.path,
 
                                 self.changeset.short_id)
 

	
 

	
 
class RootNode(DirNode):
 
    """
 
    DirNode being the root node of the repository.
 
    """
 

	
 
    def __init__(self, nodes=(), changeset=None):
 
        super(RootNode, self).__init__(path='', nodes=nodes,
 
            changeset=changeset)
 

	
 
    def __repr__(self):
 
        return '<%s>' % self.__class__.__name__
 

	
 

	
 
class SubModuleNode(Node):
 
    """
 
    represents a SubModule of Git or SubRepo of Mercurial
 
    """
 
    is_binary = False
 
    size = 0
 

	
 
    def __init__(self, name, url=None, changeset=None, alias=None):
 
        self.path = name
 
        self.kind = NodeKind.SUBMODULE
 
        self.alias = alias
 
        # we have to use emptyChangeset here since this can point to svn/git/hg
 
        # submodules we cannot get from repository
 
        self.changeset = EmptyChangeset(str(changeset), alias=alias)
 
        self.url = url or self._extract_submodule_url()
 

	
 
    def __repr__(self):
 
        return '<%s %r @ %s>' % (self.__class__.__name__, self.path,
 
                                 self.changeset.short_id)
 

	
 
    def _extract_submodule_url(self):
 
        if self.alias == 'git':
 
            #TODO: find a way to parse gits submodule file and extract the
 
            # linking URL
 
            return self.path
 
        if self.alias == 'hg':
 
            return self.path
 

	
 
    @LazyProperty
 
    def name(self):
 
        """
 
        Returns name of the node so if its path
 
        then only last part is returned.
 
        """
 
        org = safe_unicode(self.path.rstrip('/').split('/')[-1])
 
        return u'%s @ %s' % (org, self.changeset.short_id)
rhodecode/lib/vcs/utils/hgcompat.py
Show inline comments
 
"""Mercurial libs compatibility
 
"""
 
Mercurial libs compatibility
 
"""
 

	
 
"""
 
from mercurial import archival, merge as hg_merge, patch, ui
 
from mercurial.commands import clone, nullid, pull
 
from mercurial.context import memctx, memfilectx
 
from mercurial.error import RepoError, RepoLookupError, Abort
 
from mercurial.hgweb.common import get_contact
 
from mercurial.localrepo import localrepository
 
from mercurial.match import match
 
from mercurial.mdiff import diffopts
 
from mercurial.node import hex
 
from mercurial.encoding import tolocal
rhodecode/model/comment.py
Show inline comments
 
@@ -8,141 +8,146 @@
 
    :created_on: Nov 11, 2011
 
    :author: marcink
 
    :copyright: (C) 2011-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.i18n.translation import _
 
from sqlalchemy.util.compat import defaultdict
 

	
 
from rhodecode.lib.utils2 import extract_mentioned_users
 
from rhodecode.lib.utils2 import extract_mentioned_users, safe_unicode
 
from rhodecode.lib import helpers as h
 
from rhodecode.model import BaseModel
 
from rhodecode.model.db import ChangesetComment, User, Repository, Notification
 
from rhodecode.model.notification import NotificationModel
 

	
 
log = logging.getLogger(__name__)
 

	
 

	
 
class ChangesetCommentsModel(BaseModel):
 

	
 
    def __get_changeset_comment(self, changeset_comment):
 
        return self._get_instance(ChangesetComment, changeset_comment)
 

	
 
    def _extract_mentions(self, s):
 
        user_objects = []
 
        for username in extract_mentioned_users(s):
 
            user_obj = User.get_by_username(username, case_insensitive=True)
 
            if user_obj:
 
                user_objects.append(user_obj)
 
        return user_objects
 

	
 
    def create(self, text, repo_id, user_id, revision, f_path=None,
 
               line_no=None):
 
        """
 
        Creates new comment for changeset
 

	
 
        :param text:
 
        :param repo_id:
 
        :param user_id:
 
        :param revision:
 
        :param f_path:
 
        :param line_no:
 
        """
 

	
 
        if text:
 
            repo = Repository.get(repo_id)
 
            cs = repo.scm_instance.get_changeset(revision)
 
            desc = cs.message
 
            desc = "%s - %s" % (cs.short_id, h.shorter(cs.message, 256))
 
            author_email = cs.author_email
 
            comment = ChangesetComment()
 
            comment.repo = repo
 
            comment.user_id = user_id
 
            comment.revision = revision
 
            comment.text = text
 
            comment.f_path = f_path
 
            comment.line_no = line_no
 

	
 
            self.sa.add(comment)
 
            self.sa.flush()
 
            # make notification
 
            line = ''
 
            if line_no:
 
                line = _('on line %s') % line_no
 
            subj = h.link_to('Re commit: %(commit_desc)s %(line)s' % \
 
            subj = safe_unicode(
 
                h.link_to('Re commit: %(commit_desc)s %(line)s' % \
 
                                    {'commit_desc': desc, 'line': line},
 
                             h.url('changeset_home', repo_name=repo.repo_name,
 
                                   revision=revision,
 
                                   anchor='comment-%s' % comment.comment_id,
 
                                   qualified=True,
 
                                   )
 
                             )
 
            )
 

	
 
            body = text
 

	
 
            # get the current participants of this changeset
 
            recipients = ChangesetComment.get_users(revision=revision)
 

	
 
            # add changeset author if it's in rhodecode system
 
            recipients += [User.get_by_email(author_email)]
 

	
 
            NotificationModel().create(
 
              created_by=user_id, subject=subj, body=body,
 
              recipients=recipients, type_=Notification.TYPE_CHANGESET_COMMENT
 
            )
 

	
 
            mention_recipients = set(self._extract_mentions(body))\
 
                                    .difference(recipients)
 
            if mention_recipients:
 
                subj = _('[Mention]') + ' ' + subj
 
                NotificationModel().create(
 
                    created_by=user_id, subject=subj, body=body,
 
                    recipients=mention_recipients,
 
                    type_=Notification.TYPE_CHANGESET_COMMENT
 
                )
 

	
 
            return comment
 

	
 
    def delete(self, comment):
 
        """
 
        Deletes given comment
 

	
 
        :param comment_id:
 
        """
 
        comment = self.__get_changeset_comment(comment)
 
        self.sa.delete(comment)
 

	
 
        return comment
 

	
 
    def get_comments(self, repo_id, revision):
 
        return ChangesetComment.query()\
 
                .filter(ChangesetComment.repo_id == repo_id)\
 
                .filter(ChangesetComment.revision == revision)\
 
                .filter(ChangesetComment.line_no == None)\
 
                .filter(ChangesetComment.f_path == None).all()
 

	
 
    def get_inline_comments(self, repo_id, revision):
 
        comments = self.sa.query(ChangesetComment)\
 
            .filter(ChangesetComment.repo_id == repo_id)\
 
            .filter(ChangesetComment.revision == revision)\
 
            .filter(ChangesetComment.line_no != None)\
 
            .filter(ChangesetComment.f_path != None).all()
 
            .filter(ChangesetComment.f_path != None)\
 
            .order_by(ChangesetComment.comment_id.asc())\
 
            .all()
 

	
 
        paths = defaultdict(lambda: defaultdict(list))
 

	
 
        for co in comments:
 
            paths[co.f_path][co.line_no].append(co)
 
        return paths.items()
rhodecode/model/db.py
Show inline comments
 
@@ -624,49 +624,49 @@ class Repository(Base, BaseModel):
 
        for ui_ in hg_ui:
 
            if ui_.ui_active:
 
                log.debug('settings ui from db[%s]%s:%s', ui_.ui_section,
 
                          ui_.ui_key, ui_.ui_value)
 
                baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
 

	
 
        return baseui
 

	
 
    @classmethod
 
    def is_valid(cls, repo_name):
 
        """
 
        returns True if given repo name is a valid filesystem repository
 

	
 
        :param cls:
 
        :param repo_name:
 
        """
 
        from rhodecode.lib.utils import is_valid_repo
 

	
 
        return is_valid_repo(repo_name, cls.base_path())
 

	
 
    #==========================================================================
 
    # SCM PROPERTIES
 
    #==========================================================================
 

	
 
    def get_changeset(self, rev):
 
    def get_changeset(self, rev=None):
 
        return get_changeset_safe(self.scm_instance, rev)
 

	
 
    @property
 
    def tip(self):
 
        return self.get_changeset('tip')
 

	
 
    @property
 
    def author(self):
 
        return self.tip.author
 

	
 
    @property
 
    def last_change(self):
 
        return self.scm_instance.last_change
 

	
 
    def comments(self, revisions=None):
 
        """
 
        Returns comments for this repository grouped by revisions
 

	
 
        :param revisions: filter query by revisions only
 
        """
 
        cmts = ChangesetComment.query()\
 
            .filter(ChangesetComment.repo == self)
 
        if revisions:
 
            cmts = cmts.filter(ChangesetComment.revision.in_(revisions))
 
@@ -1212,49 +1212,50 @@ class Notification(Base, BaseModel):
 
    __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'
 

	
 
    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()]
 
                .filter(UserNotification.notification == self)\
 
                .order_by(UserNotification.user).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)
rhodecode/model/forms.py
Show inline comments
 
@@ -530,75 +530,75 @@ class AttrLoginValidator(formencode.vali
 
                _("The LDAP Login attribute of the CN must be specified - "
 
                  "this is the name of the attribute that is equivalent "
 
                  "to 'username'"), value, state
 
            )
 

	
 
        return value
 

	
 

	
 
#==============================================================================
 
# FORMS
 
#==============================================================================
 
class LoginForm(formencode.Schema):
 
    allow_extra_fields = True
 
    filter_extra_fields = True
 
    username = UnicodeString(
 
        strip=True,
 
        min=1,
 
        not_empty=True,
 
        messages={
 
           'empty': _('Please enter a login'),
 
           'tooShort': _('Enter a value %(min)i characters long or more')}
 
    )
 

	
 
    password = UnicodeString(
 
        strip=True,
 
        strip=False,
 
        min=3,
 
        not_empty=True,
 
        messages={
 
            'empty': _('Please enter a password'),
 
            'tooShort': _('Enter %(min)i characters or more')}
 
    )
 

	
 
    remember = StringBoolean(if_missing=False)
 

	
 
    chained_validators = [ValidAuth]
 

	
 

	
 
def UserForm(edit=False, old_data={}):
 
    class _UserForm(formencode.Schema):
 
        allow_extra_fields = True
 
        filter_extra_fields = True
 
        username = All(UnicodeString(strip=True, min=1, not_empty=True),
 
                       ValidUsername(edit, old_data))
 
        if edit:
 
            new_password = All(UnicodeString(strip=True, min=6, not_empty=False))
 
            password_confirmation = All(UnicodeString(strip=True, min=6,
 
            new_password = All(UnicodeString(strip=False, min=6, not_empty=False))
 
            password_confirmation = All(UnicodeString(strip=False, min=6,
 
                                                      not_empty=False))
 
            admin = StringBoolean(if_missing=False)
 
        else:
 
            password = All(UnicodeString(strip=True, min=6, not_empty=True))
 
            password_confirmation = All(UnicodeString(strip=True, min=6,
 
            password = All(UnicodeString(strip=False, min=6, not_empty=True))
 
            password_confirmation = All(UnicodeString(strip=False, min=6,
 
                                                      not_empty=False))
 

	
 
        active = StringBoolean(if_missing=False)
 
        name = UnicodeString(strip=True, min=1, not_empty=False)
 
        lastname = UnicodeString(strip=True, min=1, not_empty=False)
 
        email = All(Email(not_empty=True), UniqSystemEmail(old_data))
 

	
 
        chained_validators = [ValidPasswordsMatch, ValidPassword]
 

	
 
    return _UserForm
 

	
 

	
 
def UsersGroupForm(edit=False, old_data={}, available_members=[]):
 
    class _UsersGroupForm(formencode.Schema):
 
        allow_extra_fields = True
 
        filter_extra_fields = True
 

	
 
        users_group_name = All(UnicodeString(strip=True, min=1, not_empty=True),
 
                       ValidUsersGroup(edit, old_data))
 

	
 
        users_group_active = StringBoolean(if_missing=False)
 

	
 
        if edit:
 
            users_group_members = OneOf(available_members, hideList=False,
 
@@ -611,50 +611,50 @@ def UsersGroupForm(edit=False, old_data=
 
def ReposGroupForm(edit=False, old_data={}, available_groups=[]):
 
    class _ReposGroupForm(formencode.Schema):
 
        allow_extra_fields = True
 
        filter_extra_fields = False
 

	
 
        group_name = All(UnicodeString(strip=True, min=1, not_empty=True),
 
                               SlugifyName())
 
        group_description = UnicodeString(strip=True, min=1,
 
                                                not_empty=True)
 
        group_parent_id = OneOf(available_groups, hideList=False,
 
                                        testValueList=True,
 
                                        if_missing=None, not_empty=False)
 

	
 
        chained_validators = [ValidReposGroup(edit, old_data), ValidPerms('group')]
 

	
 
    return _ReposGroupForm
 

	
 

	
 
def RegisterForm(edit=False, old_data={}):
 
    class _RegisterForm(formencode.Schema):
 
        allow_extra_fields = True
 
        filter_extra_fields = True
 
        username = All(ValidUsername(edit, old_data),
 
                       UnicodeString(strip=True, min=1, not_empty=True))
 
        password = All(UnicodeString(strip=True, min=6, not_empty=True))
 
        password_confirmation = All(UnicodeString(strip=True, min=6, not_empty=True))
 
        password = All(UnicodeString(strip=False, min=6, not_empty=True))
 
        password_confirmation = All(UnicodeString(strip=False, min=6, not_empty=True))
 
        active = StringBoolean(if_missing=False)
 
        name = UnicodeString(strip=True, min=1, not_empty=False)
 
        lastname = UnicodeString(strip=True, min=1, not_empty=False)
 
        email = All(Email(not_empty=True), UniqSystemEmail(old_data))
 

	
 
        chained_validators = [ValidPasswordsMatch, ValidPassword]
 

	
 
    return _RegisterForm
 

	
 

	
 
def PasswordResetForm():
 
    class _PasswordResetForm(formencode.Schema):
 
        allow_extra_fields = True
 
        filter_extra_fields = True
 
        email = All(ValidSystemEmail(), Email(not_empty=True))
 
    return _PasswordResetForm
 

	
 

	
 
def RepoForm(edit=False, old_data={}, supported_backends=BACKENDS.keys(),
 
             repo_groups=[]):
 
    class _RepoForm(formencode.Schema):
 
        allow_extra_fields = True
 
        filter_extra_fields = False
 
        repo_name = All(UnicodeString(strip=True, min=1, not_empty=True),
 
@@ -733,41 +733,41 @@ def ApplicationUiSettingsForm():
 
        hooks_changegroup_repo_size = OneOf(['True', 'False'], if_missing=False)
 
        hooks_pretxnchangegroup_push_logger = OneOf(['True', 'False'], if_missing=False)
 
        hooks_preoutgoing_pull_logger = OneOf(['True', 'False'], if_missing=False)
 

	
 
    return _ApplicationUiSettingsForm
 

	
 

	
 
def DefaultPermissionsForm(perms_choices, register_choices, create_choices):
 
    class _DefaultPermissionsForm(formencode.Schema):
 
        allow_extra_fields = True
 
        filter_extra_fields = True
 
        overwrite_default = StringBoolean(if_missing=False)
 
        anonymous = OneOf(['True', 'False'], if_missing=False)
 
        default_perm = OneOf(perms_choices)
 
        default_register = OneOf(register_choices)
 
        default_create = OneOf(create_choices)
 

	
 
    return _DefaultPermissionsForm
 

	
 

	
 
def LdapSettingsForm(tls_reqcert_choices, search_scope_choices, tls_kind_choices):
 
    class _LdapSettingsForm(formencode.Schema):
 
        allow_extra_fields = True
 
        filter_extra_fields = True
 
        pre_validators = [LdapLibValidator]
 
        #pre_validators = [LdapLibValidator]
 
        ldap_active = StringBoolean(if_missing=False)
 
        ldap_host = UnicodeString(strip=True,)
 
        ldap_port = Number(strip=True,)
 
        ldap_tls_kind = OneOf(tls_kind_choices)
 
        ldap_tls_reqcert = OneOf(tls_reqcert_choices)
 
        ldap_dn_user = UnicodeString(strip=True,)
 
        ldap_dn_pass = UnicodeString(strip=True,)
 
        ldap_base_dn = UnicodeString(strip=True,)
 
        ldap_filter = UnicodeString(strip=True,)
 
        ldap_search_scope = OneOf(search_scope_choices)
 
        ldap_attr_login = All(AttrLoginValidator, UnicodeString(strip=True,))
 
        ldap_attr_firstname = UnicodeString(strip=True,)
 
        ldap_attr_lastname = UnicodeString(strip=True,)
 
        ldap_attr_email = UnicodeString(strip=True,)
 

	
 
    return _LdapSettingsForm
rhodecode/model/repo.py
Show inline comments
 
@@ -265,54 +265,54 @@ class RepoModel(BaseModel):
 
            if fork:
 
                if form_data.get('copy_permissions'):
 
                    repo = Repository.get(fork_parent_id)
 
                    user_perms = UserRepoToPerm.query()\
 
                        .filter(UserRepoToPerm.repository == repo).all()
 
                    group_perms = UsersGroupRepoToPerm.query()\
 
                        .filter(UsersGroupRepoToPerm.repository == repo).all()
 

	
 
                    for perm in user_perms:
 
                        UserRepoToPerm.create(perm.user, new_repo,
 
                                              perm.permission)
 

	
 
                    for perm in group_perms:
 
                        UsersGroupRepoToPerm.create(perm.users_group, new_repo,
 
                                                    perm.permission)
 
                else:
 
                    _create_default_perms()
 
            else:
 
                _create_default_perms()
 

	
 
            if not just_db:
 
                self.__create_repo(repo_name, form_data['repo_type'],
 
                                   form_data['repo_group'],
 
                                   form_data['clone_uri'])
 
                log_create_repository(new_repo.get_dict(),
 
                                      created_by=cur_user.username)
 

	
 
            # now automatically start following this repository as owner
 
            ScmModel(self.sa).toggle_following_repo(new_repo.repo_id,
 
                                                    cur_user.user_id)
 
            log_create_repository(new_repo.get_dict(),
 
                                  created_by=cur_user.username)
 
            return new_repo
 
        except:
 
            log.error(traceback.format_exc())
 
            raise
 

	
 
    def create_fork(self, form_data, cur_user):
 
        """
 
        Simple wrapper into executing celery task for fork creation
 

	
 
        :param form_data:
 
        :param cur_user:
 
        """
 
        from rhodecode.lib.celerylib import tasks, run_task
 
        run_task(tasks.create_repo_fork, form_data, cur_user)
 

	
 
    def delete(self, repo):
 
        repo = self.__get_repo(repo)
 
        try:
 
            self.sa.delete(repo)
 
            self.__delete_repo(repo)
 
        except:
 
            log.error(traceback.format_exc())
 
            raise
 

	
rhodecode/model/scm.py
Show inline comments
 
@@ -14,49 +14,49 @@
 
# 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 os
 
import time
 
import traceback
 
import logging
 
import cStringIO
 

	
 
from rhodecode.lib.vcs import get_backend
 
from rhodecode.lib.vcs.exceptions import RepositoryError
 
from rhodecode.lib.vcs.utils.lazy import LazyProperty
 
from rhodecode.lib.vcs.nodes import FileNode
 

	
 
from rhodecode import BACKENDS
 
from rhodecode.lib import helpers as h
 
from rhodecode.lib.utils2 import safe_str
 
from rhodecode.lib.utils2 import safe_str, safe_unicode
 
from rhodecode.lib.auth import HasRepoPermissionAny, HasReposGroupPermissionAny
 
from rhodecode.lib.utils import get_repos as get_filesystem_repos, make_ui, \
 
    action_logger, EmptyChangeset, REMOVED_REPO_PAT
 
from rhodecode.model import BaseModel
 
from rhodecode.model.db import Repository, RhodeCodeUi, CacheInvalidation, \
 
    UserFollowing, UserLog, User, RepoGroup
 

	
 
log = logging.getLogger(__name__)
 

	
 

	
 
class UserTemp(object):
 
    def __init__(self, user_id):
 
        self.user_id = user_id
 

	
 
    def __repr__(self):
 
        return "<%s('id:%s')>" % (self.__class__.__name__, self.user_id)
 

	
 

	
 
class RepoTemp(object):
 
    def __init__(self, repo_id):
 
        self.repo_id = repo_id
 

	
 
    def __repr__(self):
 
        return "<%s('id:%s')>" % (self.__class__.__name__, self.repo_id)
 
@@ -322,115 +322,122 @@ class ScmModel(BaseModel):
 
                .filter(UserFollowing.follows_repo_id == repo_id).count()
 

	
 
    def get_forks(self, repo_id):
 
        if not isinstance(repo_id, int):
 
            repo_id = getattr(Repository.get_by_repo_name(repo_id), 'repo_id')
 

	
 
        return self.sa.query(Repository)\
 
                .filter(Repository.fork_id == repo_id).count()
 

	
 
    def mark_as_fork(self, repo, fork, user):
 
        repo = self.__get_repo(repo)
 
        fork = self.__get_repo(fork)
 
        repo.fork = fork
 
        self.sa.add(repo)
 
        return repo
 

	
 
    def pull_changes(self, repo_name, username):
 
        dbrepo = Repository.get_by_repo_name(repo_name)
 
        clone_uri = dbrepo.clone_uri
 
        if not clone_uri:
 
            raise Exception("This repository doesn't have a clone uri")
 

	
 
        repo = dbrepo.scm_instance
 
        try:
 
            extras = {'ip': '',
 
            extras = {
 
                'ip': '',
 
                      'username': username,
 
                      'action': 'push_remote',
 
                      'repository': repo_name}
 
                'repository': repo_name,
 
                'scm': repo.alias,
 
            }
 

	
 
            #inject ui extra param to log this action via push logger
 
            for k, v in extras.items():
 
                repo._repo.ui.setconfig('rhodecode_extras', k, v)
 

	
 
            repo.pull(clone_uri)
 
            self.mark_for_invalidation(repo_name)
 
        except:
 
            log.error(traceback.format_exc())
 
            raise
 

	
 
    def commit_change(self, repo, repo_name, cs, user, author, message,
 
                      content, f_path):
 

	
 
        if repo.alias == 'hg':
 
            from rhodecode.lib.vcs.backends.hg import MercurialInMemoryChangeset as IMC
 
            from rhodecode.lib.vcs.backends.hg import \
 
                MercurialInMemoryChangeset as IMC
 
        elif repo.alias == 'git':
 
            from rhodecode.lib.vcs.backends.git import GitInMemoryChangeset as IMC
 
            from rhodecode.lib.vcs.backends.git import \
 
                GitInMemoryChangeset as IMC
 

	
 
        # decoding here will force that we have proper encoded values
 
        # in any other case this will throw exceptions and deny commit
 
        content = safe_str(content)
 
        message = safe_str(message)
 
        path = safe_str(f_path)
 
        author = safe_str(author)
 
        # message and author needs to be unicode
 
        # proper backend should then translate that into required type
 
        message = safe_unicode(message)
 
        author = safe_unicode(author)
 
        m = IMC(repo)
 
        m.change(FileNode(path, content))
 
        tip = m.commit(message=message,
 
                 author=author,
 
                 parents=[cs], branch=cs.branch)
 

	
 
        new_cs = tip.short_id
 
        action = 'push_local:%s' % new_cs
 

	
 
        action_logger(user, action, repo_name)
 

	
 
        self.mark_for_invalidation(repo_name)
 

	
 
    def create_node(self, repo, repo_name, cs, user, author, message, content,
 
                      f_path):
 
        if repo.alias == 'hg':
 
            from rhodecode.lib.vcs.backends.hg import MercurialInMemoryChangeset as IMC
 
        elif repo.alias == 'git':
 
            from rhodecode.lib.vcs.backends.git import GitInMemoryChangeset as IMC
 
        # decoding here will force that we have proper encoded values
 
        # in any other case this will throw exceptions and deny commit
 

	
 
        if isinstance(content, (basestring,)):
 
            content = safe_str(content)
 
        elif isinstance(content, (file, cStringIO.OutputType,)):
 
            content = content.read()
 
        else:
 
            raise Exception('Content is of unrecognized type %s' % (
 
                type(content)
 
            ))
 

	
 
        message = safe_str(message)
 
        message = safe_unicode(message)
 
        author = safe_unicode(author)
 
        path = safe_str(f_path)
 
        author = safe_str(author)
 
        m = IMC(repo)
 

	
 
        if isinstance(cs, EmptyChangeset):
 
            # Emptychangeset means we we're editing empty repository
 
            # EmptyChangeset means we we're editing empty repository
 
            parents = None
 
        else:
 
            parents = [cs]
 

	
 
        m.add(FileNode(path, content=content))
 
        tip = m.commit(message=message,
 
                 author=author,
 
                 parents=parents, branch=cs.branch)
 
        new_cs = tip.short_id
 
        action = 'push_local:%s' % new_cs
 

	
 
        action_logger(user, action, repo_name)
 

	
 
        self.mark_for_invalidation(repo_name)
 

	
 
    def get_nodes(self, repo_name, revision, root_path='/', flat=True):
 
        """
 
        recursive walk in root dir and return a set of all path in that dir
 
        based on repository walk function
 

	
 
        :param repo_name: name of repository
 
        :param revision: revision for which to list nodes
 
        :param root_path: root path to list
 
        :param flat: return as a list, if False returns a dict with decription
rhodecode/model/user.py
Show inline comments
 
@@ -204,52 +204,50 @@ class UserModel(BaseModel):
 
                # add ldap account always lowercase
 
                new_user.username = username
 
                new_user.password = get_crypt_password(password)
 
                new_user.api_key = generate_api_key(username)
 
                new_user.email = attrs['email'] or generate_email(username)
 
                new_user.active = attrs.get('active', True)
 
                new_user.ldap_dn = safe_unicode(user_dn)
 
                new_user.name = attrs['name']
 
                new_user.lastname = attrs['lastname']
 

	
 
                self.sa.add(new_user)
 
                return new_user
 
            except (DatabaseError,):
 
                log.error(traceback.format_exc())
 
                self.sa.rollback()
 
                raise
 
        log.debug('this %s user exists skipping creation of ldap account',
 
                  username)
 
        return None
 

	
 
    def create_registration(self, form_data):
 
        from rhodecode.model.notification import NotificationModel
 

	
 
        try:
 
            new_user = User()
 
            for k, v in form_data.items():
 
                if k != 'admin':
 
                    setattr(new_user, k, v)
 
            form_data['admin'] = False
 
            new_user = self.create(form_data)
 

	
 
            self.sa.add(new_user)
 
            self.sa.flush()
 

	
 
            # notification to admins
 
            subject = _('new user registration')
 
            body = ('New user registration\n'
 
                    '---------------------\n'
 
                    '- Username: %s\n'
 
                    '- Full Name: %s\n'
 
                    '- Email: %s\n')
 
            body = body % (new_user.username, new_user.full_name,
 
                           new_user.email)
 
            edit_url = url('edit_user', id=new_user.user_id, qualified=True)
 
            kw = {'registered_user_url': edit_url}
 
            NotificationModel().create(created_by=new_user, subject=subject,
 
                                       body=body, recipients=None,
 
                                       type_=Notification.TYPE_REGISTRATION,
 
                                       email_kwargs=kw)
 

	
 
        except:
 
            log.error(traceback.format_exc())
 
            raise
 

	
 
@@ -377,50 +375,50 @@ class UserModel(BaseModel):
 
        default_user = User.get_by_username('default', cache=True)
 
        default_user_id = default_user.user_id
 

	
 
        default_repo_perms = Permission.get_default_perms(default_user_id)
 
        default_repo_groups_perms = Permission.get_default_group_perms(default_user_id)
 

	
 
        if user.is_admin:
 
            #==================================================================
 
            # admin user have all default rights for repositories
 
            # and groups set to admin
 
            #==================================================================
 
            user.permissions[GLOBAL].add('hg.admin')
 

	
 
            # repositories
 
            for perm in default_repo_perms:
 
                r_k = perm.UserRepoToPerm.repository.repo_name
 
                p = 'repository.admin'
 
                user.permissions[RK][r_k] = p
 

	
 
            # repositories groups
 
            for perm in default_repo_groups_perms:
 
                rg_k = perm.UserRepoGroupToPerm.group.group_name
 
                p = 'group.admin'
 
                user.permissions[GK][rg_k] = p
 
            return user
 

	
 
        else:
 
            #==================================================================
 
            # set default permissions first for repositories and groups
 
            #==================================================================
 
            uid = user.user_id
 

	
 
            # default global permissions
 
            default_global_perms = self.sa.query(UserToPerm)\
 
                .filter(UserToPerm.user_id == default_user_id)
 

	
 
            for perm in default_global_perms:
 
                user.permissions[GLOBAL].add(perm.permission.permission_name)
 

	
 
            # defaults for repositories, taken from default user
 
            for perm in default_repo_perms:
 
                r_k = perm.UserRepoToPerm.repository.repo_name
 
                if perm.Repository.private and not (perm.Repository.user_id == uid):
 
                    # disable defaults for private repos,
 
                    p = 'repository.none'
 
                elif perm.Repository.user_id == uid:
 
                    # set admin if owner
 
                    p = 'repository.admin'
 
                else:
 
                    p = perm.Permission.permission_name
 

	
 
@@ -441,100 +439,103 @@ class UserModel(BaseModel):
 
            user_perms = self.sa.query(UserToPerm)\
 
                    .options(joinedload(UserToPerm.permission))\
 
                    .filter(UserToPerm.user_id == uid).all()
 

	
 
            for perm in user_perms:
 
                user.permissions[GLOBAL].add(perm.permission.permission_name)
 

	
 
            # user explicit permissions for repositories
 
            user_repo_perms = \
 
             self.sa.query(UserRepoToPerm, Permission, Repository)\
 
             .join((Repository, UserRepoToPerm.repository_id == Repository.repo_id))\
 
             .join((Permission, UserRepoToPerm.permission_id == Permission.permission_id))\
 
             .filter(UserRepoToPerm.user_id == uid)\
 
             .all()
 

	
 
            for perm in user_repo_perms:
 
                # set admin if owner
 
                r_k = perm.UserRepoToPerm.repository.repo_name
 
                if perm.Repository.user_id == uid:
 
                    p = 'repository.admin'
 
                else:
 
                    p = perm.Permission.permission_name
 
                user.permissions[RK][r_k] = p
 

	
 
        # USER GROUP
 
            #==================================================================
 
            # check if user is part of user groups for this repository and
 
            # fill in (or replace with higher) permissions
 
            #==================================================================
 

	
 
            # users group global
 
            user_perms_from_users_groups = self.sa.query(UsersGroupToPerm)\
 
                .options(joinedload(UsersGroupToPerm.permission))\
 
                .join((UsersGroupMember, UsersGroupToPerm.users_group_id ==
 
                       UsersGroupMember.users_group_id))\
 
                .filter(UsersGroupMember.user_id == uid).all()
 

	
 
            for perm in user_perms_from_users_groups:
 
                user.permissions[GLOBAL].add(perm.permission.permission_name)
 

	
 
            # users group for repositories permissions
 
            user_repo_perms_from_users_groups = \
 
             self.sa.query(UsersGroupRepoToPerm, Permission, Repository,)\
 
             .join((Repository, UsersGroupRepoToPerm.repository_id == Repository.repo_id))\
 
             .join((Permission, UsersGroupRepoToPerm.permission_id == Permission.permission_id))\
 
             .join((UsersGroupMember, UsersGroupRepoToPerm.users_group_id == UsersGroupMember.users_group_id))\
 
             .filter(UsersGroupMember.user_id == uid)\
 
             .all()
 

	
 
            for perm in user_repo_perms_from_users_groups:
 
                r_k = perm.UsersGroupRepoToPerm.repository.repo_name
 
                p = perm.Permission.permission_name
 
                cur_perm = user.permissions[RK][r_k]
 
                # overwrite permission only if it's greater than permission
 
                # given from other sources
 
                if PERM_WEIGHTS[p] > PERM_WEIGHTS[cur_perm]:
 
                    user.permissions[RK][r_k] = p
 

	
 
        # REPO GROUP
 
            #==================================================================
 
            # get access for this user for repos group and override defaults
 
            #==================================================================
 

	
 
            # user explicit permissions for repository
 
            user_repo_groups_perms = \
 
             self.sa.query(UserRepoGroupToPerm, Permission, RepoGroup)\
 
             .join((RepoGroup, UserRepoGroupToPerm.group_id == RepoGroup.group_id))\
 
             .join((Permission, UserRepoGroupToPerm.permission_id == Permission.permission_id))\
 
             .filter(UserRepoGroupToPerm.user_id == uid)\
 
             .all()
 

	
 
            for perm in user_repo_groups_perms:
 
                rg_k = perm.UserRepoGroupToPerm.group.group_name
 
                p = perm.Permission.permission_name
 
                cur_perm = user.permissions[GK][rg_k]
 
                if PERM_WEIGHTS[p] > PERM_WEIGHTS[cur_perm]:
 
                    user.permissions[GK][rg_k] = p
 

	
 
        # REPO GROUP + USER GROUP
 
            #==================================================================
 
            # check if user is part of user groups for this repo group and
 
            # fill in (or replace with higher) permissions
 
            #==================================================================
 

	
 
            # users group for repositories permissions
 
            user_repo_group_perms_from_users_groups = \
 
             self.sa.query(UsersGroupRepoGroupToPerm, Permission, RepoGroup)\
 
             .join((RepoGroup, UsersGroupRepoGroupToPerm.group_id == RepoGroup.group_id))\
 
             .join((Permission, UsersGroupRepoGroupToPerm.permission_id == Permission.permission_id))\
 
             .join((UsersGroupMember, UsersGroupRepoGroupToPerm.users_group_id == UsersGroupMember.users_group_id))\
 
             .filter(UsersGroupMember.user_id == uid)\
 
             .all()
 

	
 
            for perm in user_repo_group_perms_from_users_groups:
 
                g_k = perm.UsersGroupRepoGroupToPerm.group.group_name
 
                print perm, g_k
 
                p = perm.Permission.permission_name
 
                cur_perm = user.permissions[GK][g_k]
 
                # overwrite permission only if it's greater than permission
 
                # given from other sources
 
                if PERM_WEIGHTS[p] > PERM_WEIGHTS[cur_perm]:
 
                    user.permissions[GK][g_k] = p
 

	
rhodecode/public/css/style.css
Show inline comments
 
@@ -2499,102 +2499,106 @@ h3.files_location {
 
	background: #FAA;
 
}
 
 
.right .merge {
 
  padding: 1px 3px 1px 3px;
 
  background-color: #fca062;
 
  font-size: 10px;
 
  font-weight: bold;
 
  color: #ffffff;
 
  text-transform: uppercase;
 
  white-space: nowrap;
 
  -webkit-border-radius: 3px;
 
  -moz-border-radius: 3px;
 
  border-radius: 3px;
 
  margin-right: 2px;
 
}
 
 
.right .parent {
 
	color: #666666;
 
	clear:both;
 
}
 
.right .logtags{
 
	padding: 2px 2px 2px 2px;
 
}
 
.right .logtags .branchtag,.right .logtags .tagtag,.right .logtags .booktag{
 
    margin: 0px 2px;
 
}
 
 
.right .logtags .branchtag,.logtags .branchtag {
 
  padding: 1px 3px 1px 3px;
 
  background-color: #bfbfbf;
 
  font-size: 10px;
 
  font-weight: bold;
 
  color: #ffffff;
 
  text-transform: uppercase;
 
  white-space: nowrap;
 
  -webkit-border-radius: 3px;
 
  -moz-border-radius: 3px;
 
  border-radius: 3px;
 
}
 
.right .logtags .branchtag a:hover,.logtags .branchtag a{
 
	color: #ffffff;
 
}
 
.right .logtags .branchtag a:hover,.logtags .branchtag a:hover{
 
	text-decoration: none;
 
	color: #ffffff;
 
}
 
.right .logtags .tagtag,.logtags .tagtag {
 
  padding: 1px 3px 1px 3px;
 
  background-color: #62cffc;
 
  font-size: 10px;
 
  font-weight: bold;
 
  color: #ffffff;
 
  text-transform: uppercase;
 
  white-space: nowrap;
 
  -webkit-border-radius: 3px;
 
  -moz-border-radius: 3px;
 
  border-radius: 3px;
 
}
 
.right .logtags .tagtag a:hover,.logtags .tagtag a{
 
	color: #ffffff;
 
}
 
.right .logtags .tagtag a:hover,.logtags .tagtag a:hover{
 
    text-decoration: none;
 
    color: #ffffff;
 
}
 
.right .logbooks .bookbook,.logbooks .bookbook {
 
  padding: 1px 3px 2px;
 
.right .logbooks .bookbook,.logbooks .bookbook,.right .logtags .bookbook,.logtags .bookbook {
 
  padding: 1px 3px 1px 3px;
 
  background-color: #46A546;
 
  font-size: 9.75px;
 
  font-size: 10px;
 
  font-weight: bold;
 
  color: #ffffff;
 
  text-transform: uppercase;
 
  white-space: nowrap;
 
  -webkit-border-radius: 3px;
 
  -moz-border-radius: 3px;
 
  border-radius: 3px;
 
}
 
.right .logbooks .bookbook,.logbooks .bookbook a{
 
.right .logbooks .bookbook,.logbooks .bookbook a,.right .logtags .bookbook,.logtags .bookbook a{
 
	color: #ffffff;
 
}
 
.right .logbooks .bookbook,.logbooks .bookbook a:hover{
 
.right .logbooks .bookbook,.logbooks .bookbook a:hover,.right .logtags .bookbook,.logtags .bookbook a:hover{
 
    text-decoration: none;
 
    color: #ffffff;
 
}
 
div.browserblock {
 
	overflow: hidden;
 
	border: 1px solid #ccc;
 
	background: #f8f8f8;
 
	font-size: 100%;
 
	line-height: 125%;
 
	padding: 0;
 
    -webkit-border-radius: 6px 6px 0px 0px;
 
    -moz-border-radius: 6px 6px 0px 0px;
 
    border-radius: 6px 6px 0px 0px;	
 
}
 
 
div.browserblock .browser-header {
 
	background: #FFF;
 
	padding: 10px 0px 15px 0px;
 
	width: 100%;
 
}
 
 
div.browserblock .browser-nav {
 
	float: left
 
}
 
@@ -2697,48 +2701,56 @@ table.code-browser .browser-file {
 
    
 
}
 
.diffblock .diff-actions {
 
    padding: 2px 0px 0px 2px;
 
    float: left;
 
}
 
.diffblock  .diff-menu ul li {
 
	padding: 0px 0px 0px 0px !important;
 
}
 
.diffblock  .diff-menu ul li a{
 
	display: block;
 
	padding: 3px 8px 3px 8px !important;
 
}
 
.diffblock  .diff-menu ul li a:hover{
 
    text-decoration: none;
 
    background-color: #EEEEEE;
 
}
 
table.code-browser .browser-dir {
 
	background: url("../images/icons/folder_16.png") no-repeat scroll 3px;
 
	height: 16px;
 
	padding-left: 20px;
 
	text-align: left;
 
}
 
 
table.code-browser .submodule-dir {
 
    background: url("../images/icons/disconnect.png") no-repeat scroll 3px;
 
    height: 16px;
 
    padding-left: 20px;
 
    text-align: left;
 
}
 
 
 
.box .search {
 
	clear: both;
 
	overflow: hidden;
 
	margin: 0;
 
	padding: 0 20px 10px;
 
}
 
 
.box .search div.search_path {
 
	background: none repeat scroll 0 0 #EEE;
 
	border: 1px solid #CCC;
 
	color: blue;
 
	margin-bottom: 10px;
 
	padding: 10px 0;
 
}
 
 
.box .search div.search_path div.link {
 
	font-weight: 700;
 
	margin-left: 25px;
 
}
 
 
.box .search div.search_path div.link a {
 
	color: #003367;
 
	cursor: pointer;
 
	text-decoration: none;
 
@@ -3945,69 +3957,88 @@ form.comment-form {
 
    display: none;
 
}
 
 
.comment-form .comment {
 
    margin-left: 10px;
 
}
 
 
.comment-form .comment-help{
 
    padding: 0px 0px 5px 0px;
 
    color: #666;
 
}
 
 
.comment-form .comment-button{
 
	padding-top:5px;
 
}
 
 
.add-another-button {
 
    margin-left: 10px;
 
    margin-top: 10px;
 
    margin-bottom: 10px;
 
}
 
 
.comment .buttons {
 
	float: right;
 
	padding:2px 2px 0px 0px;
 
}
 
 
 
.show-inline-comments{
 
	position: relative;
 
	top:1px
 
}
 
 
/** comment inline form **/
 
.comment-inline-form .overlay{
 
	display: none;
 
}
 
.comment-inline-form .overlay.submitting{
 
	display:block;
 
    background: none repeat scroll 0 0 white;
 
    font-size: 16px;
 
    opacity: 0.5;
 
    position: absolute;
 
    text-align: center;
 
    vertical-align: top;
 
 
}
 
.comment-inline-form .overlay.submitting .overlay-text{
 
	width:100%;
 
	margin-top:5%;
 
}
 
 
.comment-inline-form .clearfix{
 
    background: #EEE;
 
    -webkit-border-radius: 4px;
 
    -moz-border-radius: 4px;
 
    border-radius: 4px;
 
    padding: 5px;
 
}
 
 
div.comment-inline-form {
 
    margin-top: 5px;
 
    padding:2px 6px 8px 6px;
 
 
}
 
 
.comment-inline-form strong {
 
    display: block;
 
    margin-bottom: 15px;
 
}
 
 
.comment-inline-form textarea {
 
    width: 100%;
 
    height: 100px;
 
    font-family: 'Monaco', 'Courier', 'Courier New', monospace;
 
}
 
 
form.comment-inline-form {
 
    margin-top: 10px;
 
    margin-left: 10px;
 
}
 
 
.comment-inline-form-submit {
 
    margin-top: 5px;
 
    margin-left: 525px;
 
}
 
 
.file-comments {
 
@@ -4026,135 +4057,159 @@ form.comment-inline-form {
 
 
.comment-inline-form .comment-button{
 
    padding-top:5px;
 
}
 
 
/** comment inline **/
 
.inline-comments {
 
    padding:10px 20px;
 
}
 
 
.inline-comments div.rst-block  {
 
	clear:both;
 
	overflow:hidden;
 
	margin:0;
 
	padding:0 20px 0px;
 
}
 
.inline-comments .comment {
 
    border: 1px solid #ddd;
 
    -webkit-border-radius: 4px;
 
    -moz-border-radius: 4px;
 
    border-radius: 4px;
 
    margin: 3px 3px 5px 5px;
 
    background-color: #FAFAFA;
 
}
 
.inline-comments .add-comment {
 
	padding: 2px 4px 8px 5px;
 
}
 
 
.inline-comments .comment-wrapp{
 
	padding:1px;
 
}
 
.inline-comments .comment .meta {
 
    background: #f8f8f8;
 
    padding: 4px;
 
    border-bottom: 1px solid #ddd;
 
}
 
 
.inline-comments .comment .meta img {
 
    vertical-align: middle;
 
}
 
 
.inline-comments .comment .meta .user {
 
    font-weight: bold;
 
}
 
 
.inline-comments .comment .meta .date {
 
}
 
 
.inline-comments .comment .text {
 
    background-color: #FAFAFA;
 
}
 
 
.inline-comments .comments-number{
 
    padding:0px 0px 10px 0px;
 
    font-weight: bold;
 
    color: #666;
 
    font-size: 16px;
 
}
 
.inline-comments-button .add-comment{
 
	margin:10px 5px !important;
 
}
 
	margin:2px 0px 8px 5px !important
 
}
 
 
 
.notification-paginator{
 
    padding: 0px 0px 4px 16px;
 
    float: left;    	
 
}
 
 
.notifications{
 
    border-radius: 4px 4px 4px 4px;
 
    -webkit-border-radius: 4px;
 
    -moz-border-radius: 4px;    
 
    float: right;
 
    margin: 20px 0px 0px 0px;
 
    position: absolute;
 
    text-align: center;
 
    width: 26px;
 
    z-index: 1000;
 
}
 
.notifications a{
 
	color:#888 !important;
 
	display: block;
 
	font-size: 10px;
 
	background-color: #DEDEDE !important;
 
    border-radius: 2px !important;
 
    -webkit-border-radius: 2px !important;
 
    -moz-border-radius: 2px !important;  	
 
}
 
.notifications a:hover{
 
	text-decoration: none !important;
 
	background-color: #EEEFFF !important;
 
}
 
.notification-header{
 
	padding-top:6px;
 
}
 
.notification-header .desc{
 
	font-size: 16px;
 
    height: 24px;
 
    float: left
 
}
 
.notification-list .container.unread{
 
	
 
	background: none repeat scroll 0 0 rgba(255, 255, 180, 0.6);
 
}
 
.notification-header .gravatar{
 
	
 
    background: none repeat scroll 0 0 transparent;
 
    padding: 0px 0px 0px 8px;	
 
}
 
.notification-header .desc.unread{
 
    font-weight: bold;
 
    font-size: 17px;
 
}
 
 
.notification-table{
 
	border: 1px solid #ccc;
 
    -webkit-border-radius: 6px 6px 6px 6px;
 
    -moz-border-radius: 6px 6px 6px 6px;
 
    border-radius: 6px 6px 6px 6px;
 
    clear: both;
 
    margin: 0px 20px 0px 20px;
 
}
 
.notification-header .delete-notifications{
 
    float: right;
 
    padding-top: 8px;
 
    cursor: pointer;
 
}
 
.notification-subject{
 
    clear:both;
 
    border-bottom: 1px solid #eee;
 
    padding:5px 0px 5px 38px;
 
}
 
 
.notification-body{
 
	clear:both;
 
	margin: 34px 2px 2px 8px
 
}
 
 
/****
 
  PERMS
 
*****/
 
#perms .perms_section_head {
 
   padding:10px 10px 10px 0px;
 
   font-size:16px;
 
   font-weight: bold;
 
}
 
 
#perms .perm_tag{
 
  padding: 1px 3px 1px 3px;
 
  font-size: 10px;
 
  font-weight: bold;
 
  text-transform: uppercase;
 
  white-space: nowrap;
 
  -webkit-border-radius: 3px;
 
  -moz-border-radius: 3px;
 
  border-radius: 3px;
 
}
 
 
#perms .perm_tag.admin{
 
  background-color: #B94A48;
 
  color: #ffffff;
 
}
rhodecode/public/js/rhodecode.js
Show inline comments
 
@@ -174,48 +174,76 @@ function ypjax(url,container,s_call,f_ca
 
	// wrapper of passed callback
 
	var s_wrapper = (function(o){
 
		return function(o){
 
			YUD.get(container).innerHTML=o.responseText;
 
			YUD.setStyle(container,'opacity','1.0');
 
    		//execute the given original callback
 
    		if (s_call !== undefined){
 
    			s_call(o);
 
    		}
 
		}
 
	})()	
 
	YUD.setStyle(container,'opacity','0.3');
 
	YUC.asyncRequest(method,url,{
 
		success:s_wrapper,
 
		failure:function(o){
 
			console.log(o);
 
			YUD.get(container).innerHTML='ERROR';
 
			YUD.setStyle(container,'opacity','1.0');
 
			YUD.setStyle(container,'color','red');
 
		}
 
	},args);
 
	
 
};
 

	
 
var ajaxPOST = function(url,postData,success) {
 
	// Set special header for ajax == HTTP_X_PARTIAL_XHR
 
	YUC.initHeader('X-PARTIAL-XHR',true);
 
	
 
	var toQueryString = function(o) {
 
	    if(typeof o !== 'object') {
 
	        return false;
 
	    }
 
	    var _p, _qs = [];
 
	    for(_p in o) {
 
	        _qs.push(encodeURIComponent(_p) + '=' + encodeURIComponent(o[_p]));
 
	    }
 
	    return _qs.join('&');
 
	};
 
	
 
    var sUrl = url;
 
    var callback = {
 
        success: success,
 
        failure: function (o) {
 
            alert("error");
 
        },
 
    };
 
    var postData = toQueryString(postData);
 
    var request = YAHOO.util.Connect.asyncRequest('POST', sUrl, callback, postData);
 
    return request;
 
};
 

	
 

	
 
/**
 
 * tooltip activate
 
 */
 
var tooltip_activate = function(){
 
    function toolTipsId(){
 
        var ids = [];
 
        var tts = YUQ('.tooltip');
 
        for (var i = 0; i < tts.length; i++) {
 
            // if element doesn't not have and id 
 
        	//  autogenerate one for tooltip 
 
            if (!tts[i].id){
 
                tts[i].id='tt'+((i*100)+tts.length);
 
            }
 
            ids.push(tts[i].id);
 
        }
 
        return ids
 
    };
 
    var myToolTips = new YAHOO.widget.Tooltip("tooltip", {
 
        context: [[toolTipsId()],"tl","bl",null,[0,5]],
 
        monitorresize:false,
 
        xyoffset :[0,0],
 
        autodismissdelay:300000,
 
        hidedelay:5,
 
        showdelay:20,
 
@@ -279,132 +307,312 @@ var q_filter = function(target,nodes,dis
 
	   var i;
 
	   var showing = 0;
 
	   
 
       for (i=0;i<l;i++ ){
 
    	   var n = nodes[i];
 
    	   var target_element = display_element(n)
 
    	   if(req && n.innerHTML.toLowerCase().indexOf(req) == -1){
 
    		   hide_node(target_element);
 
    	   }
 
    	   else{
 
    		   show_node(target_element);
 
    		   showing+=1;
 
    	   }
 
       }	  	   
 

	
 
	   // if repo_count is set update the number
 
	   var cnt = YUD.get('repo_count');
 
	   if(cnt){
 
		   YUD.get('repo_count').innerHTML = showing;
 
	   }       
 
       
 
	}	
 
};
 

	
 
var ajaxPOST = function(url,postData,success) {
 
    var sUrl = url;
 
    var callback = {
 
        success: success,
 
        failure: function (o) {
 
            alert("error");
 
        },
 
var tableTr = function(cls,body){
 
	var tr = document.createElement('tr');
 
	YUD.addClass(tr, cls);
 
	
 
	
 
	var cont = new YAHOO.util.Element(body);
 
	var comment_id = fromHTML(body).children[0].id.split('comment-')[1];
 
	tr.id = 'comment-tr-{0}'.format(comment_id);
 
	tr.innerHTML = '<td class="lineno-inline new-inline"></td>'+
 
    				 '<td class="lineno-inline old-inline"></td>'+ 
 
                     '<td>{0}</td>'.format(body);
 
	return tr;
 
    };
 
    var postData = postData;
 
    var request = YAHOO.util.Connect.asyncRequest('POST', sUrl, callback, postData);
 
};
 

	
 

	
 
/** comments **/
 
var removeInlineForm = function(form) {
 
	form.parentNode.removeChild(form);
 
};
 

	
 
var tableTr = function(cls,body){
 
	var form = document.createElement('tr');
 
	YUD.addClass(form, cls);
 
	form.innerHTML = '<td class="lineno-inline new-inline"></td>'+
 
    				 '<td class="lineno-inline old-inline"></td>'+ 
 
                     '<td>{0}</td>'.format(body);
 
	return form;
 
};
 

	
 
var createInlineForm = function(parent_tr, f_path, line) {
 
	var tmpl = YUD.get('comment-inline-form-template').innerHTML;
 
	tmpl = tmpl.format(f_path, line);
 
	var form = tableTr('comment-form-inline',tmpl)
 
	
 
	// create event for hide button
 
	form = new YAHOO.util.Element(form);
 
	var form_hide_button = new YAHOO.util.Element(form.getElementsByClassName('hide-inline-form')[0]);
 
	form_hide_button.on('click', function(e) {
 
		var newtr = e.currentTarget.parentNode.parentNode.parentNode.parentNode.parentNode;
 
		if(YUD.hasClass(newtr.nextElementSibling,'inline-comments-button')){
 
			YUD.setStyle(newtr.nextElementSibling,'display','');
 
		}
 
		removeInlineForm(newtr);
 
		YUD.removeClass(parent_tr, 'form-open');
 
		
 
	});
 
	
 
	return form
 
};
 

	
 
/**
 
 * Inject inline comment for on given TR this tr should be always an .line
 
 * tr containing the line. Code will detect comment, and always put the comment
 
 * block at the very bottom
 
 */
 
var injectInlineForm = function(tr){
 
	  if(!YUD.hasClass(tr, 'line')){
 
		  return
 
	  }
 
	  var submit_url = AJAX_COMMENT_URL;
 
	  if(YUD.hasClass(tr,'form-open') || YUD.hasClass(tr,'context') || YUD.hasClass(tr,'no-comment')){
 
		  return
 
	  }	
 
	  YUD.addClass(tr,'form-open');
 
	  var node = tr.parentNode.parentNode.parentNode.getElementsByClassName('full_f_path')[0];
 
	  var f_path = YUD.getAttribute(node,'path');
 
	  var lineno = getLineNo(tr);
 
	  var form = createInlineForm(tr, f_path, lineno);
 
	  var target_tr = tr;
 
	  if(YUD.hasClass(YUD.getNextSibling(tr),'inline-comments')){
 
		  target_tr = YUD.getNextSibling(tr);
 
	  var form = createInlineForm(tr, f_path, lineno, submit_url);
 
	  
 
	  var parent = tr;
 
	  while (1){
 
		  var n = parent.nextElementSibling;
 
		  // next element are comments !
 
		  if(YUD.hasClass(n,'inline-comments')){
 
			  parent = n;
 
		  }
 
		  else{
 
			  break;
 
		  }
 
	  }
 
	  YUD.insertAfter(form,target_tr);
 
	  YUD.insertAfter(form,parent);
 
	  
 
	  YUD.get('text_'+lineno).focus();
 
	  var f = YUD.get(form);
 
	  
 
	  var overlay = f.getElementsByClassName('overlay')[0];
 
	  var _form = f.getElementsByClassName('inline-form')[0];
 
	  
 
	  form.on('submit',function(e){
 
		  YUE.preventDefault(e);
 
		  
 
		  //ajax submit
 
		  var text = YUD.get('text_'+lineno).value;
 
		  var postData = {
 
	            'text':text,
 
	            'f_path':f_path,
 
	            'line':lineno
 
		  };
 
		  
 
		  if(lineno === undefined){
 
			  alert('missing line !');
 
			  return
 
		  }
 
		  if(f_path === undefined){
 
			  alert('missing file path !');
 
			  return
 
		  }
 
		  
 
		  if(text == ""){
 
			  return
 
		  }
 
		  
 
		  var success = function(o){
 
			  YUD.removeClass(tr, 'form-open');
 
			  removeInlineForm(f);			  
 
			  var json_data = JSON.parse(o.responseText);
 
	          renderInlineComment(json_data);
 
		  };
 

	
 
		  if (YUD.hasClass(overlay,'overlay')){
 
			  var w = _form.offsetWidth;
 
			  var h = _form.offsetHeight;
 
			  YUD.setStyle(overlay,'width',w+'px');
 
			  YUD.setStyle(overlay,'height',h+'px');
 
		  }		  
 
		  YUD.addClass(overlay, 'submitting');		  
 
		  
 
		  ajaxPOST(submit_url, postData, success);
 
	  });
 
	  
 
	  tooltip_activate();
 
};
 

	
 
var createInlineAddButton = function(tr,label){
 
	var html = '<div class="add-comment"><span class="ui-btn">{0}</span></div>'.format(label);
 
var deleteComment = function(comment_id){
 
	var url = AJAX_COMMENT_DELETE_URL.replace('__COMMENT_ID__',comment_id);
 
    var postData = {'_method':'delete'};
 
    var success = function(o){
 
        var n = YUD.get('comment-tr-'+comment_id);
 
        var root = n.previousElementSibling.previousElementSibling;
 
        n.parentNode.removeChild(n);
 
        
 
	var add = new YAHOO.util.Element(tableTr('inline-comments-button',html));
 
        // scann nodes, and attach add button to last one
 
        placeAddButton(root);
 
    }
 
    ajaxPOST(url,postData,success);
 
}
 

	
 

	
 
var createInlineAddButton = function(tr){
 

	
 
	var label = TRANSLATION_MAP['add another comment'];
 
	
 
	var html_el = document.createElement('div');
 
	YUD.addClass(html_el, 'add-comment');
 
	html_el.innerHTML = '<span class="ui-btn">{0}</span>'.format(label);
 
	
 
	var add = new YAHOO.util.Element(html_el);
 
	add.on('click', function(e) {
 
		injectInlineForm(tr);
 
	});
 
	return add;
 
};
 

	
 
var getLineNo = function(tr) {
 
	var line;
 
	var o = tr.children[0].id.split('_');
 
	var n = tr.children[1].id.split('_');
 

	
 
	if (n.length >= 2) {
 
		line = n[n.length-1];
 
	} else if (o.length >= 2) {
 
		line = o[o.length-1];
 
	}
 

	
 
	return line
 
};
 

	
 
var placeAddButton = function(target_tr){
 
	if(!target_tr){
 
		return
 
	}
 
	var last_node = target_tr;
 
    //scann	
 
	  while (1){
 
		  var n = last_node.nextElementSibling;
 
		  // next element are comments !
 
		  if(YUD.hasClass(n,'inline-comments')){
 
			  last_node = n;
 
			  //also remove the comment button from previos
 
			  var comment_add_buttons = last_node.getElementsByClassName('add-comment');
 
			  for(var i=0;i<comment_add_buttons.length;i++){
 
				  var b = comment_add_buttons[i];
 
				  b.parentNode.removeChild(b);
 
			  }
 
		  }
 
		  else{
 
			  break;
 
		  }
 
	  }
 
	  
 
    var add = createInlineAddButton(target_tr);
 
    // get the comment div
 
    var comment_block = last_node.getElementsByClassName('comment')[0];
 
    // attach add button
 
    YUD.insertAfter(add,comment_block);	
 
}
 

	
 
/**
 
 * Places the inline comment into the changeset block in proper line position
 
 */
 
var placeInline = function(target_container,lineno,html){
 
	  var lineid = "{0}_{1}".format(target_container,lineno);
 
	  var target_line = YUD.get(lineid);
 
	  var comment = new YAHOO.util.Element(tableTr('inline-comments',html))
 
	  
 
	  // check if there are comments already !
 
	  var parent = target_line.parentNode;
 
	  var root_parent = parent;
 
	  while (1){
 
		  var n = parent.nextElementSibling;
 
		  // next element are comments !
 
		  if(YUD.hasClass(n,'inline-comments')){
 
			  parent = n;
 
		  }
 
		  else{
 
			  break;
 
		  }
 
	  }
 
	  // put in the comment at the bottom
 
	  YUD.insertAfter(comment,parent);
 
	  
 
	  // scann nodes, and attach add button to last one
 
      placeAddButton(root_parent);
 

	
 
	  return target_line;
 
}
 

	
 
/**
 
 * make a single inline comment and place it inside
 
 */
 
var renderInlineComment = function(json_data){
 
    try{
 
	  var html =  json_data['rendered_text'];
 
	  var lineno = json_data['line_no'];
 
	  var target_id = json_data['target_id'];
 
	  placeInline(target_id, lineno, html);
 

	
 
    }catch(e){
 
  	  console.log(e);
 
    }
 
}
 

	
 
/**
 
 * Iterates over all the inlines, and places them inside proper blocks of data
 
 */
 
var renderInlineComments = function(file_comments){
 
	for (f in file_comments){
 
        // holding all comments for a FILE
 
		var box = file_comments[f];
 

	
 
		var target_id = YUD.getAttribute(box,'target_id');
 
		// actually comments with line numbers
 
        var comments = box.children;
 
        for(var i=0; i<comments.length; i++){
 
        	var data = {
 
        		'rendered_text': comments[i].outerHTML,
 
        		'line_no': YUD.getAttribute(comments[i],'line'),
 
        		'target_id': target_id
 
        	}
 
        	renderInlineComment(data);
 
        }
 
    }	
 
}
 

	
 

	
 
var fileBrowserListeners = function(current_url, node_list_url, url_base,
 
									truncated_lbl, nomatch_lbl){
 
	var current_url_branch = +"?branch=__BRANCH__";
 
	var url = url_base;
 
	var node_url = node_list_url;	
 

	
 
	YUE.on('stay_at_branch','click',function(e){
 
	    if(e.target.checked){
 
	        var uri = current_url_branch;
 
	        uri = uri.replace('__BRANCH__',e.target.value);
 
	        window.location = uri;
 
	    }
 
	    else{
 
	        window.location = current_url;
 
	    }
 
	})            
 

	
 
	var n_filter = YUD.get('node_filter');
 
	var F = YAHOO.namespace('node_filter');
 
	
 
	F.filterTimeout = null;
 
	var nodes = null;
 

	
rhodecode/templates/admin/notifications/notifications.html
Show inline comments
 
@@ -4,50 +4,53 @@
 
<%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:10px 15px;text-align: right">
 
      <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(){
 
	    	YUD.get('notification_counter').innerHTML=0;
 
    	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/admin/notifications/notifications_data.html
Show inline comments
 

	
 
%if c.notifications:
 
<%
 
unread = lambda n:{False:'unread'}.get(n)
 
%>
 
<div class="table">
 
  <div class="notification-list">
 
<div class="notification-paginator">
 
  <div class="pagination-wh pagination-left">
 
  ${c.notifications.pager('$link_previous ~2~ $link_next')}
 
  </div>
 
</div>
 

	
 
<div class="notification-list  notification-table">
 
  %for notification in c.notifications:
 
    <div id="notification_${notification.notification.notification_id}" class="container ${unread(notification.read)}">
 
      <div class="notification-header">
 
        <div class="gravatar">
 
            <img alt="gravatar" src="${h.gravatar_url(h.email(notification.notification.created_by_user.email),24)}"/>
 
        </div>
 
        <div class="desc ${unread(notification.read)}">
 
        <a href="${url('notification', notification_id=notification.notification.notification_id)}">${notification.notification.description}</a>
 
        </div>
 
        <div class="delete-notifications">
 
          <span id="${notification.notification.notification_id}" class="delete-notification delete_icon action"></span>
 
        </div>
 
      </div>
 
      <div class="notification-subject">${h.literal(notification.notification.subject)}</div>
 
    </div>
 
  %endfor
 
  </div>
 

	
 
<div class="notification-paginator">
 
  <div class="pagination-wh pagination-left">
 
  ${c.notifications.pager('$link_previous ~2~ $link_next')}
 
</div>
 
</div>
 

	
 
%else:
 
    <div class="table">${_('No notifications here yet')}</div>
 
%endif
rhodecode/templates/admin/notifications/show_notification.html
Show inline comments
 
@@ -18,37 +18,37 @@
 
<%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>
 
    <div class="table">
 
      <div id="notification_${c.notification.notification_id}">
 
        <div class="notification-header">
 
          <div class="gravatar">
 
              <img alt="gravatar" src="${h.gravatar_url(h.email(c.notification.created_by_user.email),24)}"/>
 
          </div>
 
          <div class="desc">
 
              ${c.notification.description}
 
          </div>
 
          <div class="delete-notifications">
 
            <span id="${c.notification.notification_id}" class="delete-notification delete_icon action"></span>
 
          </div>
 
        </div>
 
        <div>${h.rst_w_mentions(c.notification.body)}</div>
 
        <div class="notification-body">${h.rst_w_mentions(c.notification.body)}</div>
 
      </div>
 
    </div>
 
</div>
 
<script type="text/javascript">
 
var url = "${url('notification', notification_id='__NOTIFICATION_ID__')}";
 
var main = "${url('notifications')}";
 
   YUE.on(YUQ('.delete-notification'),'click',function(e){
 
       var notification_id = e.currentTarget.id;
 
       deleteNotification(url,notification_id,[function(){window.location=main}])
 
   })
 
</script>
 
</%def>
rhodecode/templates/admin/settings/settings.html
Show inline comments
 
@@ -189,26 +189,58 @@
 
    </script>
 

	
 
    <h3>${_('Test Email')}</h3>
 
    ${h.form(url('admin_setting', setting_id='email'),method='put')}
 
    <div class="form">
 
        <!-- fields -->
 

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

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

	
 
    <h3>${_('System Info and Packages')}</h3>
 
    <div class="form">
 
    <div>
 
        <h5 id="expand_modules" style="cursor: pointer">&darr; ${_('show')} &darr;</h5>
 
    </div>
 
      <div id="expand_modules_table"  style="display:none">
 
      <h5>Python - ${c.py_version}</h5>
 
      <h5>System - ${c.platform}</h5>
 

	
 
      <table class="table" style="margin:0px 0px 0px 20px">
 
          <colgroup>
 
              <col style="width:220px">
 
          </colgroup>
 
          <tbody>
 
              %for key, value in c.modules:
 
                  <tr>
 
                      <th style="text-align: right;padding-right:5px;">${key}</th>
 
                      <td>${value}</td>
 
                  </tr>
 
              %endfor
 
          </tbody>
 
      </table>
 
      </div>
 
    </div>
 

	
 
    <script type="text/javascript">
 
    YUE.on('expand_modules','click',function(e){
 
    	YUD.setStyle('expand_modules_table','display','');
 
    	YUD.setStyle('expand_modules','display','none');
 
    })
 
    </script>
 

	
 
</div>
 
</%def>
rhodecode/templates/base/root.html
Show inline comments
 
@@ -26,68 +26,72 @@
 
	      _gaq.push(['_setAccount', '${c.ga_code}']);
 
	      _gaq.push(['_trackPageview']);
 

	
 
	      (function() {
 
	        var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
 
	        ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
 
	        var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
 
	      })();
 
	     </script>
 
	    %endif
 

	
 
        ## JAVASCRIPT ##
 
        <%def name="js()">
 
            <script type="text/javascript" src="${h.url('/js/yui.2.9.js')}"></script>
 
            <!--[if lt IE 9]>
 
               <script language="javascript" type="text/javascript" src="${h.url('/js/excanvas.min.js')}"></script>
 
            <![endif]-->
 
            <script type="text/javascript" src="${h.url('/js/yui.flot.js')}"></script>
 
            <script type="text/javascript" src="${h.url('/js/rhodecode.js')}"></script>
 
           ## EXTRA FOR JS
 
           ${self.js_extra()}
 

	
 
            <script type="text/javascript">
 
            var follow_base_url  = "${h.url('toggle_following')}";
 
            var stop_follow_text = "${_('Stop following this repository')}";
 
            var start_follow_text = "${_('Start following this repository')}";
 

	
 
            //JS translations map
 
            var TRANSLATION_MAP = {
 
            	'add another comment':'${_("add another comment")}',
 
                'Stop following this repository':"${_('Stop following this repository')}",
 
                'Start following this repository':"${_('Start following this repository')}",
 
            };
 

	
 
            var onSuccessFollow = function(target){
 
                var f = YUD.get(target.id);
 
                var f_cnt = YUD.get('current_followers_count');
 

	
 
                if(f.getAttribute('class')=='follow'){
 
                    f.setAttribute('class','following');
 
                    f.setAttribute('title',stop_follow_text);
 
                    f.setAttribute('title',TRANSLATION_MAP['Stop following this repository']);
 

	
 
                    if(f_cnt){
 
                        var cnt = Number(f_cnt.innerHTML)+1;
 
                        f_cnt.innerHTML = cnt;
 
                    }
 
                }
 
                else{
 
                    f.setAttribute('class','follow');
 
                    f.setAttribute('title',start_follow_text);
 
                    f.setAttribute('title',TRANSLATION_MAP['Start following this repository']);
 
                    if(f_cnt){
 
                        var cnt = Number(f_cnt.innerHTML)+1;
 
                        f_cnt.innerHTML = cnt;
 
                    }
 
                }
 
            }
 

	
 
            var toggleFollowingUser = function(target,fallows_user_id,token,user_id){
 
                args = 'follows_user_id='+fallows_user_id;
 
                args+= '&amp;auth_token='+token;
 
                if(user_id != undefined){
 
                    args+="&amp;user_id="+user_id;
 
                }
 
                YUC.asyncRequest('POST',follow_base_url,{
 
                    success:function(o){
 
                    	onSuccessFollow(target);
 
                    }
 
                },args);
 
                return false;
 
            }
 

	
 
            var toggleFollowingRepo = function(target,fallows_repo_id,token,user_id){
 

	
 
                args = 'follows_repo_id='+fallows_repo_id;
rhodecode/templates/changelog/changelog.html
Show inline comments
 
@@ -42,79 +42,87 @@ ${c.repo_name} ${_('Changelog')} - ${c.r
 
					<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 id="${cs.raw_id}_changes_info" class="changes">
 
									<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>
 
								   %if cs.parents:
 
									%for p_cs in reversed(cs.parents):
 
										<div class="parent">${_('Parent')}
 
											<span class="changeset_id">${p_cs.revision}:<span class="changeset_hash">${h.link_to(h.short_id(p_cs.raw_id),
 
											h.url('changeset_home',repo_name=c.repo_name,revision=p_cs.raw_id),title=p_cs.message)}</span></span>
 
										</div>
 
									%endfor
 
								   %else:
 
                                        <div class="parent">${_('No parents')}</div>
 
                                   %endif
 

	
 
								<span class="logtags">
 
									%if len(cs.parents)>1:
 
									<span class="merge">${_('merge')}</span>
 
									%endif
 
									%if h.is_hg(c.rhodecode_repo) and cs.branch:
 
									%if cs.branch:
 
									<span class="branchtag" title="${'%s %s' % (_('branch'),cs.branch)}">
 
									   ${h.link_to(h.shorter(cs.branch),h.url('files_home',repo_name=c.repo_name,revision=cs.raw_id))}</span>
 
									   ${h.link_to(h.shorter(cs.branch),h.url('files_home',repo_name=c.repo_name,revision=cs.raw_id))}
 
                                    </span>
 
									%endif
 
                                    %if h.is_hg(c.rhodecode_repo):
 
                                      %for book in cs.bookmarks:
 
                                      <span class="bookbook" title="${'%s %s' % (_('bookmark'),book)}">
 
                                         ${h.link_to(h.shorter(book),h.url('files_home',repo_name=c.repo_name,revision=cs.raw_id))}
 
                                      </span>                   
 
                                      %endfor                
 
									%endif
 
									%for tag in cs.tags:
 
										<span class="tagtag"  title="${'%s %s' % (_('tag'),tag)}">
 
										${h.link_to(h.shorter(tag),h.url('files_home',repo_name=c.repo_name,revision=cs.raw_id))}</span>
 
									%endfor
 
								</span>
 
						</div>
 
					</div>
 

	
 
				%endfor
 
				<div class="pagination-wh pagination-left">
 
					${c.pagination.pager('$link_previous ~2~ $link_next')}
 
				</div>
 
				</div>
 
			</div>
 

	
 
			<script type="text/javascript" src="${h.url('/js/graph.js')}"></script>
 
			<script type="text/javascript">
 
				YAHOO.util.Event.onDOMReady(function(){
 

	
 
                    //Monitor range checkboxes and build a link to changesets
 
                    //ranges
 
                    var checkboxes = YUD.getElementsByClassName('changeset_range');
 
                    var url_tmpl = "${h.url('changeset_home',repo_name=c.repo_name,revision='__REVRANGE__')}";
 
@@ -156,49 +164,49 @@ ${c.repo_name} ${_('Changelog')} - ${c.r
 
                    	if(h > row_h){
 
                    		var offset = row_h - (h+12);
 
                    		YUD.setStyle(m.nextElementSibling,'display','block');
 
                    		YUD.setStyle(m.nextElementSibling,'margin-top',offset+'px');
 
                    	};
 
                    }
 
                    YUE.on(YUQ('.expand'),'click',function(e){
 
                    	var elem = e.currentTarget.parentNode.parentNode;
 
                    	YUD.setStyle(e.currentTarget,'display','none');
 
                    	YUD.setStyle(elem,'height','auto');
 

	
 
                    	//redraw the graph, max_w and jsdata are global vars
 
                        set_canvas(max_w);
 

	
 
                        var r = new BranchRenderer();
 
                        r.render(jsdata,max_w);
 

	
 
                    })
 

	
 
                    // Fetch changeset details
 
                    YUE.on(YUD.getElementsByClassName('changed_total'),'click',function(e){
 
                    	var id = e.currentTarget.id
 
                    	var url = "${h.url('changelog_details',repo_name=c.repo_name,cs='__CS__')}"
 
                    	var url = url.replace('__CS__',id);
 
                    	ypjax(url,id+'_changes_info',function(){tooltip_activate()});
 
                    	ypjax(url,id,function(){tooltip_activate()});
 
                    });
 

	
 
                    // change branch filter
 
                    YUE.on(YUD.get('branch_filter'),'change',function(e){
 
                    	var selected_branch = e.currentTarget.options[e.currentTarget.selectedIndex].value;
 
                    	var url_main = "${h.url('changelog_home',repo_name=c.repo_name)}";
 
                    	var url = "${h.url('changelog_home',repo_name=c.repo_name,branch='__BRANCH__')}";
 
                    	var url = url.replace('__BRANCH__',selected_branch);
 
                    	if(selected_branch != ''){
 
                    		window.location = url;
 
                    	}else{
 
                    		window.location = url_main;
 
                    	}
 

	
 
                    });
 

	
 
					function set_canvas(heads) {
 
						var c = document.getElementById('graph_nodes');
 
						var t = document.getElementById('graph_content');
 
						canvas = document.getElementById('graph_canvas');
 
						var div_h = t.clientHeight;
 
						c.style.height=div_h+'px';
 
						canvas.setAttribute('height',div_h);
 
						c.style.height=max_w+'px';
rhodecode/templates/changeset/changeset.html
Show inline comments
 
@@ -60,133 +60,105 @@
 
		                 <span class="removed" title="${_('removed')}">${len(c.changeset.removed)}</span>
 
		                 <span class="changed" title="${_('changed')}">${len(c.changeset.changed)}</span>
 
		                 <span class="added" title="${_('added')}">${len(c.changeset.added)}</span>
 
	                    % else:
 
                         <span class="removed" title="${_('affected %s files') % len(c.changeset.affected_files)}">!</span>
 
                         <span class="changed" title="${_('affected %s files') % len(c.changeset.affected_files)}">!</span>
 
                         <span class="added"   title="${_('affected %s files') % len(c.changeset.affected_files)}">!</span>
 
	                    % endif
 
		             </div>
 

	
 
		            %if c.changeset.parents:
 
		             %for p_cs in reversed(c.changeset.parents):
 
		                 <div class="parent">${_('Parent')}
 
                     <span class="changeset_id">${p_cs.revision}:<span class="changeset_hash">${h.link_to(h.short_id(p_cs.raw_id),
 
		                     h.url('changeset_home',repo_name=c.repo_name,revision=p_cs.raw_id),title=p_cs.message)}</span></span>
 
		                 </div>
 
		             %endfor
 
                    %else:
 
                        <div class="parent">${_('No parents')}</div>
 
                    %endif
 
		         <span class="logtags">
 
                 %if len(c.changeset.parents)>1:
 
                 <span class="merge">${_('merge')}</span>
 
                 %endif
 
		             %if c.changeset.branch:
 
		             <span class="branchtag" title="${'%s %s' % (_('branch'),c.changeset.branch)}">
 
		             ${h.link_to(c.changeset.branch,h.url('files_home',repo_name=c.repo_name,revision=c.changeset.raw_id))}</span>
 
		             ${h.link_to(c.changeset.branch,h.url('files_home',repo_name=c.repo_name,revision=c.changeset.raw_id))}
 
                     </span>
 
                     %endif
 
		             %for tag in c.changeset.tags:
 
		                 <span class="tagtag"  title="${'%s %s' % (_('tag'),tag)}">
 
		                 ${h.link_to(tag,h.url('files_home',repo_name=c.repo_name,revision=c.changeset.raw_id))}</span>
 
		             %endfor
 
		         </span>
 
	                </div>
 
	        </div>
 
	        <span>
 
	        ${_('%s files affected with %s insertions and %s deletions:') % (len(c.changeset.affected_files),c.lines_added,c.lines_deleted)}
 
	        </span>
 
	        <div class="cs_files">
 
	                %for change,filenode,diff,cs1,cs2,stat in c.changes:
 
	                    <div class="cs_${change}">
 
                            <div class="node">
 
                            %if change != 'removed':
 
                                ${h.link_to(h.safe_unicode(filenode.path),c.anchor_url(filenode.changeset.raw_id,filenode.path,request.GET)+"_target")}
 
                            %else:
 
                                ${h.link_to(h.safe_unicode(filenode.path),h.url.current(anchor=h.FID('',filenode.path)))}
 
                            %endif
 
                            </div>
 
		                    <div class="changes">${h.fancy_file_stats(stat)}</div>
 
	                    </div>
 
	                %endfor
 
	                % if c.cut_off:
 
	                  ${_('Changeset was too big and was cut off...')}
 
	                % endif
 
	        </div>
 
	    </div>
 

	
 
    </div>
 

	
 
    ## diff block
 
    <%namespace name="diff_block" file="/changeset/diff_block.html"/>
 
    ${diff_block.diff_block(c.changes)}
 

	
 
    ## template for inline comment form
 
    <%namespace name="comment" file="/changeset/changeset_file_comment.html"/>
 
    ${comment.comment_inline_form(c.changeset)}
 

	
 
    ## render comments
 
    ${comment.comments(c.changeset)}
 

	
 
    <script type="text/javascript">
 
      var deleteComment = function(comment_id){
 

	
 
          var url = "${url('changeset_comment_delete',repo_name=c.repo_name,comment_id='__COMMENT_ID__')}".replace('__COMMENT_ID__',comment_id);
 
          var postData = '_method=delete';
 
          var success = function(o){
 
              var n = YUD.get('comment-'+comment_id);
 
              n.parentNode.removeChild(n);
 
          }
 
          ajaxPOST(url,postData,success);
 
      }
 

	
 
      YUE.onDOMReady(function(){
 

	
 
    	  AJAX_COMMENT_URL = "${url('changeset_comment',repo_name=c.repo_name,revision=c.changeset.raw_id)}";
 
    	  AJAX_COMMENT_DELETE_URL = "${url('changeset_comment_delete',repo_name=c.repo_name,comment_id='__COMMENT_ID__')}"
 
          YUE.on(YUQ('.show-inline-comments'),'change',function(e){
 
              var show = 'none';
 
              var target = e.currentTarget;
 
              if(target.checked){
 
                  var show = ''
 
              }
 
              var boxid = YUD.getAttribute(target,'id_for');
 
              var comments = YUQ('#{0} .inline-comments'.format(boxid));
 
              for(c in comments){
 
                 YUD.setStyle(comments[c],'display',show);
 
              }
 
              var btns = YUQ('#{0} .inline-comments-button'.format(boxid));
 
              for(c in btns){
 
                  YUD.setStyle(btns[c],'display',show);
 
               }
 
          })
 

	
 
          YUE.on(YUQ('.line'),'click',function(e){
 
              var tr = e.currentTarget;
 
              injectInlineForm(tr);
 
          });
 

	
 
          // inject comments into they proper positions
 
          var file_comments = YUQ('.inline-comment-placeholder');
 

	
 
          for (f in file_comments){
 
              var box = file_comments[f];
 
              var inlines = box.children;
 
              for(var i=0; i<inlines.length; i++){
 
                  try{
 

	
 
                    var inline = inlines[i];
 
                    var lineno = YUD.getAttribute(inlines[i],'line');
 
                    var lineid = "{0}_{1}".format(YUD.getAttribute(inline,'target_id'),lineno);
 
                    var target_line = YUD.get(lineid);
 

	
 
                    var add = createInlineAddButton(target_line.parentNode,'${_("add another comment")}');
 
                    YUD.insertAfter(add,target_line.parentNode);
 

	
 
                    var comment = new YAHOO.util.Element(tableTr('inline-comments',inline.innerHTML))
 
                    YUD.insertAfter(comment,target_line.parentNode);
 
                  }catch(e){
 
                	  console.log(e);
 
                  }
 
              }
 
          }
 
          renderInlineComments(file_comments);
 
      })
 

	
 
    </script>
 

	
 
    </div>
 
</%def>
rhodecode/templates/changeset/changeset_comment_block.html
Show inline comments
 
new file 100644
 
<%namespace name="comment" file="/changeset/changeset_file_comment.html"/>
 
${comment.comment_block(c.co)}
rhodecode/templates/changeset/changeset_file_comment.html
Show inline comments
 
## -*- coding: utf-8 -*-
 
## usage:
 
## <%namespace name="comment" file="/changeset/changeset_file_comment.html"/>
 
## ${comment.comment_block(co)}
 
##
 
<%def name="comment_block(co)">
 
  <div class="comment" id="comment-${co.comment_id}">
 
  <div class="comment" id="comment-${co.comment_id}" line="${co.line_no}">
 
    <div class="comment-wrapp">
 
  	<div class="meta">
 
  		<span class="user">
 
  			<img src="${h.gravatar_url(co.author.email, 20)}" />
 
  			${co.author.username}
 
  		</span>
 
  		<span class="date">
 
  			${h.age(co.modified_at)}
 
  		</span>
 
      %if h.HasPermissionAny('hg.admin', 'repository.admin')() or co.author.user_id == c.rhodecode_user.user_id:
 
        <span class="buttons">
 
          <span onClick="deleteComment(${co.comment_id})" class="delete-comment ui-btn">${_('Delete')}</span>
 
        </span>
 
      %endif
 
  	</div>
 
  	<div class="text">
 
  		${h.rst_w_mentions(co.text)|n}
 
  	</div>
 
    </div>
 
  </div>
 
</%def>
 

	
 

	
 
<%def name="comment_inline_form(changeset)">
 
<div id='comment-inline-form-template' style="display:none">
 
  <div class="comment-inline-form">
 
  %if c.rhodecode_user.username != 'default':
 
      ${h.form(h.url('changeset_comment', repo_name=c.repo_name, revision=changeset.raw_id))}
 
    <div class="overlay"><div class="overlay-text">${_('Submitting...')}</div></div>
 
      ${h.form(h.url('changeset_comment', repo_name=c.repo_name, revision=changeset.raw_id),class_='inline-form')}
 
      <div class="clearfix">
 
          <div class="comment-help">${_('Commenting on line')} {1}. ${_('Comments parsed using')}
 
          <a href="${h.url('rst_help')}">RST</a> ${_('syntax')} ${_('with')}
 
          <span style="color:#003367" class="tooltip" title="${_('Use @username inside this text to send notification to this RhodeCode user')}">@mention</span> ${_('support')}
 
          </div>
 
          <textarea id="text_{1}" name="text"></textarea>
 
      </div>
 
      <div class="comment-button">
 
      <input type="hidden" name="f_path" value="{0}">
 
      <input type="hidden" name="line" value="{1}">
 
      ${h.submit('save', _('Comment'), class_='ui-btn')}
 
      ${h.submit('save', _('Comment'), class_='ui-btn save-inline-form')}
 
      ${h.reset('hide-inline-form', _('Hide'), class_='ui-btn hide-inline-form')}
 
      </div>
 
      ${h.end_form()}
 
  %else:
 
      ${h.form('')}
 
      <div class="clearfix">
 
          <div class="comment-help">
 
            ${'You need to be logged in to comment.'} <a href="${h.url('login_home',came_from=h.url.current())}">${_('Login now')}</a>
 
          </div>
 
      </div>
 
      <div class="comment-button">
 
      ${h.reset('hide-inline-form', _('Hide'), class_='ui-btn hide-inline-form')}
 
      </div>
 
      ${h.end_form()}
 
  %endif
 
  </div>
 
</div>
 
</%def>
 

	
 

	
 
<%def name="comments(changeset)">
 

	
 
<div class="comments">
 
<%def name="inlines(changeset)">
 
    <div class="comments-number">${len(c.comments)} comment(s) (${c.inline_cnt} ${_('inline')})</div>
 

	
 
    %for path, lines in c.inline_comments:
 
        <div style="display:none" class="inline-comment-placeholder" path="${path}" target_id="${h.FID(changeset.raw_id,path)}">
 
        % for line,comments in lines.iteritems():
 
            <div class="inline-comment-placeholder-line" line="${line}" target_id="${h.safeid(h.safe_unicode(path))}">
 
            <div style="display:none" class="inline-comment-placeholder" path="${path}" target_id="${h.safeid(h.safe_unicode(path))}">
 
            %for co in comments:
 
                ${comment_block(co)}
 
            %endfor
 
            </div>
 
        %endfor
 
        </div>
 
    %endfor
 

	
 
</%def>
 

	
 
<%def name="comments(changeset)">
 

	
 
<div class="comments">
 
    <div id="inline-comments-container">
 
     ${inlines(changeset)}
 
    </div>
 

	
 
    %for co in c.comments:
 
        <div id="comment-tr-${co.comment_id}">
 
        ${comment_block(co)}
 
        </div>
 
    %endfor
 
    %if c.rhodecode_user.username != 'default':
 
    <div class="comment-form">
 
        ${h.form(h.url('changeset_comment', repo_name=c.repo_name, revision=changeset.raw_id))}
 
        <strong>${_('Leave a comment')}</strong>
 
        <div class="clearfix">
 
            <div class="comment-help">
 
                ${_('Comments parsed using')} <a href="${h.url('rst_help')}">RST</a> ${_('syntax')}
 
                ${_('with')} <span style="color:#003367" class="tooltip" title="${_('Use @username inside this text to send notification to this RhodeCode user')}">@mention</span> ${_('support')}
 
            </div>
 
                ${h.textarea('text')}
 
        </div>
 
        <div class="comment-button">
 
        ${h.submit('save', _('Comment'), class_='ui-button')}
 
        </div>
 
        ${h.end_form()}
 
    </div>
 
    %endif
 
</div>
 
</%def>
rhodecode/templates/files/files_annotate.html
Show inline comments
 
deleted file
rhodecode/templates/files/files_browser.html
Show inline comments
 
@@ -49,49 +49,53 @@
 
		                 <th>${_('Mimetype')}</th>
 
		                 <th>${_('Last Revision')}</th>
 
		                 <th>${_('Last modified')}</th>
 
		                 <th>${_('Last commiter')}</th>
 
		             </tr>
 
		         </thead>
 

	
 
                <tbody id="tbody">
 
          		%if c.file.parent:
 
         		<tr class="parity0">
 
	          		<td>
 
	          			${h.link_to('..',h.url('files_home',repo_name=c.repo_name,revision=c.changeset.raw_id,f_path=c.file.parent.path),class_="browser-dir ypjax-link")}
 
	          		</td>
 
	          		<td></td>
 
	          		<td></td>
 
	          		<td></td>
 
	          		<td></td>
 
	          		<td></td>
 
				</tr>
 
          		%endif
 

	
 
		    %for cnt,node in enumerate(c.file):
 
				<tr class="parity${cnt%2}">
 
		             <td>
 
                        %if node.is_submodule():
 
                           ${h.link_to(node.name,node.url or '#',class_="submodule-dir ypjax-link")}
 
                        %else:
 
                        ${h.link_to(node.name,h.url('files_home',repo_name=c.repo_name,revision=c.changeset.raw_id,f_path=h.safe_unicode(node.path)),class_=file_class(node)+" ypjax-link")}
 
                        %endif:
 
		             </td>
 
		             <td>
 
		             %if node.is_file():
 
		             	${h.format_byte_size(node.size,binary=True)}
 
		             %endif
 
		             </td>
 
		             <td>
 
		              %if node.is_file():
 
		                  ${node.mimetype}
 
		              %endif
 
		             </td>
 
		             <td>
 
		             	%if node.is_file():
 
		             		<div class="tooltip" title="${node.last_changeset.message}">
 
		             		<pre>${'r%s:%s' % (node.last_changeset.revision,node.last_changeset.short_id)}</pre>
 
                            </div>
 
		             	%endif
 
		             </td>
 
		             <td>
 
		             	%if node.is_file():
 
		             		<span class="tooltip" title="${node.last_changeset.date}">
 
                            ${h.age(node.last_changeset.date)}</span>
 
		             	%endif
 
		             </td>
rhodecode/templates/files/files_edit.html
Show inline comments
 
@@ -35,44 +35,44 @@
 
        </ul>
 
    </div>
 
    <div class="table">
 
		<div id="files_data">
 
			<h3 class="files_location">${_('Location')}: ${h.files_breadcrumbs(c.repo_name,c.cs.revision,c.file.path)}</h3>
 
			${h.form(h.url.current(),method='post',id='eform')}
 
			<div id="body" class="codeblock">
 
            <div class="code-header">
 
                <div class="stats">
 
                    <div class="left"><img src="${h.url('/images/icons/file.png')}"/></div>
 
                    <div class="left item">${h.link_to("r%s:%s" % (c.file.changeset.revision,h.short_id(c.file.changeset.raw_id)),h.url('changeset_home',repo_name=c.repo_name,revision=c.file.changeset.raw_id))}</div>
 
                    <div class="left item">${h.format_byte_size(c.file.size,binary=True)}</div>
 
                    <div class="left item last">${c.file.mimetype}</div>
 
                    <div class="buttons">
 
                      ${h.link_to(_('show annotation'),h.url('files_annotate_home',repo_name=c.repo_name,revision=c.cs.raw_id,f_path=c.f_path),class_="ui-btn")}
 
                      ${h.link_to(_('show as raw'),h.url('files_raw_home',repo_name=c.repo_name,revision=c.cs.raw_id,f_path=c.f_path),class_="ui-btn")}
 
                      ${h.link_to(_('download as raw'),h.url('files_rawfile_home',repo_name=c.repo_name,revision=c.cs.raw_id,f_path=c.f_path),class_="ui-btn")}
 
                      % if h.HasRepoPermissionAny('repository.write','repository.admin')(c.repo_name):
 
                       % if not c.file.is_binary:
 
                        ${h.link_to(_('source'),h.url('files_home',repo_name=c.repo_name,revision=c.cs.raw_id,f_path=c.f_path),class_="ui-btn")}
 
                       % endif
 
                      % endif
 
                    </div>
 
                </div>
 
                <div class="commit">${_('Editing file')}: ${c.file.path}</div>
 
                <div class="commit">${_('Editing file')}: ${c.file.unicode_path}</div>
 
            </div>
 
			    <pre id="editor_pre"></pre>
 
				<textarea id="editor" name="content" style="display:none">${h.escape(c.file.content)|n}</textarea>
 
				<div style="padding: 10px;color:#666666">${_('commit message')}</div>
 
				<textarea id="commit" name="message" style="height: 60px;width: 99%;margin-left:4px"></textarea>
 
			</div>
 
			<div style="text-align: left;padding-top: 5px">
 
            ${h.submit('commit',_('Commit changes'),class_="ui-btn")}
 
            ${h.reset('reset',_('Reset'),class_="ui-btn")}
 
			</div>
 
			${h.end_form()}
 
			<script type="text/javascript">
 
			var reset_url = "${h.url('files_home',repo_name=c.repo_name,revision=c.cs.raw_id,f_path=c.file.path)}";
 
			initCodeMirror('editor',reset_url);
 
			</script>
 
		</div>
 
    </div>
 
</div>
 
</%def>
rhodecode/templates/files/files_source.html
Show inline comments
 
<dl>
 
	<dt style="padding-top:10px;font-size:16px">${_('History')}</dt>
 
	<dd>
 
		<div>
 
		${h.form(h.url('files_diff_home',repo_name=c.repo_name,f_path=c.f_path),method='get')}
 
		${h.hidden('diff2',c.file.changeset.raw_id)}
 
		${h.select('diff1',c.file.changeset.raw_id,c.file_history)}
 
		${h.submit('diff','diff to revision',class_="ui-btn")}
 
		${h.submit('show_rev','show at revision',class_="ui-btn")}
 
		${h.end_form()}
 
		</div>
 
	</dd>
 
</dl>
 

	
 
<div id="body" class="codeblock">
 
	<div class="code-header">
 
        <div class="stats">
 
            <div class="left img"><img src="${h.url('/images/icons/file.png')}"/></div>
 
            <div class="left item"><pre>${h.link_to("r%s:%s" % (c.file.changeset.revision,h.short_id(c.file.changeset.raw_id)),h.url('changeset_home',repo_name=c.repo_name,revision=c.file.changeset.raw_id))}</pre></div>
 
            <div class="left item"><pre class="tooltip" title="${c.file.changeset.date}">${h.link_to("r%s:%s" % (c.file.changeset.revision,h.short_id(c.file.changeset.raw_id)),h.url('changeset_home',repo_name=c.repo_name,revision=c.file.changeset.raw_id))}</pre></div>
 
            <div class="left item"><pre>${h.format_byte_size(c.file.size,binary=True)}</pre></div>
 
            <div class="left item last"><pre>${c.file.mimetype}</pre></div>
 
            <div class="buttons">
 
              %if c.annotate:
 
                ${h.link_to(_('show source'),    h.url('files_home',         repo_name=c.repo_name,revision=c.file.changeset.raw_id,f_path=c.f_path),class_="ui-btn")}
 
              %else:
 
              ${h.link_to(_('show annotation'),h.url('files_annotate_home',repo_name=c.repo_name,revision=c.file.changeset.raw_id,f_path=c.f_path),class_="ui-btn")}
 
              %endif
 
              ${h.link_to(_('show as raw'),h.url('files_raw_home',repo_name=c.repo_name,revision=c.file.changeset.raw_id,f_path=c.f_path),class_="ui-btn")}
 
              ${h.link_to(_('download as raw'),h.url('files_rawfile_home',repo_name=c.repo_name,revision=c.file.changeset.raw_id,f_path=c.f_path),class_="ui-btn")}
 
              % if h.HasRepoPermissionAny('repository.write','repository.admin')(c.repo_name):
 
               % if not c.file.is_binary:
 
                ${h.link_to(_('edit'),h.url('files_edit_home',repo_name=c.repo_name,revision=c.file.changeset.raw_id,f_path=c.f_path),class_="ui-btn")}
 
               % endif
 
              % endif
 
            </div>
 
        </div>
 
        <div class="author">
 
            <div class="gravatar">
 
                <img alt="gravatar" src="${h.gravatar_url(h.email(c.file.changeset.author),16)}"/>
 
            </div>
 
            <div title="${c.file.changeset.author}" class="user">${h.person(c.file.changeset.author)}</div>
 
        </div>
 
		<div class="commit">${h.urlify_commit(c.file.changeset.message,c.repo_name)}</div>
 
	</div>
 
	<div class="code-body">
 
	   %if c.file.is_binary:
 
	       ${_('Binary file (%s)') % c.file.mimetype}
 
	   %else:
 
		% if c.file.size < c.cut_off_limit:
 
            %if c.annotate:
 
              ${h.pygmentize_annotation(c.repo_name,c.file,linenos=True,anchorlinenos=True,lineanchors='L',cssclass="code-highlight")}
 
            %else:
 
			${h.pygmentize(c.file,linenos=True,anchorlinenos=True,lineanchors='L',cssclass="code-highlight")}
 
            %endif
 
		%else:
 
			${_('File is too big to display')} ${h.link_to(_('show as raw'),
 
			h.url('files_raw_home',repo_name=c.repo_name,revision=c.file.changeset.raw_id,f_path=c.f_path))}
 
		%endif
 
     %endif
 
	</div>
 
</div>
 

	
 
       <script type="text/javascript">
 
YUE.onDOMReady(function(){
 
           function highlight_lines(lines){
 
               for(pos in lines){
 
                 YUD.setStyle('L'+lines[pos],'background-color','#FFFFBE');
 
               }
 
           }
 
           page_highlights = location.href.substring(location.href.indexOf('#')+1).split('L');
 
           if (page_highlights.length == 2){
 
              highlight_ranges  = page_highlights[1].split(",");
 

	
 
              var h_lines = [];
 
              for (pos in highlight_ranges){
 
                   var _range = highlight_ranges[pos].split('-');
 
                   if(_range.length == 2){
 
                       var start = parseInt(_range[0]);
 
                       var end = parseInt(_range[1]);
 
                       if (start < end){
 
                           for(var i=start;i<=end;i++){
 
                               h_lines.push(i);
 
                           }
 
                       }
 
                   }
 
                   else{
 
                       h_lines.push(parseInt(highlight_ranges[pos]));
 
                   }
 
             }
 
           highlight_lines(h_lines);
 

	
 
           //remember original location
 
           var old_hash  = location.href.substring(location.href.indexOf('#'));
 

	
 
           // this makes a jump to anchor moved by 3 posstions for padding
 
           window.location.hash = '#L'+Math.max(parseInt(h_lines[0])-3,1);
 

	
 
           //sets old anchor
 
           window.location.hash = old_hash;
 

	
 
           }
 
       </script>
 
     %endif
 
	</div>
 
</div>
 

	
 
<script type="text/javascript">
 
YUE.onDOMReady(function(){
 
    YUE.on('show_rev','click',function(e){
 
    	YUE.preventDefault(e);
 
        var cs = YUD.get('diff1').value;
 
        %if c.annotate:
 
          var url = "${h.url('files_annotate_home',repo_name=c.repo_name,revision='__CS__',f_path=c.f_path)}".replace('__CS__',cs);
 
        %else:
 
        var url = "${h.url('files_home',repo_name=c.repo_name,revision='__CS__',f_path=c.f_path)}".replace('__CS__',cs);
 
        %endif
 
        window.location = url;
 
    });
 
    YUE.on('hlcode','mouseup',getSelectionLink("${_('Selection link')}"))
 
   });
 
</script>
rhodecode/templates/files/files_ypjax.html
Show inline comments
 
%if c.file:
 
    <h3 class="files_location">
 
        ${_('Location')}: ${h.files_breadcrumbs(c.repo_name,c.changeset.raw_id,c.file.path)}
 
        %if c.annotate:
 
        - ${_('annotation')}
 
        %endif
 
    </h3>
 
        %if c.file.is_dir():
 
            <%include file='files_browser.html'/>
 
        %else:
 
            <%include file='files_source.html'/>
 
        %endif
 
%else:
 
    <h2>
 
        <a href="#" onClick="javascript:parent.history.back();" target="main">${_('Go back')}</a>
 
        ${_('No files at given path')}: "${c.f_path or "/"}"
 
    </h2>
 
%endif
rhodecode/templates/forks/fork.html
Show inline comments
 
@@ -44,43 +44,43 @@
 
                     ${h.select('repo_group','',c.repo_groups,class_="medium")}
 
                 </div>
 
            </div>
 
            <div class="field">
 
                <div class="label label-textarea">
 
                    <label for="description">${_('Description')}:</label>
 
                </div>
 
                <div class="textarea text-area editor">
 
                    ${h.textarea('description',cols=23,rows=5)}
 
                </div>
 
             </div>
 
            <div class="field">
 
                <div class="label label-checkbox">
 
                    <label for="private">${_('Private')}:</label>
 
                </div>
 
                <div class="checkboxes">
 
                    ${h.checkbox('private',value="True")}
 
                </div>
 
            </div>
 
            <div class="field">
 
                <div class="label label-checkbox">
 
                    <label for="private">${_('Copy permissions')}:</label>
 
                </div>
 
                <div class="checkboxes">
 
                    ${h.checkbox('copy_permissions',value="True")}
 
                    ${h.checkbox('copy_permissions',value="True", checked="checked")}
 
                </div>
 
             </div>
 
            <div class="field">
 
                <div class="label label-checkbox">
 
                    <label for="private">${_('Update after clone')}:</label>
 
                </div>
 
                <div class="checkboxes">
 
                    ${h.checkbox('update_after_clone',value="True")}
 
                </div>
 
             </div>
 
	        <div class="buttons">
 
	          ${h.submit('',_('fork this repository'),class_="ui-button")}
 
	        </div>
 
        </div>
 
    </div>
 
    ${h.end_form()}
 
</div>
 
</%def>
rhodecode/templates/index_base.html
Show inline comments
 
@@ -98,49 +98,49 @@
 
                    ##
 
                    <td title="${repo['contact']}">${h.person(repo['contact'])}</td>
 
                    <td>
 
                      %if c.rhodecode_user.username != 'default':
 
                        <a title="${_('Subscribe to %s rss feed')%repo['name']}" class="rss_icon"  href="${h.url('rss_feed_home',repo_name=repo['name'],api_key=c.rhodecode_user.api_key)}"></a>
 
                      %else:
 
                        <a title="${_('Subscribe to %s rss feed')%repo['name']}" class="rss_icon"  href="${h.url('rss_feed_home',repo_name=repo['name'])}"></a>
 
                      %endif:
 
                    </td>
 
                    <td>
 
                      %if c.rhodecode_user.username != 'default':
 
                        <a title="${_('Subscribe to %s atom feed')%repo['name']}"  class="atom_icon" href="${h.url('atom_feed_home',repo_name=repo['name'],api_key=c.rhodecode_user.api_key)}"></a>
 
                      %else:
 
                        <a title="${_('Subscribe to %s atom feed')%repo['name']}"  class="atom_icon" href="${h.url('atom_feed_home',repo_name=repo['name'])}"></a>
 
                      %endif:
 
                    </td>
 
                </tr>
 
            %endfor
 
            </tbody>
 
            </table>
 
            </div>
 
        </div>
 
    </div>
 
    <script>
 
      YUD.get('repo_count').innerHTML = ${cnt};
 
      YUD.get('repo_count').innerHTML = ${cnt+1};
 
      var func = function(node){
 
          return node.parentNode.parentNode.parentNode.parentNode;
 
      }
 

	
 

	
 
      // groups table sorting
 
      var myColumnDefs = [
 
          {key:"name",label:"${_('Group Name')}",sortable:true,
 
              sortOptions: { sortFunction: groupNameSort }},
 
          {key:"desc",label:"${_('Description')}",sortable:true},
 
      ];
 

	
 
      var myDataSource = new YAHOO.util.DataSource(YUD.get("groups_list"));
 

	
 
      myDataSource.responseType = YAHOO.util.DataSource.TYPE_HTMLTABLE;
 
      myDataSource.responseSchema = {
 
          fields: [
 
              {key:"name"},
 
              {key:"desc"},
 
          ]
 
      };
 

	
 
      var myDataTable = new YAHOO.widget.DataTable("groups_list_wrap", myColumnDefs, myDataSource,
 
              {
rhodecode/templates/shortlog/shortlog_data.html
Show inline comments
 
## -*- coding: utf-8 -*-
 
%if c.repo_changesets:
 
<table class="table_disp">
 
	<tr>
 
	    <th class="left">${_('revision')}</th>
 
        <th class="left">${_('commit message')}</th>
 
		<th class="left">${_('age')}</th>
 
		<th class="left">${_('author')}</th>
 
		<th class="left">${_('branch')}</th>
 
		<th class="left">${_('tags')}</th>
 
	</tr>
 
%for cnt,cs in enumerate(c.repo_changesets):
 
	<tr class="parity${cnt%2}">
 
        <td>
 
            <div><pre><a href="${h.url('files_home',repo_name=c.repo_name,revision=cs.raw_id)}">r${cs.revision}:${h.short_id(cs.raw_id)}</a></pre></div>
 
        </td>
 
        <td>
 
            ${h.link_to(h.truncate(cs.message,50),
 
            ${h.link_to(h.truncate(cs.message,50) or _('No commit message'),
 
            h.url('changeset_home',repo_name=c.repo_name,revision=cs.raw_id),
 
            title=cs.message)}
 
        </td>
 
        <td><span class="tooltip" title="${cs.date}">
 
                      ${h.age(cs.date)}</span>
 
        </td>
 
		<td title="${cs.author}">${h.person(cs.author)}</td>
 
		<td>
 
			<span class="logtags">
 
                %if cs.branch:
 
				<span class="branchtag">
 
                %if h.is_hg(c.rhodecode_repo):
 
                    ${cs.branch}
 
                </span>
 
                %endif
 
                </span>
 
			</span>
 
		</td>
 
		<td>
 
			<span class="logtags">
 
				%for tag in cs.tags:
 
					<span class="tagtag">${tag}</span>
 
				%endfor
 
			</span>
 
		</td>
 
	</tr>
 
%endfor
 

	
 
</table>
 

	
 
<script type="text/javascript">
 
  YUE.onDOMReady(function(){
 
    YUE.delegate("shortlog_data","click",function(e, matchedEl, container){
 
        ypjax(e.target.href,"shortlog_data",function(){tooltip_activate();});
 
        YUE.preventDefault(e);
 
    },'.pager_link');
 
  });
 
</script>
 

	
 
<div class="pagination-wh pagination-left">
 
${c.repo_changesets.pager('$link_previous ~2~ $link_next')}
 
</div>
 
%else:
 

	
 
%if h.HasRepoPermissionAny('repository.write','repository.admin')(c.repo_name):
 
<h4>${_('Add or upload files directly via RhodeCode')}</h4>
 
<div style="margin: 20px 30px;">
 
  <div id="add_node_id" class="add_node">
 
      <a class="ui-btn" href="${h.url('files_add_home',repo_name=c.repo_name,revision=0,f_path='')}">${_('add new file')}</a>
 
  </div>
 
</div>
 
%endif
 

	
 

	
 
<h4>${_('Push new repo')}</h4>
 
<pre>
 
    ${c.rhodecode_repo.alias} clone ${c.clone_repo_url}
 
    ${c.rhodecode_repo.alias} add README # add first file
 
    ${c.rhodecode_repo.alias} commit -m "Initial" # commit with message
 
    ${c.rhodecode_repo.alias} push # push changes back
 
    ${c.rhodecode_repo.alias} push ${'origin master' if h.is_git(c.rhodecode_repo) else ''} # push changes back
 
</pre>
 

	
 
<h4>${_('Existing repository?')}</h4>
 
<pre>
 
    ${c.rhodecode_repo.alias} push ${c.clone_repo_url}
 
</pre>
 
%endif

Changeset was too big and was cut off... Show full diff anyway

0 comments (0 inline, 0 general)