# -*- coding: utf-8 -*-
"""
rhodecode.controllers.admin.admin
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Controller for Admin panel of Rhodecode
:created_on: Apr 7, 2010
:author: marcink
:copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
:license: GPLv3, see COPYING for more details.
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; version 2
# of the License or (at your opinion) any later version of the license.
#
# 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, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
# MA 02110-1301, USA.
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import logging
from pylons import request, tmpl_context as c
from rhodecode.lib.base import BaseController, render
from rhodecode.model.db import UserLog
from webhelpers.paginate import Page
from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator
log = logging.getLogger(__name__)
class AdminController(BaseController):
@LoginRequired()
def __before__(self):
super(AdminController, self).__before__()
@HasPermissionAllDecorator('hg.admin')
def index(self):
users_log = self.sa.query(UserLog).order_by(UserLog.action_date.desc())
p = int(request.params.get('page', 1))
c.users_log = Page(users_log, page=p, items_per_page=10)
c.log_data = render('admin/admin_log.html')
if request.params.get('partial'):
rhodecode.controllers.admin.ldap_settings
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ldap controller for RhodeCode
:created_on: Nov 26, 2010
import formencode
import traceback
from formencode import htmlfill
from pylons import request, response, session, tmpl_context as c, url
from pylons.controllers.util import abort, redirect
from pylons.i18n.translation import _
from rhodecode.lib import helpers as h
from rhodecode.lib.auth_ldap import LdapImportError
from rhodecode.model.settings import SettingsModel
from rhodecode.model.forms import LdapSettingsForm
from sqlalchemy.exc import DatabaseError
class LdapSettingsController(BaseController):
rhodecode.controllers.admin.permissions
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
permissions controller for Rhodecode
:created_on: Apr 27, 2010
from pylons import request, session, tmpl_context as c, url
from rhodecode.model.forms import LdapSettingsForm, DefaultPermissionsForm
from rhodecode.model.permission import PermissionModel
from rhodecode.model.user import UserModel
class PermissionsController(BaseController):
"""REST Controller styled on the Atom Publishing Protocol"""
# To properly map this controller, ensure your config/routing.py
# file has a resource setup:
# map.resource('permission', 'permissions')
rhodecode.controllers.admin.repos
Admin controller for RhodeCode
:copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
from operator import itemgetter
from paste.httpexceptions import HTTPInternalServerError
from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator, \
HasPermissionAnyDecorator
from rhodecode.lib.utils import invalidate_cache, action_logger
from rhodecode.model.db import User
from rhodecode.model.forms import RepoForm
from rhodecode.model.scm import ScmModel
from rhodecode.model.repo import RepoModel
rhodecode.controllers.admin.settings
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
settings controller for rhodecode admin
:created_on: Jul 14, 2010
from pylons import request, session, tmpl_context as c, url, app_globals as g, \
config
HasPermissionAnyDecorator, NotAnonymous
from rhodecode.lib.celerylib import tasks, run_task
from rhodecode.lib.utils import repo2db_mapper, invalidate_cache, \
set_rhodecode_config
from rhodecode.model.db import RhodeCodeUi, Repository
from rhodecode.model.forms import UserForm, ApplicationSettingsForm, \
ApplicationUiSettingsForm
from sqlalchemy import func
rhodecode.controllers.admin.users
Users crud controller for pylons
:created_on: Apr 4, 2010
from rhodecode.lib.exceptions import *
from rhodecode.model.forms import UserForm
class UsersController(BaseController):
rhodecode.controllers.branches
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
branches controller for rhodecode
:created_on: Apr 21, 2010
from pylons import tmpl_context as c
from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
from rhodecode.lib.utils import OrderedDict
class BranchesController(BaseController):
@HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
'repository.admin')
super(BranchesController, self).__before__()
hg_model = ScmModel()
c.repo_info = hg_model.get_repo(c.repo_name)
c.repo_branches = OrderedDict()
rhodecode.controllers.changelog
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
changelog controller for rhodecode
try:
import json
except ImportError:
#python 2.5 compatibility
import simplejson as json
from mercurial.graphmod import colored, CHANGESET, revisions as graph_rev
from pylons import request, session, tmpl_context as c
class ChangelogController(BaseController):
rhodecode.controllers.changeset
changeset controller for pylons
:created_on: Apr 25, 2010
from pylons import tmpl_context as c, url, request, response
from pylons.controllers.util import redirect
import rhodecode.lib.helpers as h
from rhodecode.lib.utils import EmptyChangeset
from vcs.exceptions import RepositoryError, ChangesetError
from vcs.nodes import FileNode
from vcs.utils import diffs as differ
class ChangesetController(BaseController):
package.rhodecode.controllers.error
~~~~~~~~~~~~~~
RhodeCode error controller
:created_on: Dec 8, 2010
import os
import cgi
import paste.fileapp
from pylons import tmpl_context as c, request, config
from pylons.middleware import media_path
class ErrorController(BaseController):
"""Generates error documents as and when they are required.
The ErrorDocuments middleware forwards to ErrorController when error
related status codes are returned from the application.
This behavior can be altered by changing the parameters to the
ErrorDocuments middleware in your config/middleware.py file.
rhodecode.controllers.feed
~~~~~~~~~~~~~~~~~~~~~~~~~~
Feed controller for rhodecode
:created_on: Apr 23, 2010
from pylons import url, response
from rhodecode.lib.base import BaseController
from webhelpers.feedgenerator import Atom1Feed, Rss201rev2Feed
class FeedController(BaseController):
super(FeedController, self).__before__()
#common values for feeds
self.description = 'Changes on %s repository'
self.title = "%s feed"
self.language = 'en-us'
rhodecode.controllers.files
~~~~~~~~~~~~~~~~~~~~~~~~~~~
Files controller for RhodeCode
import tempfile
from mercurial import archival
from vcs.exceptions import RepositoryError, ChangesetError, \
ChangesetDoesNotExistError, EmptyRepositoryError
class FilesController(BaseController):
rhodecode.controllers.home
Home controller for Rhodecode
:created_on: Feb 18, 2010
from pylons import tmpl_context as c, request
from rhodecode.lib.auth import LoginRequired
class HomeController(BaseController):
super(HomeController, self).__before__()
sortables = ['name', 'description', 'last_change', 'tip', 'owner']
current_sort = request.GET.get('sort', 'name')
current_sort_slug = current_sort.replace('-', '')
if current_sort_slug not in sortables:
rhodecode.controllers.journal
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Journal controller for pylons
:created_on: Nov 21, 2010
from paste.httpexceptions import HTTPInternalServerError, HTTPBadRequest
from sqlalchemy import or_
from rhodecode.lib.auth import LoginRequired, NotAnonymous
from rhodecode.lib.helpers import get_token
from rhodecode.model.db import UserLog, UserFollowing
class JournalController(BaseController):
@NotAnonymous()
super(JournalController, self).__before__()
rhodecode.controllers.login
Login controller for rhodeocode
:created_on: Apr 22, 2010
from rhodecode.lib.auth import AuthUser, HasPermissionAnyDecorator
from rhodecode.model.forms import LoginForm, RegisterForm, PasswordResetForm
class LoginController(BaseController):
super(LoginController, self).__before__()
rhodecode.controllers.search
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Search controller for rhodecode
:created_on: Aug 7, 2010
from pylons import request, response, config, session, tmpl_context as c, url
from rhodecode.lib.indexers import SCHEMA, IDX_NAME, ResultWrapper
from webhelpers.util import update_params
from whoosh.index import open_dir, EmptyIndexError
from whoosh.qparser import QueryParser, QueryParserError
from whoosh.query import Phrase
class SearchController(BaseController):
rhodecode.controllers.settings
Settings controller for rhodecode
:created_on: Jun 30, 2010
from pylons import tmpl_context as c, request, url
from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAllDecorator, \
HasRepoPermissionAnyDecorator, NotAnonymous
from rhodecode.model.forms import RepoSettingsForm, RepoForkForm
class SettingsController(BaseController):
rhodecode.controllers.shortlog
Shortlog controller for rhodecode
:created_on: Apr 18, 2010
class ShortlogController(BaseController):
super(ShortlogController, self).__before__()
repo = ScmModel().get_repo(c.repo_name)
rhodecode.controllers.summary
Summary controller for Rhodecode
import calendar
from time import mktime
from datetime import datetime, timedelta, date
from vcs.exceptions import ChangesetError
from rhodecode.model.db import Statistics
from rhodecode.lib.utils import OrderedDict, EmptyChangeset
from rhodecode.lib.celerylib import run_task
from rhodecode.lib.celerylib.tasks import get_commits_stats
rhodecode.controllers.tags
Tags controller for rhodecode
class TagsController(BaseController):
super(TagsController, self).__before__()
c.repo_tags = OrderedDict()
for name, hash_ in c.repo_info.tags.items():
rhodecode.lib.auth
~~~~~~~~~~~~~~~~~~
authentication and permission libraries
:copyright: (c) 2010 by marcink.
:license: LICENSE_NAME, see LICENSE_FILE for more details.
import random
from decorator import decorator
from pylons import config, session, url, request
from rhodecode import __platform__, PLATFORM_WIN, PLATFORM_OTHERS
if __platform__ in PLATFORM_WIN:
from hashlib import sha256
if __platform__ in PLATFORM_OTHERS:
import bcrypt
from rhodecode.lib import str2bool
from rhodecode.lib.exceptions import LdapPasswordError, LdapUsernameError
from rhodecode.lib.utils import get_repo_slug
from rhodecode.lib.auth_ldap import AuthLdap
from rhodecode.model import meta
#!/usr/bin/env python
# encoding: utf-8
# ldap authentication lib
# Copyright (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
Created on Nov 17, 2010
@author: marcink
import ldap
pass
class AuthLdap(object):
def __init__(self, server, base_dn, port=389, bind_dn='', bind_pass='',
use_ldaps=False, ldap_version=3):
self.ldap_version = ldap_version
if use_ldaps:
port = port or 689
self.LDAP_USE_LDAPS = use_ldaps
# mercurial repository backup manager
# Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
Created on Feb 28, 2010
Mercurial repositories backup manager
import tarfile
import datetime
import sys
import subprocess
logging.basicConfig(level=logging.DEBUG,
format="%(asctime)s %(levelname)-5.5s %(message)s")
class BackupManager(object):
def __init__(self, repos_location, rsa_key, backup_server):
today = datetime.datetime.now().weekday() + 1
self.backup_file_name = "mercurial_repos.%s.tar.gz" % today
self.id_rsa_path = self.get_id_rsa(rsa_key)
self.repos_path = self.get_repos_path(repos_location)
package.rhodecode.lib.celerylib.__init__
celery libs for RhodeCode
:created_on: Nov 27, 2010
import socket
from hashlib import md5
from vcs.utils.lazy import LazyProperty
from rhodecode.lib.pidlock import DaemonLock, LockHeld
from pylons import config
def str2bool(v):
return v.lower() in ["yes", "true", "t", "1"] if v else None
CELERY_ON = str2bool(config['app_conf'].get('use_celery'))
except KeyError:
CELERY_ON = False
rhodecode.lib.celerylib.tasks
RhodeCode task modules, containing all task that suppose to be run
by celery daemon
:created_on: Oct 6, 2010
from celery.decorators import task
from rhodecode.lib.celerylib import run_task, locked_task, str2bool
from rhodecode.lib.helpers import person
from rhodecode.lib.smtp_mailer import SmtpMailer
from rhodecode.lib.utils import OrderedDict, add_cache
from rhodecode.model import init_model
from rhodecode.model.db import RhodeCodeUi
from vcs.backends import get_repo
from sqlalchemy import engine_from_config
rhodecode.lib.dbmigrate.__init__
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Database migration modules
:created_on: Dec 11, 2010
from rhodecode.lib.utils import BasePasterCommand, Command, add_cache
from rhodecode.lib.db_manage import DbManage
class UpgradeDb(BasePasterCommand):
"""Command used for paster to upgrade our database to newer version
max_args = 1
min_args = 1
usage = "CONFIG_FILE"
summary = "Upgrades current db to newer version given configuration file"
group_name = "RhodeCode"
parser = Command.standard_parser(verbose=True)
def command(self):
rhodecode.lib.dbmigrate.versions.__init__
Package containing new versions of database models
# Custom Exceptions modules
Custom Exceptions modules
class LdapUsernameError(Exception):pass
class LdapPasswordError(Exception):pass
class LdapConnectionError(Exception):pass
class LdapImportError(Exception):pass
class DefaultUserException(Exception):pass
class UserOwnsReposException(Exception):pass
rhodecode.lib.hooks
~~~~~~~~~~~~~~~~~~~
Hooks runned by rhodecode
:created_on: Aug 6, 2010
import getpass
from mercurial.cmdutil import revrange
from mercurial.node import nullrev
from rhodecode.lib.utils import action_logger
def repo_size(ui, repo, hooktype=None, **kwargs):
"""Presents size of repository after push
:param ui:
:param repo:
:param hooktype:
if hooktype != 'changegroup':
return False
size_hg, size_root = 0, 0
for path, dirs, files in os.walk(repo.root):
if path.find('.hg') != -1:
for f in files:
# whoosh indexer daemon for rhodecode
Created on Jan 26, 2010
A deamon will read from task table and run tasks
from os.path import dirname as dn
from os.path import join as jn
#to get the rhodecode import
project_path = dn(dn(dn(dn(os.path.realpath(__file__)))))
sys.path.append(project_path)
from rhodecode.lib.helpers import safe_unicode
from whoosh.index import create_in, open_dir
from shutil import rmtree
from rhodecode.lib.indexers import INDEX_EXTENSIONS, SCHEMA, IDX_NAME
from vcs.exceptions import ChangesetError, RepositoryError
rhodecode.lib.middleware.https_fixup
middleware to handle https correctly
:created_on: May 23, 2010
class HttpsFixup(object):
def __init__(self, app, config):
self.application = app
self.config = config
def __call__(self, environ, start_response):
self.__fixup(environ)
return self.application(environ, start_response)
def __fixup(self, environ):
"""Function to fixup the environ as needed. In order to use this
middleware you should set this header inside your
proxy ie. nginx, apache etc.
proto = environ.get('HTTP_X_URL_SCHEME')
if str2bool(self.config.get('force_https')):
proto = 'https'
if proto == 'https':
# middleware to handle git api calls
Created on 2010-04-28
SimpleGit middleware for handling git protocol request (push/clone etc.)
It's implemented with basic auth function
from dulwich import server as dulserver
class SimpleGitUploadPackHandler(dulserver.UploadPackHandler):
def handle(self):
write = lambda x: self.proto.write_sideband(1, x)
graph_walker = dulserver.ProtocolGraphWalker(self, self.repo.object_store,
self.repo.get_peeled)
objects_iter = self.repo.fetch_objects(
graph_walker.determine_wants, graph_walker, self.progress,
get_tagged=self.get_tagged)
# Do they want any objects?
if len(objects_iter) == 0:
return
# middleware to handle mercurial api calls
SimpleHG middleware for handling mercurial protocol request (push/clone etc.)
from mercurial.error import RepoError
from mercurial.hgweb import hgweb
from mercurial.hgweb.request import wsgiapplication
from paste.auth.basic import AuthBasicAuthenticator
from paste.httpheaders import REMOTE_USER, AUTH_TYPE
from rhodecode.lib.auth import authfunc, HasPermissionAnyMiddleware
from rhodecode.lib.utils import make_ui, invalidate_cache, \
check_repo_fast, ui_sections
from webob.exc import HTTPNotFound, HTTPForbidden, HTTPInternalServerError
def is_mercurial(environ):
rhodecode.lib.utils
Utilities library for RhodeCode
from UserDict import DictMixin
from mercurial import ui, config, hg
import paste
import beaker
from paste.script.command import Command, BadCommand
from vcs.backends.base import BaseChangeset
from rhodecode.model.caching_query import FromCache
from rhodecode.model.db import Repository, User, RhodeCodeUi, UserLog
@@ -7,62 +7,60 @@
:created_on: Nov 25, 2010
:example:
.. code-block:: python
from paste.deploy import appconfig
from rhodecode.config.environment import load_environment
conf = appconfig('config:development.ini', relative_to = './../../')
load_environment(conf.global_conf, conf.local_conf)
engine = engine_from_config(config, 'sqlalchemy.')
init_model(engine)
# RUN YOUR CODE HERE
def init_model(engine):
"""Initializes db session, bind the engine with the metadata,
Call this before using any of the tables or classes in the model, preferably
once in application start
:param engine: engine to bind to
log.info("initializing db models for %s", engine)
meta.Base.metadata.bind = engine
class BaseModel(object):
"""Base Model for all RhodeCode models, it adds sql alchemy session
into instance of model
:param sa: If passed it reuses this session instead of creating a new one
def __init__(self, sa=None):
if sa is not None:
rhodecode.model.db
Database Models for RhodeCode
:created_on: Apr 08, 2010
from datetime import date
from sqlalchemy import *
from sqlalchemy.orm import relationship, backref
from sqlalchemy.orm.interfaces import MapperExtension
from rhodecode.model.meta import Base, Session
class RhodeCodeSettings(Base):
__tablename__ = 'rhodecode_settings'
__table_args__ = (UniqueConstraint('app_settings_name'), {'useexisting':True})
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)
app_settings_value = Column("app_settings_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
def __init__(self, k='', v=''):
self.app_settings_name = k
self.app_settings_value = v
rhodecode.model.permission
permissions model for RhodeCode
:created_on: Aug 20, 2010
from rhodecode.model import BaseModel
from rhodecode.model.db import User, Permission, UserToPerm, RepoToPerm
class PermissionModel(BaseModel):
"""Permissions model for RhodeCode
def get_permission(self, permission_id, cache=False):
"""Get's permissions by id
:param permission_id: id of permission to get from database
:param cache: use Cache for this query
perm = self.sa.query(Permission)
rhodecode.model.repo
~~~~~~~~~~~~~~~~~~~~
Repository model for rhodecode
:created_on: Jun 5, 2010
import shutil
from datetime import datetime
from sqlalchemy.orm import joinedload, make_transient
from vcs.backends import get_backend
from rhodecode.model.db import Repository, RepoToPerm, User, Permission, \
Statistics, RhodeCodeUi
class RepoModel(BaseModel):
@LazyProperty
def repos_path(self):
"""Get's the repositories root path from database
rhodecode.model.settings
~~~~~~~~~~~~~~~~~~~~~~~~
Settings model for RhodeCode
:created on Nov 17, 2010
from rhodecode.model.db import RhodeCodeSettings
class SettingsModel(BaseModel):
Settings model
def get(self, settings_key, cache=False):
r = self.sa.query(RhodeCodeSettings)\
.filter(RhodeCodeSettings.app_settings_name == settings_key).scalar()
if cache:
r = r.options(FromCache("sql_cache_short",
"get_setting_%s" % settings_key))
return r
def get_app_settings(self, cache=False):
"""Get's config from database, each config key is prefixed with
rhodecode.model.user
users model for RhodeCode
:created_on: Apr 9, 2010
from rhodecode.lib.exceptions import DefaultUserException, UserOwnsReposException
class UserModel(BaseModel):
def get(self, user_id, cache=False):
user = self.sa.query(User)
user = user.options(FromCache("sql_cache_short",
"get_user_%s" % user_id))
return user.get(user_id)
Status change: