Changeset - bee56f209c40
[Not reviewed]
Marcin Kuzminski - 15 years ago 2010-11-10 01:14:44
marcin@python-works.com
fixes few bugs
- fixed #53 python2.5 incompatible enumerate calls
- fixed #52 disable mercurial extension for web
- fixed #51 deleting repositories don't delete it's dependent objects
- small css updated
10 files changed with 113 insertions and 80 deletions:
0 comments (0 inline, 0 general)
docs/changelog.rst
Show inline comments
 
.. _changelog:
 

	
 
Changelog
 
=========
 

	
 

	
 
1.0.1 (**2010-11-10**)
 
----------------------
 

	
 
- fixed #53 python2.5 incompatible enumerate calls
 
- fixed #52 disable mercurial extension for web
 
- fixed #51 deleting repositories don't delete it's dependent objects
 
- small css updated
 

	
 

	
 
1.0.0 (**2010-11-02**)
 
----------------------
 

	
 
- security bugfix simplehg wasn't checking for permissions on commands
 
  other than pull or push.
 
- fixed doubled messages after push or pull in admin journal
 
- templating and css corrections, fixed repo switcher on chrome, updated titles
 
- admin menu accessible from options menu on repository view
 
- permissions cached queries
 

	
 
1.0.0rc4  (**2010-10-12**)
 
--------------------------
docs/installation.rst
Show inline comments
 
.. _installation:
 

	
 
Installation
 
============
 

	
 
``RhodeCode`` is written entirely in Python, but in order to use it's full
 
potential there are some third-party requirements. When RhodeCode is used 
 
together with celery You have to install some kind of message broker,
 
together with celery_ You have to install some kind of message broker,
 
recommended one is rabbitmq_ to make the async tasks work.
 

	
 
Of course RhodeCode works in sync mode also, then You don't have to install
 
any third party apps. Celery_ will give You large speed improvement when using
 
many big repositories. If You plan to use it for 2 or 3 small repositories, it
 
many big repositories. If You plan to use it for 5 or 10 small repositories, it
 
will work just fine without celery running.
 
   
 
After You decide to Run it with celery make sure You run celeryd and
 
message broker together with the application.   
 

	
 
Requirements for Celery
 
-----------------------
 

	
 
**Message Broker** 
 

	
 
- preferred is `RabbitMq <http://www.rabbitmq.com/>`_
 
- possible other is `Redis <http://code.google.com/p/redis/>`_
 

	
 
For installation instructions You can visit: 
 
http://ask.github.com/celery/getting-started/index.html
 
It's very nice tutorial how to start celery_ with rabbitmq_
 

	
 
Install from Cheese Shop
 
------------------------
 

	
 
Easiest way to install ``rhodecode`` is to run::
 

	
 
   easy_install rhodecode
 
 easy_install rhodecode
 

	
 
Or::
 

	
 
 pip install rhodecode
 

	
 
If you prefer to install manually simply grab latest release from
 
http://pypi.python.org/pypi/rhodecode, decompres archive and run::
 

	
 
   python setup.py install
 
 python setup.py install
 

	
 

	
 
Step by step installation example
 
---------------------------------
 

	
 

	
 
- Assuming You have installed virtualenv_ create one using. The `--no-site-packages`
 
  will make sure non of Your system libs are linked with this virtualenv_  
 
- Assuming You have installed virtualenv_ create one using. 
 
  The `--no-site-packages` will make sure non of Your system libs are linked 
 
  with this virtualenv_  
 

	
 
::
 

	
 
 virtualenv --no-site-packages /var/www/rhodecode-venv
 

	
 
- this will install new virtualenv_ into `/var/www/rhodecode-venv`. 
 
- Activate the virtualenv_ by running 
 

	
 
::
 

	
 
  source activate /var/www/rhodecode-venv/bin/activate
 
     
docs/setup.rst
Show inline comments
 
@@ -31,40 +31,52 @@ Setting up the application
 

	
 
::
 
 
 
 paster serve production.ini
 
 
 
- This command runs the rhodecode server the app should be available at the 
 
  127.0.0.1:5000. This ip and port is configurable via the production.ini 
 
  file  created in previous step
 
- Use admin account you created to login.
 
- Default permissions on each repository is read, and owner is admin. So 
 
  remember to update these if needed.
 
  
 
Note
 
----
 

	
 
RhodeCode when running without the celery it's running all it's task in sync
 
mode, for first few times when visiting summary page You can notice few
 
slow downs, this is due the statistics building it's cache. After all changesets
 
are parsed it'll take the stats from cache and run much faster. Each summary
 
page display parse at most 250 changesets in order to not stress the cpu, so
 
the full stats are going to be loaded after total_number_of_changesets/250
 
summary page visits.
 

	
 

	
 
    
 
Setting up Whoosh
 
-----------------
 

	
 
- For full text search You can either put crontab entry for
 

	
 
::
 
 
 
 python /var/www/rhodecode/<rhodecode_installation_path>/lib/indexers/daemon.py incremental <put_here_path_to_repos>
 
  
 
When using incremental mode whoosh will check last modification date of each file
 
and add it to reindex if newer file is available. Also indexing daemon checks
 
for removed files and removes them from index. Sometime You might want to rebuild
 
index from scrach, in admin pannel You can check `build from scratch` flag
 
index from scratch, in admin panel You can check `build from scratch` flag
 
and in standalone daemon You can pass `full` instead on incremental to build
 
remove previos index and build new one.
 
remove previous index and build new one.
 

	
 
Nginx virtual host example
 
--------------------------
 

	
 
