diff --git a/README.rst b/README.rst --- a/README.rst +++ b/README.rst @@ -1,6 +1,6 @@ -================================================= -Welcome to RhodeCode (RhodiumCode) documentation! -================================================= +======================== +RhodeCode documentation! +======================== ``RhodeCode`` is a fast and powerful management tool for Mercurial_ and GIT_ with a built in push/pull server and full text search. @@ -102,7 +102,6 @@ Incoming / Plans - pull requests and web based merges - per line file history - SSH based authentication with server side key management -- Redmine and other bugtrackers integration - Commit based built in wiki system - More statistics and graph (global annotation + some more statistics) - Other advancements as development continues (or you can of course make diff --git a/development.ini b/development.ini --- a/development.ini +++ b/development.ini @@ -58,13 +58,34 @@ container_auth_enabled = false proxypass_auth_enabled = false ## overwrite schema of clone url -# available vars: -# scheme - http/https -# user - current user -# pass - password -# netloc - network location -# path - usually repo_name -# clone_uri = {scheme}://{user}{pass}{netloc}{path} +## available vars: +## scheme - http/https +## user - current user +## pass - password +## netloc - network location +## path - usually repo_name + +#clone_uri = {scheme}://{user}{pass}{netloc}{path} + +## issue tracking mapping for commits messages +## uncomment url_pat, issue_server, issue_prefix to enable + + +## pattern to get the issues from commit messages +## default one used here is #1234 + +#url_pat = (?:^#|\s#)(\w+) + +## server url to the issue, each {id} will be replaced with id +## fetched from the regex + +#issue_server = https://myissueserver.com/issue/{id} + +## prefix to add to link to indicate it's an url +## #314 will be replaced by + +#issue_prefix = # + #################################### ### CELERY CONFIG #### diff --git a/docs/api/api.rst b/docs/api/api.rst --- a/docs/api/api.rst +++ b/docs/api/api.rst @@ -10,6 +10,8 @@ There's a single schema for calling all with JSON protocol both ways. An url to send API request in RhodeCode is /_admin/api +API ACCESS FOR WEB VIEWS +++++++++++++++++++++++++ API access can also be turned on for each view decorated with `@LoginRequired` decorator. To enable API access simple change standard login decorator into @@ -18,6 +20,9 @@ by adding a GET parameter to url `?api_k enabled on RSS/ATOM feed views. +API ACCESS +++++++++++ + All clients are required to send JSON-RPC spec JSON data:: { @@ -69,15 +74,47 @@ INPUT:: api_key : "" method : "pull" args : { - "repo" : "" + "repo_name" : "" } OUTPUT:: - result : "Pulled from " + result : "Pulled from " error : null +get_user +-------- + +Get's an user by username, Returns empty result if user is not found. +This command can be executed only using api_key belonging to user with admin +rights. + +INPUT:: + + api_key : "" + method : "get_user" + args : { + "username" : "" + } + +OUTPUT:: + + result: None if user does not exist or + { + "id" : "", + "username" : "", + "firstname": "", + "lastname" : "", + "email" : "", + "active" : "", + "admin" :  "", + "ldap" : "" + } + + error: null + + get_users --------- @@ -131,46 +168,11 @@ INPUT:: OUTPUT:: result: { + "id" : "", "msg" : "created new user " } error: null -get_users_groups ----------------- - -Lists all existing users groups. This command can be executed only using api_key -belonging to user with admin rights. - -INPUT:: - - api_key : "" - method : "get_users_groups" - args : { } - -OUTPUT:: - - result : [ - { - "id" : "", - "name" : "", - "active": "", - "members" : [ - { - "id" : "", - "username" : "", - "firstname": "", - "lastname" : "", - "email" : "", - "active" : "", - "admin" :  "", - "ldap" : "" - }, - … - ] - } - ] - error : null - get_users_group --------------- @@ -189,24 +191,61 @@ OUTPUT:: result : None if group not exist { - "id" : "", - "name" : "", - "active": "", + "id" : "", + "group_name" : "", + "active": "", "members" : [ - { "id" : "", - "username" : "", - "firstname": "", - "lastname" : "", - "email" : "", - "active" : "", - "admin" :  "", - "ldap" : "" - }, - … - ] + { "id" : "", + "username" : "", + "firstname": "", + "lastname" : "", + "email" : "", + "active" : "", + "admin" :  "", + "ldap" : "" + }, + … + ] } error : null +get_users_groups +---------------- + +Lists all existing users groups. This command can be executed only using +api_key belonging to user with admin rights. + +INPUT:: + + api_key : "" + method : "get_users_groups" + args : { } + +OUTPUT:: + + result : [ + { + "id" : "", + "group_name" : "", + "active": "", + "members" : [ + { + "id" : "", + "username" : "", + "firstname": "", + "lastname" : "", + "email" : "", + "active" : "", + "admin" :  "", + "ldap" : "" + }, + … + ] + } + ] + error : null + + create_users_group ------------------ @@ -218,7 +257,7 @@ INPUT:: api_key : "" method : "create_users_group" args: { - "name": "", + "group_name": "", "active":" = True" } @@ -226,7 +265,7 @@ OUTPUT:: result: { "id": "", - "msg": "created new users group " + "msg": "created new users group " } error: null @@ -253,6 +292,51 @@ OUTPUT:: } error: null +get_repo +-------- + +Gets an existing repository. This command can be executed only using api_key +belonging to user with admin rights + +INPUT:: + + api_key : "" + method : "get_repo" + args: { + "repo_name" : "" + } + +OUTPUT:: + + result: None if repository does not exist or + { + "id" : "", + "repo_name" : "" + "type" : "", + "description" : "", + "members" : [ + { "id" : "", + "username" : "", + "firstname": "", + "lastname" : "", + "email" : "", + "active" : "", + "admin" :  "", + "ldap" : "", + "permission" : "repository.(read|write|admin)" + }, + … + { + "id" : "", + "name" : "", + "active": "", + "permission" : "repository.(read|write|admin)" + }, + … + ] + } + error: null + get_repos --------- @@ -270,7 +354,7 @@ OUTPUT:: result: [ { "id" : "", - "name" : "" + "repo_name" : "" "type" : "", "description" : "" }, @@ -278,57 +362,13 @@ OUTPUT:: ] error: null -get_repo --------- - -Gets an existing repository. This command can be executed only using api_key -belonging to user with admin rights - -INPUT:: - - api_key : "" - method : "get_repo" - args: { - "name" : "" - } - -OUTPUT:: - - result: None if repository not exist - { - "id" : "", - "name" : "" - "type" : "", - "description" : "", - "members" : [ - { "id" : "", - "username" : "", - "firstname": "", - "lastname" : "", - "email" : "", - "active" : "", - "admin" :  "", - "ldap" : "", - "permission" : "repository.(read|write|admin)" - }, - … - { - "id" : "", - "name" : "", - "active": "", - "permission" : "repository.(read|write|admin)" - }, - … - ] - } - error: null get_repo_nodes -------------- returns a list of nodes and it's children in a flat list for a given path -at given revision. It's possible to specify ret_type to show only files or -dirs. This command can be executed only using api_key belonging to user +at given revision. It's possible to specify ret_type to show only `files` or +`dirs`. This command can be executed only using api_key belonging to user with admin rights INPUT:: @@ -336,7 +376,7 @@ INPUT:: api_key : "" method : "get_repo_nodes" args: { - "repo_name" : "", + "repo_name" : "", "revision" : "", "root_path" : "", "ret_type" : "" = 'all' @@ -369,7 +409,7 @@ INPUT:: api_key : "" method : "create_repo" args: { - "name" : "", + "repo_name" : "", "owner_name" : "", "description" : " = ''", "repo_type" : " = 'hg'", @@ -378,7 +418,10 @@ INPUT:: OUTPUT:: - result: None + result: { + "id": "", + "msg": "Created new repository ", + } error: null add_user_to_repo @@ -394,13 +437,15 @@ INPUT:: method : "add_user_to_repo" args: { "repo_name" : "", - "username" : "", + "username" : "", "perm" : "(None|repository.(read|write|admin))", } OUTPUT:: - result: None + result: { + "msg" : "Added perm: for in repo: " + } error: null add_users_group_to_repo @@ -416,6 +461,12 @@ INPUT:: method : "add_users_group_to_repo" args: { "repo_name" : "", - "group_name" : "", + "group_name" : "", "perm" : "(None|repository.(read|write|admin))", - } \ No newline at end of file + } +OUTPUT:: + + result: { + "msg" : Added perm: for in repo: " + } + diff --git a/docs/api/index.rst b/docs/api/index.rst --- a/docs/api/index.rst +++ b/docs/api/index.rst @@ -1,4 +1,4 @@ -.. _api: +.. _indexapi: API Reference ============= diff --git a/docs/api/models.rst b/docs/api/models.rst --- a/docs/api/models.rst +++ b/docs/api/models.rst @@ -6,14 +6,29 @@ The :mod:`models` Module .. automodule:: rhodecode.model :members: +.. automodule:: rhodecode.model.comment + :members: + +.. automodule:: rhodecode.model.notification + :members: + .. automodule:: rhodecode.model.permission :members: - + +.. automodule:: rhodecode.model.repo_permission + :members: + .. automodule:: rhodecode.model.repo :members: +.. automodule:: rhodecode.model.repos_group + :members: + .. automodule:: rhodecode.model.scm :members: - + .. automodule:: rhodecode.model.user :members: + +.. automodule:: rhodecode.model.users_group + :members: \ No newline at end of file diff --git a/docs/changelog.rst b/docs/changelog.rst --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -33,7 +33,9 @@ news - implements #330 api method for listing nodes ar particular revision - fixed #331 RhodeCode mangles repository names if the a repository group contains the "full path" to the repositories - +- #73 added linking issues in commit messages to choosen issue tracker url + based on user defined regular expression + fixes ----- diff --git a/docs/index.rst b/docs/index.rst --- a/docs/index.rst +++ b/docs/index.rst @@ -2,8 +2,8 @@ .. include:: ./../README.rst -Documentation -------------- +Users Guide +----------- **Installation:** @@ -23,7 +23,6 @@ Documentation usage/enable_git usage/statistics usage/backup - usage/api_key_access **Develop** @@ -36,7 +35,7 @@ Documentation **API** .. toctree:: - :maxdepth: 2 + :maxdepth: 1 api/index diff --git a/docs/setup.rst b/docs/setup.rst --- a/docs/setup.rst +++ b/docs/setup.rst @@ -425,7 +425,25 @@ the following in the [app:main] section forge the authentication header and could effectively become authenticated using any account of their liking. +Integration with Issue trackers +------------------------------- +RhodeCode provides a simple integration with issue trackers. It's possible +to define a regular expression that will fetch issue id stored in commit +messages and replace that with an url to this issue. To enable this simply +uncomment following variables in the ini file:: + + url_pat = (?:^#|\s#)(\w+) + issue_server = https://myissueserver.com/issue/{id} + issue_prefix = # + +`url_pat` is the regular expression that will match issues, default given regex +will match issues in format of # eg. #300. +Matched issues will be replace with the `issue_server` url replacing {id} with +id fetched from regex. Since the # is striped `issue_prefix` is added as a +prefix to url. `issue_prefix` can be something different than # if you pass +ISSUE- as issue prefix this will generate an url in format +`ISSUE-300` Hook management --------------- diff --git a/docs/usage/api_key_access.rst b/docs/usage/api_key_access.rst deleted file mode 100644 --- a/docs/usage/api_key_access.rst +++ /dev/null @@ -1,11 +0,0 @@ -.. _api_key_access: - -Access to RhodeCode via API KEY -=============================== - -Starting from version 1.2 rss/atom feeds and journal feeds -can be accessed via **api_key**. This unique key is automatically generated for -each user in RhodeCode application. Using this key it is possible to access -feeds without having to log in. When user changes his password a new API KEY -is generated for him automatically. You can check your API KEY in account -settings page. \ No newline at end of file diff --git a/production.ini b/production.ini --- a/production.ini +++ b/production.ini @@ -58,13 +58,34 @@ container_auth_enabled = false proxypass_auth_enabled = false ## overwrite schema of clone url -# available vars: -# scheme - http/https -# user - current user -# pass - password -# netloc - network location -# path - usually repo_name -# clone_uri = {scheme}://{user}{pass}{netloc}{path} +## available vars: +## scheme - http/https +## user - current user +## pass - password +## netloc - network location +## path - usually repo_name + +#clone_uri = {scheme}://{user}{pass}{netloc}{path} + +## issue tracking mapping for commits messages +## uncomment url_pat, issue_server, issue_prefix to enable + + +## pattern to get the issues from commit messages +## default one used here is #1234 + +#url_pat = (?:^#|\s#)(\w+) + +## server url to the issue, each {id} will be replaced with id +## fetched from the regex + +#issue_server = https://myissueserver.com/issue/{id} + +## prefix to add to link to indicate it's an url +## #314 will be replaced by + +#issue_prefix = # + #################################### ### CELERY CONFIG #### diff --git a/rhodecode/config/deployment.ini_tmpl b/rhodecode/config/deployment.ini_tmpl --- a/rhodecode/config/deployment.ini_tmpl +++ b/rhodecode/config/deployment.ini_tmpl @@ -58,14 +58,35 @@ container_auth_enabled = false proxypass_auth_enabled = false ## overwrite schema of clone url -# available vars: -# scheme - http/https -# user - current user -# pass - password -# netloc - network location -# path - usually repo_name +## available vars: +## scheme - http/https +## user - current user +## pass - password +## netloc - network location +## path - usually repo_name + # clone_uri = {scheme}://{user}{pass}{netloc}{path} +## issue tracking mapping for commits messages +## uncomment url_pat, issue_server, issue_prefix to enable + + +## pattern to get the issues from commit messages +## default one used here is #1234 + +#url_pat = (?:^#|\s#)(\w+) + +## server url to the issue, each {id} will be replaced with id +## fetched from the regex + +#issue_server = https://myissueserver.com/issue/{id} + +## prefix to add to link to indicate it's an url +## #314 will be replaced by + +#issue_prefix = # + + #################################### ### CELERY CONFIG #### #################################### diff --git a/rhodecode/controllers/api/api.py b/rhodecode/controllers/api/api.py --- a/rhodecode/controllers/api/api.py +++ b/rhodecode/controllers/api/api.py @@ -64,23 +64,23 @@ class ApiController(JSONRPCController): """ @HasPermissionAllDecorator('hg.admin') - def pull(self, apiuser, repo): + def pull(self, apiuser, repo_name): """ Dispatch pull action on given repo :param user: - :param repo: + :param repo_name: """ - if Repository.is_valid(repo) is False: - raise JSONRPCError('Unknown repo "%s"' % repo) + if Repository.is_valid(repo_name) is False: + raise JSONRPCError('Unknown repo "%s"' % repo_name) try: - ScmModel().pull_changes(repo, self.rhodecode_user.username) - return 'Pulled from %s' % repo + ScmModel().pull_changes(repo_name, self.rhodecode_user.username) + return 'Pulled from %s' % repo_name except Exception: - raise JSONRPCError('Unable to pull changes from "%s"' % repo) + raise JSONRPCError('Unable to pull changes from "%s"' % repo_name) @HasPermissionAllDecorator('hg.admin') def get_user(self, apiuser, username): @@ -151,10 +151,15 @@ class ApiController(JSONRPCController): raise JSONRPCError("user %s already exist" % username) try: - UserModel().create_or_update(username, password, email, firstname, - lastname, active, admin, ldap_dn) + usr = UserModel().create_or_update( + username, password, email, firstname, + lastname, active, admin, ldap_dn + ) Session.commit() - return dict(msg='created new user %s' % username) + return dict( + id=usr.user_id, + msg='created new user %s' % username + ) except Exception: log.error(traceback.format_exc()) raise JSONRPCError('failed to create user %s' % username) @@ -185,7 +190,7 @@ class ApiController(JSONRPCController): ldap=user.ldap_dn)) return dict(id=users_group.users_group_id, - name=users_group.users_group_name, + group_name=users_group.users_group_name, active=users_group.users_group_active, members=members) @@ -212,31 +217,31 @@ class ApiController(JSONRPCController): ldap=user.ldap_dn)) result.append(dict(id=users_group.users_group_id, - name=users_group.users_group_name, + group_name=users_group.users_group_name, active=users_group.users_group_active, members=members)) return result @HasPermissionAllDecorator('hg.admin') - def create_users_group(self, apiuser, name, active=True): + def create_users_group(self, apiuser, group_name, active=True): """ Creates an new usergroup - :param name: + :param group_name: :param active: """ - if self.get_users_group(apiuser, name): - raise JSONRPCError("users group %s already exist" % name) + if self.get_users_group(apiuser, group_name): + raise JSONRPCError("users group %s already exist" % group_name) try: - ug = UsersGroupModel().create(name=name, active=active) + ug = UsersGroupModel().create(name=group_name, active=active) Session.commit() return dict(id=ug.users_group_id, - msg='created new users group %s' % name) + msg='created new users group %s' % group_name) except Exception: log.error(traceback.format_exc()) - raise JSONRPCError('failed to create group %s' % name) + raise JSONRPCError('failed to create group %s' % group_name) @HasPermissionAllDecorator('hg.admin') def add_user_to_users_group(self, apiuser, group_name, username): @@ -312,7 +317,7 @@ class ApiController(JSONRPCController): return dict( id=repo.repo_id, - name=repo.repo_name, + repo_name=repo.repo_name, type=repo.repo_type, description=repo.description, members=members @@ -331,7 +336,7 @@ class ApiController(JSONRPCController): result.append( dict( id=repository.repo_id, - name=repository.repo_name, + repo_name=repository.repo_name, type=repository.repo_type, description=repository.description ) @@ -367,13 +372,13 @@ class ApiController(JSONRPCController): raise JSONRPCError(e) @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository') - def create_repo(self, apiuser, name, owner_name, description='', + def create_repo(self, apiuser, repo_name, owner_name, description='', repo_type='hg', private=False): """ Create a repository :param apiuser: - :param name: + :param repo_name: :param description: :param type: :param private: @@ -386,10 +391,10 @@ class ApiController(JSONRPCController): except NoResultFound: raise JSONRPCError('unknown user %s' % owner) - if self.get_repo(apiuser, name): - raise JSONRPCError("repo %s already exist" % name) + if Repository.get_by_repo_name(repo_name): + raise JSONRPCError("repo %s already exist" % repo_name) - groups = name.split('/') + groups = repo_name.split('/') real_name = groups[-1] groups = groups[:-1] parent_id = None @@ -405,10 +410,10 @@ class ApiController(JSONRPCController): ) parent_id = group.group_id - RepoModel().create( + repo = RepoModel().create( dict( repo_name=real_name, - repo_name_full=name, + repo_name_full=repo_name, description=description, private=private, repo_type=repo_type, @@ -418,9 +423,15 @@ class ApiController(JSONRPCController): owner ) Session.commit() + + return dict( + id=repo.repo_id, + msg="Created new repository %s" % repo.repo_name + ) + except Exception: log.error(traceback.format_exc()) - raise JSONRPCError('failed to create repository %s' % name) + raise JSONRPCError('failed to create repository %s' % repo_name) @HasPermissionAnyDecorator('hg.admin') def add_user_to_repo(self, apiuser, repo_name, username, perm): diff --git a/rhodecode/lib/db_manage.py b/rhodecode/lib/db_manage.py --- a/rhodecode/lib/db_manage.py +++ b/rhodecode/lib/db_manage.py @@ -97,6 +97,13 @@ class DbManage(object): from rhodecode.lib.dbmigrate.migrate.exceptions import \ DatabaseNotControlledError + if 'sqlite' in self.dburi: + print ( + '********************** WARNING **********************\n' + 'Make sure your version of sqlite is at least 3.7.X. \n' + 'Earlier versions are known to fail on some migrations\n' + '*****************************************************\n' + ) upgrade = ask_ok('You are about to perform database upgrade, make ' 'sure You backed up your database before. ' 'Continue ? [y/n]') @@ -161,6 +168,9 @@ class DbManage(object): print ('Adding ldap defaults') self.klass.create_ldap_options(skip_existing=True) + def step_4(self): + print ('TODO:') + upgrade_steps = [0] + range(curr_version + 1, __dbversion__ + 1) # CALL THE PROPER ORDER OF STEPS TO PERFORM FULL UPGRADE diff --git a/rhodecode/lib/helpers.py b/rhodecode/lib/helpers.py --- a/rhodecode/lib/helpers.py +++ b/rhodecode/lib/helpers.py @@ -8,6 +8,7 @@ import hashlib import StringIO import urllib import math +import logging from datetime import datetime from pygments.formatters.html import HtmlFormatter @@ -41,6 +42,8 @@ from rhodecode.lib.utils import repo_nam from rhodecode.lib import str2bool, safe_unicode, safe_str, get_changeset_safe from rhodecode.lib.markup_renderer import MarkupRenderer +log = logging.getLogger(__name__) + def _reset(name, value=None, id=NotGiven, type="reset", **attrs): """ @@ -728,7 +731,7 @@ def fancy_file_stats(stats): return literal('
%s%s
' % (width, d_a, d_d)) -def urlify_text(text): +def urlify_text(text_): import re url_pat = re.compile(r'''(http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]''' @@ -738,8 +741,42 @@ def urlify_text(text): url_full = match_obj.groups()[0] return '%(url)s' % ({'url':url_full}) - return literal(url_pat.sub(url_func, text)) + return literal(url_pat.sub(url_func, text_)) +def urlify_commit(text_): + import re + import traceback + + try: + conf = config['app_conf'] + + URL_PAT = re.compile(r'%s' % conf.get('url_pat')) + + if URL_PAT: + ISSUE_SERVER = conf.get('issue_server') + ISSUE_PREFIX = conf.get('issue_prefix') + def url_func(match_obj): + issue_id = match_obj.groups()[0] + tmpl = ( + '' + ' %(issue-prefix)s%(id-repr)s' + '' + ) + return tmpl % ( + { + 'cls':'issue-tracker-link', + 'url':ISSUE_SERVER.replace('{id}',issue_id), + 'id-repr':issue_id, + 'issue-prefix':ISSUE_PREFIX, + 'serv':ISSUE_SERVER, + } + ) + return literal(URL_PAT.sub(url_func, text_)) + except: + log.error(traceback.format_exc()) + pass + + return text_ def rst(source): return literal('
%s
' % diff --git a/rhodecode/lib/rcmail/exceptions.py b/rhodecode/lib/rcmail/exceptions.py --- a/rhodecode/lib/rcmail/exceptions.py +++ b/rhodecode/lib/rcmail/exceptions.py @@ -1,3 +1,4 @@ + class InvalidMessage(RuntimeError): """ @@ -5,6 +6,7 @@ class InvalidMessage(RuntimeError): as recipients or sender address. """ + class BadHeaders(RuntimeError): """ Raised if message contains newlines in headers. diff --git a/rhodecode/lib/rcmail/message.py b/rhodecode/lib/rcmail/message.py --- a/rhodecode/lib/rcmail/message.py +++ b/rhodecode/lib/rcmail/message.py @@ -45,6 +45,8 @@ class Message(object): :param bcc: BCC list :param extra_headers: dict of extra email headers :param attachments: list of Attachment instances + :param recipients_separator: alternative separator for any of + 'From', 'To', 'Delivered-To', 'Cc', 'Bcc' fields """ def __init__(self, @@ -56,8 +58,8 @@ class Message(object): cc=None, bcc=None, extra_headers=None, - attachments=None): - + attachments=None, + recipients_separator="; "): self.subject = subject or '' self.sender = sender @@ -70,6 +72,8 @@ class Message(object): self.bcc = bcc or [] self.extra_headers = extra_headers or {} + self.recipients_separator = recipients_separator + @property def send_to(self): return set(self.recipients) | set(self.bcc or ()) | set(self.cc or ()) @@ -92,7 +96,8 @@ class Message(object): To=self.recipients, From=self.sender, Body=self.body, - Html=self.html) + Html=self.html, + separator=self.recipients_separator) if self.bcc: response.base['Bcc'] = self.bcc diff --git a/rhodecode/lib/rcmail/response.py b/rhodecode/lib/rcmail/response.py --- a/rhodecode/lib/rcmail/response.py +++ b/rhodecode/lib/rcmail/response.py @@ -141,12 +141,14 @@ class MailResponse(object): MailResponse.to_message. This lets you change it and work with it, then send it out when it's ready. """ - def __init__(self, To=None, From=None, Subject=None, Body=None, Html=None): + def __init__(self, To=None, From=None, Subject=None, Body=None, Html=None, + separator="; "): self.Body = Body self.Html = Html self.base = MailBase([('To', To), ('From', From), ('Subject', Subject)]) self.multipart = self.Body and self.Html self.attachments = [] + self.separator = separator def __contains__(self, key): return self.base.__contains__(key) @@ -298,7 +300,7 @@ class MailResponse(object): self.base.body = self.Html self.base.content_encoding['Content-Type'] = ('text/html', {}) - return to_message(self.base) + return to_message(self.base, separator=self.separator) def all_parts(self): """ @@ -310,7 +312,7 @@ class MailResponse(object): def keys(self): return self.base.keys() -def to_message(mail): +def to_message(mail, separator="; "): """ Given a MailBase message, this will construct a MIMEPart that is canonicalized for use with the Python email API. @@ -339,10 +341,16 @@ def to_message(mail): for k in mail.keys(): if k in ADDRESS_HEADERS_WHITELIST: - out[k.encode('ascii')] = header_to_mime_encoding(mail[k]) + out[k.encode('ascii')] = header_to_mime_encoding( + mail[k], + not_email=False, + separator=separator + ) else: - out[k.encode('ascii')] = header_to_mime_encoding(mail[k], - not_email=True) + out[k.encode('ascii')] = header_to_mime_encoding( + mail[k], + not_email=True + ) out.extract_payload(mail) @@ -403,12 +411,12 @@ class MIMEPart(MIMEBase): self.is_multipart()) -def header_to_mime_encoding(value, not_email=False): +def header_to_mime_encoding(value, not_email=False, separator=", "): if not value: return "" encoder = Charset(DEFAULT_ENCODING) if type(value) == list: - return "; ".join(properly_encode_header( + return separator.join(properly_encode_header( v, encoder, not_email) for v in value) else: return properly_encode_header(value, encoder, not_email) diff --git a/rhodecode/lib/rcmail/smtp_mailer.py b/rhodecode/lib/rcmail/smtp_mailer.py --- a/rhodecode/lib/rcmail/smtp_mailer.py +++ b/rhodecode/lib/rcmail/smtp_mailer.py @@ -59,7 +59,8 @@ class SmtpMailer(object): if isinstance(recipients, basestring): recipients = [recipients] - msg = Message(subject, recipients, body, html, self.mail_from) + msg = Message(subject, recipients, body, html, self.mail_from, + recipients_separator=", ") raw_msg = msg.to_message() if self.ssl: diff --git a/rhodecode/lib/utils.py b/rhodecode/lib/utils.py --- a/rhodecode/lib/utils.py +++ b/rhodecode/lib/utils.py @@ -152,12 +152,12 @@ def get_repos(path, recursive=False): """ Scans given path for repos and return (name,(type,path)) tuple - :param path: path to scann for repositories + :param path: path to scan for repositories :param recursive: recursive search and return names with subdirs in front """ # remove ending slash for better results - path = path.rstrip('/') + path = path.rstrip(os.sep) def _get_repos(p): if not os.access(p, os.W_OK): diff --git a/rhodecode/model/notification.py b/rhodecode/model/notification.py --- a/rhodecode/model/notification.py +++ b/rhodecode/model/notification.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- """ rhodecode.model.notification - ~~~~~~~~~~~~~~ + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Model for notifications diff --git a/rhodecode/templates/changelog/changelog.html b/rhodecode/templates/changelog/changelog.html --- a/rhodecode/templates/changelog/changelog.html +++ b/rhodecode/templates/changelog/changelog.html @@ -48,7 +48,7 @@
${h.checkbox(cs.short_id,class_="changeset_range")} - ${cs.revision}:${h.short_id(cs.raw_id)} + ${cs.revision}:${h.short_id(cs.raw_id)}
diff --git a/rhodecode/templates/changeset/changeset.html b/rhodecode/templates/changeset/changeset.html --- a/rhodecode/templates/changeset/changeset.html +++ b/rhodecode/templates/changeset/changeset.html @@ -49,7 +49,7 @@ ${h.person(c.changeset.author)}
${h.email_or_none(c.changeset.author)}
-
${h.wrap_paragraphs(c.changeset.message)}
+
${h.urlify_commit(h.wrap_paragraphs(c.changeset.message))}
@@ -95,7 +95,7 @@
%if change != 'removed': - ${h.link_to(h.safe_unicode(filenode.path),c.anchor_url(filenode.changeset.raw_id,filenode.path))} + ${h.link_to(h.safe_unicode(filenode.path),c.anchor_url(filenode.changeset.raw_id,filenode.path)+"_target")} %else: ${h.link_to(h.safe_unicode(filenode.path),h.url.current(anchor=h.FID('',filenode.path)))} %endif @@ -138,11 +138,15 @@ YUE.on(YUQ('.show-inline-comments'),'change',function(e){ var show = 'none'; var target = e.currentTarget; + console.log(target); if(target.checked){ var show = '' } + console.log('aa') var boxid = YUD.getAttribute(target,'id_for'); + console.log(boxid); var comments = YUQ('#{0} .inline-comments'.format(boxid)); + console.log(comments) for(c in comments){ YUD.setStyle(comments[c],'display',show); } diff --git a/rhodecode/templates/changeset/changeset_range.html b/rhodecode/templates/changeset/changeset_range.html --- a/rhodecode/templates/changeset/changeset_range.html +++ b/rhodecode/templates/changeset/changeset_range.html @@ -41,7 +41,7 @@ ${h.link_to('r%s:%s' % (cs.revision,h.short_id(cs.raw_id)),h.url('changeset_home',repo_name=c.repo_name,revision=cs.raw_id))}
${h.person(cs.author)}
${cs.date} -
${h.link_to(h.wrap_paragraphs(cs.message),h.url('changeset_home',repo_name=c.repo_name,revision=cs.raw_id))}
+
${h.urlify_commit(h.wrap_paragraphs(cs.message))}
%endfor diff --git a/rhodecode/templates/changeset/diff_block.html b/rhodecode/templates/changeset/diff_block.html --- a/rhodecode/templates/changeset/diff_block.html +++ b/rhodecode/templates/changeset/diff_block.html @@ -7,8 +7,8 @@ %for change,filenode,diff,cs1,cs2,stat in changes: %if change !='removed': -
-
+
+
diff --git a/rhodecode/templates/shortlog/shortlog_data.html b/rhodecode/templates/shortlog/shortlog_data.html --- a/rhodecode/templates/shortlog/shortlog_data.html +++ b/rhodecode/templates/shortlog/shortlog_data.html @@ -2,16 +2,19 @@ %if c.repo_changesets: - + + - %for cnt,cs in enumerate(c.repo_changesets): + -
${_('commit message')}${_('revision')}${_('commit message')} ${_('age')} ${_('author')}${_('revision')} ${_('branch')} ${_('tags')}
+ + ${h.link_to(h.truncate(cs.message,50), h.url('changeset_home',repo_name=c.repo_name,revision=cs.raw_id), title=cs.message)} @@ -20,7 +23,6 @@ ${h.age(cs.date)} ${h.person(cs.author)} ${cs.branch}