Changeset - dc2584ba5fbc
[Not reviewed]
Merge default
0 80 6
Marcin Kuzminski - 14 years ago 2012-03-28 19:54:16
marcin@python-works.com
merged beta into default branch
83 files changed:
Changeset was too big and was cut off... Show full diff anyway
0 comments (0 inline, 0 general)
.hgignore
Show inline comments
 
@@ -6,6 +6,7 @@ syntax: glob
 
*.egg
 

	
 
syntax: regexp
 
^rcextensions
 
^build
 
^docs/build/
 
^docs/_build/
README.rst
Show inline comments
 
@@ -15,10 +15,11 @@ RhodeCode is similar in some respects to
 
however RhodeCode can be run as standalone hosted application on your own server.
 
It is open source and donation ware and focuses more on providing a customized, 
 
self administered interface for Mercurial_ and GIT_  repositories. 
 
RhodeCode is powered by a vcs_ library that Lukasz Balcerzak and I created to 
 
handle multiple different version control systems.
 
RhodeCode works on *nix systems and Windows it is powered by a vcs_ library 
 
that Lukasz Balcerzak and Marcin Kuzminski created to handle multiple 
 
different version control systems.
 

	
 
RhodeCode uses `Semantic Versioning <http://semver.org/>`_
 
RhodeCode uses `PEP386 versioning http://www.python.org/dev/peps/pep-0386/`_
 

	
 
Installation
 
------------
 
@@ -99,7 +100,7 @@ RhodeCode Features
 
- Intelligent cache with invalidation after push or project change, provides 
 
  high performance and always up to date data.
 
- Rss / atom feeds, gravatar support, download sources as zip/tar/gz
 
- Async tasks for speed and performance using celery_ (works without them too)  
 
- Optional async tasks for speed and performance using celery_  
 
- Backup scripts can do backup of whole app and send it over scp to desired 
 
  location 
 
- Based on pylons / sqlalchemy / sqlite / whoosh / vcs
development.ini
Show inline comments
 
@@ -93,6 +93,11 @@ issue_prefix = #
 
## all running rhodecode instances. Leave empty if you don't use it
 
instance_id = 
 

	
 
## alternative return HTTP header for failed authentication. Default HTTP
 
## response is 401 HTTPUnauthorized. Currently HG clients have troubles with 
 
## handling that. Set this variable to 403 to return HTTPForbidden
 
auth_ret_code =
 

	
 
####################################
 
###        CELERY CONFIG        ####
 
####################################
 
@@ -171,6 +176,7 @@ beaker.cache.sql_cache_long.key_length =
 

	
 
beaker.session.type = file
 
beaker.session.key = rhodecode
 
# secure cookie requires AES python libraries
 
#beaker.session.encrypt_key = g654dcno0-9873jhgfreyu
 
#beaker.session.validate_key = 9712sds2212c--zxc123
 
beaker.session.timeout = 36000
 
@@ -207,13 +213,13 @@ logview.pylons.util = #eee
 
sqlalchemy.db1.url = postgresql://postgres:qwe@localhost/rhodecode
 
sqlalchemy.db1.echo = false
 
sqlalchemy.db1.pool_recycle = 3600
 
sqlalchemy.convert_unicode = true
 
sqlalchemy.db1.convert_unicode = true
 

	
 
################################
 
### LOGGING CONFIGURATION   ####
 
################################
 
[loggers]
 
keys = root, routes, rhodecode, sqlalchemy, beaker, templates
 
keys = root, routes, rhodecode, sqlalchemy, beaker, templates, whoosh_indexer
 

	
 
[handlers]
 
keys = console, console_sql
 
@@ -259,6 +265,12 @@ handlers = console_sql
 
qualname = sqlalchemy.engine
 
propagate = 0
 

	
 
[logger_whoosh_indexer]
 
level = DEBUG
 
handlers = 
 
qualname = whoosh_indexer
 
propagate = 1
 

	
 
##############
 
## HANDLERS ##
 
##############
docs/api/api.rst
Show inline comments
 
@@ -27,7 +27,7 @@ API ACCESS
 
All clients are required to send JSON-RPC spec JSON data::
 

	
 
    {   
 
        "id:<id>,
 
        "id:"<id>",
 
        "api_key":"<api_key>",
 
        "method":"<method_name>",
 
        "args":{"<arg_key>":"<arg_val>"}
 
@@ -50,9 +50,9 @@ Simply provide
 
RhodeCode API will return always a JSON-RPC response::
 

	
 
    {   
 
        "id":<id>,
 
        "result": "<result>",
 
        "error": null
 
        "id":<id>, # matching id sent by request
 
        "result": "<result>"|null, # JSON formatted result, null if any errors
 
        "error": "null"|<error_message> # JSON formatted error (if any)
 
    }
 

	
 
All responses from API will be `HTTP/1.0 200 OK`, if there's an error while
 
@@ -72,6 +72,7 @@ belonging to user with admin rights
 

	
 
INPUT::
 

	
 
    id : <id_for_response>
 
    api_key : "<api_key>"
 
    method :  "pull"
 
    args :    {
 
@@ -94,6 +95,7 @@ rights.
 

	
 
INPUT::
 

	
 
    id : <id_for_response>
 
    api_key : "<api_key>"
 
    method :  "get_user"
 
    args :    { 
 
@@ -111,7 +113,15 @@ OUTPUT::
 
                "email" :    "<email>",
 
                "active" :   "<bool>",
 
                "admin" :    "<bool>",
 
                "ldap_dn" :  "<ldap_dn>"
 
                "ldap_dn" :  "<ldap_dn>",
 
                "last_login": "<last_login>",
 
                "permissions": {
 
                    "global": ["hg.create.repository",
 
                               "repository.read",
 
                               "hg.register.manual_activate"],
 
                    "repositories": {"repo1": "repository.none"},
 
                    "repositories_groups": {"Group1": "group.read"}
 
                 },
 
            }
 

	
 
    error:  null
 
@@ -126,6 +136,7 @@ belonging to user with admin rights.
 

	
 
INPUT::
 

	
 
    id : <id_for_response>
 
    api_key : "<api_key>"
 
    method :  "get_users"
 
    args :    { }
 
@@ -141,7 +152,8 @@ OUTPUT::
 
                "email" :    "<email>",
 
                "active" :   "<bool>",
 
                "admin" :    "<bool>",
 
                "ldap_dn" :  "<ldap_dn>"
 
                "ldap_dn" :  "<ldap_dn>",
 
                "last_login": "<last_login>",
 
              },
 
    	      …
 
            ]
 
@@ -157,6 +169,7 @@ be executed only using api_key belonging
 

	
 
INPUT::
 

	
 
    id : <id_for_response>
 
    api_key : "<api_key>"
 
    method :  "create_user"
 
    args :    {
 
@@ -188,6 +201,7 @@ be executed only using api_key belonging
 

	
 
INPUT::
 

	
 
    id : <id_for_response>
 
    api_key : "<api_key>"
 
    method :  "update_user"
 
    args :    {
 
@@ -220,6 +234,7 @@ belonging to user with admin rights.
 

	
 
INPUT::
 

	
 
    id : <id_for_response>
 
    api_key : "<api_key>"
 
    method :  "get_users_group"
 
    args :    {
 
@@ -258,6 +273,7 @@ api_key belonging to user with admin rig
 

	
 
INPUT::
 

	
 
    id : <id_for_response>
 
    api_key : "<api_key>"
 
    method :  "get_users_groups"
 
    args :    { }
 
@@ -296,6 +312,7 @@ belonging to user with admin rights
 

	
 
INPUT::
 

	
 
    id : <id_for_response>
 
    api_key : "<api_key>"
 
    method :  "create_users_group"
 
    args:     {
 
@@ -322,6 +339,7 @@ belonging to user with admin rights
 

	
 
INPUT::
 

	
 
    id : <id_for_response>
 
    api_key : "<api_key>"
 
    method :  "add_user_users_group"
 
    args:     {
 
@@ -350,6 +368,7 @@ using api_key belonging to user with adm
 

	
 
INPUT::
 

	
 
    id : <id_for_response>
 
    api_key : "<api_key>"
 
    method :  "remove_user_from_users_group"
 
    args:     {
 
@@ -370,12 +389,14 @@ OUTPUT::
 
get_repo
 
--------
 

	
 
Gets an existing repository by it's name or repository_id. This command can 
 
Gets an existing repository by it's name or repository_id. Members will return
 
either users_group or user associated to that repository. This command can 
 
be executed only using api_key belonging to user with admin rights.
 

	
 

	
 
INPUT::
 

	
 
    id : <id_for_response>
 
    api_key : "<api_key>"
 
    method :  "get_repo"
 
    args:     {
 
@@ -391,7 +412,9 @@ OUTPUT::
 
                "type" :        "<type>",
 
                "description" : "<description>",
 
                "members" :     [
 
                                  { "id" :         "<userid>",
 
                                  { 
 
                                    "type": "user",
 
                                    "id" :         "<userid>",
 
                                    "username" :   "<username>",
 
                                    "firstname":   "<firstname>",
 
                                    "lastname" :   "<lastname>",
 
@@ -402,7 +425,8 @@ OUTPUT::
 
                                    "permission" : "repository.(read|write|admin)"
 
                                  },
 
                                  …
 
                                  {
 
                                  { 
 
                                    "type": "users_group",
 
                                    "id" :       "<usersgroupid>",
 
                                    "name" :     "<usersgroupname>",
 
                                    "active":    "<bool>",
 
@@ -423,6 +447,7 @@ belonging to user with admin rights
 

	
 
INPUT::
 

	
 
    id : <id_for_response>
 
    api_key : "<api_key>"
 
    method :  "get_repos"
 
    args:     { }
 
@@ -452,6 +477,7 @@ with admin rights
 

	
 
INPUT::
 

	
 
    id : <id_for_response>
 
    api_key : "<api_key>"
 
    method :  "get_repo_nodes"
 
    args:     {
 
@@ -485,6 +511,7 @@ and create "baz" repository with "bar" a
 

	
 
INPUT::
 

	
 
    id : <id_for_response>
 
    api_key : "<api_key>"
 
    method :  "create_repo"
 
    args:     {
 
@@ -514,6 +541,7 @@ belonging to user with admin rights.
 

	
 
INPUT::
 

	
 
    id : <id_for_response>
 
    api_key : "<api_key>"
 
    method :  "delete_repo"
 
    args:     {
 
@@ -538,6 +566,7 @@ with admin rights.
 

	
 
INPUT::
 

	
 
    id : <id_for_response>
 
    api_key : "<api_key>"
 
    method :  "grant_user_permission"
 
    args:     {
 
@@ -563,6 +592,7 @@ only using api_key belonging to user wit
 

	
 
INPUT::
 

	
 
    id : <id_for_response>
 
    api_key : "<api_key>"
 
    method  : "revoke_user_permission"
 
    args:     {
 
@@ -588,6 +618,7 @@ api_key belonging to user with admin rig
 

	
 
INPUT::
 

	
 
    id : <id_for_response>
 
    api_key : "<api_key>"
 
    method :  "grant_users_group_permission"
 
    args:     {
 
@@ -612,6 +643,7 @@ executed only using api_key belonging to
 

	
 
INPUT::
 

	
 
    id : <id_for_response>
 
    api_key : "<api_key>"
 
    method  : "revoke_users_group_permission"
 
    args:     {
docs/changelog.rst
Show inline comments
 
@@ -5,7 +5,45 @@ Changelog
 
=========
 

	
 

	
 
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
 

	
 
fixes
 
+++++
 

	
 
- fixed #390 cache invalidation problems on repos inside group
 
- fixed #385 clone by ID url was loosing proxy prefix in URL
 
- fixed some unicode problems with waitress
 
- fixed issue with escaping < and > in changeset commits
 
- fixed error occurring during recursive group creation in API 
 
  create_repo function
 
- fixed #393 py2.5 fixes for routes url generator
 
- fixed #397 Private repository groups shows up before login
 
- fixed #396 fixed problems with revoking users in nested groups
 
- fixed mysql unicode issues + specified InnoDB as default engine with 
 
  utf8 charset
 
- #406 trim long branch/tag names in changelog to not break UI
 
  
 
1.3.3 (**2012-03-02**)
 
----------------------
 

	
docs/index.rst
Show inline comments
 
@@ -23,7 +23,8 @@ Users Guide
 
   usage/git_support
 
   usage/statistics
 
   usage/backup
 
   
 
   usage/debugging
 

	
 
**Develop**
 

	
 
.. toctree::
docs/setup.rst
Show inline comments
 
@@ -20,9 +20,10 @@ following command to do this::
 

	
 

	
 
Next, you need to create the databases used by RhodeCode. I recommend that you
 
use sqlite (default) or postgresql. If you choose a database other than the
 
use postgresql or sqlite (default). If you choose a database other than the
 
default ensure you properly adjust the db url in your production.ini
 
configuration file to use this other database. Create the databases by running
 
configuration file to use this other database. RhodeCode currently supports
 
postgresql, sqlite and mysql databases. Create the database by running
 
the following command::
 

	
 
    paster setup-app production.ini
 
@@ -57,15 +58,18 @@ You are now ready to use RhodeCode, to r
 
- In the admin panel you can toggle ldap, anonymous, permissions settings. As
 
  well as edit more advanced options on users and repositories
 

	
 
Try copying your own mercurial repository into the "root" directory you are
 
using, then from within the RhodeCode web application choose Admin >
 
repositories. Then choose Add New Repository. Add the repository you copied 
 
into the root. Test that you can browse your repository from within RhodeCode 
 
and then try cloning your repository from RhodeCode with::
 
Optionally users can create `rcextensions` package that extends RhodeCode
 
functionality. To do this simply execute::
 

	
 
    paster make-rcext production.ini
 

	
 
    hg clone http://127.0.0.1:5000/<repository name>
 
This will create `rcextensions` package in the same place that your `ini` file
 
lives. With `rcextensions` it's possible to add additional mapping for whoosh, 
 
stats and add additional code into the push/pull/create repo hooks. For example
 
for sending signals to build-bots such as jenkins.
 
Please see the `__init__.py` file inside `rcextensions` package 
 
for more details.
 

	
 
where *repository name* is replaced by the name of your repository.
 

	
 
Using RhodeCode with SSH
 
------------------------
docs/upgrade.rst
Show inline comments
 
@@ -47,6 +47,9 @@ This will upgrade the schema and update 
 
and will always recheck the settings of the application, if there are no new 
 
options that need to be set.
 

	
 
.. note::
 
   If you're using Celery, make sure you restart all instances of it after
 
   upgrade.
 

	
 
.. _virtualenv: http://pypi.python.org/pypi/virtualenv  
 
.. _python: http://www.python.org/
docs/usage/debugging.rst
Show inline comments
 
new file 100644
 
.. _debugging:
 

	
 
===================
 
Debugging RhodeCode
 
===================
 

	
 
If you encountered problems with RhodeCode here are some instructions how to
 
possibly debug them.
 

	
 
** First make sure you're using the latest version available.**
 

	
 
enable detailed debug
 
---------------------
 

	
 
RhodeCode uses standard python logging modules to log it's output.
 
By default only loggers with INFO level are displayed. To enable full output
 
change `level = DEBUG` for all logging handlers in currently used .ini file. 
 
This change will allow to see much more detailed output in the logfile or
 
console. This generally helps a lot to track issues.
 

	
 

	
 
enable interactive debug mode
 
-----------------------------
 

	
 
To enable interactive debug mode simply comment out `set debug = false` in
 
.ini file, this will trigger and interactive debugger each time there an
 
error in browser, or send a http link if error occured in the backend. This
 
is a great tool for fast debugging as you get a handy python console right
 
in the web view. ** NEVER ENABLE THIS ON PRODUCTION ** the interactive console
 
can be a serious security threat to you system.
docs/usage/general.rst
Show inline comments
 
@@ -71,6 +71,11 @@ RhodeCode will send mails on user regist
 
on errors the mails will have a detailed traceback of error.
 

	
 

	
 
Mails are also sent for code comments. If someone comments on a changeset
 
mail is sent to all participants, the person who commited the changeset 
 
(if present in RhodeCode), and to all people mentioned with @mention system.
 

	
 

	
 
Trending source files
 
---------------------
 

	
init.d/rhodecode-daemon4
Show inline comments
 
new file 100644
 
#!/bin/bash
 
###########################################
 
#### THIS IS AN ARCH LINUX RC.D SCRIPT ####
 
###########################################
 

	
 
. /etc/rc.conf
 
. /etc/rc.d/functions
 

	
 
DAEMON=rhodecode
 
APP_HOMEDIR="/srv"
 
APP_PATH="$APP_HOMEDIR/$DAEMON"
 
CONF_NAME="production.ini"
 
LOG_FILE="/var/log/$DAEMON.log"
 
PID_FILE="/run/daemons/$DAEMON"
 
APPL=/usr/bin/paster
 
RUN_AS="*****"
 

	
 
ARGS="serve --daemon \
 
--user=$RUN_AS \
 
--group=$RUN_AS \
 
--pid-file=$PID_FILE \
 
--log-file=$LOG_FILE \
 
$APP_PATH/$CONF_NAME"
 

	
 
[ -r /etc/conf.d/$DAEMON ] && . /etc/conf.d/$DAEMON
 

	
 
if [[ -r $PID_FILE ]]; then
 
    read -r PID < "$PID_FILE"
 
    if [[ $PID && ! -d /proc/$PID ]]; then
 
        unset PID
 
        rm_daemon $DAEMON
 
    fi
 
fi
 

	
 
case "$1" in
 
start)
 
    stat_busy "Starting $DAEMON"
 
    export HOME=$APP_PATH
 
    [ -z "$PID" ] && $APPL $ARGS &>/dev/null
 
    if [ $? = 0 ]; then
 
        add_daemon $DAEMON
 
        stat_done
 
    else
 
        stat_fail
 
        exit 1
 
    fi
 
    ;;
 
stop)
 
    stat_busy "Stopping $DAEMON"
 
    [ -n "$PID" ] && kill $PID &>/dev/null 
 
    if [ $? = 0 ]; then
 
        rm_daemon $DAEMON
 
        stat_done
 
    else
 
        stat_fail
 
        exit 1
 
    fi
 
    ;;
 
restart)
 
    $0 stop
 
    sleep 1
 
    $0 start
 
    ;;
 
status)
 
    stat_busy "Checking $name status";
 
    ck_status $name
 
    ;;
 
*)
 
    echo "usage: $0 {start|stop|restart|status}"
 
esac
 
\ No newline at end of file
production.ini
Show inline comments
 
@@ -93,6 +93,11 @@ issue_prefix = #
 
## all running rhodecode instances. Leave empty if you don't use it
 
instance_id = 
 

	
 
## alternative return HTTP header for failed authentication. Default HTTP
 
## response is 401 HTTPUnauthorized. Currently HG clients have troubles with 
 
## handling that. Set this variable to 403 to return HTTPForbidden
 
auth_ret_code =
 

	
 
####################################
 
###        CELERY CONFIG        ####
 
####################################
 
@@ -208,13 +213,13 @@ logview.pylons.util = #eee
 
sqlalchemy.db1.url = postgresql://postgres:qwe@localhost/rhodecode
 
sqlalchemy.db1.echo = false
 
sqlalchemy.db1.pool_recycle = 3600
 
sqlalchemy.convert_unicode = true
 
sqlalchemy.db1.convert_unicode = true
 

	
 
################################
 
### LOGGING CONFIGURATION   ####
 
################################
 
[loggers]
 
keys = root, routes, rhodecode, sqlalchemy, beaker, templates
 
keys = root, routes, rhodecode, sqlalchemy, beaker, templates, whoosh_indexer
 

	
 
[handlers]
 
keys = console, console_sql
 
@@ -260,6 +265,12 @@ handlers = console_sql
 
qualname = sqlalchemy.engine
 
propagate = 0
 

	
 
[logger_whoosh_indexer]
 
level = DEBUG
 
handlers = 
 
qualname = whoosh_indexer
 
propagate = 1
 

	
 
##############
 
## HANDLERS ##
 
##############
requires.txt
Show inline comments
 
Pylons==1.0.0
 
Beaker==1.6.3
 
WebHelpers>=1.2
 
WebHelpers==1.3
 
formencode==1.2.4
 
SQLAlchemy==0.7.4
 
Mako==0.5.0
 
SQLAlchemy==0.7.6
 
Mako==0.6.2
 
pygments>=1.4
 
whoosh>=2.3.0,<2.4
 
celery>=2.2.5,<2.3
 
babel
 
python-dateutil>=1.5.0,<2.0.0
 
dulwich>=0.8.0,<0.9.0
 
https://github.com/jelmer/dulwich/tarball/master
 
webob==1.0.8
 
markdown==2.1.1
 
docutils==0.8.1
 
py-bcrypt
 
mercurial>=2.1,<2.2
 
\ No newline at end of file
 
mercurial>=2.1,<2.2
rhodecode/__init__.py
Show inline comments
 
@@ -4,7 +4,7 @@
 
    ~~~~~~~~~~~~~~~~~~
 

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

	
 
    :created_on: Apr 9, 2010
 
    :author: marcink
 
@@ -26,8 +26,18 @@
 
import sys
 
import platform
 

	
 
VERSION = (1, 3, 3)
 
__version__ = '.'.join((str(each) for each in VERSION[:4]))
 
VERSION = (1, 3, 4)
 

	
 
try:
 
    from rhodecode.lib import get_current_revision
 
    _rev = get_current_revision()
 
    if _rev:
 
        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'
 
@@ -39,16 +49,16 @@ PLATFORM_OTHERS = ('Linux', 'Darwin', 'F
 
requirements = [
 
    "Pylons==1.0.0",
 
    "Beaker==1.6.3",
 
    "WebHelpers>=1.2",
 
    "WebHelpers==1.3",
 
    "formencode==1.2.4",
 
    "SQLAlchemy==0.7.4",
 
    "Mako==0.5.0",
 
    "SQLAlchemy==0.7.6",
 
    "Mako==0.6.2",
 
    "pygments>=1.4",
 
    "whoosh>=2.3.0,<2.4",
 
    "celery>=2.2.5,<2.3",
 
    "babel",
 
    "python-dateutil>=1.5.0,<2.0.0",
 
    "dulwich>=0.8.0,<0.9.0",
 
    "dulwich>=0.8.4,<0.9.0",
 
    "webob==1.0.8",
 
    "markdown==2.1.1",
 
    "docutils==0.8.1",
 
@@ -65,17 +75,6 @@ else:
 
    requirements.append("mercurial>=2.1,<2.2")
 

	
 

	
 
try:
 
    from rhodecode.lib import get_current_revision
 
    _rev = get_current_revision(quiet=True)
 
except ImportError:
 
    # this is needed when doing some setup.py operations
 
    _rev = False
 

	
 
if len(VERSION) > 3 and _rev:
 
    __version__ += ' [rev:%s]' % _rev[0]
 

	
 

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

	
 
@@ -90,3 +89,6 @@ CELERY_ON = False
 

	
 
# link to config for pylons
 
CONFIG = {}
 

	
 
# Linked module for extensions
 
EXTENSIONS = {}
rhodecode/config/conf.py
Show inline comments
 
new file 100644
 
# -*- coding: utf-8 -*-
 
"""
 
    package.rhodecode.config.conf
 
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 

	
 
    Various config settings for RhodeCode
 

	
 
    :created_on: Mar 7, 2012
 
    :author: marcink
 
    :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
 
    :license: <name>, see LICENSE_FILE for more details.
 
"""
 
from rhodecode import EXTENSIONS
 

	
 
from rhodecode.lib.utils2 import __get_lem
 

	
 

	
 
# language map is also used by whoosh indexer, which for those specified
 
# extensions will index it's content
 
LANGUAGES_EXTENSIONS_MAP = __get_lem()
 

	
 
#==============================================================================
 
# WHOOSH INDEX EXTENSIONS
 
#==============================================================================
 
# EXTENSIONS WE WANT TO INDEX CONTENT OFF USING WHOOSH
 
INDEX_EXTENSIONS = LANGUAGES_EXTENSIONS_MAP.keys()
 

	
 
# list of readme files to search in file tree and display in summary
 
# attached weights defines the search  order lower is first
 
ALL_READMES = [
 
    ('readme', 0), ('README', 0), ('Readme', 0),
 
    ('doc/readme', 1), ('doc/README', 1), ('doc/Readme', 1),
 
    ('Docs/readme', 2), ('Docs/README', 2), ('Docs/Readme', 2),
 
    ('DOCS/readme', 2), ('DOCS/README', 2), ('DOCS/Readme', 2),
 
    ('docs/readme', 2), ('docs/README', 2), ('docs/Readme', 2),
 
]
 

	
 
# extension together with weights to search lower is first
 
RST_EXTS = [
 
    ('', 0), ('.rst', 1), ('.rest', 1),
 
    ('.RST', 2), ('.REST', 2),
 
    ('.txt', 3), ('.TXT', 3)
 
]
 

	
 
MARKDOWN_EXTS = [
 
    ('.md', 1), ('.MD', 1),
 
    ('.mkdn', 2), ('.MKDN', 2),
 
    ('.mdown', 3), ('.MDOWN', 3),
 
    ('.markdown', 4), ('.MARKDOWN', 4)
 
]
 

	
 
PLAIN_EXTS = [('.text', 2), ('.TEXT', 2)]
 

	
 
ALL_EXTS = MARKDOWN_EXTS + RST_EXTS + PLAIN_EXTS
 

	
 
DATETIME_FORMAT = "%Y-%m-%d %H:%M:%S"
 

	
 
DATE_FORMAT = "%Y-%m-%d"
rhodecode/config/deployment.ini_tmpl
Show inline comments
 
@@ -93,6 +93,11 @@ issue_prefix = #
 
## all running rhodecode instances. Leave empty if you don't use it
 
instance_id = 
 

	
 
## alternative return HTTP header for failed authentication. Default HTTP
 
## response is 401 HTTPUnauthorized. Currently HG clients have troubles with 
 
## handling that. Set this variable to 403 to return HTTPForbidden
 
auth_ret_code =
 

	
 
####################################
 
###        CELERY CONFIG        ####
 
####################################
 
@@ -218,13 +223,13 @@ sqlalchemy.db1.url = sqlite:///%(here)s/
 

	
 
sqlalchemy.db1.echo = false
 
sqlalchemy.db1.pool_recycle = 3600
 
sqlalchemy.convert_unicode = true
 
sqlalchemy.db1.convert_unicode = true
 

	
 
################################
 
### LOGGING CONFIGURATION   ####
 
################################
 
[loggers]
 
keys = root, routes, rhodecode, sqlalchemy, beaker, templates
 
keys = root, routes, rhodecode, sqlalchemy, beaker, templates, whoosh_indexer
 

	
 
[handlers]
 
keys = console, console_sql
 
@@ -270,6 +275,12 @@ handlers = console_sql
 
qualname = sqlalchemy.engine
 
propagate = 0
 

	
 
[logger_whoosh_indexer]
 
level = DEBUG
 
handlers = 
 
qualname = whoosh_indexer
 
propagate = 1
 

	
 
##############
 
## HANDLERS ##
 
##############
rhodecode/config/environment.py
Show inline comments
 
@@ -2,21 +2,24 @@
 

	
 
import os
 
import logging
 
import rhodecode
 

	
 
from mako.lookup import TemplateLookup
 
from pylons.configuration import PylonsConfig
 
from pylons.error import handle_mako_error
 

	
 
import rhodecode
 
# don't remove this import it does magic for celery
 
from rhodecode.lib import celerypylons
 

	
 
import rhodecode.lib.app_globals as app_globals
 
import rhodecode.lib.helpers
 

	
 
from rhodecode.config.routing import make_map
 
# don't remove this import it does magic for celery
 
from rhodecode.lib import celerypylons, str2bool
 
from rhodecode.lib import engine_from_config
 

	
 
from rhodecode.lib import helpers
 
from rhodecode.lib.auth import set_available_permissions
 
from rhodecode.lib.utils import repo2db_mapper, make_ui, set_rhodecode_config
 
from rhodecode.lib.utils import repo2db_mapper, make_ui, set_rhodecode_config,\
 
    load_rcextensions
 
from rhodecode.lib.utils2 import engine_from_config, str2bool
 
from rhodecode.model import init_model
 
from rhodecode.model.scm import ScmModel
 

	
 
@@ -24,17 +27,20 @@ log = logging.getLogger(__name__)
 

	
 

	
 
def load_environment(global_conf, app_conf, initial=False):
 
    """Configure the Pylons environment via the ``pylons.config``
 
    """
 
    Configure the Pylons environment via the ``pylons.config``
 
    object
 
    """
 
    config = PylonsConfig()
 

	
 
    # Pylons paths
 
    root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
 
    paths = dict(root=root,
 
                 controllers=os.path.join(root, 'controllers'),
 
                 static_files=os.path.join(root, 'public'),
 
                 templates=[os.path.join(root, 'templates')])
 
    paths = dict(
 
        root=root,
 
        controllers=os.path.join(root, 'controllers'),
 
        static_files=os.path.join(root, 'public'),
 
        templates=[os.path.join(root, 'templates')]
 
    )
 

	
 
    # Initialize config with the basic options
 
    config.init_app(global_conf, app_conf, package='rhodecode', paths=paths)
 
@@ -44,8 +50,11 @@ def load_environment(global_conf, app_co
 

	
 
    config['routes.map'] = make_map(config)
 
    config['pylons.app_globals'] = app_globals.Globals(config)
 
    config['pylons.h'] = rhodecode.lib.helpers
 
    config['pylons.h'] = helpers
 
    rhodecode.CONFIG = config
 

	
 
    load_rcextensions(root_path=config['here'])
 

	
 
    # Setup cache object as early as possible
 
    import pylons
 
    pylons.cache._push_object(config['pylons.app_globals'].cache)
rhodecode/config/rcextensions/__init__.py
Show inline comments
 
new file 100644
 
# Additional mappings that are not present in the pygments lexers
 
# used for building stats
 
# format is {'ext':'Name'} eg. {'py':'Python'}
 
# NOTE: that this will overide any mappings in LANGUAGES_EXTENSIONS_MAP
 
# build by pygments
 
EXTRA_MAPPINGS = {}
 

	
 
#==============================================================================
 
# WHOOSH INDEX EXTENSIONS
 
#==============================================================================
 
# if INDEX_EXTENSIONS is [] it'll use pygments lexers extensions by default.
 
# To set your own just add to this list extensions to index with content
 
INDEX_EXTENSIONS = []
 

	
 
# additional extensions for indexing besides the default from pygments
 
# those get's added to INDEX_EXTENSIONS
 
EXTRA_INDEX_EXTENSIONS = []
 

	
 

	
 
#==============================================================================
 
# POST CREATE REPOSITORY HOOK
 
#==============================================================================
 
# this function will be executed after each repository is created
 
def _crhook(*args, **kwargs):
 
    """
 
    Post create repository HOOK
 
    kwargs available:
 
     :param repo_name:
 
     :param repo_type:
 
     :param description:
 
     :param private:
 
     :param created_on:
 
     :param enable_downloads:
 
     :param repo_id:
 
     :param user_id:
 
     :param enable_statistics:
 
     :param clone_uri:
 
     :param fork_id:
 
     :param group_id:
 
     :param created_by:
 
    """
 
    return 0
 
CREATE_REPO_HOOK = _crhook
 

	
 

	
 
#==============================================================================
 
# POST PUSH HOOK
 
#==============================================================================
 

	
 
# this function will be executed after each push it's runned after the build-in
 
# hook that rhodecode uses for logging pushes
 
def _pushhook(*args, **kwargs):
 
    """
 
    Post push hook
 
    kwargs available:
 

	
 
      :param username: name of user who pushed
 
      :param ip: ip of who pushed
 
      :param action: pull
 
      :param repository: repository name
 
      :param pushed_revs: generator of pushed revisions
 
    """
 
    return 0
 
PUSH_HOOK = _pushhook
 

	
 

	
 
#==============================================================================
 
# POST PULL HOOK
 
#==============================================================================
 

	
 
# this function will be executed after each push it's runned after the build-in
 
# hook that rhodecode uses for logging pushes
 
def _pullhook(*args, **kwargs):
 
    """
 
    Post pull hook
 
    kwargs available::
 

	
 
      :param username: name of user who pulled
 
      :param ip: ip of who pushed
 
      :param action: pull
 
      :param repository: repository name
 
    """
 
    return 0
 
PULL_HOOK = _pullhook
rhodecode/config/rcextensions/make_rcextensions.py
Show inline comments
 
new file 100644
 
# -*- coding: utf-8 -*-
 
"""
 
    rhodecode.config.rcextensions.make_rcextensions
 
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 

	
 
    Whoosh indexing module for RhodeCode
 

	
 
    :created_on: Mar 6, 2012
 
    :author: marcink
 
    :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
 
    :license: GPLv3, see COPYING for more details.
 
"""
 
# This program is free software: you can redistribute it and/or modify
 
# it under the terms of the GNU General Public License as published by
 
# the Free Software Foundation, either version 3 of the License, or
 
# (at your option) any later version.
 
#
 
# This program is distributed in the hope that it will be useful,
 
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
# GNU General Public License for more details.
 
#
 
# You should have received a copy of the GNU General Public License
 
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
import os
 
import sys
 
import pkg_resources
 
import traceback
 
import logging
 
from os.path import dirname as dn, join as jn
 

	
 
#to get the rhodecode import
 
sys.path.append(dn(dn(dn(os.path.realpath(__file__)))))
 

	
 
from rhodecode.lib.utils import BasePasterCommand, Command, ask_ok
 

	
 
log = logging.getLogger(__name__)
 

	
 

	
 
class MakeRcExt(BasePasterCommand):
 

	
 
    max_args = 1
 
    min_args = 1
 

	
 
    usage = "CONFIG_FILE"
 
    summary = "Creates additional extensions for rhodecode"
 
    group_name = "RhodeCode"
 
    takes_config_file = -1
 
    parser = Command.standard_parser(verbose=True)
 

	
 
    def command(self):
 
        logging.config.fileConfig(self.path_to_ini_file)
 
        from pylons import config
 

	
 
        def _make_file(ext_file):
 
            bdir = os.path.split(ext_file)[0]
 
            if not os.path.isdir(bdir):
 
                os.makedirs(bdir)
 
            with open(ext_file, 'wb') as f:
 
                f.write(tmpl)
 
                log.info('Writen new extensions file to %s' % ext_file)
 

	
 
        here = config['here']
 
        tmpl = pkg_resources.resource_string(
 
            'rhodecode', jn('config', 'rcextensions', '__init__.py')
 
        )
 
        ext_file = jn(here, 'rcextensions', '__init__.py')
 
        if os.path.exists(ext_file):
 
            msg = ('Extension file already exists, do you want '
 
                   'to overwrite it ? [y/n]')
 
            if ask_ok(msg):
 
                _make_file(ext_file)
 
            else:
 
                log.info('nothing done...')
 
        else:
 
            _make_file(ext_file)
 

	
 
    def update_parser(self):
 
        pass
rhodecode/controllers/admin/repos.py
Show inline comments
 
@@ -284,7 +284,6 @@ class ReposController(BaseController):
 

	
 
        :param repo_name:
 
        """
 

	
 
        try:
 
            RepoModel().revoke_user_permission(repo=repo_name,
 
                                               user=request.POST['user_id'])
rhodecode/controllers/admin/users.py
Show inline comments
 
@@ -145,11 +145,12 @@ class UsersController(BaseController):
 
        user_model = UserModel()
 
        try:
 
            user_model.delete(id)
 
            Session.commit()
 
            h.flash(_('successfully deleted user'), category='success')
 
            Session.commit()
 
        except (UserOwnsReposException, DefaultUserException), e:
 
            h.flash(str(e), category='warning')
 
            h.flash(e, category='warning')
 
        except Exception:
 
            log.error(traceback.format_exc())
 
            h.flash(_('An error occurred during deletion of user'),
 
                    category='error')
 
        return redirect(url('users'))
rhodecode/controllers/admin/users_groups.py
Show inline comments
 
@@ -32,8 +32,9 @@ from pylons import request, session, tmp
 
from pylons.controllers.util import abort, redirect
 
from pylons.i18n.translation import _
 

	
 
from rhodecode.lib import helpers as h
 
from rhodecode.lib.exceptions import UsersGroupsAssignedException
 
from rhodecode.lib import helpers as h, safe_unicode
 
from rhodecode.lib.utils2 import safe_unicode
 
from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator
 
from rhodecode.lib.base import BaseController, render
 

	
rhodecode/controllers/api/__init__.py
Show inline comments
 
@@ -233,10 +233,10 @@ class JSONRPCController(WSGIController):
 
        try:
 
            return json.dumps(response)
 
        except TypeError, e:
 
            log.debug('Error encoding response: %s' % e)
 
            log.error('API FAILED. Error encoding response: %s' % e)
 
            return json.dumps(
 
                dict(
 
                    self._req_id,
 
                    id=self._req_id,
 
                    result=None,
 
                    error="Error encoding response"
 
                )
rhodecode/controllers/api/api.py
Show inline comments
 
@@ -30,16 +30,15 @@ import logging
 

	
 
from rhodecode.controllers.api import JSONRPCController, JSONRPCError
 
from rhodecode.lib.auth import HasPermissionAllDecorator, \
 
    HasPermissionAnyDecorator, PasswordGenerator
 
    HasPermissionAnyDecorator, PasswordGenerator, AuthUser
 

	
 
from rhodecode.model.meta import Session
 
from rhodecode.model.scm import ScmModel
 
from rhodecode.model.db import User, UsersGroup, RepoGroup, Repository
 
from rhodecode.model.db import User, UsersGroup, Repository
 
from rhodecode.model.repo import RepoModel
 
from rhodecode.model.user import UserModel
 
from rhodecode.model.users_group import UsersGroupModel
 
from rhodecode.model.repos_group import ReposGroupModel
 

	
 
from rhodecode.lib.utils import map_groups
 

	
 
log = logging.getLogger(__name__)
 

	
 
@@ -100,7 +99,9 @@ class ApiController(JSONRPCController):
 
            email=user.email,
 
            active=user.active,
 
            admin=user.admin,
 
            ldap_dn=user.ldap_dn
 
            ldap_dn=user.ldap_dn,
 
            last_login=user.last_login,
 
            permissions=AuthUser(user_id=user.user_id).permissions
 
        )
 

	
 
    @HasPermissionAllDecorator('hg.admin')
 
@@ -122,7 +123,8 @@ class ApiController(JSONRPCController):
 
                    email=user.email,
 
                    active=user.active,
 
                    admin=user.admin,
 
                    ldap_dn=user.ldap_dn
 
                    ldap_dn=user.ldap_dn,
 
                    last_login=user.last_login,
 
                )
 
            )
 
        return result
 
@@ -282,7 +284,7 @@ class ApiController(JSONRPCController):
 
    @HasPermissionAllDecorator('hg.admin')
 
    def add_user_to_users_group(self, apiuser, group_name, username):
 
        """"
 
        Add a user to a group
 
        Add a user to a users group
 

	
 
        :param apiuser:
 
        :param group_name:
 
@@ -360,7 +362,7 @@ class ApiController(JSONRPCController):
 
            user = user.user
 
            members.append(
 
                dict(
 
                    type_="user",
 
                    type="user",
 
                    id=user.user_id,
 
                    username=user.username,
 
                    firstname=user.name,
 
@@ -377,7 +379,7 @@ class ApiController(JSONRPCController):
 
            users_group = users_group.users_group
 
            members.append(
 
                dict(
 
                    type_="users_group",
 
                    type="users_group",
 
                    id=users_group.users_group_id,
 
                    name=users_group.users_group_name,
 
                    active=users_group.users_group_active,
 
@@ -464,15 +466,10 @@ class ApiController(JSONRPCController):
 
            if Repository.get_by_repo_name(repo_name):
 
                raise JSONRPCError("repo %s already exist" % repo_name)
 

	
 
            groups = repo_name.split('/')
 
            groups = repo_name.split(Repository.url_sep())
 
            real_name = groups[-1]
 
            groups = groups[:-1]
 
            parent_id = None
 
            for g in groups:
 
                group = RepoGroup.get_by_group_name(g)
 
                if not group:
 
                    group = ReposGroupModel().create(g, '', parent_id)
 
                parent_id = group.group_id
 
            # create structure of groups
 
            group = map_groups(repo_name)
 

	
 
            repo = RepoModel().create(
 
                dict(
 
@@ -481,7 +478,7 @@ class ApiController(JSONRPCController):
 
                    description=description,
 
                    private=private,
 
                    repo_type=repo_type,
 
                    repo_group=parent_id,
 
                    repo_group=group.group_id if group else None,
 
                    clone_uri=clone_uri
 
                ),
 
                owner
rhodecode/controllers/branches.py
Show inline comments
 
@@ -31,7 +31,7 @@ import binascii
 
from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
 
from rhodecode.lib.base import BaseRepoController, render
 
from rhodecode.lib.compat import OrderedDict
 
from rhodecode.lib import safe_unicode
 
from rhodecode.lib.utils2 import safe_unicode
 
log = logging.getLogger(__name__)
 

	
 

	
rhodecode/controllers/changeset.py
Show inline comments
 
@@ -51,13 +51,18 @@ from rhodecode.lib.diffs import wrapped_
 
log = logging.getLogger(__name__)
 

	
 

	
 
def anchor_url(revision, path):
 
def _update_with_GET(params, GET):
 
    for k in ['diff1', 'diff2', 'diff']:
 
        params[k] += GET.getall(k)
 

	
 

	
 
def anchor_url(revision, path, GET):
 
    fid = h.FID(revision, path)
 
    return h.url.current(anchor=fid, **dict(request.GET))
 
    return h.url.current(anchor=fid, **dict(GET))
 

	
 

	
 
def get_ignore_ws(fid, GET):
 
    ig_ws_global = request.GET.get('ignorews')
 
    ig_ws_global = GET.get('ignorews')
 
    ig_ws = filter(lambda k: k.startswith('WS'), GET.getall(fid))
 
    if ig_ws:
 
        try:
 
@@ -67,12 +72,13 @@ def get_ignore_ws(fid, GET):
 
    return ig_ws_global
 

	
 

	
 
def _ignorews_url(fileid=None):
 

	
 
def _ignorews_url(GET, fileid=None):
 
    fileid = str(fileid) if fileid else None
 
    params = defaultdict(list)
 
    _update_with_GET(params, GET)
 
    lbl = _('show white space')
 
    ig_ws = get_ignore_ws(fileid, request.GET)
 
    ln_ctx = get_line_ctx(fileid, request.GET)
 
    ig_ws = get_ignore_ws(fileid, GET)
 
    ln_ctx = get_line_ctx(fileid, GET)
 
    # global option
 
    if fileid is None:
 
        if ig_ws is None:
 
@@ -98,7 +104,7 @@ def _ignorews_url(fileid=None):
 

	
 

	
 
def get_line_ctx(fid, GET):
 
    ln_ctx_global = request.GET.get('context')
 
    ln_ctx_global = GET.get('context')
 
    ln_ctx = filter(lambda k: k.startswith('C'), GET.getall(fid))
 

	
 
    if ln_ctx:
 
@@ -112,16 +118,19 @@ def get_line_ctx(fid, GET):
 
        return
 

	
 

	
 
def _context_url(fileid=None):
 
def _context_url(GET, fileid=None):
 
    """
 
    Generates url for context lines
 

	
 
    :param fileid:
 
    """
 
    ig_ws = get_ignore_ws(fileid, request.GET)
 
    ln_ctx = (get_line_ctx(fileid, request.GET) or 3) * 2
 

	
 
    fileid = str(fileid) if fileid else None
 
    ig_ws = get_ignore_ws(fileid, GET)
 
    ln_ctx = (get_line_ctx(fileid, GET) or 3) * 2
 

	
 
    params = defaultdict(list)
 
    _update_with_GET(params, GET)
 

	
 
    # global option
 
    if fileid is None:
 
@@ -162,7 +171,7 @@ class ChangesetController(BaseRepoContro
 
        c.anchor_url = anchor_url
 
        c.ignorews_url = _ignorews_url
 
        c.context_url = _context_url
 

	
 
        limit_off = request.GET.get('fulldiff')
 
        #get ranges of revisions if preset
 
        rev_range = revision.split('...')[:2]
 
        enable_comments = True
 
@@ -220,7 +229,7 @@ class ChangesetController(BaseRepoContro
 
                ign_whitespace_lcl = get_ignore_ws(fid, request.GET)
 
                lim = self.cut_off_limit
 
                if cumulative_diff > self.cut_off_limit:
 
                    lim = -1
 
                    lim = -1 if limit_off is None else None
 
                size, cs1, cs2, diff, st = wrapped_diff(
 
                    filenode_old=None,
 
                    filenode_new=node,
 
@@ -251,7 +260,7 @@ class ChangesetController(BaseRepoContro
 
                ign_whitespace_lcl = get_ignore_ws(fid, request.GET)
 
                lim = self.cut_off_limit
 
                if cumulative_diff > self.cut_off_limit:
 
                    lim = -1
 
                    lim = -1 if limit_off is None else None
 
                size, cs1, cs2, diff, st = wrapped_diff(
 
                    filenode_old=filenode_old,
 
                    filenode_new=node,
rhodecode/controllers/feed.py
Show inline comments
 
@@ -28,7 +28,7 @@ import logging
 
from pylons import url, response, tmpl_context as c
 
from pylons.i18n.translation import _
 

	
 
from rhodecode.lib import safe_unicode
 
from rhodecode.lib.utils2 import safe_unicode
 
from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
 
from rhodecode.lib.base import BaseRepoController
 

	
rhodecode/controllers/files.py
Show inline comments
 
@@ -32,24 +32,26 @@ from pylons.i18n.translation import _
 
from pylons.controllers.util import redirect
 
from pylons.decorators import jsonify
 

	
 
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.lib import diffs
 
from rhodecode.lib import helpers as h
 

	
 
from rhodecode.lib.compat import OrderedDict
 
from rhodecode.lib import convert_line_endings, detect_mode, safe_str
 
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 import diffs
 
import rhodecode.lib.helpers as h
 
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.controllers.changeset import anchor_url, _ignorews_url,\
 
    _context_url, get_line_ctx, get_ignore_ws
 
from rhodecode.lib.diffs import wrapped_diff
 
from rhodecode.model.scm import ScmModel
 

	
 

	
 
log = logging.getLogger(__name__)
 

	
 
@@ -447,7 +449,7 @@ class FilesController(BaseRepoController
 
            ign_whitespace_lcl = get_ignore_ws(fid, request.GET)
 

	
 
            lim = request.GET.get('fulldiff') or self.cut_off_limit
 
            _, cs1, cs2, diff, st = wrapped_diff(filenode_old=node1,
 
            _, cs1, cs2, diff, st = diffs.wrapped_diff(filenode_old=node1,
 
                                         filenode_new=node2,
 
                                         cut_off_limit=lim,
 
                                         ignore_whitespace=ign_whitespace_lcl,
rhodecode/controllers/home.py
Show inline comments
 
@@ -44,7 +44,7 @@ class HomeController(BaseController):
 
    def index(self):
 
        c.repos_list = self.scm_model.get_repos()
 
        c.groups = self.scm_model.get_repos_groups()
 

	
 
        c.group = None
 
        return render('/index.html')
 

	
 
    def repo_switcher(self):
rhodecode/controllers/summary.py
Show inline comments
 
@@ -26,6 +26,7 @@
 
import traceback
 
import calendar
 
import logging
 
import urllib
 
from time import mktime
 
from datetime import timedelta, date
 
from urlparse import urlparse
 
@@ -39,15 +40,15 @@ from pylons.i18n.translation import _
 

	
 
from beaker.cache import cache_region, region_invalidate
 

	
 
from rhodecode.config.conf import ALL_READMES, ALL_EXTS, LANGUAGES_EXTENSIONS_MAP
 
from rhodecode.model.db import Statistics, CacheInvalidation
 
from rhodecode.lib import ALL_READMES, ALL_EXTS
 
from rhodecode.lib.utils2 import safe_unicode
 
from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
 
from rhodecode.lib.base import BaseRepoController, render
 
from rhodecode.lib.utils import EmptyChangeset
 
from rhodecode.lib.markup_renderer import MarkupRenderer
 
from rhodecode.lib.celerylib import run_task
 
from rhodecode.lib.celerylib.tasks import get_commits_stats, \
 
    LANGUAGES_EXTENSIONS_MAP
 
from rhodecode.lib.celerylib.tasks import get_commits_stats
 
from rhodecode.lib.helpers import RepoPage
 
from rhodecode.lib.compat import json, OrderedDict
 

	
 
@@ -91,34 +92,37 @@ class SummaryController(BaseRepoControll
 

	
 
        uri_tmpl = config.get('clone_uri', default_clone_uri)
 
        uri_tmpl = uri_tmpl.replace('{', '%(').replace('}', ')s')
 

	
 
        decoded_path = safe_unicode(urllib.unquote(parsed_url.path))
 
        uri_dict = {
 
           'user': username,
 
           'pass': password,
 
           'scheme': parsed_url.scheme,
 
           'netloc': parsed_url.netloc,
 
           'path': parsed_url.path
 
           'path': decoded_path
 
        }
 

	
 
        uri = uri_tmpl % uri_dict
 
        # generate another clone url by id
 
        uri_dict.update({'path': '/_%s' % c.dbrepo.repo_id})
 
        uri_dict.update(
 
         {'path': decoded_path.replace(repo_name, '_%s' % c.dbrepo.repo_id)}
 
        )
 
        uri_id = uri_tmpl % uri_dict
 

	
 
        c.clone_repo_url = uri
 
        c.clone_repo_url_id = uri_id
 
        c.repo_tags = OrderedDict()
 
        for name, hash in c.rhodecode_repo.tags.items()[:10]:
 
        for name, hash_ in c.rhodecode_repo.tags.items()[:10]:
 
            try:
 
                c.repo_tags[name] = c.rhodecode_repo.get_changeset(hash)
 
                c.repo_tags[name] = c.rhodecode_repo.get_changeset(hash_)
 
            except ChangesetError:
 
                c.repo_tags[name] = EmptyChangeset(hash)
 
                c.repo_tags[name] = EmptyChangeset(hash_)
 

	
 
        c.repo_branches = OrderedDict()
 
        for name, hash in c.rhodecode_repo.branches.items()[:10]:
 
        for name, hash_ in c.rhodecode_repo.branches.items()[:10]:
 
            try:
 
                c.repo_branches[name] = c.rhodecode_repo.get_changeset(hash)
 
                c.repo_branches[name] = c.rhodecode_repo.get_changeset(hash_)
 
            except ChangesetError:
 
                c.repo_branches[name] = EmptyChangeset(hash)
 
                c.repo_branches[name] = EmptyChangeset(hash_)
 

	
 
        td = date.today() + timedelta(days=1)
 
        td_1m = td - timedelta(days=calendar.mdays[td.month])
 
@@ -175,7 +179,7 @@ class SummaryController(BaseRepoControll
 
        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_repo)
 
        c.readme_data, c.readme_file = self.__get_readme_data(c.rhodecode_db_repo)
 
        return render('summary/summary.html')
 

	
 
    def __get_readme_data(self, repo):
 
@@ -206,7 +210,7 @@ class SummaryController(BaseRepoControll
 

	
 
            return readme_data, readme_file
 

	
 
        key = repo.name + '_README'
 
        key = repo.repo_name + '_README'
 
        inv = CacheInvalidation.invalidate(key)
 
        if inv is not None:
 
            region_invalidate(_get_readme_from_cache, None, key)
rhodecode/lib/__init__.py
Show inline comments
 
# -*- coding: utf-8 -*-
 
"""
 
    rhodecode.lib.__init__
 
    ~~~~~~~~~~~~~~~~~~~~~~~
 

	
 
    Some simple helper functions
 

	
 
    :created_on: Jan 5, 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 os
 
import re
 
from rhodecode.lib.vcs.utils.lazy import LazyProperty
 

	
 

	
 
def __get_lem():
 
    from pygments import lexers
 
    from string import lower
 
    from collections import defaultdict
 

	
 
    d = defaultdict(lambda: [])
 

	
 
    def __clean(s):
 
        s = s.lstrip('*')
 
        s = s.lstrip('.')
 

	
 
        if s.find('[') != -1:
 
            exts = []
 
            start, stop = s.find('['), s.find(']')
 

	
 
            for suffix in s[start + 1:stop]:
 
                exts.append(s[:s.find('[')] + suffix)
 
            return map(lower, exts)
 
        else:
 
            return map(lower, [s])
 

	
 
    for lx, t in sorted(lexers.LEXERS.items()):
 
        m = map(__clean, t[-2])
 
        if m:
 
            m = reduce(lambda x, y: x + y, m)
 
            for ext in m:
 
                desc = lx.replace('Lexer', '')
 
                d[ext].append(desc)
 

	
 
    return dict(d)
 

	
 
# language map is also used by whoosh indexer, which for those specified
 
# extensions will index it's content
 
LANGUAGES_EXTENSIONS_MAP = __get_lem()
 

	
 
# Additional mappings that are not present in the pygments lexers
 
# NOTE: that this will overide any mappings in LANGUAGES_EXTENSIONS_MAP
 
ADDITIONAL_MAPPINGS = {'xaml': 'XAML'}
 

	
 
LANGUAGES_EXTENSIONS_MAP.update(ADDITIONAL_MAPPINGS)
 

	
 
# list of readme files to search in file tree and display in summary
 
# attached weights defines the search  order lower is first
 
ALL_READMES = [
 
    ('readme', 0), ('README', 0), ('Readme', 0),
 
    ('doc/readme', 1), ('doc/README', 1), ('doc/Readme', 1),
 
    ('Docs/readme', 2), ('Docs/README', 2), ('Docs/Readme', 2),
 
    ('DOCS/readme', 2), ('DOCS/README', 2), ('DOCS/Readme', 2),
 
    ('docs/readme', 2), ('docs/README', 2), ('docs/Readme', 2),
 
]
 

	
 
# extension together with weights to search lower is first
 
RST_EXTS = [
 
    ('', 0), ('.rst', 1), ('.rest', 1),
 
    ('.RST', 2), ('.REST', 2),
 
    ('.txt', 3), ('.TXT', 3)
 
]
 

	
 
MARKDOWN_EXTS = [
 
    ('.md', 1), ('.MD', 1),
 
    ('.mkdn', 2), ('.MKDN', 2),
 
    ('.mdown', 3), ('.MDOWN', 3),
 
    ('.markdown', 4), ('.MARKDOWN', 4)
 
]
 

	
 
PLAIN_EXTS = [('.text', 2), ('.TEXT', 2)]
 

	
 
ALL_EXTS = MARKDOWN_EXTS + RST_EXTS + PLAIN_EXTS
 

	
 

	
 
def str2bool(_str):
 
    """
 
    returs True/False value from given string, it tries to translate the
 
    string into boolean
 

	
 
    :param _str: string value to translate into boolean
 
    :rtype: boolean
 
    :returns: boolean from given string
 
    """
 
    if _str is None:
 
        return False
 
    if _str in (True, False):
 
        return _str
 
    _str = str(_str).strip().lower()
 
    return _str in ('t', 'true', 'y', 'yes', 'on', '1')
 

	
 

	
 
def convert_line_endings(line, mode):
 
    """
 
    Converts a given line  "line end" accordingly to given mode
 

	
 
    Available modes are::
 
        0 - Unix
 
        1 - Mac
 
        2 - DOS
 

	
 
    :param line: given line to convert
 
    :param mode: mode to convert to
 
    :rtype: str
 
    :return: converted line according to mode
 
    """
 
    from string import replace
 

	
 
    if mode == 0:
 
            line = replace(line, '\r\n', '\n')
 
            line = replace(line, '\r', '\n')
 
    elif mode == 1:
 
            line = replace(line, '\r\n', '\r')
 
            line = replace(line, '\n', '\r')
 
    elif mode == 2:
 
            line = re.sub("\r(?!\n)|(?<!\r)\n", "\r\n", line)
 
    return line
 

	
 

	
 
def detect_mode(line, default):
 
    """
 
    Detects line break for given line, if line break couldn't be found
 
    given default value is returned
 

	
 
    :param line: str line
 
    :param default: default
 
    :rtype: int
 
    :return: value of line end on of 0 - Unix, 1 - Mac, 2 - DOS
 
    """
 
    if line.endswith('\r\n'):
 
        return 2
 
    elif line.endswith('\n'):
 
        return 0
 
    elif line.endswith('\r'):
 
        return 1
 
    else:
 
        return default
 

	
 

	
 
def generate_api_key(username, salt=None):
 
    """
 
    Generates unique API key for given username, if salt is not given
 
    it'll be generated from some random string
 

	
 
    :param username: username as string
 
    :param salt: salt to hash generate KEY
 
    :rtype: str
 
    :returns: sha1 hash from username+salt
 
    """
 
    from tempfile import _RandomNameSequence
 
    import hashlib
 

	
 
    if salt is None:
 
        salt = _RandomNameSequence().next()
 

	
 
    return hashlib.sha1(username + salt).hexdigest()
 

	
 

	
 
def safe_unicode(str_, from_encoding=None):
 
    """
 
    safe unicode function. Does few trick to turn str_ into unicode
 

	
 
    In case of UnicodeDecode error we try to return it with encoding detected
 
    by chardet library if it fails fallback to unicode with errors replaced
 

	
 
    :param str_: string to decode
 
    :rtype: unicode
 
    :returns: unicode object
 
    """
 
    if isinstance(str_, unicode):
 
        return str_
 

	
 
    if not from_encoding:
 
        import rhodecode
 
        DEFAULT_ENCODING = rhodecode.CONFIG.get('default_encoding','utf8')
 
        from_encoding = DEFAULT_ENCODING
 

	
 
    try:
 
        return unicode(str_)
 
    except UnicodeDecodeError:
 
        pass
 

	
 
    try:
 
        return unicode(str_, from_encoding)
 
    except UnicodeDecodeError:
 
        pass
 

	
 
    try:
 
        import chardet
 
        encoding = chardet.detect(str_)['encoding']
 
        if encoding is None:
 
            raise Exception()
 
        return str_.decode(encoding)
 
    except (ImportError, UnicodeDecodeError, Exception):
 
        return unicode(str_, from_encoding, 'replace')
 

	
 

	
 
def safe_str(unicode_, to_encoding=None):
 
    """
 
    safe str function. Does few trick to turn unicode_ into string
 

	
 
    In case of UnicodeEncodeError we try to return it with encoding detected
 
    by chardet library if it fails fallback to string with errors replaced
 

	
 
    :param unicode_: unicode to encode
 
    :rtype: str
 
    :returns: str object
 
    """
 

	
 
    # if it's not basestr cast to str
 
    if not isinstance(unicode_, basestring):
 
        return str(unicode_)
 

	
 
    if isinstance(unicode_, str):
 
        return unicode_
 

	
 
    if not to_encoding:
 
        import rhodecode
 
        DEFAULT_ENCODING = rhodecode.CONFIG.get('default_encoding','utf8')
 
        to_encoding = DEFAULT_ENCODING
 

	
 
    try:
 
        return unicode_.encode(to_encoding)
 
    except UnicodeEncodeError:
 
        pass
 

	
 
    try:
 
        import chardet
 
        encoding = chardet.detect(unicode_)['encoding']
 
        print encoding
 
        if encoding is None:
 
            raise UnicodeEncodeError()
 

	
 
        return unicode_.encode(encoding)
 
    except (ImportError, UnicodeEncodeError):
 
        return unicode_.encode(to_encoding, 'replace')
 

	
 
    return safe_str
 

	
 

	
 
def engine_from_config(configuration, prefix='sqlalchemy.', **kwargs):
 
    """
 
    Custom engine_from_config functions that makes sure we use NullPool for
 
    file based sqlite databases. This prevents errors on sqlite. This only
 
    applies to sqlalchemy versions < 0.7.0
 

	
 
    """
 
    import sqlalchemy
 
    from sqlalchemy import engine_from_config as efc
 
    import logging
 

	
 
    if int(sqlalchemy.__version__.split('.')[1]) < 7:
 

	
 
        # This solution should work for sqlalchemy < 0.7.0, and should use
 
        # proxy=TimerProxy() for execution time profiling
 

	
 
        from sqlalchemy.pool import NullPool
 
        url = configuration[prefix + 'url']
 

	
 
        if url.startswith('sqlite'):
 
            kwargs.update({'poolclass': NullPool})
 
        return efc(configuration, prefix, **kwargs)
 
    else:
 
        import time
 
        from sqlalchemy import event
 
        from sqlalchemy.engine import Engine
 

	
 
        log = logging.getLogger('sqlalchemy.engine')
 
        BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = xrange(30, 38)
 
        engine = efc(configuration, prefix, **kwargs)
 

	
 
        def color_sql(sql):
 
            COLOR_SEQ = "\033[1;%dm"
 
            COLOR_SQL = YELLOW
 
            normal = '\x1b[0m'
 
            return ''.join([COLOR_SEQ % COLOR_SQL, sql, normal])
 

	
 
        if configuration['debug']:
 
            #attach events only for debug configuration
 

	
 
            def before_cursor_execute(conn, cursor, statement,
 
                                    parameters, context, executemany):
 
                context._query_start_time = time.time()
 
                log.info(color_sql(">>>>> STARTING QUERY >>>>>"))
 

	
 

	
 
            def after_cursor_execute(conn, cursor, statement,
 
                                    parameters, context, executemany):
 
                total = time.time() - context._query_start_time
 
                log.info(color_sql("<<<<< TOTAL TIME: %f <<<<<" % total))
 

	
 
            event.listen(engine, "before_cursor_execute",
 
                         before_cursor_execute)
 
            event.listen(engine, "after_cursor_execute",
 
                         after_cursor_execute)
 

	
 
    return engine
 

	
 

	
 
def age(curdate):
 
    """
 
    turns a datetime into an age string.
 

	
 
    :param curdate: datetime object
 
    :rtype: unicode
 
    :returns: unicode words describing age
 
    """
 

	
 
    from datetime import datetime
 
    from webhelpers.date import time_ago_in_words
 

	
 
    _ = lambda s: s
 

	
 
    if not curdate:
 
        return ''
 

	
 
    agescales = [(_(u"year"), 3600 * 24 * 365),
 
                 (_(u"month"), 3600 * 24 * 30),
 
                 (_(u"day"), 3600 * 24),
 
                 (_(u"hour"), 3600),
 
                 (_(u"minute"), 60),
 
                 (_(u"second"), 1), ]
 

	
 
    age = datetime.now() - curdate
 
    age_seconds = (age.days * agescales[2][1]) + age.seconds
 
    pos = 1
 
    for scale in agescales:
 
        if scale[1] <= age_seconds:
 
            if pos == 6:
 
                pos = 5
 
            return '%s %s' % (time_ago_in_words(curdate,
 
                                                agescales[pos][0]), _('ago'))
 
        pos += 1
 

	
 
    return _(u'just now')
 

	
 

	
 
def uri_filter(uri):
 
    """
 
    Removes user:password from given url string
 

	
 
    :param uri:
 
    :rtype: unicode
 
    :returns: filtered list of strings
 
    """
 
    if not uri:
 
        return ''
 

	
 
    proto = ''
 

	
 
    for pat in ('https://', 'http://'):
 
        if uri.startswith(pat):
 
            uri = uri[len(pat):]
 
            proto = pat
 
            break
 

	
 
    # remove passwords and username
 
    uri = uri[uri.find('@') + 1:]
 

	
 
    # get the port
 
    cred_pos = uri.find(':')
 
    if cred_pos == -1:
 
        host, port = uri, None
 
    else:
 
        host, port = uri[:cred_pos], uri[cred_pos + 1:]
 

	
 
    return filter(None, [proto, host, port])
 

	
 

	
 
def credentials_filter(uri):
 
    """
 
    Returns a url with removed credentials
 

	
 
    :param uri:
 
    """
 

	
 
    uri = uri_filter(uri)
 
    #check if we have port
 
    if len(uri) > 2 and uri[2]:
 
        uri[2] = ':' + uri[2]
 

	
 
    return ''.join(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
 

	
 

	
 
def get_current_revision(quiet=False):
 
@@ -450,16 +22,3 @@ def get_current_revision(quiet=False):
 
            print ("Cannot retrieve rhodecode's revision. Original error "
 
                   "was: %s" % err)
 
        return None
 

	
 

	
 
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
 

	
 
    return sorted(usrs.keys())
rhodecode/lib/auth.py
Show inline comments
 
@@ -43,7 +43,7 @@ if __platform__ in PLATFORM_WIN:
 
if __platform__ in PLATFORM_OTHERS:
 
    import bcrypt
 

	
 
from rhodecode.lib import str2bool, safe_unicode
 
from rhodecode.lib.utils2 import str2bool, safe_unicode
 
from rhodecode.lib.exceptions import LdapPasswordError, LdapUsernameError
 
from rhodecode.lib.utils import get_repo_slug, get_repos_group_slug
 
from rhodecode.lib.auth_ldap import AuthLdap
 
@@ -521,8 +521,7 @@ class PermsDecorator(object):
 
        self.user = cls.rhodecode_user
 
        self.user_perms = self.user.permissions
 
        log.debug('checking %s permissions %s for %s %s',
 
           self.__class__.__name__, self.required_perms, cls,
 
               self.user)
 
           self.__class__.__name__, self.required_perms, cls, self.user)
 

	
 
        if self.check_permissions():
 
            log.debug('Permission granted for %s %s' % (cls, self.user))
 
@@ -604,6 +603,7 @@ class HasRepoPermissionAnyDecorator(Perm
 
            user_perms = set([self.user_perms['repositories'][repo_name]])
 
        except KeyError:
 
            return False
 

	
 
        if self.required_perms.intersection(user_perms):
 
            return True
 
        return False
 
@@ -655,29 +655,37 @@ class PermsFunction(object):
 

	
 
        for perm in perms:
 
            if perm not in available_perms:
 
                raise Exception("'%s' permission in not defined" % perm)
 
                raise Exception("'%s' permission is not defined" % perm)
 
        self.required_perms = set(perms)
 
        self.user_perms = None
 
        self.granted_for = ''
 
        self.repo_name = None
 
        self.group_name = None
 

	
 
    def __call__(self, check_Location=''):
 
        user = request.user
 
        log.debug('checking %s %s %s', self.__class__.__name__,
 
                  self.required_perms, user)
 
        cls_name = self.__class__.__name__
 
        check_scope = {
 
            'HasPermissionAll': '',
 
            'HasPermissionAny': '',
 
            'HasRepoPermissionAll': 'repo:%s' % self.repo_name,
 
            'HasRepoPermissionAny': 'repo:%s' % self.repo_name,
 
            'HasReposGroupPermissionAll': 'group:%s' % self.group_name,
 
            'HasReposGroupPermissionAny': 'group:%s' % self.group_name,
 
        }.get(cls_name, '?')
 
        log.debug('checking cls:%s %s usr:%s %s @ %s', cls_name,
 
                  self.required_perms, user, check_scope,
 
                  check_Location or 'unspecified location')
 
        if not user:
 
            log.debug('Empty request user')
 
            return False
 
        self.user_perms = user.permissions
 
        self.granted_for = user
 

	
 
        if self.check_permissions():
 
            log.debug('Permission granted %s @ %s', self.granted_for,
 
            log.debug('Permission granted for user: %s @ %s', user,
 
                      check_Location or 'unspecified location')
 
            return True
 

	
 
        else:
 
            log.debug('Permission denied for %s @ %s', self.granted_for,
 
            log.debug('Permission denied for user: %s @ %s', user,
 
                        check_Location or 'unspecified location')
 
            return False
 

	
 
@@ -701,7 +709,6 @@ class HasPermissionAny(PermsFunction):
 

	
 

	
 
class HasRepoPermissionAll(PermsFunction):
 

	
 
    def __call__(self, repo_name=None, check_Location=''):
 
        self.repo_name = repo_name
 
        return super(HasRepoPermissionAll, self).__call__(check_Location)
 
@@ -711,19 +718,17 @@ class HasRepoPermissionAll(PermsFunction
 
            self.repo_name = get_repo_slug(request)
 

	
 
        try:
 
            self.user_perms = set(
 
            self._user_perms = set(
 
                [self.user_perms['repositories'][self.repo_name]]
 
            )
 
        except KeyError:
 
            return False
 
        self.granted_for = self.repo_name
 
        if self.required_perms.issubset(self.user_perms):
 
        if self.required_perms.issubset(self._user_perms):
 
            return True
 
        return False
 

	
 

	
 
class HasRepoPermissionAny(PermsFunction):
 

	
 
    def __call__(self, repo_name=None, check_Location=''):
 
        self.repo_name = repo_name
 
        return super(HasRepoPermissionAny, self).__call__(check_Location)
 
@@ -733,13 +738,12 @@ class HasRepoPermissionAny(PermsFunction
 
            self.repo_name = get_repo_slug(request)
 

	
 
        try:
 
            self.user_perms = set(
 
            self._user_perms = set(
 
                [self.user_perms['repositories'][self.repo_name]]
 
            )
 
        except KeyError:
 
            return False
 
        self.granted_for = self.repo_name
 
        if self.required_perms.intersection(self.user_perms):
 
        if self.required_perms.intersection(self._user_perms):
 
            return True
 
        return False
 

	
 
@@ -751,13 +755,12 @@ class HasReposGroupPermissionAny(PermsFu
 

	
 
    def check_permissions(self):
 
        try:
 
            self.user_perms = set(
 
            self._user_perms = set(
 
                [self.user_perms['repositories_groups'][self.group_name]]
 
            )
 
        except KeyError:
 
            return False
 
        self.granted_for = self.repo_name
 
        if self.required_perms.intersection(self.user_perms):
 
        if self.required_perms.intersection(self._user_perms):
 
            return True
 
        return False
 

	
 
@@ -769,13 +772,12 @@ class HasReposGroupPermissionAll(PermsFu
 

	
 
    def check_permissions(self):
 
        try:
 
            self.user_perms = set(
 
            self._user_perms = set(
 
                [self.user_perms['repositories_groups'][self.group_name]]
 
            )
 
        except KeyError:
 
            return False
 
        self.granted_for = self.repo_name
 
        if self.required_perms.issubset(self.user_perms):
 
        if self.required_perms.issubset(self._user_perms):
 
            return True
 
        return False
 

	
 
@@ -788,12 +790,16 @@ class HasPermissionAnyMiddleware(object)
 
        self.required_perms = set(perms)
 

	
 
    def __call__(self, user, repo_name):
 
        # repo_name MUST be unicode, since we handle keys in permission
 
        # dict by unicode
 
        repo_name = safe_unicode(repo_name)
 
        usr = AuthUser(user.user_id)
 
        try:
 
            self.user_perms = set([usr.permissions['repositories'][repo_name]])
 
        except:
 
        except Exception:
 
            log.error('Exception while accessing permissions %s' %
 
                      traceback.format_exc())
 
            self.user_perms = set()
 
        self.granted_for = ''
 
        self.username = user.username
 
        self.repo_name = repo_name
 
        return self.check_permissions()
 
@@ -803,7 +809,13 @@ class HasPermissionAnyMiddleware(object)
 
                  'permissions %s for user:%s repository:%s', self.user_perms,
 
                                                self.username, self.repo_name)
 
        if self.required_perms.intersection(self.user_perms):
 
            log.debug('permission granted')
 
            log.debug('permission granted for user:%s on repo:%s' % (
 
                          self.username, self.repo_name
 
                     )
 
            )
 
            return True
 
        log.debug('permission denied')
 
        log.debug('permission denied for user:%s on repo:%s' % (
 
                      self.username, self.repo_name
 
                 )
 
        )
 
        return False
rhodecode/lib/base.py
Show inline comments
 
@@ -7,6 +7,8 @@ import time
 
import traceback
 

	
 
from paste.auth.basic import AuthBasicAuthenticator
 
from paste.httpexceptions import HTTPUnauthorized, HTTPForbidden
 
from paste.httpheaders import WWW_AUTHENTICATE
 

	
 
from pylons import config, tmpl_context as c, request, session, url
 
from pylons.controllers import WSGIController
 
@@ -15,7 +17,7 @@ from pylons.templating import render_mak
 

	
 
from rhodecode import __version__, BACKENDS
 

	
 
from rhodecode.lib import str2bool, safe_unicode
 
from rhodecode.lib.utils2 import str2bool, safe_unicode
 
from rhodecode.lib.auth import AuthUser, get_container_username, authfunc,\
 
    HasPermissionAnyMiddleware, CookieStoreWrapper
 
from rhodecode.lib.utils import get_repo_slug, invalidate_cache
 
@@ -28,6 +30,22 @@ from rhodecode.model.scm import ScmModel
 
log = logging.getLogger(__name__)
 

	
 

	
 
class BasicAuth(AuthBasicAuthenticator):
 

	
 
    def __init__(self, realm, authfunc, auth_http_code=None):
 
        self.realm = realm
 
        self.authfunc = authfunc
 
        self._rc_auth_http_code = auth_http_code
 

	
 
    def build_authentication(self):
 
        head = WWW_AUTHENTICATE.tuples('Basic realm="%s"' % self.realm)
 
        if self._rc_auth_http_code and self._rc_auth_http_code == '403':
 
            # return 403 if alternative http return code is specified in
 
            # RhodeCode config
 
            return HTTPForbidden(headers=head)
 
        return HTTPUnauthorized(headers=head)
 

	
 

	
 
class BaseVCSController(object):
 

	
 
    def __init__(self, application, config):
 
@@ -36,7 +54,8 @@ class BaseVCSController(object):
 
        # base path of repo locations
 
        self.basepath = self.config['base_path']
 
        #authenticate this mercurial request using authfunc
 
        self.authenticate = AuthBasicAuthenticator('', authfunc)
 
        self.authenticate = BasicAuth('', authfunc,
 
                                      config.get('auth_ret_code'))
 
        self.ipaddr = '0.0.0.0'
 

	
 
    def _handle_request(self, environ, start_response):
rhodecode/lib/caching_query.py
Show inline comments
 
@@ -24,7 +24,7 @@ from beaker.exceptions import BeakerExce
 
from sqlalchemy.orm.interfaces import MapperOption
 
from sqlalchemy.orm.query import Query
 
from sqlalchemy.sql import visitors
 
from rhodecode.lib import safe_str
 
from rhodecode.lib.utils2 import safe_str
 

	
 

	
 
class CachingQuery(Query):
rhodecode/lib/celerylib/__init__.py
Show inline comments
 
@@ -36,7 +36,7 @@ from decorator import decorator
 

	
 
from rhodecode.lib.vcs.utils.lazy import LazyProperty
 
from rhodecode import CELERY_ON
 
from rhodecode.lib import str2bool, safe_str
 
from rhodecode.lib.utils2 import str2bool, safe_str
 
from rhodecode.lib.pidlock import DaemonLock, LockHeld
 
from rhodecode.model import init_model
 
from rhodecode.model import meta
rhodecode/lib/celerylib/tasks.py
Show inline comments
 
@@ -40,7 +40,7 @@ from pylons.i18n.translation import _
 
from rhodecode.lib.vcs import get_backend
 

	
 
from rhodecode import CELERY_ON
 
from rhodecode.lib import LANGUAGES_EXTENSIONS_MAP, safe_str
 
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
 
@@ -147,6 +147,7 @@ def get_commits_stats(repo_name, ts_min_
 
             last_rev, last_rev + parse_limit)
 
        )
 
        for cs in repo[last_rev:last_rev + parse_limit]:
 
            log.debug('parsing %s' % cs)
 
            last_cs = cs  # remember last parsed changeset
 
            k = lmktime([cs.date.timetuple()[0], cs.date.timetuple()[1],
 
                          cs.date.timetuple()[2], 0, 0, 0, 0, 0, 0])
 
@@ -233,10 +234,10 @@ def get_commits_stats(repo_name, ts_min_
 
            lock.release()
 
            return False
 

	
 
        #final release
 
        # final release
 
        lock.release()
 

	
 
        #execute another task if celery is enabled
 
        # execute another task if celery is enabled
 
        if len(repo.revisions) > 1 and CELERY_ON:
 
            run_task(get_commits_stats, repo_name, ts_min_y, ts_max_y)
 
        return True
 
@@ -327,7 +328,7 @@ def send_email(recipients, subject, body
 
    DBS = get_session()
 

	
 
    email_config = config
 
    subject = "%s %s" % (email_config.get('email_prefix'), subject)
 
    subject = "%s %s" % (email_config.get('email_prefix', ''), subject)
 
    if not recipients:
 
        # if recipients are not defined we send to email_config + all admins
 
        admins = [u.email for u in User.query()
 
@@ -395,6 +396,7 @@ def create_repo_fork(form_data, cur_user
 
    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()
rhodecode/lib/celerypylons/commands.py
Show inline comments
 
import rhodecode
 
from rhodecode.lib.utils import BasePasterCommand, Command
 
from rhodecode.lib.utils import BasePasterCommand, Command, load_rcextensions
 
from celery.app import app_or_default
 
from celery.bin import camqadm, celerybeat, celeryd, celeryev
 

	
 
from rhodecode.lib import str2bool
 
from rhodecode.lib.utils2 import str2bool
 

	
 
__all__ = ['CeleryDaemonCommand', 'CeleryBeatCommand',
 
           'CAMQPAdminCommand', 'CeleryEventCommand']
 
@@ -39,9 +39,11 @@ class CeleryCommand(BasePasterCommand):
 
            raise Exception('Please enable celery_on in .ini config '
 
                            'file before running celeryd')
 
        rhodecode.CELERY_ON = CELERY_ON
 
        load_rcextensions(config['here'])
 
        cmd = self.celery_command(app_or_default())
 
        return cmd.run(**vars(self.options))
 

	
 

	
 
class CeleryDaemonCommand(CeleryCommand):
 
    """Start the celery worker
 

	
 
@@ -82,6 +84,7 @@ class CAMQPAdminCommand(CeleryCommand):
 
    parser = Command.standard_parser(quiet=True)
 
    celery_command = camqadm.AMQPAdminCommand
 

	
 

	
 
class CeleryEventCommand(CeleryCommand):
 
    """Celery event command.
 

	
rhodecode/lib/compat.py
Show inline comments
 
@@ -25,16 +25,93 @@
 
# 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)
 

	
 

	
 
#==============================================================================
 
# izip_longest
 
@@ -44,11 +121,11 @@ try:
 
except ImportError:
 
    import itertools
 

	
 
    def izip_longest(*args, **kwds): # noqa
 
    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
 
            yield counter()  # yields the fillvalue, or raises IndexError
 

	
 
        fillers = itertools.repeat(fillvalue)
 
        iters = [itertools.chain(it, sentinel(), fillers)
rhodecode/lib/db_manage.py
Show inline comments
 
@@ -376,7 +376,7 @@ class DbManage(object):
 

	
 
        if not self.tests and not test_repo_path:
 
            path = raw_input(
 
                 'Enter a valid path to store repositories. '
 
                 'Enter a valid absolute path to store repositories. '
 
                 'All repositories in that path will be added automatically:'
 
            )
 
        else:
 
@@ -388,8 +388,12 @@ class DbManage(object):
 
            path_ok = False
 
            log.error('Given path %s is not a valid directory' % path)
 

	
 
        elif not os.path.isabs(path):
 
            path_ok = False
 
            log.error('Given path %s is not an absolute path' % path)
 

	
 
        # check write access
 
        if not os.access(path, os.W_OK) and path_ok:
 
        elif not os.access(path, os.W_OK) and path_ok:
 
            path_ok = False
 
            log.error('No write permission to given path %s' % path)
 

	
rhodecode/lib/dbmigrate/migrate/exceptions.py
Show inline comments
 
@@ -71,9 +71,6 @@ class InvalidScriptError(ScriptError):
 
    """Invalid script error."""
 

	
 

	
 
class InvalidVersionError(Error):
 
    """Invalid version error."""
 

	
 
# migrate.changeset
 

	
 
class NotSupportedError(Error):
rhodecode/lib/dbmigrate/schema/db_1_2_0.py
Show inline comments
 
@@ -39,7 +39,7 @@ from rhodecode.lib.vcs.utils.helpers imp
 
from rhodecode.lib.vcs.exceptions import VCSError
 
from rhodecode.lib.vcs.utils.lazy import LazyProperty
 

	
 
from rhodecode.lib import str2bool, safe_str, get_changeset_safe, \
 
from rhodecode.lib.utils2 import str2bool, safe_str, get_changeset_safe, \
 
    generate_api_key, safe_unicode
 
from rhodecode.lib.exceptions import UsersGroupsAssignedException
 
from rhodecode.lib.compat import json
 
@@ -717,7 +717,7 @@ class Repository(Base, BaseModel):
 
        return repo
 

	
 

	
 
class RepoGroup(Base, BaseModel):
 
class Group(Base, BaseModel):
 
    __tablename__ = 'groups'
 
    __table_args__ = (UniqueConstraint('group_name', 'group_parent_id'),
 
                      CheckConstraint('group_id != group_parent_id'), {'extend_existing':True},)
 
@@ -728,8 +728,7 @@ class RepoGroup(Base, BaseModel):
 
    group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
 
    group_description = Column("group_description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
 

	
 
    parent_group = relationship('RepoGroup', remote_side=group_id)
 

	
 
    parent_group = relationship('Group', remote_side=group_id)
 

	
 
    def __init__(self, group_name='', parent_group=None):
 
        self.group_name = group_name
rhodecode/lib/helpers.py
Show inline comments
 
@@ -39,12 +39,20 @@ from webhelpers.html.tags import _set_in
 

	
 
from rhodecode.lib.annotate import annotate_highlight
 
from rhodecode.lib.utils import repo_name_slug
 
from rhodecode.lib import str2bool, safe_unicode, safe_str, get_changeset_safe
 
from rhodecode.lib.utils2 import str2bool, safe_unicode, safe_str, \
 
    get_changeset_safe
 
from rhodecode.lib.markup_renderer import MarkupRenderer
 

	
 
log = logging.getLogger(__name__)
 

	
 

	
 
def shorter(text, size=20):
 
    postfix = '...'
 
    if len(text) > size:
 
        return text[:size - len(postfix)] + postfix
 
    return text
 

	
 

	
 
def _reset(name, value=None, id=NotGiven, type="reset", **attrs):
 
    """
 
    Reset button
 
@@ -67,7 +75,7 @@ def FID(raw_id, path):
 
    :param path:
 
    """
 

	
 
    return 'C-%s-%s' % (short_id(raw_id), md5(path).hexdigest()[:12])
 
    return 'C-%s-%s' % (short_id(raw_id), md5(safe_str(path)).hexdigest()[:12])
 

	
 

	
 
def get_token():
 
@@ -86,6 +94,7 @@ def get_token():
 
            session.save()
 
    return session[token_key]
 

	
 

	
 
class _GetError(object):
 
    """Get error from form_errors, and represent it as span wrapped error
 
    message
 
@@ -101,6 +110,7 @@ class _GetError(object):
 

	
 
get_error = _GetError()
 

	
 

	
 
class _ToolTip(object):
 

	
 
    def __call__(self, tooltip_title, trim_at=50):
 
@@ -112,6 +122,7 @@ class _ToolTip(object):
 
        return escape(tooltip_title)
 
tooltip = _ToolTip()
 

	
 

	
 
class _FilesBreadCrumbs(object):
 

	
 
    def __call__(self, repo_name, rev, paths):
 
@@ -136,8 +147,10 @@ class _FilesBreadCrumbs(object):
 

	
 
files_breadcrumbs = _FilesBreadCrumbs()
 

	
 

	
 
class CodeHtmlFormatter(HtmlFormatter):
 
    """My code Html Formatter for source codes
 
    """
 
    My code Html Formatter for source codes
 
    """
 

	
 
    def wrap(self, source, outfile):
 
@@ -319,7 +332,7 @@ flash = _Flash()
 
# SCM FILTERS available via h.
 
#==============================================================================
 
from rhodecode.lib.vcs.utils import author_name, author_email
 
from rhodecode.lib import credentials_filter, age as _age
 
from rhodecode.lib.utils2 import credentials_filter, age as _age
 
from rhodecode.model.db import User
 

	
 
age = lambda  x: _age(x)
 
@@ -759,10 +772,10 @@ def fancy_file_stats(stats):
 
    d_v = d if d > 0 else ''
 

	
 
    def cgen(l_type):
 
        mapping = {'tr': 'top-right-rounded-corner',
 
                   'tl': 'top-left-rounded-corner',
 
                   'br': 'bottom-right-rounded-corner',
 
                   'bl': 'bottom-left-rounded-corner'}
 
        mapping = {'tr': 'top-right-rounded-corner-mid',
 
                   'tl': 'top-left-rounded-corner-mid',
 
                   'br': 'bottom-right-rounded-corner-mid',
 
                   'bl': 'bottom-left-rounded-corner-mid'}
 
        map_getter = lambda x: mapping[x]
 

	
 
        if l_type == 'a' and d_v:
 
@@ -801,6 +814,12 @@ def urlify_text(text_):
 

	
 

	
 
def urlify_changesets(text_, repository):
 
    """
 
    Extract revision ids from changeset and make link from them
 

	
 
    :param text_:
 
    :param repository:
 
    """
 
    import re
 
    URL_PAT = re.compile(r'([0-9a-fA-F]{12,})')
 

	
 
@@ -839,8 +858,8 @@ def urlify_commit(text_, repository=None
 
    import re
 
    import traceback
 

	
 
    # urlify changesets
 
    text_ = urlify_changesets(text_, repository)
 
    def escaper(string):
 
        return string.replace('<', '&lt;').replace('>', '&gt;')
 

	
 
    def linkify_others(t, l):
 
        urls = re.compile(r'(\<a.*?\<\/a\>)',)
 
@@ -852,6 +871,11 @@ def urlify_commit(text_, repository=None
 
                links.append(e)
 

	
 
        return ''.join(links)
 

	
 

	
 
    # urlify changesets - extrac revisions and make link out of them
 
    text_ = urlify_changesets(escaper(text_), repository)
 

	
 
    try:
 
        conf = config['app_conf']
 

	
rhodecode/lib/hooks.py
Show inline comments
 
@@ -27,9 +27,10 @@ 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 repo_size(ui, repo, hooktype=None, **kwargs):
 
@@ -78,14 +79,19 @@ def log_pull_action(ui, repo, **kwargs):
 
    :param repo:
 
    """
 

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

	
 
    action_logger(username, action, repository, extra_params['ip'],
 
                  commit=True)
 
    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
 

	
 

	
 
@@ -97,10 +103,10 @@ def log_push_action(ui, repo, **kwargs):
 
    :param repo:
 
    """
 

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

	
 
    def get_revs(repo, rev_opt):
 
@@ -119,16 +125,22 @@ def log_push_action(ui, repo, **kwargs):
 

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

	
 
    action_logger(username, action, repository, extra_params['ip'],
 
                  commit=True)
 
    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
 
    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
 
@@ -151,5 +163,12 @@ def log_create_repository(repository_dic
 

	
 
    """
 

	
 
    callback = getattr(EXTENSIONS, 'CREATE_REPO_HOOK', None)
 
    if isfunction(callback):
 
        kw = {}
 
        kw.update(repository_dict)
 
        kw.update({'created_by': created_by})
 
        kw.update(kwargs)
 
        return callback(**kw)
 

	
 
    return 0
rhodecode/lib/indexers/__init__.py
Show inline comments
 
@@ -25,6 +25,7 @@
 
import os
 
import sys
 
import traceback
 
import logging
 
from os.path import dirname as dn, join as jn
 

	
 
#to get the rhodecode import
 
@@ -46,11 +47,9 @@ from rhodecode.model import init_model
 
from rhodecode.model.scm import ScmModel
 
from rhodecode.model.repo import RepoModel
 
from rhodecode.config.environment import load_environment
 
from rhodecode.lib import LANGUAGES_EXTENSIONS_MAP, LazyProperty
 
from rhodecode.lib.utils import BasePasterCommand, Command, add_cache
 

	
 
# EXTENSIONS WE WANT TO INDEX CONTENT OFF
 
INDEX_EXTENSIONS = LANGUAGES_EXTENSIONS_MAP.keys()
 
from rhodecode.lib.utils2 import LazyProperty
 
from rhodecode.lib.utils import BasePasterCommand, Command, add_cache,\
 
    load_rcextensions
 

	
 
# CUSTOM ANALYZER wordsplit + lowercase filter
 
ANALYZER = RegexTokenizer(expression=r"\w+") | LowercaseFilter()
 
@@ -84,18 +83,17 @@ class MakeIndex(BasePasterCommand):
 
    parser = Command.standard_parser(verbose=True)
 

	
 
    def command(self):
 

	
 
        logging.config.fileConfig(self.path_to_ini_file)
 
        from pylons import config
 
        add_cache(config)
 
        engine = engine_from_config(config, 'sqlalchemy.db1.')
 
        init_model(engine)
 

	
 
        index_location = config['index_dir']
 
        repo_location = self.options.repo_location \
 
            if self.options.repo_location else RepoModel().repos_path
 
        repo_list = map(strip, self.options.repo_list.split(',')) \
 
            if self.options.repo_list else None
 

	
 
        load_rcextensions(config['here'])
 
        #======================================================================
 
        # WHOOSH DAEMON
 
        #======================================================================
 
@@ -105,7 +103,7 @@ class MakeIndex(BasePasterCommand):
 
            l = DaemonLock(file_=jn(dn(dn(index_location)), 'make_index.lock'))
 
            WhooshIndexingDaemon(index_location=index_location,
 
                                 repo_location=repo_location,
 
                                 repo_list=repo_list)\
 
                                 repo_list=repo_list,)\
 
                .run(full_index=self.options.full_index)
 
            l.release()
 
        except LockHeld:
rhodecode/lib/indexers/daemon.py
Show inline comments
 
@@ -38,34 +38,17 @@ from os.path import join as jn
 
project_path = dn(dn(dn(dn(os.path.realpath(__file__)))))
 
sys.path.append(project_path)
 

	
 

	
 
from rhodecode.config.conf import INDEX_EXTENSIONS
 
from rhodecode.model.scm import ScmModel
 
from rhodecode.lib import safe_unicode
 
from rhodecode.lib.indexers import INDEX_EXTENSIONS, SCHEMA, IDX_NAME
 
from rhodecode.lib.utils2 import safe_unicode
 
from rhodecode.lib.indexers import SCHEMA, IDX_NAME
 

	
 
from rhodecode.lib.vcs.exceptions import ChangesetError, RepositoryError, \
 
    NodeDoesNotExistError
 

	
 
from whoosh.index import create_in, open_dir
 

	
 

	
 
log = logging.getLogger('whooshIndexer')
 
# create logger
 
log.setLevel(logging.DEBUG)
 
log.propagate = False
 
# create console handler and set level to debug
 
ch = logging.StreamHandler()
 
ch.setLevel(logging.DEBUG)
 

	
 
# create formatter
 
formatter = logging.Formatter("%(asctime)s - %(name)s -"
 
                              " %(levelname)s - %(message)s")
 

	
 
# add formatter to ch
 
ch.setFormatter(formatter)
 

	
 
# add ch to logger
 
log.addHandler(ch)
 
log = logging.getLogger('whoosh_indexer')
 

	
 

	
 
class WhooshIndexingDaemon(object):
 
@@ -103,7 +86,8 @@ class WhooshIndexingDaemon(object):
 
            self.initial = True
 

	
 
    def get_paths(self, repo):
 
        """recursive walk in root dir and return a set of all path in that dir
 
        """
 
        recursive walk in root dir and return a set of all path in that dir
 
        based on repository walk function
 
        """
 
        index_paths_ = set()
 
@@ -127,32 +111,39 @@ class WhooshIndexingDaemon(object):
 
        return mktime(node.last_changeset.date.timetuple())
 

	
 
    def add_doc(self, writer, path, repo, repo_name):
 
        """Adding doc to writer this function itself fetches data from
 
        the instance of vcs backend"""
 
        node = self.get_node(repo, path)
 
        """
 
        Adding doc to writer this function itself fetches data from
 
        the instance of vcs backend
 
        """
 

	
 
        #we just index the content of chosen files, and skip binary files
 
        node = self.get_node(repo, path)
 
        indexed = indexed_w_content = 0
 
        # we just index the content of chosen files, and skip binary files
 
        if node.extension in INDEX_EXTENSIONS and not node.is_binary:
 

	
 
            u_content = node.content
 
            if not isinstance(u_content, unicode):
 
                log.warning('  >> %s Could not get this content as unicode '
 
                          'replacing with empty content', path)
 
                            'replacing with empty content' % path)
 
                u_content = u''
 
            else:
 
                log.debug('    >> %s [WITH CONTENT]' % path)
 
                indexed_w_content += 1
 

	
 
        else:
 
            log.debug('    >> %s' % path)
 
            #just index file name without it's content
 
            # just index file name without it's content
 
            u_content = u''
 
            indexed += 1
 

	
 
        writer.add_document(owner=unicode(repo.contact),
 
                        repository=safe_unicode(repo_name),
 
                        path=safe_unicode(path),
 
                        content=u_content,
 
                        modtime=self.get_node_mtime(node),
 
                        extension=node.extension)
 
        writer.add_document(
 
            owner=unicode(repo.contact),
 
            repository=safe_unicode(repo_name),
 
            path=safe_unicode(path),
 
            content=u_content,
 
            modtime=self.get_node_mtime(node),
 
            extension=node.extension
 
        )
 
        return indexed, indexed_w_content
 

	
 
    def build_index(self):
 
        if os.path.exists(self.index_location):
 
@@ -164,19 +155,25 @@ class WhooshIndexingDaemon(object):
 

	
 
        idx = create_in(self.index_location, SCHEMA, indexname=IDX_NAME)
 
        writer = idx.writer()
 

	
 
        log.debug('BUILDIN INDEX FOR EXTENSIONS %s' % INDEX_EXTENSIONS)
 
        for repo_name, repo in self.repo_paths.items():
 
            log.debug('building index @ %s' % repo.path)
 

	
 
            i_cnt = iwc_cnt = 0
 
            for idx_path in self.get_paths(repo):
 
                self.add_doc(writer, idx_path, repo, repo_name)
 
                i, iwc = self.add_doc(writer, idx_path, repo, repo_name)
 
                i_cnt += i
 
                iwc_cnt += iwc
 
            log.debug('added %s files %s with content for repo %s' % (
 
                         i_cnt + iwc_cnt, iwc_cnt, repo.path)
 
            )
 

	
 
        log.debug('>> COMMITING CHANGES <<')
 
        writer.commit(merge=True)
 
        log.debug('>>> FINISHED BUILDING INDEX <<<')
 

	
 
    def update_index(self):
 
        log.debug('STARTING INCREMENTAL INDEXING UPDATE')
 
        log.debug('STARTING INCREMENTAL INDEXING UPDATE FOR EXTENSIONS %s' %
 
                  INDEX_EXTENSIONS)
 

	
 
        idx = open_dir(self.index_location, indexname=self.indexname)
 
        # The set of all paths in the index
 
@@ -215,14 +212,19 @@ class WhooshIndexingDaemon(object):
 
        # Loop over the files in the filesystem
 
        # Assume we have a function that gathers the filenames of the
 
        # documents to be indexed
 
        ri_cnt = riwc_cnt = 0
 
        for repo_name, repo in self.repo_paths.items():
 
            for path in self.get_paths(repo):
 
                if path in to_index or path not in indexed_paths:
 
                    # This is either a file that's changed, or a new file
 
                    # that wasn't indexed before. So index it!
 
                    self.add_doc(writer, path, repo, repo_name)
 
                    i, iwc = self.add_doc(writer, path, repo, repo_name)
 
                    log.debug('re indexing %s' % path)
 

	
 
                    ri_cnt += i
 
                    riwc_cnt += iwc
 
        log.debug('added %s files %s with content for repo %s' % (
 
                     ri_cnt + riwc_cnt, riwc_cnt, repo.path)
 
        )
 
        log.debug('>> COMMITING CHANGES <<')
 
        writer.commit(merge=True)
 
        log.debug('>>> FINISHED REBUILDING INDEX <<<')
rhodecode/lib/markup_renderer.py
Show inline comments
 
@@ -27,7 +27,7 @@
 
import re
 
import logging
 

	
 
from rhodecode.lib import safe_unicode
 
from rhodecode.lib.utils2 import safe_unicode
 

	
 
log = logging.getLogger(__name__)
 

	
rhodecode/lib/middleware/https_fixup.py
Show inline comments
 
@@ -23,7 +23,7 @@
 
# You should have received a copy of the GNU General Public License
 
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
 

	
 
from rhodecode.lib import str2bool
 
from rhodecode.lib.utils2 import str2bool
 

	
 

	
 
class HttpsFixup(object):
rhodecode/lib/middleware/simplegit.py
Show inline comments
 
@@ -65,11 +65,11 @@ dulserver.DEFAULT_HANDLERS = {
 
}
 

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

	
 
from paste.httpheaders import REMOTE_USER, AUTH_TYPE
 

	
 
from rhodecode.lib import safe_str
 
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
 
@@ -86,7 +86,9 @@ GIT_PROTO_PAT = re.compile(r'^/(.+)/(inf
 
def is_git(environ):
 
    path_info = environ['PATH_INFO']
 
    isgit_path = GIT_PROTO_PAT.match(path_info)
 
    log.debug('is a git path %s pathinfo : %s' % (isgit_path, path_info))
 
    log.debug('pathinfo: %s detected as GIT %s' % (
 
        path_info, isgit_path != None)
 
    )
 
    return isgit_path
 

	
 

	
 
@@ -113,6 +115,10 @@ class SimpleGit(BaseVCSController):
 
        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
 
        #======================================================================
 
@@ -121,7 +127,6 @@ class SimpleGit(BaseVCSController):
 
        #======================================================================
 
        # CHECK ANONYMOUS PERMISSION
 
        #======================================================================
 

	
 
        if action in ['pull', 'push']:
 
            anonymous_user = self.__get_user('default')
 
            username = anonymous_user.username
 
@@ -177,13 +182,9 @@ class SimpleGit(BaseVCSController):
 
        #===================================================================
 
        # GIT REQUEST HANDLING
 
        #===================================================================
 
        repo_path = safe_str(os.path.join(self.basepath, repo_name))
 
        repo_path = os.path.join(safe_str(self.basepath), safe_str(repo_name))
 
        log.debug('Repository path is %s' % repo_path)
 

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

	
 
        try:
 
            #invalidate cache on push
 
            if action == 'push':
 
@@ -204,7 +205,7 @@ class SimpleGit(BaseVCSController):
 
        """
 
        _d = {'/' + repo_name: Repo(repo_path)}
 
        backend = dulserver.DictBackend(_d)
 
        gitserve = HTTPGitApplication(backend)
 
        gitserve = make_wsgi_chain(backend)
 

	
 
        return gitserve
 

	
rhodecode/lib/middleware/simplehg.py
Show inline comments
 
@@ -27,13 +27,14 @@
 
import os
 
import logging
 
import traceback
 
import urllib
 

	
 
from mercurial.error import RepoError
 
from mercurial.hgweb import hgweb_mod
 

	
 
from paste.httpheaders import REMOTE_USER, AUTH_TYPE
 

	
 
from rhodecode.lib import safe_str
 
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 make_ui, is_valid_repo, ui_sections
 
@@ -45,13 +46,21 @@ log = logging.getLogger(__name__)
 

	
 

	
 
def is_mercurial(environ):
 
    """Returns True if request's target is mercurial server - header
 
    """
 
    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'):
 
        return True
 
    return False
 
        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):
 
@@ -76,16 +85,20 @@ class SimpleHg(BaseVCSController):
 
        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
 
        #======================================================================
 
        if action in ['pull', 'push']:
 
            anonymous_user = self.__get_user('default')
 

	
 
            username = anonymous_user.username
 
            anonymous_perm = self._check_permission(action, anonymous_user,
 
                                                    repo_name)
 
@@ -132,30 +145,28 @@ class SimpleHg(BaseVCSController):
 
                                                         start_response)
 

	
 
                    #check permissions for this repository
 
                    perm = self._check_permission(action, user,
 
                                                   repo_name)
 
                    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}
 
        # 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
 
        }
 

	
 
        #======================================================================
 
        # MERCURIAL REQUEST HANDLING
 
        #======================================================================
 

	
 
        repo_path = safe_str(os.path.join(self.basepath, repo_name))
 
        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)
 

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

	
 
        try:
 
            # invalidate cache on push
 
            if action == 'push':
rhodecode/lib/utils.py
Show inline comments
 
@@ -51,9 +51,12 @@ from rhodecode.lib.caching_query import 
 

	
 
from rhodecode.model import meta
 
from rhodecode.model.db import Repository, User, RhodeCodeUi, \
 
    UserLog, RepoGroup, RhodeCodeSetting, UserRepoGroupToPerm
 
    UserLog, RepoGroup, RhodeCodeSetting, UserRepoGroupToPerm,\
 
    CacheInvalidation
 
from rhodecode.model.meta import Session
 
from rhodecode.model.repos_group import ReposGroupModel
 
from rhodecode.lib.utils2 import safe_str, safe_unicode
 
from rhodecode.lib.vcs.utils.fakemod import create_module
 

	
 
log = logging.getLogger(__name__)
 

	
 
@@ -61,7 +64,8 @@ REMOVED_REPO_PAT = re.compile(r'rm__\d{8
 

	
 

	
 
def recursive_replace(str_, replace=' '):
 
    """Recursive replace of given sign to just one instance
 
    """
 
    Recursive replace of given sign to just one instance
 

	
 
    :param str_: given string
 
    :param replace: char to find and replace multiple instances
 
@@ -79,7 +83,8 @@ def recursive_replace(str_, replace=' ')
 

	
 

	
 
def repo_name_slug(value):
 
    """Return slug of name of repository
 
    """
 
    Return slug of name of repository
 
    This function is called on each creation/modification
 
    of repository to prevent bad names in repo
 
    """
 
@@ -154,7 +159,10 @@ def action_logger(user, action, repo, ip
 
        user_log.user_ip = ipaddr
 
        sa.add(user_log)
 

	
 
        log.info('Adding user %s, action %s on %s' % (user_obj, action, repo))
 
        log.info(
 
            'Adding user %s, action %s on %s' % (user_obj, action,
 
                                                 safe_unicode(repo))
 
        )
 
        if commit:
 
            sa.commit()
 
    except:
 
@@ -198,12 +206,13 @@ def get_repos(path, recursive=False):
 
def is_valid_repo(repo_name, base_path):
 
    """
 
    Returns True if given path is a valid repository False otherwise
 

	
 
    :param repo_name:
 
    :param base_path:
 

	
 
    :return True: if given path is a valid repository
 
    """
 
    full_path = os.path.join(base_path, repo_name)
 
    full_path = os.path.join(safe_str(base_path), safe_str(repo_name))
 

	
 
    try:
 
        get_scm(full_path)
 
@@ -219,7 +228,7 @@ def is_valid_repos_group(repos_group_nam
 
    :param repo_name:
 
    :param base_path:
 
    """
 
    full_path = os.path.join(base_path, repos_group_name)
 
    full_path = os.path.join(safe_str(base_path), safe_str(repos_group_name))
 

	
 
    # check if it's not a repo
 
    if is_valid_repo(repos_group_name, base_path):
 
@@ -258,7 +267,8 @@ ui_sections = ['alias', 'auth',
 

	
 

	
 
def make_ui(read_from='file', path=None, checkpaths=True):
 
    """A function that will read python rc files or database
 
    """
 
    A function that will read python rc files or database
 
    and make an mercurial ui object from read options
 

	
 
    :param path: path to mercurial config file
 
@@ -371,15 +381,16 @@ class EmptyChangeset(BaseChangeset):
 
        return 0
 

	
 

	
 
def map_groups(groups):
 
def map_groups(path):
 
    """
 
    Checks for groups existence, and creates groups structures.
 
    It returns last group in structure
 
    Given a full path to a repository, create all nested groups that this
 
    repo is inside. This function creates parent-child relationships between
 
    groups and creates default perms for all new groups.
 

	
 
    :param groups: list of groups structure
 
    :param paths: full path to repository
 
    """
 
    sa = meta.Session
 

	
 
    groups = path.split(Repository.url_sep())
 
    parent = None
 
    group = None
 

	
 
@@ -391,22 +402,18 @@ def map_groups(groups):
 
        group = RepoGroup.get_by_group_name(group_name)
 
        desc = '%s group' % group_name
 

	
 
#        # WTF that doesn't work !?
 
#        if group is None:
 
#            group = rgm.create(group_name, desc, parent, just_db=True)
 
#            sa.commit()
 

	
 
        # skip folders that are now removed repos
 
        if REMOVED_REPO_PAT.match(group_name):
 
            break
 

	
 
        if group is None:
 
            log.debug('creating group level: %s group_name: %s' % (lvl, group_name))
 
            log.debug('creating group level: %s group_name: %s' % (lvl,
 
                                                                   group_name))
 
            group = RepoGroup(group_name, parent)
 
            group.group_description = desc
 
            sa.add(group)
 
            rgm._create_default_perms(group)
 
            sa.commit()
 
            sa.flush()
 
        parent = group
 
    return group
 

	
 
@@ -429,7 +436,7 @@ def repo2db_mapper(initial_repo_list, re
 
    added = []
 

	
 
    for name, repo in initial_repo_list.items():
 
        group = map_groups(name.split(Repository.url_sep()))
 
        group = map_groups(name)
 
        if not rm.get_by_repo_name(name, cache=False):
 
            log.info('repository %s not found creating default' % name)
 
            added.append(name)
 
@@ -446,13 +453,19 @@ def repo2db_mapper(initial_repo_list, re
 
    sa.commit()
 
    removed = []
 
    if remove_obsolete:
 
        #remove from database those repositories that are not in the filesystem
 
        # remove from database those repositories that are not in the filesystem
 
        for repo in sa.query(Repository).all():
 
            if repo.repo_name not in initial_repo_list.keys():
 
                log.debug("Removing non existing repository found in db %s" %
 
                          repo.repo_name)
 
                removed.append(repo.repo_name)
 
                sa.delete(repo)
 
                sa.commit()
 

	
 
    # clear cache keys
 
    log.debug("Clearing cache keys now...")
 
    CacheInvalidation.clear_cache()
 
    sa.commit()
 
    return added, removed
 

	
 

	
 
@@ -484,6 +497,30 @@ def add_cache(settings):
 
            beaker.cache.cache_regions[region] = region_settings
 

	
 

	
 
def load_rcextensions(root_path):
 
    import rhodecode
 
    from rhodecode.config import conf
 

	
 
    path = os.path.join(root_path, 'rcextensions', '__init__.py')
 
    if os.path.isfile(path):
 
        rcext = create_module('rc', path)
 
        EXT = rhodecode.EXTENSIONS = rcext
 
        log.debug('Found rcextensions now loading %s...' % rcext)
 

	
 
        # Additional mappings that are not present in the pygments lexers
 
        conf.LANGUAGES_EXTENSIONS_MAP.update(getattr(EXT, 'EXTRA_MAPPINGS', {}))
 

	
 
        #OVERRIDE OUR EXTENSIONS FROM RC-EXTENSIONS (if present)
 

	
 
        if getattr(EXT, 'INDEX_EXTENSIONS', []) != []:
 
            log.debug('settings custom INDEX_EXTENSIONS')
 
            conf.INDEX_EXTENSIONS = getattr(EXT, 'INDEX_EXTENSIONS', [])
 

	
 
        #ADDITIONAL MAPPINGS
 
        log.debug('adding extra into INDEX_EXTENSIONS')
 
        conf.INDEX_EXTENSIONS.extend(getattr(EXT, 'EXTRA_INDEX_EXTENSIONS', []))
 

	
 

	
 
#==============================================================================
 
# TEST FUNCTIONS AND CREATORS
 
#==============================================================================
 
@@ -624,6 +661,6 @@ class BasePasterCommand(Command):
 
        """
 
        from pylons import config as pylonsconfig
 

	
 
        path_to_ini_file = os.path.realpath(conf)
 
        conf = paste.deploy.appconfig('config:' + path_to_ini_file)
 
        self.path_to_ini_file = os.path.realpath(conf)
 
        conf = paste.deploy.appconfig('config:' + self.path_to_ini_file)
 
        pylonsconfig.init_app(conf.global_conf, conf.local_conf)
rhodecode/lib/utils2.py
Show inline comments
 
new file 100644
 
# -*- coding: utf-8 -*-
 
"""
 
    rhodecode.lib.utils
 
    ~~~~~~~~~~~~~~~~~~~
 

	
 
    Some simple helper functions
 

	
 
    :created_on: Jan 5, 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
 
from rhodecode.lib.vcs.utils.lazy import LazyProperty
 

	
 

	
 
def __get_lem():
 
    """
 
    Get language extension map based on what's inside pygments lexers
 
    """
 
    from pygments import lexers
 
    from string import lower
 
    from collections import defaultdict
 

	
 
    d = defaultdict(lambda: [])
 

	
 
    def __clean(s):
 
        s = s.lstrip('*')
 
        s = s.lstrip('.')
 

	
 
        if s.find('[') != -1:
 
            exts = []
 
            start, stop = s.find('['), s.find(']')
 

	
 
            for suffix in s[start + 1:stop]:
 
                exts.append(s[:s.find('[')] + suffix)
 
            return map(lower, exts)
 
        else:
 
            return map(lower, [s])
 

	
 
    for lx, t in sorted(lexers.LEXERS.items()):
 
        m = map(__clean, t[-2])
 
        if m:
 
            m = reduce(lambda x, y: x + y, m)
 
            for ext in m:
 
                desc = lx.replace('Lexer', '')
 
                d[ext].append(desc)
 

	
 
    return dict(d)
 

	
 
def str2bool(_str):
 
    """
 
    returs True/False value from given string, it tries to translate the
 
    string into boolean
 

	
 
    :param _str: string value to translate into boolean
 
    :rtype: boolean
 
    :returns: boolean from given string
 
    """
 
    if _str is None:
 
        return False
 
    if _str in (True, False):
 
        return _str
 
    _str = str(_str).strip().lower()
 
    return _str in ('t', 'true', 'y', 'yes', 'on', '1')
 

	
 

	
 
def convert_line_endings(line, mode):
 
    """
 
    Converts a given line  "line end" accordingly to given mode
 

	
 
    Available modes are::
 
        0 - Unix
 
        1 - Mac
 
        2 - DOS
 

	
 
    :param line: given line to convert
 
    :param mode: mode to convert to
 
    :rtype: str
 
    :return: converted line according to mode
 
    """
 
    from string import replace
 

	
 
    if mode == 0:
 
            line = replace(line, '\r\n', '\n')
 
            line = replace(line, '\r', '\n')
 
    elif mode == 1:
 
            line = replace(line, '\r\n', '\r')
 
            line = replace(line, '\n', '\r')
 
    elif mode == 2:
 
            line = re.sub("\r(?!\n)|(?<!\r)\n", "\r\n", line)
 
    return line
 

	
 

	
 
def detect_mode(line, default):
 
    """
 
    Detects line break for given line, if line break couldn't be found
 
    given default value is returned
 

	
 
    :param line: str line
 
    :param default: default
 
    :rtype: int
 
    :return: value of line end on of 0 - Unix, 1 - Mac, 2 - DOS
 
    """
 
    if line.endswith('\r\n'):
 
        return 2
 
    elif line.endswith('\n'):
 
        return 0
 
    elif line.endswith('\r'):
 
        return 1
 
    else:
 
        return default
 

	
 

	
 
def generate_api_key(username, salt=None):
 
    """
 
    Generates unique API key for given username, if salt is not given
 
    it'll be generated from some random string
 

	
 
    :param username: username as string
 
    :param salt: salt to hash generate KEY
 
    :rtype: str
 
    :returns: sha1 hash from username+salt
 
    """
 
    from tempfile import _RandomNameSequence
 
    import hashlib
 

	
 
    if salt is None:
 
        salt = _RandomNameSequence().next()
 

	
 
    return hashlib.sha1(username + salt).hexdigest()
 

	
 

	
 
def safe_unicode(str_, from_encoding=None):
 
    """
 
    safe unicode function. Does few trick to turn str_ into unicode
 

	
 
    In case of UnicodeDecode error we try to return it with encoding detected
 
    by chardet library if it fails fallback to unicode with errors replaced
 

	
 
    :param str_: string to decode
 
    :rtype: unicode
 
    :returns: unicode object
 
    """
 
    if isinstance(str_, unicode):
 
        return str_
 

	
 
    if not from_encoding:
 
        import rhodecode
 
        DEFAULT_ENCODING = rhodecode.CONFIG.get('default_encoding','utf8')
 
        from_encoding = DEFAULT_ENCODING
 

	
 
    try:
 
        return unicode(str_)
 
    except UnicodeDecodeError:
 
        pass
 

	
 
    try:
 
        return unicode(str_, from_encoding)
 
    except UnicodeDecodeError:
 
        pass
 

	
 
    try:
 
        import chardet
 
        encoding = chardet.detect(str_)['encoding']
 
        if encoding is None:
 
            raise Exception()
 
        return str_.decode(encoding)
 
    except (ImportError, UnicodeDecodeError, Exception):
 
        return unicode(str_, from_encoding, 'replace')
 

	
 

	
 
def safe_str(unicode_, to_encoding=None):
 
    """
 
    safe str function. Does few trick to turn unicode_ into string
 

	
 
    In case of UnicodeEncodeError we try to return it with encoding detected
 
    by chardet library if it fails fallback to string with errors replaced
 

	
 
    :param unicode_: unicode to encode
 
    :rtype: str
 
    :returns: str object
 
    """
 

	
 
    # if it's not basestr cast to str
 
    if not isinstance(unicode_, basestring):
 
        return str(unicode_)
 

	
 
    if isinstance(unicode_, str):
 
        return unicode_
 

	
 
    if not to_encoding:
 
        import rhodecode
 
        DEFAULT_ENCODING = rhodecode.CONFIG.get('default_encoding','utf8')
 
        to_encoding = DEFAULT_ENCODING
 

	
 
    try:
 
        return unicode_.encode(to_encoding)
 
    except UnicodeEncodeError:
 
        pass
 

	
 
    try:
 
        import chardet
 
        encoding = chardet.detect(unicode_)['encoding']
 
        print encoding
 
        if encoding is None:
 
            raise UnicodeEncodeError()
 

	
 
        return unicode_.encode(encoding)
 
    except (ImportError, UnicodeEncodeError):
 
        return unicode_.encode(to_encoding, 'replace')
 

	
 
    return safe_str
 

	
 

	
 
def engine_from_config(configuration, prefix='sqlalchemy.', **kwargs):
 
    """
 
    Custom engine_from_config functions that makes sure we use NullPool for
 
    file based sqlite databases. This prevents errors on sqlite. This only
 
    applies to sqlalchemy versions < 0.7.0
 

	
 
    """
 
    import sqlalchemy
 
    from sqlalchemy import engine_from_config as efc
 
    import logging
 

	
 
    if int(sqlalchemy.__version__.split('.')[1]) < 7:
 

	
 
        # This solution should work for sqlalchemy < 0.7.0, and should use
 
        # proxy=TimerProxy() for execution time profiling
 

	
 
        from sqlalchemy.pool import NullPool
 
        url = configuration[prefix + 'url']
 

	
 
        if url.startswith('sqlite'):
 
            kwargs.update({'poolclass': NullPool})
 
        return efc(configuration, prefix, **kwargs)
 
    else:
 
        import time
 
        from sqlalchemy import event
 
        from sqlalchemy.engine import Engine
 

	
 
        log = logging.getLogger('sqlalchemy.engine')
 
        BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = xrange(30, 38)
 
        engine = efc(configuration, prefix, **kwargs)
 

	
 
        def color_sql(sql):
 
            COLOR_SEQ = "\033[1;%dm"
 
            COLOR_SQL = YELLOW
 
            normal = '\x1b[0m'
 
            return ''.join([COLOR_SEQ % COLOR_SQL, sql, normal])
 

	
 
        if configuration['debug']:
 
            #attach events only for debug configuration
 

	
 
            def before_cursor_execute(conn, cursor, statement,
 
                                    parameters, context, executemany):
 
                context._query_start_time = time.time()
 
                log.info(color_sql(">>>>> STARTING QUERY >>>>>"))
 

	
 

	
 
            def after_cursor_execute(conn, cursor, statement,
 
                                    parameters, context, executemany):
 
                total = time.time() - context._query_start_time
 
                log.info(color_sql("<<<<< TOTAL TIME: %f <<<<<" % total))
 

	
 
            event.listen(engine, "before_cursor_execute",
 
                         before_cursor_execute)
 
            event.listen(engine, "after_cursor_execute",
 
                         after_cursor_execute)
 

	
 
    return engine
 

	
 

	
 
def age(curdate):
 
    """
 
    turns a datetime into an age string.
 

	
 
    :param curdate: datetime object
 
    :rtype: unicode
 
    :returns: unicode words describing age
 
    """
 

	
 
    from datetime import datetime
 
    from webhelpers.date import time_ago_in_words
 

	
 
    _ = lambda s: s
 

	
 
    if not curdate:
 
        return ''
 

	
 
    agescales = [(_(u"year"), 3600 * 24 * 365),
 
                 (_(u"month"), 3600 * 24 * 30),
 
                 (_(u"day"), 3600 * 24),
 
                 (_(u"hour"), 3600),
 
                 (_(u"minute"), 60),
 
                 (_(u"second"), 1), ]
 

	
 
    age = datetime.now() - curdate
 
    age_seconds = (age.days * agescales[2][1]) + age.seconds
 
    pos = 1
 
    for scale in agescales:
 
        if scale[1] <= age_seconds:
 
            if pos == 6:
 
                pos = 5
 
            return '%s %s' % (time_ago_in_words(curdate,
 
                                                agescales[pos][0]), _('ago'))
 
        pos += 1
 

	
 
    return _(u'just now')
 

	
 

	
 
def uri_filter(uri):
 
    """
 
    Removes user:password from given url string
 

	
 
    :param uri:
 
    :rtype: unicode
 
    :returns: filtered list of strings
 
    """
 
    if not uri:
 
        return ''
 

	
 
    proto = ''
 

	
 
    for pat in ('https://', 'http://'):
 
        if uri.startswith(pat):
 
            uri = uri[len(pat):]
 
            proto = pat
 
            break
 

	
 
    # remove passwords and username
 
    uri = uri[uri.find('@') + 1:]
 

	
 
    # get the port
 
    cred_pos = uri.find(':')
 
    if cred_pos == -1:
 
        host, port = uri, None
 
    else:
 
        host, port = uri[:cred_pos], uri[cred_pos + 1:]
 

	
 
    return filter(None, [proto, host, port])
 

	
 

	
 
def credentials_filter(uri):
 
    """
 
    Returns a url with removed credentials
 

	
 
    :param uri:
 
    """
 

	
 
    uri = uri_filter(uri)
 
    #check if we have port
 
    if len(uri) > 2 and uri[2]:
 
        uri[2] = ':' + uri[2]
 

	
 
    return ''.join(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
 

	
 

	
 
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
 

	
 
    return sorted(usrs.keys())
rhodecode/lib/vcs/backends/git/changeset.py
Show inline comments
 
@@ -68,19 +68,24 @@ class GitChangeset(BaseChangeset):
 
    def branch(self):
 
        # TODO: Cache as we walk (id <-> branch name mapping)
 
        refs = self.repository._repo.get_refs()
 
        heads = [(key[len('refs/heads/'):], val) for key, val in refs.items()
 
            if key.startswith('refs/heads/')]
 
        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
 

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

	
 
    def _fix_path(self, path):
 
        """
 
@@ -92,6 +97,7 @@ class GitChangeset(BaseChangeset):
 
        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('/')
 
@@ -103,24 +109,23 @@ class GitChangeset(BaseChangeset):
 
            splitted = path.split('/')
 
            dirs, name = splitted[:-1], splitted[-1]
 
            curdir = ''
 

	
 
            # initially extract things from root dir
 
            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
 

	
 
            for dir in dirs:
 
                if curdir:
 
                    curdir = '/'.join((curdir, dir))
 
                else:
 
                    curdir = dir
 
                #if curdir in self._paths:
 
                    ## This path have been already traversed
 
                    ## Update tree and continue
 
                    #tree = self.repository._repo[self._paths[curdir]]
 
                    #continue
 
                dir_id = None
 
                for item, stat, id in tree.iteritems():
 
                    if curdir:
 
                        item_path = '/'.join((curdir, item))
 
                    else:
 
                        item_path = item
 
                    self._paths[item_path] = id
 
                    self._stat_modes[item_path] = stat
 
                    if dir == item:
 
                        dir_id = id
 
                if dir_id:
 
@@ -130,13 +135,16 @@ class GitChangeset(BaseChangeset):
 
                        raise ChangesetError('%s is not a directory' % curdir)
 
                else:
 
                    raise ChangesetError('%s have not been found' % curdir)
 
            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
 

	
 
                # 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"
rhodecode/model/__init__.py
Show inline comments
 
@@ -85,14 +85,14 @@ class BaseModel(object):
 

	
 
        if isinstance(instance, cls):
 
            return instance
 
        elif isinstance(instance, int) or str(instance).isdigit():
 
        elif isinstance(instance, (int, long)) or str(instance).isdigit():
 
            return cls.get(instance)
 
        else:
 
            if instance:
 
                if callback is None:
 
                    raise Exception(
 
                        'given object must be int or Instance of %s got %s, '
 
                        'no callback provided' % (cls, type(instance))
 
                        'given object must be int, long or Instance of %s '
 
                        'got %s, no callback provided' % (cls, type(instance))
 
                    )
 
                else:
 
                    return callback(instance)
rhodecode/model/comment.py
Show inline comments
 
@@ -29,7 +29,7 @@ import traceback
 
from pylons.i18n.translation import _
 
from sqlalchemy.util.compat import defaultdict
 

	
 
from rhodecode.lib import extract_mentioned_users
 
from rhodecode.lib.utils2 import extract_mentioned_users
 
from rhodecode.lib import helpers as h
 
from rhodecode.model import BaseModel
 
from rhodecode.model.db import ChangesetComment, User, Repository, Notification
 
@@ -63,6 +63,7 @@ class ChangesetCommentsModel(BaseModel):
 
        :param f_path:
 
        :param line_no:
 
        """
 

	
 
        if text:
 
            repo = Repository.get(repo_id)
 
            cs = repo.scm_instance.get_changeset(revision)
 
@@ -78,7 +79,6 @@ class ChangesetCommentsModel(BaseModel):
 

	
 
            self.sa.add(comment)
 
            self.sa.flush()
 

	
 
            # make notification
 
            line = ''
 
            if line_no:
rhodecode/model/db.py
Show inline comments
 
@@ -39,7 +39,8 @@ from rhodecode.lib.vcs.utils.helpers imp
 
from rhodecode.lib.vcs.exceptions import VCSError
 
from rhodecode.lib.vcs.utils.lazy import LazyProperty
 

	
 
from rhodecode.lib import str2bool, safe_str, get_changeset_safe, safe_unicode
 
from rhodecode.lib.utils2 import str2bool, safe_str, get_changeset_safe, \
 
    safe_unicode
 
from rhodecode.lib.compat import json
 
from rhodecode.lib.caching_query import FromCache
 

	
 
@@ -145,12 +146,18 @@ class BaseModel(object):
 
        obj = cls.query().get(id_)
 
        Session.delete(obj)
 

	
 
    def __repr__(self):
 
        if hasattr(self, '__unicode__'):
 
            # python repr needs to return str
 
            return safe_str(self.__unicode__())
 
        return '<DB:%s>' % (self.__class__.__name__)
 

	
 
class RhodeCodeSetting(Base, BaseModel):
 
    __tablename__ = 'rhodecode_settings'
 
    __table_args__ = (
 
        UniqueConstraint('app_settings_name'),
 
        {'extend_existing': True}
 
        {'extend_existing': True, 'mysql_engine':'InnoDB',
 
         'mysql_charset': 'utf8'}
 
    )
 
    app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
 
    app_settings_name = Column("app_settings_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
 
@@ -181,8 +188,8 @@ class RhodeCodeSetting(Base, BaseModel):
 
        """
 
        self._app_settings_value = safe_unicode(val)
 

	
 
    def __repr__(self):
 
        return "<%s('%s:%s')>" % (
 
    def __unicode__(self):
 
        return u"<%s('%s:%s')>" % (
 
            self.__class__.__name__,
 
            self.app_settings_name, self.app_settings_value
 
        )
 
@@ -224,7 +231,8 @@ class RhodeCodeUi(Base, BaseModel):
 
    __tablename__ = 'rhodecode_ui'
 
    __table_args__ = (
 
        UniqueConstraint('ui_key'),
 
        {'extend_existing': True}
 
        {'extend_existing': True, 'mysql_engine':'InnoDB',
 
         'mysql_charset': 'utf8'}
 
    )
 

	
 
    HOOK_UPDATE = 'changegroup.update'
 
@@ -274,7 +282,8 @@ class User(Base, BaseModel):
 
    __tablename__ = 'users'
 
    __table_args__ = (
 
        UniqueConstraint('username'), UniqueConstraint('email'),
 
        {'extend_existing': True}
 
        {'extend_existing': True, 'mysql_engine':'InnoDB',
 
         'mysql_charset': 'utf8'}
 
    )
 
    user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
 
    username = Column("username", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
 
@@ -294,10 +303,15 @@ class User(Base, BaseModel):
 
    repositories = relationship('Repository')
 
    user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
 
    repo_to_perm = relationship('UserRepoToPerm', primaryjoin='UserRepoToPerm.user_id==User.user_id', cascade='all')
 
    repo_group_to_perm = relationship('UserRepoGroupToPerm', primaryjoin='UserRepoGroupToPerm.user_id==User.user_id', cascade='all')
 

	
 
    group_member = relationship('UsersGroupMember', cascade='all')
 

	
 
    notifications = relationship('UserNotification',)
 
    notifications = relationship('UserNotification', cascade='all')
 
    # notifications assigned to this user
 
    user_created_notifications = relationship('Notification', cascade='all')
 
    # comments created by this user
 
    user_comments = relationship('ChangesetComment', cascade='all')
 

	
 
    @hybrid_property
 
    def email(self):
 
@@ -328,8 +342,8 @@ class User(Base, BaseModel):
 
    def is_admin(self):
 
        return self.admin
 

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

	
 
    @classmethod
 
@@ -376,6 +390,9 @@ class User(Base, BaseModel):
 

	
 
    def __json__(self):
 
        return dict(
 
            user_id=self.user_id,
 
            first_name=self.name,
 
            last_name=self.lastname,
 
            email=self.email,
 
            full_name=self.full_name,
 
            full_name_or_username=self.full_name_or_username,
 
@@ -386,7 +403,10 @@ class User(Base, BaseModel):
 

	
 
class UserLog(Base, BaseModel):
 
    __tablename__ = 'user_logs'
 
    __table_args__ = {'extend_existing': True}
 
    __table_args__ = (
 
        {'extend_existing': True, 'mysql_engine':'InnoDB',
 
         'mysql_charset': 'utf8'},
 
    )
 
    user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
 
    user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
 
    repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True)
 
@@ -405,7 +425,10 @@ class UserLog(Base, BaseModel):
 

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

	
 
    users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
 
    users_group_name = Column("users_group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
 
@@ -413,9 +436,10 @@ class UsersGroup(Base, BaseModel):
 

	
 
    members = relationship('UsersGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
 
    users_group_to_perm = relationship('UsersGroupToPerm', cascade='all')
 
    users_group_repo_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
 

	
 
    def __repr__(self):
 
        return '<userGroup(%s)>' % (self.users_group_name)
 
    def __unicode__(self):
 
        return u'<userGroup(%s)>' % (self.users_group_name)
 

	
 
    @classmethod
 
    def get_by_group_name(cls, group_name, cache=False,
 
@@ -443,7 +467,10 @@ class UsersGroup(Base, BaseModel):
 

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

	
 
    users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
 
    users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
 
@@ -461,7 +488,8 @@ class Repository(Base, BaseModel):
 
    __tablename__ = 'repositories'
 
    __table_args__ = (
 
        UniqueConstraint('repo_name'),
 
        {'extend_existing': True},
 
        {'extend_existing': True, 'mysql_engine':'InnoDB',
 
         'mysql_charset': 'utf8'},
 
    )
 

	
 
    repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
 
@@ -489,9 +517,9 @@ class Repository(Base, BaseModel):
 

	
 
    logs = relationship('UserLog')
 

	
 
    def __repr__(self):
 
        return "<%s('%s:%s')>" % (self.__class__.__name__,
 
                                  self.repo_id, self.repo_name)
 
    def __unicode__(self):
 
        return u"<%s('%s:%s')>" % (self.__class__.__name__,self.repo_id,
 
                                   self.repo_name)
 

	
 
    @classmethod
 
    def url_sep(cls):
 
@@ -710,7 +738,8 @@ class RepoGroup(Base, BaseModel):
 
    __table_args__ = (
 
        UniqueConstraint('group_name', 'group_parent_id'),
 
        CheckConstraint('group_id != group_parent_id'),
 
        {'extend_existing': True},
 
        {'extend_existing': True, 'mysql_engine':'InnoDB',
 
         'mysql_charset': 'utf8'},
 
    )
 
    __mapper_args__ = {'order_by': 'group_name'}
 

	
 
@@ -728,8 +757,8 @@ class RepoGroup(Base, BaseModel):
 
        self.group_name = group_name
 
        self.parent_group = parent_group
 

	
 
    def __repr__(self):
 
        return "<%s('%s:%s')>" % (self.__class__.__name__, self.group_id,
 
    def __unicode__(self):
 
        return u"<%s('%s:%s')>" % (self.__class__.__name__, self.group_id,
 
                                  self.group_name)
 

	
 
    @classmethod
 
@@ -837,13 +866,16 @@ class RepoGroup(Base, BaseModel):
 

	
 
class Permission(Base, BaseModel):
 
    __tablename__ = 'permissions'
 
    __table_args__ = {'extend_existing': True}
 
    __table_args__ = (
 
        {'extend_existing': True, 'mysql_engine':'InnoDB',
 
         'mysql_charset': 'utf8'},
 
    )
 
    permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
 
    permission_name = Column("permission_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
 
    permission_longname = Column("permission_longname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
 

	
 
    def __repr__(self):
 
        return "<%s('%s:%s')>" % (
 
    def __unicode__(self):
 
        return u"<%s('%s:%s')>" % (
 
            self.__class__.__name__, self.permission_id, self.permission_name
 
        )
 

	
 
@@ -874,7 +906,8 @@ class UserRepoToPerm(Base, BaseModel):
 
    __tablename__ = 'repo_to_perm'
 
    __table_args__ = (
 
        UniqueConstraint('user_id', 'repository_id', 'permission_id'),
 
        {'extend_existing': True}
 
        {'extend_existing': True, 'mysql_engine':'InnoDB',
 
         'mysql_charset': 'utf8'}
 
    )
 
    repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
 
    user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
 
@@ -894,15 +927,16 @@ class UserRepoToPerm(Base, BaseModel):
 
        Session.add(n)
 
        return n
 

	
 
    def __repr__(self):
 
        return '<user:%s => %s >' % (self.user, self.repository)
 
    def __unicode__(self):
 
        return u'<user:%s => %s >' % (self.user, self.repository)
 

	
 

	
 
class UserToPerm(Base, BaseModel):
 
    __tablename__ = 'user_to_perm'
 
    __table_args__ = (
 
        UniqueConstraint('user_id', 'permission_id'),
 
        {'extend_existing': True}
 
        {'extend_existing': True, 'mysql_engine':'InnoDB',
 
         'mysql_charset': 'utf8'}
 
    )
 
    user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
 
    user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
 
@@ -916,7 +950,8 @@ class UsersGroupRepoToPerm(Base, BaseMod
 
    __tablename__ = 'users_group_repo_to_perm'
 
    __table_args__ = (
 
        UniqueConstraint('repository_id', 'users_group_id', 'permission_id'),
 
        {'extend_existing': True}
 
        {'extend_existing': True, 'mysql_engine':'InnoDB',
 
         'mysql_charset': 'utf8'}
 
    )
 
    users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
 
    users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
 
@@ -936,15 +971,16 @@ class UsersGroupRepoToPerm(Base, BaseMod
 
        Session.add(n)
 
        return n
 

	
 
    def __repr__(self):
 
        return '<userGroup:%s => %s >' % (self.users_group, self.repository)
 
    def __unicode__(self):
 
        return u'<userGroup:%s => %s >' % (self.users_group, self.repository)
 

	
 

	
 
class UsersGroupToPerm(Base, BaseModel):
 
    __tablename__ = 'users_group_to_perm'
 
    __table_args__ = (
 
        UniqueConstraint('users_group_id', 'permission_id',),
 
        {'extend_existing': True}
 
        {'extend_existing': True, 'mysql_engine':'InnoDB',
 
         'mysql_charset': 'utf8'}
 
    )
 
    users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
 
    users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
 
@@ -958,7 +994,8 @@ class UserRepoGroupToPerm(Base, BaseMode
 
    __tablename__ = 'user_repo_group_to_perm'
 
    __table_args__ = (
 
        UniqueConstraint('user_id', 'group_id', 'permission_id'),
 
        {'extend_existing': True}
 
        {'extend_existing': True, 'mysql_engine':'InnoDB',
 
         'mysql_charset': 'utf8'}
 
    )
 

	
 
    group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
 
@@ -975,7 +1012,8 @@ class UsersGroupRepoGroupToPerm(Base, Ba
 
    __tablename__ = 'users_group_repo_group_to_perm'
 
    __table_args__ = (
 
        UniqueConstraint('users_group_id', 'group_id'),
 
        {'extend_existing': True}
 
        {'extend_existing': True, 'mysql_engine':'InnoDB',
 
         'mysql_charset': 'utf8'}
 
    )
 

	
 
    users_group_repo_group_to_perm_id = Column("users_group_repo_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
 
@@ -990,7 +1028,11 @@ class UsersGroupRepoGroupToPerm(Base, Ba
 

	
 
class Statistics(Base, BaseModel):
 
    __tablename__ = 'statistics'
 
    __table_args__ = (UniqueConstraint('repository_id'), {'extend_existing': True})
 
    __table_args__ = (
 
         UniqueConstraint('repository_id'),
 
         {'extend_existing': True, 'mysql_engine':'InnoDB',
 
          'mysql_charset': 'utf8'}
 
    )
 
    stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
 
    repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
 
    stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
 
@@ -1006,7 +1048,8 @@ class UserFollowing(Base, BaseModel):
 
    __table_args__ = (
 
        UniqueConstraint('user_id', 'follows_repository_id'),
 
        UniqueConstraint('user_id', 'follows_user_id'),
 
        {'extend_existing': True}
 
        {'extend_existing': True, 'mysql_engine':'InnoDB',
 
         'mysql_charset': 'utf8'}
 
    )
 

	
 
    user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
 
@@ -1027,7 +1070,11 @@ class UserFollowing(Base, BaseModel):
 

	
 
class CacheInvalidation(Base, BaseModel):
 
    __tablename__ = 'cache_invalidation'
 
    __table_args__ = (UniqueConstraint('cache_key'), {'extend_existing': True})
 
    __table_args__ = (
 
        UniqueConstraint('cache_key'),
 
        {'extend_existing': True, 'mysql_engine':'InnoDB',
 
         'mysql_charset': 'utf8'},
 
    )
 
    cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
 
    cache_key = Column("cache_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
 
    cache_args = Column("cache_args", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
 
@@ -1038,14 +1085,17 @@ class CacheInvalidation(Base, BaseModel)
 
        self.cache_args = cache_args
 
        self.cache_active = False
 

	
 
    def __repr__(self):
 
        return "<%s('%s:%s')>" % (self.__class__.__name__,
 
    def __unicode__(self):
 
        return u"<%s('%s:%s')>" % (self.__class__.__name__,
 
                                  self.cache_id, self.cache_key)
 
    @classmethod
 
    def clear_cache(cls):
 
        cls.query().delete()
 

	
 
    @classmethod
 
    def _get_key(cls, key):
 
        """
 
        Wrapper for generating a key
 
        Wrapper for generating a key, together with a prefix
 

	
 
        :param key:
 
        """
 
@@ -1054,13 +1104,26 @@ class CacheInvalidation(Base, BaseModel)
 
        iid = rhodecode.CONFIG.get('instance_id')
 
        if iid:
 
            prefix = iid
 
        return "%s%s" % (prefix, key)
 
        return "%s%s" % (prefix, key), prefix, key.rstrip('_README')
 

	
 
    @classmethod
 
    def get_by_key(cls, key):
 
        return cls.query().filter(cls.cache_key == key).scalar()
 

	
 
    @classmethod
 
    def _get_or_create_key(cls, key, prefix, org_key):
 
        inv_obj = Session.query(cls).filter(cls.cache_key == key).scalar()
 
        if not inv_obj:
 
            try:
 
                inv_obj = CacheInvalidation(key, org_key)
 
                Session.add(inv_obj)
 
                Session.commit()
 
            except Exception:
 
                log.error(traceback.format_exc())
 
                Session.rollback()
 
        return inv_obj
 

	
 
    @classmethod
 
    def invalidate(cls, key):
 
        """
 
        Returns Invalidation object if this given key should be invalidated
 
@@ -1069,10 +1132,12 @@ class CacheInvalidation(Base, BaseModel)
 

	
 
        :param key:
 
        """
 
        return cls.query()\
 
                .filter(CacheInvalidation.cache_key == key)\
 
                .filter(CacheInvalidation.cache_active == False)\
 
                .scalar()
 

	
 
        key, _prefix, _org_key = cls._get_key(key)
 
        inv = cls._get_or_create_key(key, _prefix, _org_key)
 

	
 
        if inv and inv.cache_active is False:
 
            return inv
 

	
 
    @classmethod
 
    def set_invalidate(cls, key):
 
@@ -1082,17 +1147,16 @@ class CacheInvalidation(Base, BaseModel)
 
        :param key:
 
        """
 

	
 
        log.debug('marking %s for invalidation' % key)
 
        inv_obj = Session.query(cls)\
 
            .filter(cls.cache_key == key).scalar()
 
        if inv_obj:
 
            inv_obj.cache_active = False
 
        else:
 
            log.debug('cache key not found in invalidation db -> creating one')
 
            inv_obj = CacheInvalidation(key)
 
        key, _prefix, _org_key = cls._get_key(key)
 
        inv_objs = Session.query(cls).filter(cls.cache_args == _org_key).all()
 
        log.debug('marking %s key[s] %s for invalidation' % (len(inv_objs),
 
                                                             _org_key))
 
        try:
 
            for inv_obj in inv_objs:
 
                if inv_obj:
 
                    inv_obj.cache_active = False
 

	
 
        try:
 
            Session.add(inv_obj)
 
                Session.add(inv_obj)
 
            Session.commit()
 
        except Exception:
 
            log.error(traceback.format_exc())
 
@@ -1113,7 +1177,10 @@ class CacheInvalidation(Base, BaseModel)
 

	
 
class ChangesetComment(Base, BaseModel):
 
    __tablename__ = 'changeset_comments'
 
    __table_args__ = ({'extend_existing': True},)
 
    __table_args__ = (
 
        {'extend_existing': True, 'mysql_engine':'InnoDB',
 
         'mysql_charset': 'utf8'},
 
    )
 
    comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True)
 
    repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
 
    revision = Column('revision', String(40), nullable=False)
 
@@ -1142,7 +1209,10 @@ class ChangesetComment(Base, BaseModel):
 

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

	
 
    TYPE_CHANGESET_COMMENT = u'cs_comment'
 
    TYPE_MESSAGE = u'message'
 
@@ -1194,7 +1264,8 @@ class UserNotification(Base, BaseModel):
 
    __tablename__ = 'user_to_notification'
 
    __table_args__ = (
 
        UniqueConstraint('user_id', 'notification_id'),
 
        {'extend_existing': True}
 
        {'extend_existing': True, 'mysql_engine':'InnoDB',
 
         'mysql_charset': 'utf8'}
 
    )
 
    user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), primary_key=True)
 
    notification_id = Column("notification_id", Integer(), ForeignKey('notifications.notification_id'), primary_key=True)
 
@@ -1212,7 +1283,10 @@ class UserNotification(Base, BaseModel):
 

	
 
class DbMigrateVersion(Base, BaseModel):
 
    __tablename__ = 'db_migrate_version'
 
    __table_args__ = {'extend_existing': True}
 
    __table_args__ = (
 
        {'extend_existing': True, 'mysql_engine':'InnoDB',
 
         'mysql_charset': 'utf8'},
 
    )
 
    repository_id = Column('repository_id', String(250), primary_key=True)
 
    repository_path = Column('repository_path', Text)
 
    version = Column('version', Integer)
rhodecode/model/notification.py
Show inline comments
 
@@ -32,6 +32,7 @@ import datetime
 
from pylons.i18n.translation import _
 

	
 
import rhodecode
 
from rhodecode.config.conf import DATETIME_FORMAT
 
from rhodecode.lib import helpers as h
 
from rhodecode.model import BaseModel
 
from rhodecode.model.db import Notification, User, UserNotification
 
@@ -47,11 +48,11 @@ class NotificationModel(BaseModel):
 
    def __get_notification(self, notification):
 
        if isinstance(notification, Notification):
 
            return notification
 
        elif isinstance(notification, int):
 
        elif isinstance(notification, (int, long)):
 
            return Notification.get(notification)
 
        else:
 
            if notification:
 
                raise Exception('notification must be int or Instance'
 
                raise Exception('notification must be int, long or Instance'
 
                                ' of Notification got %s' % type(notification))
 

	
 
    def create(self, created_by, subject, body, recipients=None,
 
@@ -111,6 +112,7 @@ class NotificationModel(BaseModel):
 
            kwargs.update(email_kwargs)
 
            email_body_html = EmailNotificationModel()\
 
                                .get_email_tmpl(type_, **kwargs)
 

	
 
            run_task(tasks.send_email, rec.email, email_subject, email_body,
 
                     email_body_html)
 

	
 
@@ -176,14 +178,13 @@ class NotificationModel(BaseModel):
 
            notification.TYPE_REGISTRATION: _('registered in RhodeCode')
 
        }
 

	
 
        DATETIME_FORMAT = "%Y-%m-%d %H:%M:%S"
 

	
 
        tmpl = "%(user)s %(action)s %(when)s"
 
        if show_age:
 
            when = h.age(notification.created_on)
 
        else:
 
            DTF = lambda d: datetime.datetime.strftime(d, DATETIME_FORMAT)
 
            when = DTF(notification.created_on)
 

	
 
        data = dict(
 
            user=notification.created_by_user.username,
 
            action=_map[notification.type_], when=when,
rhodecode/model/repo.py
Show inline comments
 
@@ -29,15 +29,15 @@ import traceback
 
from datetime import datetime
 

	
 
from rhodecode.lib.vcs.backends import get_backend
 

	
 
from rhodecode.lib import LazyProperty
 
from rhodecode.lib import safe_str, safe_unicode
 
from rhodecode.lib.compat import json
 
from rhodecode.lib.utils2 import LazyProperty, safe_str, safe_unicode
 
from rhodecode.lib.caching_query import FromCache
 
from rhodecode.lib.hooks import log_create_repository
 

	
 
from rhodecode.model import BaseModel
 
from rhodecode.model.db import Repository, UserRepoToPerm, User, Permission, \
 
    Statistics, UsersGroup, UsersGroupRepoToPerm, RhodeCodeUi, RepoGroup
 
from rhodecode.lib import helpers as h
 

	
 

	
 
log = logging.getLogger(__name__)
 
@@ -95,25 +95,28 @@ class RepoModel(BaseModel):
 
        return repo.scalar()
 

	
 
    def get_users_js(self):
 

	
 
        users = self.sa.query(User).filter(User.active == True).all()
 
        u_tmpl = '''{id:%s, fname:"%s", lname:"%s", nname:"%s"},'''
 
        users_array = '[%s]' % '\n'.join([u_tmpl % (u.user_id, u.name,
 
                                                    u.lastname, u.username)
 
                                        for u in users])
 
        return users_array
 
        return json.dumps([
 
            {
 
             'id': u.user_id,
 
             'fname': u.name,
 
             'lname': u.lastname,
 
             'nname': u.username,
 
             'gravatar_lnk': h.gravatar_url(u.email, 14)
 
            } for u in users]
 
        )
 

	
 
    def get_users_groups_js(self):
 
        users_groups = self.sa.query(UsersGroup)\
 
            .filter(UsersGroup.users_group_active == True).all()
 

	
 
        g_tmpl = '''{id:%s, grname:"%s",grmembers:"%s"},'''
 

	
 
        users_groups_array = '[%s]' % '\n'.join([g_tmpl % \
 
                                    (gr.users_group_id, gr.users_group_name,
 
                                     len(gr.members))
 
                                        for gr in users_groups])
 
        return users_groups_array
 
        return json.dumps([
 
            {
 
             'id': gr.users_group_id,
 
             'grname': gr.users_group_name,
 
             'grmembers': len(gr.members),
 
            } for gr in users_groups]
 
        )
 

	
 
    def _get_defaults(self, repo_name):
 
        """
 
@@ -346,6 +349,7 @@ class RepoModel(BaseModel):
 
        :param repo: Instance of Repository, repository_id, or repository name
 
        :param user: Instance of User, user_id or username
 
        """
 

	
 
        user = self.__get_user(user)
 
        repo = self.__get_repo(repo)
 

	
rhodecode/model/repos_group.py
Show inline comments
 
@@ -28,7 +28,7 @@ import logging
 
import traceback
 
import shutil
 

	
 
from rhodecode.lib import LazyProperty
 
from rhodecode.lib.utils2 import LazyProperty
 

	
 
from rhodecode.model import BaseModel
 
from rhodecode.model.db import RepoGroup, RhodeCodeUi, UserRepoGroupToPerm, \
rhodecode/model/scm.py
Show inline comments
 
@@ -35,7 +35,7 @@ from rhodecode.lib.vcs.nodes import File
 

	
 
from rhodecode import BACKENDS
 
from rhodecode.lib import helpers as h
 
from rhodecode.lib import safe_str
 
from rhodecode.lib.utils2 import safe_str
 
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
 
@@ -235,13 +235,13 @@ class ScmModel(BaseModel):
 
        return group_iter
 

	
 
    def mark_for_invalidation(self, repo_name):
 
        """Puts cache invalidation task into db for
 
        """
 
        Puts cache invalidation task into db for
 
        further global cache invalidation
 

	
 
        :param repo_name: this repo that should invalidation take place
 
        """
 
        CacheInvalidation.set_invalidate(repo_name)
 
        CacheInvalidation.set_invalidate(repo_name + "_README")
 

	
 
    def toggle_following_repo(self, follow_repo_id, user_id):
 

	
rhodecode/model/user.py
Show inline comments
 
@@ -29,18 +29,19 @@ import traceback
 
from pylons import url
 
from pylons.i18n.translation import _
 

	
 
from rhodecode.lib import safe_unicode
 
from rhodecode.lib.utils2 import safe_unicode, generate_api_key
 
from rhodecode.lib.caching_query import FromCache
 

	
 
from rhodecode.model import BaseModel
 
from rhodecode.model.db import User, UserRepoToPerm, Repository, Permission, \
 
    UserToPerm, UsersGroupRepoToPerm, UsersGroupToPerm, UsersGroupMember, \
 
    Notification, RepoGroup, UserRepoGroupToPerm, UsersGroup
 
    Notification, RepoGroup, UserRepoGroupToPerm, UsersGroup,\
 
    UsersGroupRepoGroupToPerm
 
from rhodecode.lib.exceptions import DefaultUserException, \
 
    UserOwnsReposException
 

	
 
from sqlalchemy.exc import DatabaseError
 
from rhodecode.lib import generate_api_key
 

	
 
from sqlalchemy.orm import joinedload
 

	
 
log = logging.getLogger(__name__)
 
@@ -298,14 +299,16 @@ class UserModel(BaseModel):
 
        try:
 
            if user.username == 'default':
 
                raise DefaultUserException(
 
                                _("You can't remove this user since it's"
 
                                  " crucial for entire application"))
 
                    _(u"You can't remove this user since it's"
 
                      " crucial for entire application")
 
                )
 
            if user.repositories:
 
                raise UserOwnsReposException(_('This user still owns %s '
 
                                               'repositories and cannot be '
 
                                               'removed. Switch owners or '
 
                                               'remove those repositories') \
 
                                               % user.repositories)
 
                repos = [x.repo_name for x in user.repositories]
 
                raise UserOwnsReposException(
 
                    _(u'user "%s" still owns %s repositories and cannot be '
 
                      'removed. Switch owners or remove those repositories. %s')
 
                    % (user.username, len(repos), ', '.join(repos))
 
                )
 
            self.sa.delete(user)
 
        except:
 
            log.error(traceback.format_exc())
 
@@ -409,7 +412,7 @@ class UserModel(BaseModel):
 
            for perm in default_global_perms:
 
                user.permissions[GLOBAL].add(perm.permission.permission_name)
 

	
 
            # default for repositories
 
            # 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):
 
@@ -423,17 +426,18 @@ class UserModel(BaseModel):
 

	
 
                user.permissions[RK][r_k] = p
 

	
 
            # default for repositories groups
 
            # defaults for repositories groups taken from default user permission
 
            # on given group
 
            for perm in default_repo_groups_perms:
 
                rg_k = perm.UserRepoGroupToPerm.group.group_name
 
                p = perm.Permission.permission_name
 
                user.permissions[GK][rg_k] = p
 

	
 
            #==================================================================
 
            # overwrite default with user permissions if any
 
            # overwrite defaults with user permissions if any found
 
            #==================================================================
 

	
 
            # user global
 
            # user global permissions
 
            user_perms = self.sa.query(UserToPerm)\
 
                    .options(joinedload(UserToPerm.permission))\
 
                    .filter(UserToPerm.user_id == uid).all()
 
@@ -441,7 +445,7 @@ class UserModel(BaseModel):
 
            for perm in user_perms:
 
                user.permissions[GLOBAL].add(perm.permission.permission_name)
 

	
 
            # user repositories
 
            # user explicit permissions for repositories
 
            user_repo_perms = \
 
             self.sa.query(UserRepoToPerm, Permission, Repository)\
 
             .join((Repository, UserRepoToPerm.repository_id == Repository.repo_id))\
 
@@ -459,8 +463,8 @@ class UserModel(BaseModel):
 
                user.permissions[RK][r_k] = p
 

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

	
 
            # users group global
 
@@ -473,7 +477,7 @@ class UserModel(BaseModel):
 
            for perm in user_perms_from_users_groups:
 
                user.permissions[GLOBAL].add(perm.permission.permission_name)
 

	
 
            # users group repositories
 
            # 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))\
 
@@ -495,12 +499,12 @@ class UserModel(BaseModel):
 
            # get access for this user for repos group and override defaults
 
            #==================================================================
 

	
 
            # user repositories groups
 
            # 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(UserRepoToPerm.user_id == uid)\
 
             .filter(UserRepoGroupToPerm.user_id == uid)\
 
             .all()
 

	
 
            for perm in user_repo_groups_perms:
 
@@ -510,6 +514,30 @@ class UserModel(BaseModel):
 
                if PERM_WEIGHTS[p] > PERM_WEIGHTS[cur_perm]:
 
                    user.permissions[GK][rg_k] = p
 

	
 
            #==================================================================
 
            # 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
 

	
 
        return user
 

	
 
    def has_perm(self, user, perm):
rhodecode/public/css/style.css
Show inline comments
 
@@ -185,6 +185,41 @@ div.options a {
 
	border-bottom-right-radius: 8px;
 
}
 
 
.top-left-rounded-corner-mid {
 
    -webkit-border-top-left-radius: 4px;
 
    -khtml-border-radius-topleft: 4px;
 
    -moz-border-radius-topleft: 4px;
 
    border-top-left-radius: 4px;
 
}
 
 
.top-right-rounded-corner-mid {
 
    -webkit-border-top-right-radius: 4px;
 
    -khtml-border-radius-topright: 4px;
 
    -moz-border-radius-topright: 4px;
 
    border-top-right-radius: 4px;
 
}
 
 
.bottom-left-rounded-corner-mid {
 
    -webkit-border-bottom-left-radius: 4px;
 
    -khtml-border-radius-bottomleft: 4px;
 
    -moz-border-radius-bottomleft: 4px;
 
    border-bottom-left-radius: 4px;
 
}
 
 
.bottom-right-rounded-corner-mid {
 
    -webkit-border-bottom-right-radius: 4px;
 
    -khtml-border-radius-bottomright: 4px;
 
    -moz-border-radius-bottomright: 4px;
 
    border-bottom-right-radius: 4px;
 
}
 
 
.help-block {
 
    color: #999999;
 
    display: block;
 
    margin-bottom: 0;
 
    margin-top: 5px;
 
}
 
 
#header {
 
	margin: 0;
 
	padding: 0 10px;
 
@@ -197,18 +232,16 @@ div.options a {
 
	-moz-border-radius: 0px 0px 8px 8px;
 
	border-radius: 0px 0px 8px 8px;
 
	height: 37px;
 
    background-color: #eedc94;
 
    background-color: #003B76;
 
    background-repeat: repeat-x;
 
    background-image: -khtml-gradient(linear, left top, left bottom, from(#fceec1),
 
        to(#eedc94) );
 
    background-image: -khtml-gradient(linear, left top, left bottom, from(#003B76), to(#00376E) );
 
    background-image: -moz-linear-gradient(top, #003b76, #00376e);
 
    background-image: -ms-linear-gradient(top, #003b76, #00376e);
 
    background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #003b76), color-stop(100%, #00376e) );
 
    background-image: -webkit-linear-gradient(top, #003b76, #00376e);
 
    background-image: -o-linear-gradient(top, #003b76, #00376e);
 
    background-image: linear-gradient(top, #003b76, #00376e);
 
    filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#003b76',
 
        endColorstr='#00376e', GradientType=0 );
 
    filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#003b76',endColorstr='#00376e', GradientType=0 );
 
	box-shadow: 0 2px 2px rgba(0, 0, 0, 0.6);
 
}
 
 
@@ -260,9 +293,9 @@ div.options a {
 
	min-height: 44px;
 
	clear: both;
 
	position: relative;
 
    background-color: #eedc94;
 
    background-color: #003B76;
 
    background-repeat: repeat-x;
 
    background-image: -khtml-gradient(linear, left top, left bottom, from(#fceec1),to(#eedc94) );
 
    background-image: -khtml-gradient(linear, left top, left bottom, from(#003B76), to(#00376E) );
 
    background-image: -moz-linear-gradient(top, #003b76, #00376e);
 
    background-image: -ms-linear-gradient(top, #003b76, #00376e);
 
    background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #003b76),color-stop(100%, #00376e) );
 
@@ -289,6 +322,14 @@ div.options a {
 
    -moz-border-radius: 0px 0px 0px 0px;
 
    border-radius: 0px 0px 0px 0px;	
 
}
 
 
.ie7 #header #header-inner.hover,
 
.ie8 #header #header-inner.hover,
 
.ie9 #header #header-inner.hover
 
{
 
    z-index: auto !important;
 
}
 
 
#header #header-inner #home a {
 
	height: 40px;
 
	width: 46px;
 
@@ -997,9 +1038,9 @@ tbody .yui-dt-editable { cursor: pointer
 
#content div.box div.title {
 
	clear: both;
 
	overflow: hidden;
 
	background-color: #eedc94;
 
	background-color: #003B76;
 
	background-repeat: repeat-x;
 
	background-image: -khtml-gradient(linear, left top, left bottom, from(#fceec1), to(#eedc94) );
 
	background-image: -khtml-gradient(linear, left top, left bottom, from(#003B76), to(#00376E) );
 
	background-image: -moz-linear-gradient(top, #003b76, #00376e);
 
	background-image: -ms-linear-gradient(top, #003b76, #00376e);
 
	background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #003b76), color-stop(100%, #00376e) );
 
@@ -1758,33 +1799,21 @@ div.form div.fields div.field div.button
 
}
 
 
#footer div#footer-inner {
 
	background-color: #eedc94; background-repeat : repeat-x;
 
	background-image : -khtml-gradient( linear, left top, left bottom,
 
	from( #fceec1), to( #eedc94)); background-image : -moz-linear-gradient(
 
	top, #003b76, #00376e); background-image : -ms-linear-gradient( top,
 
	#003b76, #00376e); background-image : -webkit-gradient( linear, left
 
	top, left bottom, color-stop( 0%, #003b76), color-stop( 100%, #00376e));
 
	background-color: #003B76; 
 
	background-repeat : repeat-x;
 
	background-image : -khtml-gradient( linear, left top, left bottom, from(#003B76), to(#00376E)); 
 
	background-image : -moz-linear-gradient(top, #003b76, #00376e); 
 
	background-image : -ms-linear-gradient( top, #003b76, #00376e); 
 
	background-image : -webkit-gradient( linear, left top, left bottom, color-stop( 0%, #003b76), color-stop( 100%, #00376e));
 
	background-image : -webkit-linear-gradient( top, #003b76, #00376e));
 
	background-image : -o-linear-gradient( top, #003b76, #00376e));
 
	background-image : linear-gradient( top, #003b76, #00376e); filter :
 
	progid : DXImageTransform.Microsoft.gradient ( startColorstr =
 
	'#003b76', endColorstr = '#00376e', GradientType = 0);
 
	background-image : linear-gradient( top, #003b76, #00376e); 
 
	filter :progid : DXImageTransform.Microsoft.gradient ( startColorstr = '#003b76', endColorstr = '#00376e', GradientType = 0);
 
	box-shadow: 0 2px 2px rgba(0, 0, 0, 0.6);
 
	-webkit-border-radius: 4px 4px 4px 4px;
 
	-khtml-border-radius: 4px 4px 4px 4px;
 
	-moz-border-radius: 4px 4px 4px 4px;
 
	border-radius: 4px 4px 4px 4px;
 
	background-repeat: repeat-x;
 
	background-image: -khtml-gradient(linear, left top, left bottom, from(#fceec1),
 
		to(#eedc94) );
 
	background-image: -moz-linear-gradient(top, #003b76, #00376e);
 
	background-image: -ms-linear-gradient(top, #003b76, #00376e);
 
	background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #003b76), color-stop(100%, #00376e) );
 
	background-image: -webkit-linear-gradient(top, #003b76, #00376e);
 
	background-image: -o-linear-gradient(top, #003b76, #00376e);
 
	background-image: linear-gradient(top, #003b76, #00376e);
 
	filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#003b76',
 
		endColorstr='#00376e', GradientType=0 );
 
}
 
 
#footer div#footer-inner p {
 
@@ -1808,30 +1837,18 @@ div.form div.fields div.field div.button
 
	clear: both;
 
	overflow: hidden;
 
	position: relative;
 
	background-color: #eedc94; background-repeat : repeat-x;
 
	background-image : -khtml-gradient( linear, left top, left bottom,
 
	from( #fceec1), to( #eedc94)); background-image : -moz-linear-gradient(
 
	top, #003b76, #00376e); background-image : -ms-linear-gradient( top,
 
	#003b76, #00376e); background-image : -webkit-gradient( linear, left
 
	top, left bottom, color-stop( 0%, #003b76), color-stop( 100%, #00376e));
 
	background-color: #003B76; 
 
	background-repeat : repeat-x;
 
	background-image : -khtml-gradient( linear, left top, left bottom, from(#003B76), to(#00376E)); 
 
	background-image : -moz-linear-gradient( top, #003b76, #00376e); 
 
	background-image : -ms-linear-gradient( top, #003b76, #00376e);
 
	background-image : -webkit-gradient( linear, left top, left bottom, color-stop( 0%, #003b76), color-stop( 100%, #00376e));
 
	background-image : -webkit-linear-gradient( top, #003b76, #00376e));
 
	background-image : -o-linear-gradient( top, #003b76, #00376e));
 
	background-image : linear-gradient( top, #003b76, #00376e); filter :
 
	progid : DXImageTransform.Microsoft.gradient ( startColorstr =
 
	'#003b76', endColorstr = '#00376e', GradientType = 0);
 
	background-image : linear-gradient( top, #003b76, #00376e); 
 
	filter : progid : DXImageTransform.Microsoft.gradient ( startColorstr = '#003b76', endColorstr = '#00376e', GradientType = 0);
 
	margin: 0 auto;
 
	padding: 0;
 
	background-repeat: repeat-x;
 
	background-image: -khtml-gradient(linear, left top, left bottom, from(#fceec1),
 
		to(#eedc94) );
 
	background-image: -moz-linear-gradient(top, #003b76, #00376e);
 
	background-image: -ms-linear-gradient(top, #003b76, #00376e);
 
	background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #003b76), color-stop(100%, #00376e) );
 
	background-image: -webkit-linear-gradient(top, #003b76, #00376e);
 
	background-image: -o-linear-gradient(top, #003b76, #00376e);
 
	background-image: linear-gradient(top, #003b76, #00376e);
 
	filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#003b76',
 
		endColorstr='#00376e', GradientType=0 );
 
}
 
 
#login div.inner {
 
@@ -1908,16 +1925,14 @@ div.form div.fields div.field div.button
 
    width: 278px;
 
    
 
    background-repeat: repeat-x;
 
    background-image: -khtml-gradient(linear, left top, left bottom, from(#fceec1),
 
        to(#eedc94) );
 
    background-image: -khtml-gradient(linear, left top, left bottom, from(#003B76), to(#00376E) );
 
    background-image: -moz-linear-gradient(top, #003b76, #00376e);
 
    background-image: -ms-linear-gradient(top, #003b76, #00376e);
 
    background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #003b76), color-stop(100%, #00376e) );
 
    background-image: -webkit-linear-gradient(top, #003b76, #00376e);
 
    background-image: -o-linear-gradient(top, #003b76, #00376e);
 
    background-image: linear-gradient(top, #003b76, #00376e);
 
    filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#003b76',
 
        endColorstr='#00376e', GradientType=0 );
 
    filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#003b76', endColorstr='#00376e', GradientType=0 );
 
 
	z-index: 999;
 
	-webkit-border-radius: 0px 0px 4px 4px;
 
@@ -2060,10 +2075,9 @@ div.form div.fields div.field div.button
 
	clear: both;
 
	overflow: hidden;
 
	position: relative;
 
    background-color: #eedc94;
 
    background-color: #003B76;
 
    background-repeat: repeat-x;
 
    background-image: -khtml-gradient(linear, left top, left bottom, from(#fceec1),
 
        to(#eedc94) );
 
    background-image: -khtml-gradient(linear, left top, left bottom, from(#003B76), to(#00376E) );
 
    background-image: -moz-linear-gradient(top, #003b76, #00376e);
 
    background-image: -ms-linear-gradient(top, #003b76, #00376e);
 
    background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #003b76), color-stop(100%, #00376e) );
 
@@ -2794,12 +2808,12 @@ table.code-browser .browser-dir {
 
}
 
 
.ac .yui-ac {
 
	position: relative;
 
	position: inherit;
 
	font-size: 100%;
 
}
 
 
.ac .perm_ac {
 
	width: 15em;
 
	width: 20em;
 
}
 
 
.ac .yui-ac-input {
 
@@ -2809,16 +2823,15 @@ table.code-browser .browser-dir {
 
.ac .yui-ac-container {
 
	position: absolute;
 
	top: 1.6em;
 
	width: 100%;
 
	width: auto;
 
}
 
 
.ac .yui-ac-content {
 
	position: absolute;
 
	width: 100%;
 
	border: 1px solid gray;
 
	background: #fff;
 
	overflow: hidden;
 
	z-index: 9050;
 
	
 
}
 
 
.ac .yui-ac-shadow {
 
@@ -2827,7 +2840,7 @@ table.code-browser .browser-dir {
 
	background: #000;
 
	-moz-opacity: 0.1px;
 
	opacity: .10;
 
	filter: alpha(opacity =     10);
 
	filter: alpha(opacity = 10);
 
	z-index: 9049;
 
	margin: .3em;
 
}
 
@@ -2836,6 +2849,7 @@ table.code-browser .browser-dir {
 
	width: 100%;
 
	margin: 0;
 
	padding: 0;
 
	z-index: 9050;
 
}
 
 
.ac .yui-ac-content li {
 
@@ -2843,15 +2857,28 @@ table.code-browser .browser-dir {
 
	white-space: nowrap;
 
	margin: 0;
 
	padding: 2px 5px;
 
	height: 18px;
 
	z-index: 9050;
 
	display: block;
 
	width: auto !important;
 
}
 
 
.ac .yui-ac-content li .ac-container-wrap{
 
    width: auto;
 
}
 
 
.ac .yui-ac-content li.yui-ac-prehighlight {
 
	background: #B3D4FF;
 
	z-index: 9050;
 
}
 
 
.ac .yui-ac-content li.yui-ac-highlight {
 
	background: #556CB5;
 
	color: #FFF;
 
	z-index: 9050;
 
}
 
.ac .yui-ac-bd{
 
	z-index: 9050;
 
}
 
 
.follow {
 
@@ -3006,17 +3033,14 @@ table.code-browser .browser-dir {
 
.error_msg {
 
	background-color: #c43c35;
 
	background-repeat: repeat-x;
 
	background-image: -khtml-gradient(linear, left top, left bottom, from(#ee5f5b),
 
		to(#c43c35) );
 
	background-image: -khtml-gradient(linear, left top, left bottom, from(#ee5f5b), to(#c43c35) );
 
	background-image: -moz-linear-gradient(top, #ee5f5b, #c43c35);
 
	background-image: -ms-linear-gradient(top, #ee5f5b, #c43c35);
 
	background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #ee5f5b),
 
		color-stop(100%, #c43c35) );
 
	background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #ee5f5b), color-stop(100%, #c43c35) );
 
	background-image: -webkit-linear-gradient(top, #ee5f5b, #c43c35);
 
	background-image: -o-linear-gradient(top, #ee5f5b, #c43c35);
 
	background-image: linear-gradient(top, #ee5f5b, #c43c35);
 
	filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ee5f5b',
 
		endColorstr='#c43c35', GradientType=0 );
 
	filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ee5f5b',endColorstr='#c43c35', GradientType=0 );
 
	border-color: #c43c35 #c43c35 #882a25;
 
}
 
 
@@ -3024,51 +3048,42 @@ table.code-browser .browser-dir {
 
	color: #404040 !important;
 
	background-color: #eedc94;
 
	background-repeat: repeat-x;
 
	background-image: -khtml-gradient(linear, left top, left bottom, from(#fceec1),
 
		to(#eedc94) );
 
	background-image: -khtml-gradient(linear, left top, left bottom, from(#fceec1), to(#eedc94) );
 
	background-image: -moz-linear-gradient(top, #fceec1, #eedc94);
 
	background-image: -ms-linear-gradient(top, #fceec1, #eedc94);
 
	background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #fceec1),
 
		color-stop(100%, #eedc94) );
 
	background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #fceec1), color-stop(100%, #eedc94) );
 
	background-image: -webkit-linear-gradient(top, #fceec1, #eedc94);
 
	background-image: -o-linear-gradient(top, #fceec1, #eedc94);
 
	background-image: linear-gradient(top, #fceec1, #eedc94);
 
	filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fceec1',
 
		endColorstr='#eedc94', GradientType=0 );
 
	filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fceec1', endColorstr='#eedc94', GradientType=0 );
 
	border-color: #eedc94 #eedc94 #e4c652;
 
}
 
 
.success_msg {
 
	background-color: #57a957;
 
	background-repeat: repeat-x !important;
 
	background-image: -khtml-gradient(linear, left top, left bottom, from(#62c462),
 
		to(#57a957) );
 
	background-image: -khtml-gradient(linear, left top, left bottom, from(#62c462), to(#57a957) );
 
	background-image: -moz-linear-gradient(top, #62c462, #57a957);
 
	background-image: -ms-linear-gradient(top, #62c462, #57a957);
 
	background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #62c462),
 
		color-stop(100%, #57a957) );
 
	background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #62c462), color-stop(100%, #57a957) );
 
	background-image: -webkit-linear-gradient(top, #62c462, #57a957);
 
	background-image: -o-linear-gradient(top, #62c462, #57a957);
 
	background-image: linear-gradient(top, #62c462, #57a957);
 
	filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#62c462',
 
		endColorstr='#57a957', GradientType=0 );
 
	filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#62c462', endColorstr='#57a957', GradientType=0 );
 
	border-color: #57a957 #57a957 #3d773d;
 
}
 
 
.notice_msg {
 
	background-color: #339bb9;
 
	background-repeat: repeat-x;
 
	background-image: -khtml-gradient(linear, left top, left bottom, from(#5bc0de),
 
		to(#339bb9) );
 
	background-image: -khtml-gradient(linear, left top, left bottom, from(#5bc0de), to(#339bb9) );
 
	background-image: -moz-linear-gradient(top, #5bc0de, #339bb9);
 
	background-image: -ms-linear-gradient(top, #5bc0de, #339bb9);
 
	background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #5bc0de),
 
		color-stop(100%, #339bb9) );
 
	background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #5bc0de), color-stop(100%, #339bb9) );
 
	background-image: -webkit-linear-gradient(top, #5bc0de, #339bb9);
 
	background-image: -o-linear-gradient(top, #5bc0de, #339bb9);
 
	background-image: linear-gradient(top, #5bc0de, #339bb9);
 
	filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#5bc0de',
 
		endColorstr='#339bb9', GradientType=0 );
 
	filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#5bc0de', endColorstr='#339bb9', GradientType=0 );
 
	border-color: #339bb9 #339bb9 #22697d;
 
}
 
 
@@ -3096,8 +3111,7 @@ table.code-browser .browser-dir {
 
}
 
 
#msg_close {
 
	background: transparent url("../icons/cross_grey_small.png") no-repeat
 
		scroll 0 0;
 
	background: transparent url("../icons/cross_grey_small.png") no-repeat scroll 0 0;
 
	cursor: pointer;
 
	height: 16px;
 
	position: absolute;
 
@@ -3105,7 +3119,12 @@ table.code-browser .browser-dir {
 
	top: 5px;
 
	width: 16px;
 
}
 
 
div#legend_data{
 
	padding-left:10px;
 
}
 
div#legend_container table{
 
	border: none !important;
 
}
 
div#legend_container table,div#legend_choices table {
 
	width: auto !important;
 
}
 
@@ -4115,6 +4134,56 @@ form.comment-inline-form {
 
    padding:5px 0px 5px 38px;
 
}
 
 
/****
 
  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;
 
}
 
 
#perms .perm_tag.write{
 
  background-color: #B94A48;
 
  color: #ffffff;    
 
}
 
 
#perms .perm_tag.read{
 
  background-color: #468847;
 
  color: #ffffff;    
 
}
 
 
#perms .perm_tag.none{
 
  background-color: #bfbfbf;
 
  color: #ffffff;    
 
}
 
 
.perm-gravatar{
 
	vertical-align:middle;
 
	padding:2px;
 
}
 
.perm-gravatar-ac{
 
    vertical-align:middle;
 
    padding:2px;
 
    width: 14px;
 
    height: 14px;	
 
}
 
 
/*****************************************************************************
 
                                  DIFFS CSS
rhodecode/public/js/rhodecode.js
Show inline comments
 
@@ -609,6 +609,178 @@ var deleteNotification = function(url, n
 
};	
 

	
 

	
 
/** MEMBERS AUTOCOMPLETE WIDGET **/
 

	
 
var MembersAutoComplete = function (users_list, groups_list, group_lbl, members_lbl) {
 
    var myUsers = users_list;
 
    var myGroups = groups_list;
 

	
 
    // Define a custom search function for the DataSource of users
 
    var matchUsers = function (sQuery) {
 
            // Case insensitive matching
 
            var query = sQuery.toLowerCase();
 
            var i = 0;
 
            var l = myUsers.length;
 
            var matches = [];
 

	
 
            // Match against each name of each contact
 
            for (; i < l; i++) {
 
                contact = myUsers[i];
 
                if ((contact.fname.toLowerCase().indexOf(query) > -1) || (contact.lname.toLowerCase().indexOf(query) > -1) || (contact.nname && (contact.nname.toLowerCase().indexOf(query) > -1))) {
 
                    matches[matches.length] = contact;
 
                }
 
            }
 
            return matches;
 
        };
 

	
 
    // Define a custom search function for the DataSource of usersGroups
 
    var matchGroups = function (sQuery) {
 
            // Case insensitive matching
 
            var query = sQuery.toLowerCase();
 
            var i = 0;
 
            var l = myGroups.length;
 
            var matches = [];
 

	
 
            // Match against each name of each contact
 
            for (; i < l; i++) {
 
                matched_group = myGroups[i];
 
                if (matched_group.grname.toLowerCase().indexOf(query) > -1) {
 
                    matches[matches.length] = matched_group;
 
                }
 
            }
 
            return matches;
 
        };
 

	
 
    //match all
 
    var matchAll = function (sQuery) {
 
            u = matchUsers(sQuery);
 
            g = matchGroups(sQuery);
 
            return u.concat(g);
 
        };
 

	
 
    // DataScheme for members
 
    var memberDS = new YAHOO.util.FunctionDataSource(matchAll);
 
    memberDS.responseSchema = {
 
        fields: ["id", "fname", "lname", "nname", "grname", "grmembers", "gravatar_lnk"]
 
    };
 

	
 
    // DataScheme for owner
 
    var ownerDS = new YAHOO.util.FunctionDataSource(matchUsers);
 
    ownerDS.responseSchema = {
 
        fields: ["id", "fname", "lname", "nname", "gravatar_lnk"]
 
    };
 

	
 
    // Instantiate AutoComplete for perms
 
    var membersAC = new YAHOO.widget.AutoComplete("perm_new_member_name", "perm_container", memberDS);
 
    membersAC.useShadow = false;
 
    membersAC.resultTypeList = false;
 

	
 
    // Instantiate AutoComplete for owner
 
    var ownerAC = new YAHOO.widget.AutoComplete("user", "owner_container", ownerDS);
 
    ownerAC.useShadow = false;
 
    ownerAC.resultTypeList = false;
 

	
 

	
 
    // Helper highlight function for the formatter
 
    var highlightMatch = function (full, snippet, matchindex) {
 
            return full.substring(0, matchindex) 
 
            + "<span class='match'>" 
 
            + full.substr(matchindex, snippet.length) 
 
            + "</span>" + full.substring(matchindex + snippet.length);
 
        };
 

	
 
    // Custom formatter to highlight the matching letters
 
    var custom_formatter = function (oResultData, sQuery, sResultMatch) {
 
            var query = sQuery.toLowerCase();
 
            var _gravatar = function(res, em, group){
 
            	if (group !== undefined){
 
            		em = '/images/icons/group.png'
 
            	}
 
            	tmpl = '<div class="ac-container-wrap"><img class="perm-gravatar-ac" src="{0}"/>{1}</div>'
 
            	return tmpl.format(em,res)
 
            }
 
            // group
 
            if (oResultData.grname != undefined) {
 
                var grname = oResultData.grname;
 
                var grmembers = oResultData.grmembers;
 
                var grnameMatchIndex = grname.toLowerCase().indexOf(query);
 
                var grprefix = "{0}: ".format(group_lbl);
 
                var grsuffix = " (" + grmembers + "  )";
 
                var grsuffix = " ({0}  {1})".format(grmembers, members_lbl);
 

	
 
                if (grnameMatchIndex > -1) {
 
                    return _gravatar(grprefix + highlightMatch(grname, query, grnameMatchIndex) + grsuffix,null,true);
 
                }
 
			    return _gravatar(grprefix + oResultData.grname + grsuffix, null,true);
 
            // Users
 
            } else if (oResultData.fname != undefined) {
 
                var fname = oResultData.fname,
 
                    lname = oResultData.lname,
 
                    nname = oResultData.nname || "",
 
                    // Guard against null value
 
                    fnameMatchIndex = fname.toLowerCase().indexOf(query),
 
                    lnameMatchIndex = lname.toLowerCase().indexOf(query),
 
                    nnameMatchIndex = nname.toLowerCase().indexOf(query),
 
                    displayfname, displaylname, displaynname;
 

	
 
                if (fnameMatchIndex > -1) {
 
                    displayfname = highlightMatch(fname, query, fnameMatchIndex);
 
                } else {
 
                    displayfname = fname;
 
                }
 

	
 
                if (lnameMatchIndex > -1) {
 
                    displaylname = highlightMatch(lname, query, lnameMatchIndex);
 
                } else {
 
                    displaylname = lname;
 
                }
 

	
 
                if (nnameMatchIndex > -1) {
 
                    displaynname = "(" + highlightMatch(nname, query, nnameMatchIndex) + ")";
 
                } else {
 
                    displaynname = nname ? "(" + nname + ")" : "";
 
                }
 

	
 
                return _gravatar(displayfname + " " + displaylname + " " + displaynname, oResultData.gravatar_lnk);
 
            } else {
 
                return '';
 
            }
 
        };
 
    membersAC.formatResult = custom_formatter;
 
    ownerAC.formatResult = custom_formatter;
 

	
 
    var myHandler = function (sType, aArgs) {
 

	
 
            var myAC = aArgs[0]; // reference back to the AC instance
 
            var elLI = aArgs[1]; // reference to the selected LI element
 
            var oData = aArgs[2]; // object literal of selected item's result data
 
            //fill the autocomplete with value
 
            if (oData.nname != undefined) {
 
                //users
 
                myAC.getInputEl().value = oData.nname;
 
                YUD.get('perm_new_member_type').value = 'user';
 
            } else {
 
                //groups
 
                myAC.getInputEl().value = oData.grname;
 
                YUD.get('perm_new_member_type').value = 'users_group';
 
            }
 
        };
 

	
 
    membersAC.itemSelectEvent.subscribe(myHandler);
 
    if(ownerAC.itemSelectEvent){
 
    	ownerAC.itemSelectEvent.subscribe(myHandler);
 
    }
 

	
 
    return {
 
        memberDS: memberDS,
 
        ownerDS: ownerDS,
 
        membersAC: membersAC,
 
        ownerAC: ownerAC,
 
    };
 
}
 

	
 

	
 

	
 
/**
 
 * QUICK REPO MENU
 
 */
 
@@ -700,6 +872,19 @@ var nameSort = function(a, b, desc, fiel
 
    return compState;
 
};
 

	
 
var permNameSort = function(a, b, desc, field) {
 
    var a_ = fromHTML(a.getData(field));
 
    var b_ = fromHTML(b.getData(field));
 
    // extract name from table
 

	
 
    a_ = a_.children[0].innerHTML;
 
    b_ = b_.children[0].innerHTML;      
 
    
 
    var comp = YAHOO.util.Sort.compare;
 
    var compState = comp(a_, b_, desc);
 
    return compState;
 
};
 

	
 
var groupNameSort = function(a, b, desc, field) {
 
    var a_ = fromHTML(a.getData(field));
 
    var b_ = fromHTML(b.getData(field));
rhodecode/templates/_data_table/_dt_elements.html
Show inline comments
 
@@ -40,7 +40,14 @@
 
  </ul>
 
</%def>
 

	
 
<%def name="repo_name(name,rtype,private,fork_of)">
 
<%def name="repo_name(name,rtype,private,fork_of,short_name=False, admin=False)">
 
    <%
 
    def get_name(name,short_name=short_name):
 
      if short_name:
 
        return name.split('/')[-1]
 
      else:
 
        return name
 
    %>
 
  <div style="white-space: nowrap">
 
   ##TYPE OF REPO
 
   %if h.is_hg(rtype):
 
@@ -57,7 +64,11 @@
 
   %endif
 

	
 
   ##NAME
 
   ${h.link_to(name,h.url('summary_home',repo_name=name),class_="repo_name")}
 
   %if admin:
 
    ${h.link_to(get_name(name),h.url('edit_repo',repo_name=name),class_="repo_name")}
 
   %else:
 
    ${h.link_to(get_name(name),h.url('summary_home',repo_name=name),class_="repo_name")}
 
   %endif
 
   %if fork_of:
 
        <a href="${h.url('summary_home',repo_name=fork_of)}">
 
        <img class="icon" alt="${_('fork')}" title="${_('Fork of')} ${fork_of}" src="${h.url('/images/icons/arrow_divide.png')}"/></a>
rhodecode/templates/admin/repos/repo_add_base.html
Show inline comments
 
@@ -21,6 +21,7 @@ ${h.form(url('repos'))}
 
            </div>
 
            <div class="input">
 
                ${h.text('clone_uri',class_="small")}
 
                <span class="help-block">${_('Optional http[s] url from which repository should be cloned.')}</span>
 
            </div>
 
         </div>
 
         <div class="field">
 
@@ -28,7 +29,8 @@ ${h.form(url('repos'))}
 
                 <label for="repo_group">${_('Repository group')}:</label>
 
             </div>
 
             <div class="input">
 
                 ${h.select('repo_group','',c.repo_groups,class_="medium")}
 
                 ${h.select('repo_group',request.GET.get('parent_group'),c.repo_groups,class_="medium")}
 
                 <span class="help-block">${_('Optional select a group to put this repository into.')}</span>
 
             </div>
 
         </div>
 
        <div class="field">
 
@@ -37,6 +39,7 @@ ${h.form(url('repos'))}
 
            </div>
 
            <div class="input">
 
                ${h.select('repo_type','hg',c.backends,class_="small")}
 
                <span class="help-block">${_('Type of repository to create.')}</span>
 
            </div>
 
         </div>
 
        <div class="field">
 
@@ -44,15 +47,17 @@ ${h.form(url('repos'))}
 
                <label for="description">${_('Description')}:</label>
 
            </div>
 
            <div class="textarea text-area editor">
 
                ${h.textarea('description',cols=23,rows=5)}
 
                ${h.textarea('description')}
 
                <span class="help-block">${_('Keep it short and to the point. Use a README file for longer descriptions.')}</span>
 
            </div>
 
         </div>
 
        <div class="field">
 
            <div class="label label-checkbox">
 
                <label for="private">${_('Private')}:</label>
 
                <label for="private">${_('Private repository')}:</label>
 
            </div>
 
            <div class="checkboxes">
 
                ${h.checkbox('private',value="True")}
 
                <span class="help-block">${_('Private repositories are only visible to people explicitly added as collaborators.')}</span>
 
            </div>
 
         </div>
 
        <div class="buttons">
rhodecode/templates/admin/repos/repo_edit.html
Show inline comments
 
@@ -41,6 +41,7 @@
 
	           </div>
 
	           <div class="input">
 
	               ${h.text('clone_uri',class_="medium")}
 
                 <span class="help-block">${_('Optional http[s] url from which repository should be cloned.')}</span>
 
	           </div>
 
	        </div>
 
	        <div class="field">
 
@@ -49,6 +50,7 @@
 
	            </div>
 
	            <div class="input">
 
	                ${h.select('repo_group','',c.repo_groups,class_="medium")}
 
                    <span class="help-block">${_('Optional select a group to put this repository into.')}</span>
 
	            </div>
 
	        </div>
 
            <div class="field">
 
@@ -64,16 +66,18 @@
 
                    <label for="description">${_('Description')}:</label>
 
                </div>
 
                <div class="textarea text-area editor">
 
                    ${h.textarea('description',cols=23,rows=5)}
 
                    ${h.textarea('description')}
 
                    <span class="help-block">${_('Keep it short and to the point. Use a README file for longer descriptions.')}</span>
 
                </div>
 
            </div>
 

	
 
            <div class="field">
 
                <div class="label label-checkbox">
 
                    <label for="private">${_('Private')}:</label>
 
                    <label for="private">${_('Private repository')}:</label>
 
                </div>
 
                <div class="checkboxes">
 
                    ${h.checkbox('private',value="True")}
 
                    <span class="help-block">${_('Private repositories are only visible to people explicitly added as collaborators.')}</span>
 
                </div>
 
            </div>
 
            <div class="field">
 
@@ -82,6 +86,7 @@
 
                </div>
 
                <div class="checkboxes">
 
                    ${h.checkbox('enable_statistics',value="True")}
 
                    <span class="help-block">${_('Enable statistics window on summary page.')}</span>
 
                </div>
 
            </div>
 
            <div class="field">
 
@@ -90,15 +95,17 @@
 
                </div>
 
                <div class="checkboxes">
 
                    ${h.checkbox('enable_downloads',value="True")}
 
                    <span class="help-block">${_('Enable download menu on summary page.')}</span>
 
                </div>
 
            </div>
 
            <div class="field">
 
                <div class="label">
 
                    <label for="user">${_('Owner')}:</label>
 
                </div>
 
                <div class="input input-small ac">
 
                <div class="input input-medium ac">
 
                    <div class="perm_ac">
 
                       ${h.text('user',class_='yui-ac-input')}
 
                       <span class="help-block">${_('Change owner of this repository.')}</span>
 
                       <div id="owner_container"></div>
 
                    </div>
 
                </div>
rhodecode/templates/admin/repos/repo_edit_perms.html
Show inline comments
 
@@ -25,7 +25,7 @@
 
            <td>${h.radio('u_perm_%s' % r2p.user.username,'repository.write')}</td>
 
            <td>${h.radio('u_perm_%s' % r2p.user.username,'repository.admin')}</td>
 
            <td style="white-space: nowrap;">
 
                <img style="vertical-align:bottom" src="${h.url('/images/icons/user.png')}"/>${r2p.user.username}
 
                <img class="perm-gravatar" src="${h.gravatar_url(r2p.user.email,14)}"/>${r2p.user.username}
 
            </td>
 
            <td>
 
              %if r2p.user.username !='default':
 
@@ -46,7 +46,7 @@
 
            <td>${h.radio('g_perm_%s' % g2p.users_group.users_group_name,'repository.write')}</td>
 
            <td>${h.radio('g_perm_%s' % g2p.users_group.users_group_name,'repository.admin')}</td>
 
            <td style="white-space: nowrap;">
 
                <img  style="vertical-align:bottom" src="${h.url('/images/icons/group.png')}"/>${g2p.users_group.users_group_name}
 
                <img class="perm-gravatar" src="${h.url('/images/icons/group.png')}"/>${g2p.users_group.users_group_name}
 
            </td>
 
            <td>
 
                <span class="delete_icon action_button" onclick="ajaxActionUsersGroup(${g2p.users_group.users_group_id},'${'id%s'%id(g2p.users_group.users_group_name)}')">
 
@@ -117,165 +117,12 @@ YUE.onDOMReady(function () {
 
        YUD.setStyle('add_perm', 'opacity', '0.6');
 
        YUD.setStyle('add_perm', 'cursor', 'default');
 
    });
 
    MembersAutoComplete(
 
    		  ${c.users_array|n},
 
    		  ${c.users_groups_array|n},
 
    		  "${_('Group')}",
 
    		  "${_('members')}"
 
    		);
 
});
 

	
 
YAHOO.example.FnMultipleFields = function () {
 
    var myUsers = ${c.users_array|n};
 
    var myGroups = ${c.users_groups_array|n};
 

	
 
    // Define a custom search function for the DataSource of users
 
    var matchUsers = function (sQuery) {
 
            // Case insensitive matching
 
            var query = sQuery.toLowerCase();
 
            var i = 0;
 
            var l = myUsers.length;
 
            var matches = [];
 

	
 
            // Match against each name of each contact
 
            for (; i < l; i++) {
 
                contact = myUsers[i];
 
                if ((contact.fname.toLowerCase().indexOf(query) > -1) || (contact.lname.toLowerCase().indexOf(query) > -1) || (contact.nname && (contact.nname.toLowerCase().indexOf(query) > -1))) {
 
                    matches[matches.length] = contact;
 
                }
 
            }
 
            return matches;
 
        };
 

	
 
    // Define a custom search function for the DataSource of usersGroups
 
    var matchGroups = function (sQuery) {
 
            // Case insensitive matching
 
            var query = sQuery.toLowerCase();
 
            var i = 0;
 
            var l = myGroups.length;
 
            var matches = [];
 

	
 
            // Match against each name of each contact
 
            for (; i < l; i++) {
 
                matched_group = myGroups[i];
 
                if (matched_group.grname.toLowerCase().indexOf(query) > -1) {
 
                    matches[matches.length] = matched_group;
 
                }
 
            }
 
            return matches;
 
        };
 

	
 
    //match all
 
    var matchAll = function (sQuery) {
 
            u = matchUsers(sQuery);
 
            g = matchGroups(sQuery);
 
            return u.concat(g);
 
        };
 

	
 
    // DataScheme for members
 
    var memberDS = new YAHOO.util.FunctionDataSource(matchAll);
 
    memberDS.responseSchema = {
 
        fields: ["id", "fname", "lname", "nname", "grname", "grmembers"]
 
    };
 

	
 
    // DataScheme for owner
 
    var ownerDS = new YAHOO.util.FunctionDataSource(matchUsers);
 
    ownerDS.responseSchema = {
 
        fields: ["id", "fname", "lname", "nname"]
 
    };
 

	
 
    // Instantiate AutoComplete for perms
 
    var membersAC = new YAHOO.widget.AutoComplete("perm_new_member_name", "perm_container", memberDS);
 
    membersAC.useShadow = false;
 
    membersAC.resultTypeList = false;
 

	
 
    // Instantiate AutoComplete for owner
 
    var ownerAC = new YAHOO.widget.AutoComplete("user", "owner_container", ownerDS);
 
    ownerAC.useShadow = false;
 
    ownerAC.resultTypeList = false;
 

	
 

	
 
    // Helper highlight function for the formatter
 
    var highlightMatch = function (full, snippet, matchindex) {
 
            return full.substring(0, matchindex) + "<span class='match'>" + full.substr(matchindex, snippet.length) + "</span>" + full.substring(matchindex + snippet.length);
 
        };
 

	
 
    // Custom formatter to highlight the matching letters
 
    var custom_formatter = function (oResultData, sQuery, sResultMatch) {
 
            var query = sQuery.toLowerCase();
 

	
 
            if (oResultData.grname != undefined) {
 
                var grname = oResultData.grname;
 
                var grmembers = oResultData.grmembers;
 
                var grnameMatchIndex = grname.toLowerCase().indexOf(query);
 
                var grprefix = "${_('Group')}: ";
 
                var grsuffix = " (" + grmembers + "  ${_('members')})";
 

	
 
                if (grnameMatchIndex > -1) {
 
                    return grprefix + highlightMatch(grname, query, grnameMatchIndex) + grsuffix;
 
                }
 

	
 
                return grprefix + oResultData.grname + grsuffix;
 
            } else if (oResultData.fname != undefined) {
 

	
 
                var fname = oResultData.fname,
 
                    lname = oResultData.lname,
 
                    nname = oResultData.nname || "",
 
                    // Guard against null value
 
                    fnameMatchIndex = fname.toLowerCase().indexOf(query),
 
                    lnameMatchIndex = lname.toLowerCase().indexOf(query),
 
                    nnameMatchIndex = nname.toLowerCase().indexOf(query),
 
                    displayfname, displaylname, displaynname;
 

	
 
                if (fnameMatchIndex > -1) {
 
                    displayfname = highlightMatch(fname, query, fnameMatchIndex);
 
                } else {
 
                    displayfname = fname;
 
                }
 

	
 
                if (lnameMatchIndex > -1) {
 
                    displaylname = highlightMatch(lname, query, lnameMatchIndex);
 
                } else {
 
                    displaylname = lname;
 
                }
 

	
 
                if (nnameMatchIndex > -1) {
 
                    displaynname = "(" + highlightMatch(nname, query, nnameMatchIndex) + ")";
 
                } else {
 
                    displaynname = nname ? "(" + nname + ")" : "";
 
                }
 

	
 
                return displayfname + " " + displaylname + " " + displaynname;
 
            } else {
 
                return '';
 
            }
 
        };
 
    membersAC.formatResult = custom_formatter;
 
    ownerAC.formatResult = custom_formatter;
 

	
 
    var myHandler = function (sType, aArgs) {
 

	
 
            var myAC = aArgs[0]; // reference back to the AC instance
 
            var elLI = aArgs[1]; // reference to the selected LI element
 
            var oData = aArgs[2]; // object literal of selected item's result data
 
            //fill the autocomplete with value
 
            if (oData.nname != undefined) {
 
                //users
 
                myAC.getInputEl().value = oData.nname;
 
                YUD.get('perm_new_member_type').value = 'user';
 
            } else {
 
                //groups
 
                myAC.getInputEl().value = oData.grname;
 
                YUD.get('perm_new_member_type').value = 'users_group';
 
            }
 

	
 
        };
 

	
 
    membersAC.itemSelectEvent.subscribe(myHandler);
 
    if(ownerAC.itemSelectEvent){
 
    	ownerAC.itemSelectEvent.subscribe(myHandler);
 
    }
 

	
 
    return {
 
        memberDS: memberDS,
 
        ownerDS: ownerDS,
 
        membersAC: membersAC,
 
        ownerAC: ownerAC,
 
    };
 
}();
 

	
 
</script>
rhodecode/templates/admin/repos/repos.html
Show inline comments
 
@@ -48,7 +48,7 @@
 
                ${dt.quick_menu(repo['name'])}
 
              </td>
 
              <td class="reponame">
 
                ${dt.repo_name(repo['name'],repo['dbrepo']['repo_type'],repo['dbrepo']['private'],repo['dbrepo_fork'].get('repo_name'))}
 
                ${dt.repo_name(repo['name'],repo['dbrepo']['repo_type'],repo['dbrepo']['private'],repo['dbrepo_fork'].get('repo_name'), admin=True)}
 
              </td>
 
              ##DESCRIPTION
 
              <td><span class="tooltip" title="${h.tooltip(repo['description'])}">
rhodecode/templates/admin/repos_groups/repos_group_edit_perms.html
Show inline comments
 
@@ -15,7 +15,7 @@
 
            <td>${h.radio('u_perm_%s' % r2p.user.username,'group.write')}</td>
 
            <td>${h.radio('u_perm_%s' % r2p.user.username,'group.admin')}</td>
 
            <td style="white-space: nowrap;">
 
                <img style="vertical-align:bottom" src="${h.url('/images/icons/user.png')}"/>${r2p.user.username}
 
                <img class="perm-gravatar" src="${h.gravatar_url(r2p.user.email,14)}"/>${r2p.user.username}
 
            </td>
 
            <td>
 
              %if r2p.user.username !='default':
 
@@ -35,7 +35,7 @@
 
            <td>${h.radio('g_perm_%s' % g2p.users_group.users_group_name,'group.write')}</td>
 
            <td>${h.radio('g_perm_%s' % g2p.users_group.users_group_name,'group.admin')}</td>
 
            <td style="white-space: nowrap;">
 
                <img  style="vertical-align:bottom" src="${h.url('/images/icons/group.png')}"/>${g2p.users_group.users_group_name}
 
                <img class="perm-gravatar" src="${h.url('/images/icons/group.png')}"/>${g2p.users_group.users_group_name}
 
            </td>
 
            <td>
 
                <span class="delete_icon action_button" onclick="ajaxActionUsersGroup(${g2p.users_group.users_group_id},'${'id%s'%id(g2p.users_group.users_group_name)}')">
 
@@ -68,7 +68,7 @@
 
</table>
 
<script type="text/javascript">
 
function ajaxActionUser(user_id, field_id) {
 
    var sUrl = "${h.url('delete_repos_group_user_perm',group_name=c.repos_group.name)}";
 
    var sUrl = "${h.url('delete_repos_group_user_perm',group_name=c.repos_group.group_name)}";
 
    var callback = {
 
        success: function (o) {
 
            var tr = YUD.get(String(field_id));
 
@@ -83,7 +83,7 @@ function ajaxActionUser(user_id, field_i
 
};
 

	
 
function ajaxActionUsersGroup(users_group_id,field_id){
 
    var sUrl = "${h.url('delete_repos_group_users_group_perm',group_name=c.repos_group.name)}";
 
    var sUrl = "${h.url('delete_repos_group_users_group_perm',group_name=c.repos_group.group_name)}";
 
    var callback = {
 
        success:function(o){
 
            var tr = YUD.get(String(field_id));
 
@@ -106,165 +106,12 @@ YUE.onDOMReady(function () {
 
        YUD.setStyle('add_perm', 'opacity', '0.6');
 
        YUD.setStyle('add_perm', 'cursor', 'default');
 
    });
 
    MembersAutoComplete(
 
            ${c.users_array|n},
 
            ${c.users_groups_array|n},
 
            "${_('Group')}",
 
            "${_('members')}"
 
          );
 
});
 

	
 
YAHOO.example.FnMultipleFields = function () {
 
    var myUsers = ${c.users_array|n};
 
    var myGroups = ${c.users_groups_array|n};
 

	
 
    // Define a custom search function for the DataSource of users
 
    var matchUsers = function (sQuery) {
 
            // Case insensitive matching
 
            var query = sQuery.toLowerCase();
 
            var i = 0;
 
            var l = myUsers.length;
 
            var matches = [];
 

	
 
            // Match against each name of each contact
 
            for (; i < l; i++) {
 
                contact = myUsers[i];
 
                if ((contact.fname.toLowerCase().indexOf(query) > -1) || (contact.lname.toLowerCase().indexOf(query) > -1) || (contact.nname && (contact.nname.toLowerCase().indexOf(query) > -1))) {
 
                    matches[matches.length] = contact;
 
                }
 
            }
 
            return matches;
 
        };
 

	
 
    // Define a custom search function for the DataSource of usersGroups
 
    var matchGroups = function (sQuery) {
 
            // Case insensitive matching
 
            var query = sQuery.toLowerCase();
 
            var i = 0;
 
            var l = myGroups.length;
 
            var matches = [];
 

	
 
            // Match against each name of each contact
 
            for (; i < l; i++) {
 
                matched_group = myGroups[i];
 
                if (matched_group.grname.toLowerCase().indexOf(query) > -1) {
 
                    matches[matches.length] = matched_group;
 
                }
 
            }
 
            return matches;
 
        };
 

	
 
    //match all
 
    var matchAll = function (sQuery) {
 
            u = matchUsers(sQuery);
 
            g = matchGroups(sQuery);
 
            return u.concat(g);
 
        };
 

	
 
    // DataScheme for members
 
    var memberDS = new YAHOO.util.FunctionDataSource(matchAll);
 
    memberDS.responseSchema = {
 
        fields: ["id", "fname", "lname", "nname", "grname", "grmembers"]
 
    };
 

	
 
    // DataScheme for owner
 
    var ownerDS = new YAHOO.util.FunctionDataSource(matchUsers);
 
    ownerDS.responseSchema = {
 
        fields: ["id", "fname", "lname", "nname"]
 
    };
 

	
 
    // Instantiate AutoComplete for perms
 
    var membersAC = new YAHOO.widget.AutoComplete("perm_new_member_name", "perm_container", memberDS);
 
    membersAC.useShadow = false;
 
    membersAC.resultTypeList = false;
 

	
 
    // Instantiate AutoComplete for owner
 
    var ownerAC = new YAHOO.widget.AutoComplete("user", "owner_container", ownerDS);
 
    ownerAC.useShadow = false;
 
    ownerAC.resultTypeList = false;
 

	
 

	
 
    // Helper highlight function for the formatter
 
    var highlightMatch = function (full, snippet, matchindex) {
 
            return full.substring(0, matchindex) + "<span class='match'>" + full.substr(matchindex, snippet.length) + "</span>" + full.substring(matchindex + snippet.length);
 
        };
 

	
 
    // Custom formatter to highlight the matching letters
 
    var custom_formatter = function (oResultData, sQuery, sResultMatch) {
 
            var query = sQuery.toLowerCase();
 

	
 
            if (oResultData.grname != undefined) {
 
                var grname = oResultData.grname;
 
                var grmembers = oResultData.grmembers;
 
                var grnameMatchIndex = grname.toLowerCase().indexOf(query);
 
                var grprefix = "${_('Group')}: ";
 
                var grsuffix = " (" + grmembers + "  ${_('members')})";
 

	
 
                if (grnameMatchIndex > -1) {
 
                    return grprefix + highlightMatch(grname, query, grnameMatchIndex) + grsuffix;
 
                }
 

	
 
                return grprefix + oResultData.grname + grsuffix;
 
            } else if (oResultData.fname != undefined) {
 

	
 
                var fname = oResultData.fname,
 
                    lname = oResultData.lname,
 
                    nname = oResultData.nname || "",
 
                    // Guard against null value
 
                    fnameMatchIndex = fname.toLowerCase().indexOf(query),
 
                    lnameMatchIndex = lname.toLowerCase().indexOf(query),
 
                    nnameMatchIndex = nname.toLowerCase().indexOf(query),
 
                    displayfname, displaylname, displaynname;
 

	
 
                if (fnameMatchIndex > -1) {
 
                    displayfname = highlightMatch(fname, query, fnameMatchIndex);
 
                } else {
 
                    displayfname = fname;
 
                }
 

	
 
                if (lnameMatchIndex > -1) {
 
                    displaylname = highlightMatch(lname, query, lnameMatchIndex);
 
                } else {
 
                    displaylname = lname;
 
                }
 

	
 
                if (nnameMatchIndex > -1) {
 
                    displaynname = "(" + highlightMatch(nname, query, nnameMatchIndex) + ")";
 
                } else {
 
                    displaynname = nname ? "(" + nname + ")" : "";
 
                }
 

	
 
                return displayfname + " " + displaylname + " " + displaynname;
 
            } else {
 
                return '';
 
            }
 
        };
 
    membersAC.formatResult = custom_formatter;
 
    ownerAC.formatResult = custom_formatter;
 

	
 
    var myHandler = function (sType, aArgs) {
 

	
 
            var myAC = aArgs[0]; // reference back to the AC instance
 
            var elLI = aArgs[1]; // reference to the selected LI element
 
            var oData = aArgs[2]; // object literal of selected item's result data
 
            //fill the autocomplete with value
 
            if (oData.nname != undefined) {
 
                //users
 
                myAC.getInputEl().value = oData.nname;
 
                YUD.get('perm_new_member_type').value = 'user';
 
            } else {
 
                //groups
 
                myAC.getInputEl().value = oData.grname;
 
                YUD.get('perm_new_member_type').value = 'users_group';
 
            }
 

	
 
        };
 

	
 
    membersAC.itemSelectEvent.subscribe(myHandler);
 
    if(ownerAC.itemSelectEvent){
 
        ownerAC.itemSelectEvent.subscribe(myHandler);
 
    }
 

	
 
    return {
 
        memberDS: memberDS,
 
        ownerDS: ownerDS,
 
        membersAC: membersAC,
 
        ownerAC: ownerAC,
 
    };
 
}();
 

	
 
</script>
rhodecode/templates/admin/repos_groups/repos_groups.html
Show inline comments
 
@@ -17,5 +17,5 @@
 
    ${self.menu('admin')}
 
</%def>
 
<%def name="main()">
 
        <%include file="/index_base.html" args="parent=self"/>
 
        <%include file="/index_base.html" args="parent=self,short_repo_names=True"/>
 
</%def>
rhodecode/templates/admin/repos_groups/repos_groups_show.html
Show inline comments
 
@@ -44,7 +44,7 @@
 
                      <td>
 
                          <div style="white-space: nowrap">
 
                          <img class="icon" alt="${_('Repositories group')}" src="${h.url('/images/icons/database_link.png')}"/>
 
                          ${h.link_to(h.literal(' &raquo; '.join([g.name for g in gr.parents+[gr]])),url('edit_repos_group',id=gr.group_id))}
 
                          ${h.link_to(h.literal(' &raquo; '.join(map(h.safe_unicode,[g.name for g in gr.parents+[gr]]))),url('edit_repos_group',id=gr.group_id))}
 
                          </div>
 
                      </td>
 
                      <td>${gr.group_description}</td>
rhodecode/templates/admin/users/user_edit_my_account.html
Show inline comments
 
@@ -113,52 +113,45 @@
 
    <div class="title">
 
        <h5>
 
        <input class="q_filter_box" id="q_filter" size="15" type="text" name="filter" value="${_('quick filter...')}"/>
 
        ${_('My repositories')}
 
        <a id="show_my" class="link-white" href="#my">${_('My repos')}</a> / <a id="show_perms" class="link-white" href="#perms">${_('My permissions')}</a>
 
        </h5>
 
         %if h.HasPermissionAny('hg.admin','hg.create.repository')():
 
         <ul class="links">
 
           <li>
 
             <span>${h.link_to(_('ADD REPOSITORY'),h.url('admin_settings_create_repository'))}</span>
 
             <span>${h.link_to(_('ADD'),h.url('admin_settings_create_repository'))}</span>
 
           </li>
 
         </ul>
 
         %endif
 
    </div>
 
    <!-- end box / title -->
 
    <div class="table">
 
	    <table>
 
    <div id="my" class="table">
 
        <div id='repos_list_wrap' class="yui-skin-sam">
 
        <table id="repos_list">
 
	    <thead>
 
            <tr>
 
            <th></th>
 
            <th class="left">${_('Name')}</th>
 
            <th class="left">${_('revision')}</th>
 
            <th colspan="2" class="left">${_('action')}</th>
 
            <th class="left">${_('Revision')}</th>
 
            <th class="left">${_('Action')}</th>
 
            <th class="left">${_('Action')}</th>
 
	    </thead>
 
	     <tbody>
 
         <%namespace name="dt" file="/_data_table/_dt_elements.html"/>
 
	     %if c.user_repos:
 
		     %for repo in c.user_repos:
 
		        <tr>
 
		            <td>
 
                     %if h.is_hg(repo['dbrepo']['repo_type']):
 
                       <img class="icon" title="${_('Mercurial repository')}" alt="${_('Mercurial repository')}" src="${h.url('/images/icons/hgicon.png')}"/>
 
                     %elif h.is_git(repo['dbrepo']['repo_type']):
 
                       <img class="icon" title="${_('Git repository')}" alt="${_('Git repository')}" src="${h.url('/images/icons/giticon.png')}"/>
 
                     %else:
 

	
 
                     %endif
 
		             %if repo['dbrepo']['private']:
 
		                <img class="icon" alt="${_('private')}" src="${h.url('/images/icons/lock.png')}"/>
 
		             %else:
 
		                <img class="icon" alt="${_('public')}" src="${h.url('/images/icons/lock_open.png')}"/>
 
		             %endif
 

	
 
		            ${h.link_to(repo['name'], h.url('summary_home',repo_name=repo['name']),class_="repo_name")}
 
		            %if repo['dbrepo_fork']:
 
		            	<a href="${h.url('summary_home',repo_name=repo['dbrepo_fork']['repo_name'])}">
 
		            	<img class="icon" alt="${_('public')}"
 
		            	title="${_('Fork of')} ${repo['dbrepo_fork']['repo_name']}"
 
		            	src="${h.url('/images/icons/arrow_divide.png')}"/></a>
 
		            %endif
 
		            </td>
 
		            <td><span class="tooltip" title="${repo['last_change']}">${("r%s:%s") % (repo['rev'],h.short_id(repo['tip']))}</span></td>
 
                    ##QUICK MENU
 
                    <td class="quick_repo_menu">
 
                      ${dt.quick_menu(repo['name'])}
 
                    </td>
 
                    ##REPO NAME AND ICONS
 
                    <td class="reponame">
 
                      ${dt.repo_name(repo['name'],repo['dbrepo']['repo_type'],repo['dbrepo']['private'],repo['dbrepo_fork'].get('repo_name'))}
 
                    </td>
 
                    ##LAST REVISION
 
                    <td>
 
                        ${dt.revision(repo['name'],repo['rev'],repo['tip'],repo['author'],repo['last_msg'])}
 
                    </td>
 
		            <td><a href="${h.url('repo_settings_home',repo_name=repo['name'])}" title="${_('edit')}"><img class="icon" alt="${_('private')}" src="${h.url('/images/icons/application_form_edit.png')}"/></a></td>
 
		            <td>
 
	                  ${h.form(url('repo_settings_delete', repo_name=repo['name']),method='delete')}
 
@@ -177,14 +170,144 @@
 
	     %endif
 
	     </tbody>
 
	     </table>
 
       </div>
 
    </div>
 
    <div id="perms" class="table" style="display:none">
 
           %for section in sorted(c.rhodecode_user.permissions.keys()):
 
            <div class="perms_section_head">${section.replace("_"," ").capitalize()}</div>
 

	
 
            <div id='tbl_list_wrap_${section}' class="yui-skin-sam">
 
            <table id="tbl_list_${section}">
 
              <thead>
 
                  <tr>
 
                  <th class="left">${_('Name')}</th>
 
                  <th class="left">${_('Permission')}</th>
 
              </thead>
 
              <tbody>
 
            %for k in c.rhodecode_user.permissions[section]:
 
           <%
 
           if section != 'global':
 
               section_perm = c.rhodecode_user.permissions[section].get(k)
 
               _perm = section_perm.split('.')[-1]
 
           else:
 
               _perm = section_perm = None
 
           %>
 
            %if _perm not in ['none']:
 
                <tr>
 
                    <td>
 
                        %if section == 'repositories':
 
                            <a href="${h.url('summary_home',repo_name=k)}">${k}</a>
 
                        %elif section == 'repositories_groups':
 
                            <a href="${h.url('repos_group_home',group_name=k)}">${k}</a>
 
                        %else:
 
                            ${k}
 
                        %endif
 
                    </td>
 
                    <td>
 
                        %if section == 'global':
 
                         ${h.bool2icon(True)}
 
                        %else:
 
                        <span class="perm_tag ${_perm}">${section_perm}</span>
 
                        %endif
 
                     </td>
 
                </tr>
 
             %endif
 
            %endfor
 
            </tbody>
 
            </table>
 
            </div>
 
           %endfor
 
    </div>
 
</div>
 
<script type="text/javascript">
 
var nodes = YUQ('div.table tr td a.repo_name');
 
var target = 'q_filter';
 
var func = function(node){
 
    return node.parentNode.parentNode;
 
var filter_activate = function(){
 
    var nodes = YUQ('#my tr td a.repo_name');
 
    var func = function(node){
 
        return node.parentNode.parentNode.parentNode.parentNode;
 
    }
 
    q_filter('q_filter',YUQ('#my tr td a.repo_name'),func);
 
}
 
q_filter(target,nodes,func);
 

	
 
YUE.on('show_my','click',function(e){
 
    YUD.setStyle('perms','display','none');
 
    YUD.setStyle('my','display','');
 
    YUD.get('q_filter').removeAttribute('disabled');
 
    filter_activate();
 
    YUE.preventDefault(e);
 
})
 
YUE.on('show_perms','click',function(e){
 
    YUD.setStyle('my','display','none');
 
    YUD.setStyle('perms','display','');
 
    YUD.setAttribute('q_filter','disabled','disabled');
 
    YUE.preventDefault(e);
 
})
 

	
 

	
 
// main table sorting
 
var myColumnDefs = [
 
    {key:"menu",label:"",sortable:false,className:"quick_repo_menu hidden"},
 
    {key:"name",label:"${_('Name')}",sortable:true,
 
        sortOptions: { sortFunction: nameSort }},
 
    {key:"tip",label:"${_('Tip')}",sortable:true,
 
        sortOptions: { sortFunction: revisionSort }},
 
    {key:"action1",label:"",sortable:false},
 
    {key:"action2",label:"",sortable:false},
 
];
 

	
 
var myDataSource = new YAHOO.util.DataSource(YUD.get("repos_list"));
 
myDataSource.responseType = YAHOO.util.DataSource.TYPE_HTMLTABLE;
 
myDataSource.responseSchema = {
 
    fields: [
 
        {key:"menu"},
 
        {key:"name"},
 
        {key:"tip"},
 
        {key:"action1"},
 
        {key:"action2"},
 
    ]
 
};
 
var trans_defs =  {
 
    sortedBy:{key:"name",dir:"asc"},
 
    MSG_SORTASC:"${_('Click to sort ascending')}",
 
    MSG_SORTDESC:"${_('Click to sort descending')}",
 
    MSG_EMPTY:"${_('No records found.')}",
 
    MSG_ERROR:"${_('Data error.')}",
 
    MSG_LOADING:"${_('Loading...')}",
 
}
 
var myDataTable = new YAHOO.widget.DataTable("repos_list_wrap", myColumnDefs, myDataSource,trans_defs);
 
myDataTable.subscribe('postRenderEvent',function(oArgs) {
 
    tooltip_activate();
 
    quick_repo_menu();
 
    filter_activate();
 
});
 

	
 
var permsColumnDefs = [
 
    {key:"name",label:"${_('Name')}",sortable:true, sortOptions: { sortFunction: permNameSort }},
 
    {key:"perm",label:"${_('Permission')}",sortable:false,},
 
];
 

	
 
// perms repos table
 
var myDataSource2 = new YAHOO.util.DataSource(YUD.get("tbl_list_repositories"));
 
myDataSource2.responseType = YAHOO.util.DataSource.TYPE_HTMLTABLE;
 
myDataSource2.responseSchema = {
 
    fields: [
 
        {key:"name"},
 
        {key:"perm"},
 
    ]
 
};
 

	
 
new YAHOO.widget.DataTable("tbl_list_wrap_repositories", permsColumnDefs, myDataSource2, trans_defs);
 

	
 
//perms groups table
 
var myDataSource3 = new YAHOO.util.DataSource(YUD.get("tbl_list_repositories_groups"));
 
myDataSource3.responseType = YAHOO.util.DataSource.TYPE_HTMLTABLE;
 
myDataSource3.responseSchema = {
 
    fields: [
 
        {key:"name"},
 
        {key:"perm"},
 
    ]
 
};
 

	
 
new YAHOO.widget.DataTable("tbl_list_wrap_repositories_groups", permsColumnDefs, myDataSource3, trans_defs);
 

	
 
</script>
 
</%def>
rhodecode/templates/admin/users_groups/users_groups.html
Show inline comments
 
@@ -37,7 +37,7 @@
 
            %for cnt,u_group in enumerate(c.users_groups_list):
 
                <tr class="parity${cnt%2}">
 
                    <td>${h.link_to(u_group.users_group_name,h.url('edit_users_group', id=u_group.users_group_id))}</td>
 
                    <td><span class="tooltip" title="${', '.join([x.user.username for x in u_group.members[:50]])}">${len(u_group.members)}</span></td>
 
                    <td><span class="tooltip" title="${', '.join(map(h.safe_unicode,[x.user.username for x in u_group.members[:50]]))}">${len(u_group.members)}</span></td>
 
                    <td>${h.bool2icon(u_group.users_group_active)}</td>
 
                    <td>
 
                        ${h.form(url('users_group', id=u_group.users_group_id),method='delete')}
rhodecode/templates/base/root.html
Show inline comments
 
## -*- coding: utf-8 -*-
 
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
 
<!DOCTYPE html>
 
<html xmlns="http://www.w3.org/1999/xhtml">
 
    <head>
 
        <title>${self.title()}</title>
 
@@ -37,7 +37,7 @@
 
        ## JAVASCRIPT ##
 
        <%def name="js()">
 
            <script type="text/javascript" src="${h.url('/js/yui.2.9.js')}"></script>
 
            <!--[if IE]>
 
            <!--[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>
 
@@ -130,6 +130,17 @@
 
        ${self.js()}
 
    </head>
 
    <body id="body">
 
        ${next.body()}
 
     ## IE hacks
 
      <!--[if IE 7]>
 
      <script>YUD.addClass(document.body,'ie7')</script>
 
      <![endif]-->            
 
      <!--[if IE 8]>
 
      <script>YUD.addClass(document.body,'ie8')</script>
 
      <![endif]-->
 
      <!--[if IE 9]>
 
      <script>YUD.addClass(document.body,'ie9')</script>
 
      <![endif]-->    
 

	
 
      ${next.body()}
 
    </body>
 
</html>
rhodecode/templates/changelog/changelog.html
Show inline comments
 
@@ -93,11 +93,11 @@ ${c.repo_name} ${_('Changelog')} - ${c.r
 
									%endif
 
									%if h.is_hg(c.rhodecode_repo) and cs.branch:
 
									<span class="branchtag" title="${'%s %s' % (_('branch'),cs.branch)}">
 
									   ${h.link_to(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
 
									%for tag in cs.tags:
 
										<span class="tagtag"  title="${'%s %s' % (_('tag'),tag)}">
 
										${h.link_to(tag,h.url('files_home',repo_name=c.repo_name,revision=cs.raw_id))}</span>
 
										${h.link_to(h.shorter(tag),h.url('files_home',repo_name=c.repo_name,revision=cs.raw_id))}</span>
 
									%endfor
 
								</span>
 
						</div>
rhodecode/templates/changeset/changeset.html
Show inline comments
 
@@ -36,8 +36,8 @@
 
                <div class="diff-actions">
 
                  <a href="${h.url('raw_changeset_home',repo_name=c.repo_name,revision=c.changeset.raw_id,diff='show')}" title="${_('raw diff')}" class="tooltip"><img class="icon" src="${h.url('/images/icons/page_white.png')}"/></a>
 
                  <a href="${h.url('raw_changeset_home',repo_name=c.repo_name,revision=c.changeset.raw_id,diff='download')}" title="${_('download diff')}" class="tooltip"><img class="icon" src="${h.url('/images/icons/page_white_get.png')}"/></a>
 
                  ${c.ignorews_url()}
 
                  ${c.context_url()}
 
                  ${c.ignorews_url(request.GET)}
 
                  ${c.context_url(request.GET)}
 
                </div>
 
                <div class="comments-number" style="float:right;padding-right:5px">${len(c.comments)} comment(s) (${c.inline_cnt} ${_('inline')})</div>
 
			</div>
 
@@ -91,14 +91,14 @@
 
	                </div>
 
	        </div>
 
	        <span>
 
	        ${_('%s files affected with %s additions and %s deletions:') % (len(c.changeset.affected_files),c.lines_added,c.lines_deleted)}
 
	        ${_('%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)+"_target")}
 
                                ${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
rhodecode/templates/changeset/diff_block.html
Show inline comments
 
@@ -7,7 +7,7 @@
 

	
 
%for change,filenode,diff,cs1,cs2,stat in changes:
 
    %if change !='removed':
 
    <div id="${h.FID(filenode.changeset.raw_id,filenode.path)}_target" style="clear:both;height:90px;margin-top:-60px"></div>
 
    <div id="${h.FID(filenode.changeset.raw_id,filenode.path)}_target" style="clear:both;margin-top:25px"></div>
 
    <div id="${h.FID(filenode.changeset.raw_id,filenode.path)}" class="diffblock  margined comm">
 
        <div class="code-header">
 
            <div class="changeset_header">
 
@@ -19,8 +19,8 @@
 
                  <a href="${h.url('files_diff_home',repo_name=c.repo_name,f_path=h.safe_unicode(filenode.path),diff2=cs2,diff1=cs1,diff='diff',fulldiff=1)}" title="${_('diff')}" class="tooltip"><img class="icon" src="${h.url('/images/icons/page_white_go.png')}"/></a>
 
                  <a href="${h.url('files_diff_home',repo_name=c.repo_name,f_path=h.safe_unicode(filenode.path),diff2=cs2,diff1=cs1,diff='raw')}" title="${_('raw diff')}" class="tooltip"><img class="icon" src="${h.url('/images/icons/page_white.png')}"/></a>
 
                  <a href="${h.url('files_diff_home',repo_name=c.repo_name,f_path=h.safe_unicode(filenode.path),diff2=cs2,diff1=cs1,diff='download')}" title="${_('download diff')}" class="tooltip"><img class="icon" src="${h.url('/images/icons/page_white_get.png')}"/></a>
 
                  ${c.ignorews_url(h.FID(filenode.changeset.raw_id,filenode.path))}
 
                  ${c.context_url(h.FID(filenode.changeset.raw_id,filenode.path))}
 
                  ${c.ignorews_url(request.GET, h.FID(filenode.changeset.raw_id,filenode.path))}
 
                  ${c.context_url(request.GET, h.FID(filenode.changeset.raw_id,filenode.path))}
 
                </div>
 
                <span style="float:right;margin-top:-3px">
 
                  <label>
rhodecode/templates/index.html
Show inline comments
 
@@ -4,5 +4,5 @@
 
<%def name="breadcrumbs()"></%def>
 
<%def name="page_nav()">${self.menu('home')}</%def>
 
<%def name="main()">
 
    	<%include file="index_base.html" args="parent=self"/>
 
   <%include file="index_base.html" args="parent=self"/>
 
</%def>
rhodecode/templates/index_base.html
Show inline comments
 
@@ -9,7 +9,11 @@
 
                %if h.HasPermissionAny('hg.admin','hg.create.repository')():
 
                <ul class="links">
 
                  <li>
 
                  %if c.group:
 
                    <span>${h.link_to(_('ADD REPOSITORY'),h.url('admin_settings_create_repository',parent_group=c.group.group_id))}</span>
 
                  %else:
 
                    <span>${h.link_to(_('ADD REPOSITORY'),h.url('admin_settings_create_repository'))}</span>
 
                  %endif
 
                  </li>
 
                </ul>
 
                %endif
 
@@ -77,7 +81,7 @@
 
                    </td>
 
                    ##REPO NAME AND ICONS
 
                    <td class="reponame">
 
                      ${dt.repo_name(repo['name'],repo['dbrepo']['repo_type'],repo['dbrepo']['private'],repo['dbrepo_fork'].get('repo_name'))}
 
                      ${dt.repo_name(repo['name'],repo['dbrepo']['repo_type'],repo['dbrepo']['private'],repo['dbrepo_fork'].get('repo_name'),pageargs.get('short_repo_names'))}
 
                    </td>
 
                    ##DESCRIPTION
 
                    <td><span class="tooltip" title="${h.tooltip(repo['description'])}">
 
@@ -115,7 +119,7 @@
 
        </div>
 
    </div>
 
    <script>
 
      YUD.get('repo_count').innerHTML = ${cnt+1};
 
      YUD.get('repo_count').innerHTML = ${cnt};
 
      var func = function(node){
 
          return node.parentNode.parentNode.parentNode.parentNode;
 
      }
rhodecode/templates/journal/journal.html
Show inline comments
 
@@ -48,7 +48,7 @@
 
                  <tr>
 
                  <th></th>
 
                  <th class="left">${_('Name')}</th>
 
                  <th class="left">${_('Tip')}</th>
 
                  <th class="left">${_('Revision')}</th>
 
                  <th class="left">${_('Action')}</th>
 
                  <th class="left">${_('Action')}</th>
 
             </thead>
rhodecode/templates/settings/repo_settings.html
Show inline comments
 
@@ -34,12 +34,22 @@
 
                    ${h.text('repo_name',class_="small")}
 
                </div>
 
             </div>
 
	       <div class="field">
 
	           <div class="label">
 
	               <label for="clone_uri">${_('Clone uri')}:</label>
 
	           </div>
 
	           <div class="input">
 
	               ${h.text('clone_uri',class_="medium")}
 
                 <span class="help-block">${_('Optional http[s] url from which repository should be cloned.')}</span>
 
	           </div>
 
	        </div>
 
            <div class="field">
 
                <div class="label">
 
                    <label for="repo_group">${_('Repository group')}:</label>
 
                </div>
 
                <div class="input">
 
                    ${h.select('repo_group','',c.repo_groups,class_="medium")}
 
                    <span class="help-block">${_('Optional select a group to put this repository into.')}</span>
 
                </div>
 
            </div>
 
            <div class="field">
 
@@ -47,16 +57,18 @@
 
                    <label for="description">${_('Description')}:</label>
 
                </div>
 
                <div class="textarea text-area editor">
 
                    ${h.textarea('description',cols=23,rows=5)}
 
                    ${h.textarea('description')}
 
                    <span class="help-block">${_('Keep it short and to the point. Use a README file for longer descriptions.')}</span>
 
                </div>
 
            </div>
 

	
 
            <div class="field">
 
                <div class="label label-checkbox">
 
                    <label for="private">${_('Private')}:</label>
 
                    <label for="private">${_('Private repository')}:</label>
 
                </div>
 
                <div class="checkboxes">
 
                    ${h.checkbox('private',value="True")}
 
                    <span class="help-block">${_('Private repositories are only visible to people explicitly added as collaborators.')}</span>
 
                </div>
 
            </div>
 

	
rhodecode/tests/functional/test_changeset_comments.py
Show inline comments
 
@@ -2,7 +2,8 @@ from rhodecode.tests import *
 
from rhodecode.model.db import ChangesetComment, Notification, User, \
 
    UserNotification
 

	
 
class TestChangeSetCommentrController(TestController):
 

	
 
class TestChangeSetCommentsController(TestController):
 

	
 
    def setUp(self):
 
        for x in ChangesetComment.query().all():
 
@@ -27,7 +28,7 @@ class TestChangeSetCommentrController(Te
 
        rev = '27cd5cce30c96924232dffcd24178a07ffeb5dfc'
 
        text = u'CommentOnRevision'
 

	
 
        params = {'text':text}
 
        params = {'text': text}
 
        response = self.app.post(url(controller='changeset', action='comment',
 
                                     repo_name=HG_REPO, revision=rev),
 
                                     params=params)
 
@@ -42,13 +43,18 @@ class TestChangeSetCommentrController(Te
 
        self.assertTrue('''<div class="comments-number">%s '''
 
                        '''comment(s) (0 inline)</div>''' % 1 in response.body)
 

	
 
        self.assertEqual(Notification.query().count(), 1)
 
        self.assertEqual(ChangesetComment.query().count(), 1)
 

	
 
        self.assertEqual(Notification.query().count(), 1)
 
        notification = Notification.query().all()[0]
 

	
 
        self.assertEqual(notification.type_, Notification.TYPE_CHANGESET_COMMENT)
 
        self.assertTrue((u'/vcs_test_hg/changeset/27cd5cce30c96924232df'
 
                          'fcd24178a07ffeb5dfc#comment-1') in notification.subject)
 
        ID = ChangesetComment.query().first().comment_id
 
        self.assertEqual(notification.type_,
 
                         Notification.TYPE_CHANGESET_COMMENT)
 
        sbj = (u'/vcs_test_hg/changeset/'
 
               '27cd5cce30c96924232dffcd24178a07ffeb5dfc#comment-%s' % ID)
 
        print "%s vs %s" % (sbj, notification.subject)
 
        self.assertTrue(sbj in notification.subject)
 

	
 
    def test_create_inline(self):
 
        self.log_user()
 
@@ -57,7 +63,7 @@ class TestChangeSetCommentrController(Te
 
        f_path = 'vcs/web/simplevcs/views/repository.py'
 
        line = 'n1'
 

	
 
        params = {'text':text, 'f_path':f_path, 'line':line}
 
        params = {'text': text, 'f_path': f_path, 'line': line}
 
        response = self.app.post(url(controller='changeset', action='comment',
 
                                     repo_name=HG_REPO, revision=rev),
 
                                     params=params)
 
@@ -76,11 +82,16 @@ class TestChangeSetCommentrController(Te
 
                        '''repositorypy">''' in response.body)
 

	
 
        self.assertEqual(Notification.query().count(), 1)
 
        notification = Notification.query().all()[0]
 
        self.assertEqual(ChangesetComment.query().count(), 1)
 

	
 
        self.assertEqual(notification.type_, Notification.TYPE_CHANGESET_COMMENT)
 
        self.assertTrue((u'/vcs_test_hg/changeset/27cd5cce30c96924232df'
 
                          'fcd24178a07ffeb5dfc#comment-1') in notification.subject)
 
        notification = Notification.query().all()[0]
 
        ID = ChangesetComment.query().first().comment_id
 
        self.assertEqual(notification.type_,
 
                         Notification.TYPE_CHANGESET_COMMENT)
 
        sbj = (u'/vcs_test_hg/changeset/'
 
               '27cd5cce30c96924232dffcd24178a07ffeb5dfc#comment-%s' % ID)
 
        print "%s vs %s" % (sbj, notification.subject)
 
        self.assertTrue(sbj in notification.subject)
 

	
 
    def test_create_with_mention(self):
 
        self.log_user()
 
@@ -103,7 +114,6 @@ class TestChangeSetCommentrController(Te
 
        self.assertTrue('''<div class="comments-number">%s '''
 
                        '''comment(s) (0 inline)</div>''' % 1 in response.body)
 

	
 

	
 
        self.assertEqual(Notification.query().count(), 2)
 
        users = [x.user.username for x in UserNotification.query().all()]
 

	
 
@@ -115,7 +125,7 @@ class TestChangeSetCommentrController(Te
 
        rev = '27cd5cce30c96924232dffcd24178a07ffeb5dfc'
 
        text = u'CommentOnRevision'
 

	
 
        params = {'text':text}
 
        params = {'text': text}
 
        response = self.app.post(url(controller='changeset', action='comment',
 
                                     repo_name=HG_REPO, revision=rev),
 
                                     params=params)
 
@@ -124,7 +134,6 @@ class TestChangeSetCommentrController(Te
 
        self.assertEqual(len(comments), 1)
 
        comment_id = comments[0].comment_id
 

	
 

	
 
        self.app.delete(url(controller='changeset',
 
                                    action='delete_comment',
 
                                    repo_name=HG_REPO,
rhodecode/tests/functional/test_login.py
Show inline comments
 
# -*- coding: utf-8 -*-
 
from rhodecode.tests import *
 
from rhodecode.model.db import User, Notification
 
from rhodecode.lib import generate_api_key
 
from rhodecode.lib.utils2 import generate_api_key
 
from rhodecode.lib.auth import check_password
 
from rhodecode.model.meta import Session
 

	
 

	
 
class TestLoginController(TestController):
 

	
 
    def tearDown(self):
rhodecode/tests/test_libs.py
Show inline comments
 
@@ -65,22 +65,20 @@ TEST_URLS += [
 

	
 
class TestLibs(unittest.TestCase):
 

	
 

	
 
    def test_uri_filter(self):
 
        from rhodecode.lib import uri_filter
 
        from rhodecode.lib.utils2 import uri_filter
 

	
 
        for url in TEST_URLS:
 
            self.assertEqual(uri_filter(url[0]), url[1])
 

	
 
    def test_credentials_filter(self):
 
        from rhodecode.lib import credentials_filter
 
        from rhodecode.lib.utils2 import credentials_filter
 

	
 
        for url in TEST_URLS:
 
            self.assertEqual(credentials_filter(url[0]), url[2])
 

	
 

	
 
    def test_str2bool(self):
 
        from rhodecode.lib import str2bool
 
        from rhodecode.lib.utils2 import str2bool
 
        test_cases = [
 
            ('t', True),
 
            ('true', True),
 
@@ -103,9 +101,8 @@ class TestLibs(unittest.TestCase):
 
        for case in test_cases:
 
            self.assertEqual(str2bool(case[0]), case[1])
 

	
 

	
 
    def test_mention_extractor(self):
 
        from rhodecode.lib import extract_mentioned_users
 
        from rhodecode.lib.utils2 import extract_mentioned_users
 
        sample = ("@first hi there @marcink here's my email marcin@email.com "
 
                  "@lukaszb check it pls @ ttwelve @D[] @one@two@three "
 
                  "@MARCIN    @maRCiN @2one_more22")

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

0 comments (0 inline, 0 general)