Sample config for nginx::
 

	
 
 server {
 
    listen          80;
 
    server_name     hg.myserver.com;
 
    access_log      /var/log/nginx/rhodecode.access.log;
 
    error_log       /var/log/nginx/rhodecode.error.log;
 
    location / {
rhodecode/__init__.py
Show inline comments
 
@@ -15,21 +15,21 @@
 
# 
 
# 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.
 
"""
 
Created on April 9, 2010
 
RhodeCode, a web based repository management based on pylons
 
versioning implementation: http://semver.org/
 
@author: marcink
 
"""
 

	
 
VERSION = (1, 0, 0,)
 
VERSION = (1, 0, 1,)
 

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

	
 
def get_version():
 
    """
 
    Returns shorter version (digit parts only) as string.
 
    """
 
    return '.'.join((str(each) for each in VERSION[:3]))
rhodecode/lib/helpers.py
Show inline comments
 
@@ -55,46 +55,46 @@ def recursive_replace(str, replace=' '):
 
    :param str: given string
 
    :param replace:char to find and replace multiple instances
 
        
 
    Examples::
 
    >>> recursive_replace("Mighty---Mighty-Bo--sstones",'-')
 
    'Mighty-Mighty-Bo-sstones'
 
    """
 

	
 
    if str.find(replace * 2) == -1:
 
        return str
 
    else:
 
        str = str.replace(replace * 2, replace)
 
        return recursive_replace(str, replace)  
 
        return recursive_replace(str, replace)
 

	
 
class _ToolTip(object):
 
    
 

	
 
    def __call__(self, tooltip_title, trim_at=50):
 
        """
 
        Special function just to wrap our text into nice formatted autowrapped
 
        text
 
        :param tooltip_title:
 
        """
 
        
 

	
 
        return wrap_paragraphs(escape(tooltip_title), trim_at)\
 
                       .replace('\n', '<br/>')
 
    
 

	
 
    def activate(self):
 
        """
 
        Adds tooltip mechanism to the given Html all tooltips have to have 
 
        set class tooltip and set attribute tooltip_title.
 
        Then a tooltip will be generated based on that
 
        All with yui js tooltip
 
        """
 
        
 

	
 
        js = '''
 
        YAHOO.util.Event.onDOMReady(function(){
 
            function toolTipsId(){
 
                var ids = [];
 
                var tts = YAHOO.util.Dom.getElementsByClassName('tooltip');
 
                
 
                for (var i = 0; i < tts.length; i++) {
 
                    //if element doesn not have and id autgenerate one for tooltip
 
                    
 
                    if (!tts[i].id){
 
                        tts[i].id='tt'+i*100;
 
                    }
 
@@ -181,140 +181,140 @@ class _ToolTip(object):
 

	
 
                        this.cfg.setProperty("xy",xy_pos);
 

	
 
                  });
 
                  
 
            //Mouse out 
 
            myToolTips.contextMouseOutEvent.subscribe(
 
                function(type, args) {
 
                    var context = args[0];
 
                    
 
                });
 
        });
 
        '''         
 
        '''
 
        return literal(js)
 

	
 
tooltip = _ToolTip()
 

	
 
class _FilesBreadCrumbs(object):
 
    
 

	
 
    def __call__(self, repo_name, rev, paths):
 
        url_l = [link_to(repo_name, url('files_home',
 
                                        repo_name=repo_name,
 
                                        revision=rev, f_path=''))]
 
        paths_l = paths.split('/')
 
        
 
        for cnt, p in enumerate(paths_l, 1):
 

	
 
        for cnt, p in enumerate(paths_l):
 
            if p != '':
 
                url_l.append(link_to(p, url('files_home',
 
                                            repo_name=repo_name,
 
                                            revision=rev,
 
                                            f_path='/'.join(paths_l[:cnt]))))
 
                                            f_path='/'.join(paths_l[:cnt + 1]))))
 

	
 
        return literal('/'.join(url_l))
 

	
 
files_breadcrumbs = _FilesBreadCrumbs()
 
class CodeHtmlFormatter(HtmlFormatter):
 

	
 
    def wrap(self, source, outfile):
 
        return self._wrap_div(self._wrap_pre(self._wrap_code(source)))
 

	
 
    def _wrap_code(self, source):
 
        for cnt, it in enumerate(source, 1):
 
        for cnt, it in enumerate(source):
 
            i, t = it
 
            t = '<div id="#S-%s">%s</div>' % (cnt, t)
 
            t = '<div id="#S-%s">%s</div>' % (cnt + 1, t)
 
            yield i, t
 
def pygmentize(filenode, **kwargs):
 
    """
 
    pygmentize function using pygments
 
    :param filenode:
 
    """
 
    return literal(code_highlight(filenode.content,
 
                                  filenode.lexer, CodeHtmlFormatter(**kwargs)))
 

	
 
def pygmentize_annotation(filenode, **kwargs):
 
    """
 
    pygmentize function for annotation
 
    :param filenode:
 
    """
 
    
 

	
 
    color_dict = {}
 
    def gen_color():
 
        """generator for getting 10k of evenly distibuted colors using hsv color
 
        and golden ratio.
 
        """        
 
        """
 
        import colorsys
 
        n = 10000
 
        golden_ratio = 0.618033988749895
 
        h = 0.22717784590367374
 
        #generate 10k nice web friendly colors in the same order
 
        for c in xrange(n):
 
            h += golden_ratio
 
            h %= 1
 
            HSV_tuple = [h, 0.95, 0.95]
 
            RGB_tuple = colorsys.hsv_to_rgb(*HSV_tuple)
 
            yield map(lambda x:str(int(x * 256)), RGB_tuple)           
 
            yield map(lambda x:str(int(x * 256)), RGB_tuple)
 

	
 
    cgenerator = gen_color()
 
        
 

	
 
    def get_color_string(cs):
 
        if color_dict.has_key(cs):
 
            col = color_dict[cs]
 
        else:
 
            col = color_dict[cs] = cgenerator.next()
 
        return "color: rgb(%s)! important;" % (', '.join(col))
 
        
 

	
 
    def url_func(changeset):
 
        tooltip_html = "<div style='font-size:0.8em'><b>Author:</b>" + \
 
        " %s<br/><b>Date:</b> %s</b><br/><b>Message:</b> %s<br/></div>" 
 
        
 
        " %s<br/><b>Date:</b> %s</b><br/><b>Message:</b> %s<br/></div>"
 

	
 
        tooltip_html = tooltip_html % (changeset.author,
 
                                               changeset.date,
 
                                               tooltip(changeset.message))
 
        lnk_format = 'r%-5s:%s' % (changeset.revision,
 
                                 changeset.short_id)
 
        uri = link_to(
 
                lnk_format,
 
                url('changeset_home', repo_name=changeset.repository.name,
 
                    revision=changeset.short_id),
 
                style=get_color_string(changeset.short_id),
 
                class_='tooltip',
 
                tooltip_title=tooltip_html
 
              )
 
        
 

	
 
        uri += '\n'
 
        return uri   
 
        return uri
 
    return literal(annotate_highlight(filenode, url_func, **kwargs))
 
      
 

	
 
def repo_name_slug(value):
 
    """Return slug of name of repository
 
    This function is called on each creation/modification
 
    of repository to prevent bad names in repo
 
    """
 
    slug = remove_formatting(value)
 
    slug = strip_tags(slug)
 
    
 

	
 
    for c in """=[]\;'"<>,/~!@#$%^&*()+{}|: """:
 
        slug = slug.replace(c, '-')
 
    slug = recursive_replace(slug, '-')
 
    slug = collapse(slug, '-')
 
    return slug
 

	
 
def get_changeset_safe(repo, rev):
 
    from vcs.backends.base import BaseRepository
 
    from 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()
 
    return cs
 

	
 

	
 
flash = _Flash()
 

	
 

	
 
#===============================================================================
 
@@ -349,35 +349,35 @@ HasRepoPermissionAny, HasRepoPermissionA
 
# GRAVATAR URL
 
#===============================================================================
 
import hashlib
 
import urllib
 
from pylons import request
 

	
 
def gravatar_url(email_address, size=30):
 
    ssl_enabled = 'https' == request.environ.get('HTTP_X_URL_SCHEME')
 
    default = 'identicon'
 
    baseurl_nossl = "http://www.gravatar.com/avatar/"
 
    baseurl_ssl = "https://secure.gravatar.com/avatar/"
 
    baseurl = baseurl_ssl if ssl_enabled else baseurl_nossl
 
        
 
    
 

	
 

	
 
    # construct the url
 
    gravatar_url = baseurl + hashlib.md5(email_address.lower()).hexdigest() + "?"
 
    gravatar_url += urllib.urlencode({'d':default, 's':str(size)})
 

	
 
    return gravatar_url
 

	
 
def safe_unicode(str):
 
    """safe unicode function. In case of UnicodeDecode error we try to return
 
    unicode with errors replace, if this failes we return unicode with 
 
    string_escape decoding """
 
    
 

	
 
    try:
 
        u_str = unicode(str)
 
    except UnicodeDecodeError:
 
        try:
 
            u_str = unicode(str, 'utf-8', 'replace')
 
        except UnicodeDecodeError:
 
            #incase we have a decode error just represent as byte string
 
            u_str = unicode(str(str).encode('string_escape'))
 
        
 

	
 
    return u_str
rhodecode/lib/utils.py
Show inline comments
 
@@ -212,24 +212,29 @@ def make_ui(read_from='file', path=None,
 

	
 
    if read_from == 'file':
 
        if not os.path.isfile(path):
 
            log.warning('Unable to read config file %s' % path)
 
            return False
 
        log.debug('reading hgrc from %s', path)
 
        cfg = config.config()
 
        cfg.read(path)
 
        for section in ui_sections:
 
            for k, v in cfg.items(section):
 
                baseui.setconfig(section, k, v)
 
                log.debug('settings ui from file[%s]%s:%s', section, k, v)
 

	
 
        for k, v in baseui.configitems('extensions'):
 
            baseui.setconfig('extensions', k, '0')
 
        #just enable mq
 
        baseui.setconfig('extensions', 'mq', '1')
 
        if checkpaths:check_repo_dir(cfg.items('paths'))
 

	
 

	
 
    elif read_from == 'db':
 
        hg_ui = get_hg_ui_cached()
 
        for ui_ in hg_ui:
 
            if ui_.ui_active:
 
                log.debug('settings ui from db[%s]%s:%s', ui_.ui_section, ui_.ui_key, ui_.ui_value)
 
                baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
 

	
 

	
 
    return baseui
rhodecode/model/db.py
Show inline comments
 
@@ -13,127 +13,129 @@ class RhodeCodeSettings(Base):
 
    app_settings_id = Column("app_settings_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=True)
 
    app_settings_name = Column("app_settings_name", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
 
    app_settings_value = Column("app_settings_value", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
 

	
 
class RhodeCodeUi(Base):
 
    __tablename__ = 'rhodecode_ui'
 
    __table_args__ = {'useexisting':True}
 
    ui_id = Column("ui_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=True)
 
    ui_section = Column("ui_section", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
 
    ui_key = Column("ui_key", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
 
    ui_value = Column("ui_value", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
 
    ui_active = Column("ui_active", BOOLEAN(), nullable=True, unique=None, default=True)
 
    
 
    
 
class User(Base): 
 

	
 

	
 
class User(Base):
 
    __tablename__ = 'users'
 
    __table_args__ = (UniqueConstraint('username'), UniqueConstraint('email'), {'useexisting':True})
 
    user_id = Column("user_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=True)
 
    username = Column("username", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
 
    password = Column("password", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
 
    active = Column("active", BOOLEAN(), nullable=True, unique=None, default=None)
 
    admin = Column("admin", BOOLEAN(), nullable=True, unique=None, default=False)
 
    name = Column("name", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
 
    lastname = Column("lastname", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
 
    email = Column("email", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
 
    last_login = Column("last_login", DATETIME(timezone=False), nullable=True, unique=None, default=None)
 
    
 

	
 
    user_log = relation('UserLog')
 
    user_perms = relation('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id")
 
    
 

	
 
    @LazyProperty
 
    def full_contact(self):
 
        return '%s %s <%s>' % (self.name, self.lastname, self.email)
 
        
 

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

	
 
    def update_lastlogin(self):
 
        """Update user lastlogin"""
 
        import datetime
 
        
 

	
 
        try:
 
            session = Session.object_session(self)
 
            self.last_login = datetime.datetime.now()
 
            session.add(self)
 
            session.commit()
 
            log.debug('updated user %s lastlogin', self.username)
 
        except Exception:
 
            session.rollback()        
 
    
 
      
 
class UserLog(Base): 
 
            session.rollback()
 

	
 

	
 
class UserLog(Base):
 
    __tablename__ = 'user_logs'
 
    __table_args__ = {'useexisting':True}
 
    user_log_id = Column("user_log_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=True)
 
    user_id = Column("user_id", INTEGER(), ForeignKey(u'users.user_id'), nullable=False, unique=None, default=None)
 
    repository_id = Column("repository_id", INTEGER(length=None, convert_unicode=False, assert_unicode=None), ForeignKey(u'repositories.repo_id'), nullable=False, unique=None, default=None)
 
    repository_name = Column("repository_name", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
 
    user_ip = Column("user_ip", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None) 
 
    user_ip = Column("user_ip", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
 
    action = Column("action", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
 
    action_date = Column("action_date", DATETIME(timezone=False), nullable=True, unique=None, default=None)
 
    revision = Column('revision', TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
 
    
 

	
 
    user = relation('User')
 
    repository = relation('Repository')
 
    
 

	
 
class Repository(Base):
 
    __tablename__ = 'repositories'
 
    __table_args__ = (UniqueConstraint('repo_name'), {'useexisting':True},)
 
    repo_id = Column("repo_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=True)
 
    repo_name = Column("repo_name", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
 
    user_id = Column("user_id", INTEGER(), ForeignKey(u'users.user_id'), nullable=False, unique=False, default=None)
 
    private = Column("private", BOOLEAN(), nullable=True, unique=None, default=None)
 
    description = Column("description", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
 
    fork_id = Column("fork_id", INTEGER(), ForeignKey(u'repositories.repo_id'), nullable=True, unique=False, default=None)
 
    
 

	
 
    user = relation('User')
 
    fork = relation('Repository', remote_side=repo_id)
 
    repo_to_perm = relation('RepoToPerm', cascade='all')
 
    
 
    stats = relation('Statistics', cascade='all')
 

	
 

	
 
    def __repr__(self):
 
        return "<Repository('id:%s:%s')>" % (self.repo_id, self.repo_name)
 
        
 

	
 
class Permission(Base):
 
    __tablename__ = 'permissions'
 
    __table_args__ = {'useexisting':True}
 
    permission_id = Column("permission_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=True)
 
    permission_name = Column("permission_name", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
 
    permission_longname = Column("permission_longname", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
 
    
 

	
 
    def __repr__(self):
 
        return "<Permission('%s:%s')>" % (self.permission_id, self.permission_name)
 

	
 
class RepoToPerm(Base):
 
    __tablename__ = 'repo_to_perm'
 
    __table_args__ = (UniqueConstraint('user_id', 'repository_id'), {'useexisting':True})
 
    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(u'users.user_id'), nullable=False, unique=None, default=None)
 
    permission_id = Column("permission_id", INTEGER(), ForeignKey(u'permissions.permission_id'), nullable=False, unique=None, default=None)
 
    repository_id = Column("repository_id", INTEGER(), ForeignKey(u'repositories.repo_id'), nullable=False, unique=None, default=None) 
 
    
 
    repository_id = Column("repository_id", INTEGER(), ForeignKey(u'repositories.repo_id'), nullable=False, unique=None, default=None)
 

	
 
    user = relation('User')
 
    permission = relation('Permission')
 
    repository = relation('Repository')
 

	
 
class UserToPerm(Base):
 
    __tablename__ = 'user_to_perm'
 
    __table_args__ = (UniqueConstraint('user_id', 'permission_id'), {'useexisting':True})
 
    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(u'users.user_id'), nullable=False, unique=None, default=None)
 
    permission_id = Column("permission_id", INTEGER(), ForeignKey(u'permissions.permission_id'), nullable=False, unique=None, default=None)
 
    
 

	
 
    user = relation('User')
 
    permission = relation('Permission')
 

	
 
class Statistics(Base):
 
    __tablename__ = 'statistics'
 
    __table_args__ = (UniqueConstraint('repository_id'), {'useexisting':True})
 
    stat_id = Column("stat_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=True)
 
    repository_id = Column("repository_id", INTEGER(), ForeignKey(u'repositories.repo_id'), nullable=False, unique=True, default=None)
 
    stat_on_revision = Column("stat_on_revision", INTEGER(), nullable=False)
 
    commit_activity = Column("commit_activity", BLOB(), nullable=False)#JSON data
 
    commit_activity_combined = Column("commit_activity_combined", BLOB(), nullable=False)#JSON data
 
    languages = Column("languages", BLOB(), nullable=False)#JSON data
 
    
 
    repository = relation('Repository')
 

	
 
    repository = relation('Repository', single_parent=True)
 

	
rhodecode/public/css/style.css
Show inline comments
 
@@ -261,25 +261,25 @@ color:#FFF;
 
font-size:14px;
 
text-transform:uppercase;
 
margin:13px 0 0 13px;
 
padding:0;
 
}
 
 
#header #header-inner #logo a {
 
color:#fff;
 
text-decoration:none;
 
}
 
 
#header #header-inner #logo a:hover {
 
color:#dabf29;
 
color:#bfe3ff;
 
}
 
 
#header #header-inner #quick,#header #header-inner #quick ul {
 
position:relative;
 
float:right;
 
list-style-type:none;
 
list-style-position:outside;
 
margin:10px 5px 0 0;
 
padding:0;
 
}
 
 
#header #header-inner #quick li {
 
@@ -410,25 +410,25 @@ min-width:167px;
 
margin:0;
 
padding:12px 9px 7px 24px;
 
}
 
 
#header #header-inner #quick li ul li a.public_repo,#header #header-inner #quick li ul li a.public_repo:hover {
 
background:url("../images/icons/lock_open.png") no-repeat scroll 4px 9px #FFF;
 
min-width:167px;
 
margin:0;
 
padding:12px 9px 7px 24px;
 
}
 
 
#header #header-inner #quick li ul li a.repos,#header #header-inner #quick li ul li a.repos:hover {
 
background:url("../images/icons/folder_edit.png") no-repeat scroll 4px 9px #FFF;
 
background:url("../images/icons/database_edit.png") no-repeat scroll 4px 9px #FFF;
 
width:167px;
 
margin:0;
 
padding:12px 9px 7px 24px;
 
}
 
 
#header #header-inner #quick li ul li a.users,#header #header-inner #quick li ul li a.users:hover {
 
background:#FFF url("../images/icons/user_edit.png") no-repeat 4px 9px;
 
width:167px;
 
margin:0;
 
padding:12px 9px 7px 24px;
 
}
 
 
@@ -1001,25 +1001,25 @@ padding:6px 12px;
 
}
 
 
#content div.box table {
 
width:100%;
 
border-collapse:collapse;
 
margin:0;
 
padding:0;
 
}
 
 
#content div.box table th {
 
background:#eee;
 
border-bottom:1px solid #ddd;
 
padding:10px;
 
padding:5px 0px 5px 5px;
 
}
 
 
#content div.box table th.left {
 
text-align:left;
 
}
 
 
#content div.box table th.right {
 
text-align:right;
 
}
 
 
#content div.box table th.center {
 
text-align:center;
 
@@ -1384,25 +1384,24 @@ padding-bottom:4px;
 
 
.trending_language_tbl,.trending_language_tbl td {
 
border:0 !important;
 
margin:0 !important;
 
padding:0 !important;
 
}
 
 
.trending_language {
 
background-color:#003367;
 
color:#FFF;
 
display:block;
 
min-width:20px;
 
max-width:400px;
 
text-decoration:none;
 
height:12px;
 
margin-bottom:4px;
 
margin-left:5px;
 
white-space:pre;
 
padding:3px;
 
}
 
 
h3.files_location {
 
font-size:1.8em;
 
font-weight:700;
 
border-bottom:none !important;
 
@@ -1533,43 +1532,45 @@ background:#BFB;
 
}
 
 
.right .changes .changed {
 
background:#FD8;
 
}
 
 
.right .changes .removed {
 
background:#F88;
 
}
 
 
.right .merge {
 
vertical-align:top;
 
font-size:60%;
 
font-size:0.75em;
 
font-weight:700;
 
}
 
 
.right .parent {
 
font-size:90%;
 
font-family:monospace;
 
}
 
 
.right .logtags .branchtag {
 
background:#FFF url("../images/icons/arrow_branch.png") no-repeat right 6px;
 
display:block;
 
padding:8px 16px 0 0;
 
font-size:0.8em;
 
padding:11px 16px 0 0;
 
}
 
 
.right .logtags .tagtag {
 
background:#FFF url("../images/icons/tag_blue.png") no-repeat right 6px;
 
display:block;
 
padding:6px 18px 0 0;
 
font-size:0.8em;
 
padding:11px 16px 0 0;
 
}
 
 
div.browserblock {
 
overflow:hidden;
 
border:1px solid #ccc;
 
background:#f8f8f8;
 
font-size:100%;
 
line-height:125%;
 
padding:0;
 
}
 
 
div.browserblock .browser-header {
 
@@ -1692,24 +1693,25 @@ z-index:2;
 
 
.yui-tt {
 
visibility:hidden;
 
position:absolute;
 
color:#666;
 
background-color:#FFF;
 
font-family:arial, helvetica, verdana, sans-serif;
 
border:2px solid #003367;
 
font:100% sans-serif;
 
width:auto;
 
opacity:1px;
 
padding:8px;
 
white-space: pre;
 
}
 
 
.ac {
 
vertical-align:top;
 
}
 
 
.ac .yui-ac {
 
position:relative;
 
font-family:arial;
 
font-size:100%;
 
}
 
 
@@ -2015,25 +2017,24 @@ padding:0;
 
float:left;
 
}
 
 
#header #header-inner #quick li:hover ul ul,#header #header-inner #quick li:hover ul ul ul,#header #header-inner #quick li:hover ul ul ul ul,#content #left #menu ul.closed,#content #left #menu li ul.collapsed,.yui-tt-shadow {
 
display:none;
 
}
 
 
#header #header-inner #quick li:hover ul,#header #header-inner #quick li li:hover ul,#header #header-inner #quick li li li:hover ul,#header #header-inner #quick li li li li:hover ul,#content #left #menu ul.opened,#content #left #menu li ul.expanded {
 
display:block;
 
}
 
 
#content div.box div.title ul.links li a:hover,#content div.box div.title ul.links li.ui-tabs-selected a {
 
background:url("../../images/title_tab_selected.png") no-repeat bottom center;
 
color:#bfe3ff;
 
}
 
 
#content div.box ol.lower-roman,#content div.box ol.upper-roman,#content div.box ol.lower-alpha,#content div.box ol.upper-alpha,#content div.box ol.decimal {
 
margin:10px 24px 10px 44px;
 
}
 
 
#content div.box div.form,#content div.box div.table,#content div.box div.traffic {
 
clear:both;
 
overflow:hidden;
 
margin:0;
 
padding:0 20px 10px;
rhodecode/templates/files/files_browser.html
Show inline comments
 
@@ -34,47 +34,47 @@
 
         		<tr class="parity0">
 
	          		<td>		          		
 
	          			${h.link_to('..',h.url('files_home',repo_name=c.repo_name,revision=c.cur_rev,f_path=c.files_list.parent.path),class_="browser-dir")}
 
	          		</td>
 
	          		<td></td>
 
	          		<td></td>
 
	          		<td></td>
 
	          		<td></td>
 
	          		<td></td>
 
				</tr>	          		
 
          		%endif
 
		         	
 
		    %for cnt,node in enumerate(c.files_list,1):
 
				<tr class="parity${cnt%2}">
 
		    %for cnt,node in enumerate(c.files_list):
 
				<tr class="parity${(cnt+1)%2}">
 
		             <td>
 
						${h.link_to(node.name,h.url('files_home',repo_name=c.repo_name,revision=c.cur_rev,f_path=node.path),class_=file_class(node))}
 
		             </td>
 
		             <td>
 
		             %if node.is_file():
 
		             	${h.format_byte_size(node.size,binary=True)}
 
		             %endif	
 
		             </td>
 
		             <td>
 
		              %if node.is_file():
 
		                  ${node.mimetype}
 
		              %endif
 
		             </td>
 
		             <td>
 
		             	%if node.is_file():
 
		             		${node.last_changeset.revision}
 
		             	%endif
 
	             	  %if node.is_file():
 
	             		  ${node.last_changeset.revision}
 
	             	  %endif
 
		             </td>
 
		             <td>
 
		             	%if node.is_file():
 
		             		${h.age(node.last_changeset._ctx.date())} - ${node.last_changeset.date}
 
		             	%endif
 
		              %if node.is_file():
 
		                  ${h.age(node.last_changeset._ctx.date())} - ${node.last_changeset.date}
 
		              %endif
 
		             </td>
 
		             <td>
 
		             	%if node.is_file():
 
		             		${node.last_changeset.author}
 
		             	%endif                    
 
		              %if node.is_file():
 
		                  ${node.last_changeset.author}
 
		              %endif                    
 
		             </td>
 
				</tr>
 
			%endfor
 
		</table>
 
	</div>
 
</div>
 
\ No newline at end of file
rhodecode/templates/summary/summary.html
Show inline comments
 
@@ -122,29 +122,31 @@ E.onDOMReady(function(e){
 
			  		for (k in data){
 
			  		    total += data[k];
 
			  		    no_data = false;
 
			  		} 
 
					var tbl = document.createElement('table');
 
					tbl.setAttribute('class','trending_language_tbl');
 
			  		for (k in data){
 
				  		var tr = document.createElement('tr');
 
				  		var percentage = Math.round((data[k]/total*100),2);
 
						var value = data[k];
 
				  		var td1 = document.createElement('td');
 
				  		td1.width=150;
 
                        
 
				  		var trending_language_label = document.createElement('div');
 
				  		trending_language_label.innerHTML = k;
 
				  		td1.appendChild(trending_language_label);
 

	
 
				  		var td2 = document.createElement('td');
 
				  		td2.setAttribute('style','padding-right: 12px ! important;');
 
			  		    var trending_language = document.createElement('div');
 
			  		    trending_language.title = k;
 
			  		    trending_language.innerHTML = "<b>"+percentage+"% "+value+" ${_('files')}</b>";
 
			  		    trending_language.setAttribute("class", 'trending_language top-right-rounded-corner bottom-right-rounded-corner');
 
			  		    trending_language.style.width=percentage+"%";
 
						td2.appendChild(trending_language);
 
						
 
						tr.appendChild(td1);
 
						tr.appendChild(td2);
 
			  		    tbl.appendChild(tr);
 
			  		    
 
			  		}
0 comments (0 inline, 0 general)