##// END OF EJS Templates
changes for rhodecode release 1.1.6
marcink -
r1165:c5af1d3c rhodecode-0.0.1.1.6 default
parent child Browse files
Show More
@@ -1,132 +1,132 b''
1 1
2 2 =================================================
3 3 Welcome to RhodeCode (RhodiumCode) documentation!
4 4 =================================================
5 5
6 6 ``RhodeCode`` (formerly hg-app) is a Pylons framework based Mercurial repository
7 7 browser/management tool with a built in push/pull server and full text search.
8 8 It works on http/https and has a built in permission/authentication system with
9 9 the ability to authenticate via LDAP.
10 10
11 11 RhodeCode is similar in some respects to github or bitbucket_,
12 12 however RhodeCode can be run as standalone hosted application on your own server.
13 13 It is open source and donation ware and focuses more on providing a customized,
14 14 self administered interface for Mercurial(and soon GIT) repositories.
15 15 RhodeCode is powered by a vcs_ library that Lukasz Balcerzak and I created to
16 16 handle multiple different version control systems.
17 17
18 18 RhodeCode uses `Semantic Versioning <http://semver.org/>`_
19 19
20 20 RhodeCode demo
21 21 --------------
22 22
23 23 http://demo.rhodecode.org
24 24
25 25 The default access is anonymous but you can login to an administrative account
26 26 using the following credentials:
27 27
28 28 - username: demo
29 29 - password: demo
30 30
31 31 Source code
32 32 -----------
33 33
34 34 The latest source for RhodeCode can be obtained from official RhodeCode instance
35 35 https://hg.rhodecode.org
36 36
37 Rarely updated source code and issue tracker is available at bitbcuket
37 Rarely updated source code and issue tracker is available at bitbucket
38 38 http://bitbucket.org/marcinkuzminski/rhodecode
39 39
40 40 Installation
41 41 ------------
42 42
43 43 Please visit http://packages.python.org/RhodeCode/installation.html
44 44
45 45
46 46 RhodeCode Features
47 47 ------------------
48 48
49 49 - Has it's own middleware to handle mercurial_ protocol requests.
50 50 Each request can be logged and authenticated.
51 - Runs on threads unlike hgweb. You can make multiple pulls/pushes simultaneous. Supports http/https
52 and LDAP
51 - Runs on threads unlike hgweb. You can make multiple pulls/pushes simultaneous.
52 Supports http/https and LDAP
53 53 - Full permissions (private/read/write/admin) and authentication per project.
54 54 One account for web interface and mercurial_ push/pull/clone operations.
55 55 - Mako templates let's you customize the look and feel of the application.
56 56 - Beautiful diffs, annotations and source code browsing all colored by pygments.
57 57 - Mercurial_ branch graph and yui-flot powered graphs with zooming and statistics
58 58 - Admin interface with user/permission management. Admin activity journal, logs
59 59 pulls, pushes, forks, registrations and other actions made by all users.
60 60 - Server side forks. It is possible to fork a project and modify it freely without
61 61 breaking the main repository.
62 62 - Full text search powered by Whoosh on the source files, and file names.
63 63 Build in indexing daemons, with optional incremental index build
64 64 (no external search servers required all in one application)
65 65 - Setup project descriptions and info inside built in db for easy, non
66 66 file-system operations
67 67 - Intelligent cache with invalidation after push or project change, provides high
68 68 performance and always up to date data.
69 69 - Rss / atom feeds, gravatar support, download sources as zip/tar/gz
70 70 - Async tasks for speed and performance using celery_ (works without them too)
71 71 - Backup scripts can do backup of whole app and send it over scp to desired
72 72 location
73 73 - Based on pylons / sqlalchemy / sqlite / whoosh / vcs
74 74
75 75
76 76 .. include:: ./docs/screenshots.rst
77 77
78 78
79 79 Incoming / Plans
80 80 ----------------
81 81
82 82 - Project grouping
83 83 - User groups/teams
84 84 - SSH based authentication with server side key management
85 85 - Code review (probably based on hg-review)
86 86 - Full git_ support, with push/pull server (currently in beta tests)
87 87 - Redmine integration
88 88 - Public accessible activity feeds
89 89 - Commit based built in wiki system
90 90 - Clone points and cloning from remote repositories into RhodeCode
91 91 - More statistics and graph (global annotation + some more statistics)
92 92 - Other advancements as development continues (or you can of course make additions and or requests)
93 93
94 94 License
95 95 -------
96 96
97 97 ``RhodeCode`` is released under the GPL_ license.
98 98
99 99
100 100 Mailing group Q&A
101 101 -----------------
102 102
103 103 Join the `Google group <http://groups.google.com/group/rhodecode>`_
104 104
105 105 Open an issue at `issue tracker <http://bitbucket.org/marcinkuzminski/rhodecode/issues>`_
106 106
107 107 Join #rhodecode on FreeNode (irc.freenode.net)
108 108 or use http://webchat.freenode.net/?channels=rhodecode for web access to irc.
109 109
110 110 Online documentation
111 111 --------------------
112 112
113 113 Online documentation for the current version of RhodeCode is available at
114 114 http://packages.python.org/RhodeCode/.
115 115 You may also build the documentation for yourself - go into ``docs/`` and run::
116 116
117 117 make html
118 118
119 (You need to have sphinx installed to build the documentation. If you don't
120 have sphinx installed you can install it via the command: ``easy_install sphinx``)
119 (You need to have sphinx_ installed to build the documentation. If you don't
120 have sphinx_ installed you can install it via the command: ``easy_install sphinx``)
121 121
122 122 .. _virtualenv: http://pypi.python.org/pypi/virtualenv
123 123 .. _python: http://www.python.org/
124 .. _django: http://www.djangoproject.com/
124 .. _sphinx: http://sphinx.pocoo.org/
125 125 .. _mercurial: http://mercurial.selenic.com/
126 126 .. _bitbucket: http://bitbucket.org/
127 127 .. _subversion: http://subversion.tigris.org/
128 128 .. _git: http://git-scm.com/
129 129 .. _celery: http://celeryproject.org/
130 130 .. _Sphinx: http://sphinx.pocoo.org/
131 131 .. _GPL: http://www.gnu.org/licenses/gpl.html
132 132 .. _vcs: http://pypi.python.org/pypi/vcs No newline at end of file
@@ -1,213 +1,225 b''
1 1 .. _changelog:
2 2
3 3 Changelog
4 4 =========
5 5
6 6
7 1.1.6 (**2011-03-21**)
8 ======================
9
10 news
11 ----
12
13 fixes
14 -----
15
16 - fixed #136 installation support for FreeBSD
17 - RhodeCode will check for python version during installation
18
7 19 1.1.5 (**2011-03-17**)
8 20 ======================
9 21
10 22 news
11 23 ----
12 24
13 25 - basic windows support, by exchanging pybcrypt into sha256 for windows only
14 26 highly inspired by idea of mantis406
15 27
16 28 fixes
17 29 -----
18 30
19 31 - fixed sorting by author in main page
20 32 - fixed crashes with diffs on binary files
21 33 - fixed #131 problem with boolean values for LDAP
22 34 - fixed #122 mysql problems thanks to striker69
23 35 - fixed problem with errors on calling raw/raw_files/annotate functions
24 36 with unknown revisions
25 37 - fixed returned rawfiles attachment names with international character
26 38 - cleaned out docs, big thanks to Jason Harris
27 39
28 40 1.1.4 (**2011-02-19**)
29 41 ======================
30 42
31 43 news
32 44 ----
33 45
34 46 fixes
35 47 -----
36 48
37 49 - fixed formencode import problem on settings page, that caused server crash
38 50 when that page was accessed as first after server start
39 51 - journal fixes
40 52 - fixed option to access repository just by entering http://server/<repo_name>
41 53
42 54
43 55 1.1.3 (**2011-02-16**)
44 56 ======================
45 57
46 58 news
47 59 ----
48 60
49 61 - implemented #102 allowing the '.' character in username
50 62 - added option to access repository just by entering http://server/<repo_name>
51 63 - celery task ignores result for better performance
52 64
53 65 fixes
54 66 -----
55 67
56 68 - fixed ehlo command and non auth mail servers on smtp_lib. Thanks to
57 69 apollo13 and Johan Walles
58 70 - small fixes in journal
59 71 - fixed problems with getting setting for celery from .ini files
60 72 - registration, password reset and login boxes share the same title as main
61 73 application now
62 74 - fixed #113: to high permissions to fork repository
63 75 - fixed problem with '[' chars in commit messages in journal
64 76 - removed issue with space inside renamed repository after deletion
65 77 - db transaction fixes when filesystem repository creation failed
66 78 - fixed #106 relation issues on databases different than sqlite
67 79 - fixed static files paths links to use of url() method
68 80
69 81 1.1.2 (**2011-01-12**)
70 82 ======================
71 83
72 84 news
73 85 ----
74 86
75 87
76 88 fixes
77 89 -----
78 90
79 91 - fixes #98 protection against float division of percentage stats
80 92 - fixed graph bug
81 93 - forced webhelpers version since it was making troubles during installation
82 94
83 95 1.1.1 (**2011-01-06**)
84 96 ======================
85 97
86 98 news
87 99 ----
88 100
89 101 - added force https option into ini files for easier https usage (no need to
90 102 set server headers with this options)
91 103 - small css updates
92 104
93 105 fixes
94 106 -----
95 107
96 108 - fixed #96 redirect loop on files view on repositories without changesets
97 109 - fixed #97 unicode string passed into server header in special cases (mod_wsgi)
98 110 and server crashed with errors
99 111 - fixed large tooltips problems on main page
100 112 - fixed #92 whoosh indexer is more error proof
101 113
102 114 1.1.0 (**2010-12-18**)
103 115 ======================
104 116
105 117 news
106 118 ----
107 119
108 120 - rewrite of internals for vcs >=0.1.10
109 121 - uses mercurial 1.7 with dotencode disabled for maintaining compatibility
110 122 with older clients
111 123 - anonymous access, authentication via ldap
112 124 - performance upgrade for cached repos list - each repository has it's own
113 125 cache that's invalidated when needed.
114 126 - performance upgrades on repositories with large amount of commits (20K+)
115 127 - main page quick filter for filtering repositories
116 128 - user dashboards with ability to follow chosen repositories actions
117 129 - sends email to admin on new user registration
118 130 - added cache/statistics reset options into repository settings
119 131 - more detailed action logger (based on hooks) with pushed changesets lists
120 132 and options to disable those hooks from admin panel
121 133 - introduced new enhanced changelog for merges that shows more accurate results
122 134 - new improved and faster code stats (based on pygments lexers mapping tables,
123 135 showing up to 10 trending sources for each repository. Additionally stats
124 136 can be disabled in repository settings.
125 137 - gui optimizations, fixed application width to 1024px
126 138 - added cut off (for large files/changesets) limit into config files
127 139 - whoosh, celeryd, upgrade moved to paster command
128 140 - other than sqlite database backends can be used
129 141
130 142 fixes
131 143 -----
132 144
133 145 - fixes #61 forked repo was showing only after cache expired
134 146 - fixes #76 no confirmation on user deletes
135 147 - fixes #66 Name field misspelled
136 148 - fixes #72 block user removal when he owns repositories
137 149 - fixes #69 added password confirmation fields
138 150 - fixes #87 RhodeCode crashes occasionally on updating repository owner
139 151 - fixes #82 broken annotations on files with more than 1 blank line at the end
140 152 - a lot of fixes and tweaks for file browser
141 153 - fixed detached session issues
142 154 - fixed when user had no repos he would see all repos listed in my account
143 155 - fixed ui() instance bug when global hgrc settings was loaded for server
144 156 instance and all hgrc options were merged with our db ui() object
145 157 - numerous small bugfixes
146 158
147 159 (special thanks for TkSoh for detailed feedback)
148 160
149 161
150 162 1.0.2 (**2010-11-12**)
151 163 ======================
152 164
153 165 news
154 166 ----
155 167
156 168 - tested under python2.7
157 169 - bumped sqlalchemy and celery versions
158 170
159 171 fixes
160 172 -----
161 173
162 174 - fixed #59 missing graph.js
163 175 - fixed repo_size crash when repository had broken symlinks
164 176 - fixed python2.5 crashes.
165 177
166 178
167 179 1.0.1 (**2010-11-10**)
168 180 ======================
169 181
170 182 news
171 183 ----
172 184
173 185 - small css updated
174 186
175 187 fixes
176 188 -----
177 189
178 190 - fixed #53 python2.5 incompatible enumerate calls
179 191 - fixed #52 disable mercurial extension for web
180 192 - fixed #51 deleting repositories don't delete it's dependent objects
181 193
182 194
183 195 1.0.0 (**2010-11-02**)
184 196 ======================
185 197
186 198 - security bugfix simplehg wasn't checking for permissions on commands
187 199 other than pull or push.
188 200 - fixed doubled messages after push or pull in admin journal
189 201 - templating and css corrections, fixed repo switcher on chrome, updated titles
190 202 - admin menu accessible from options menu on repository view
191 203 - permissions cached queries
192 204
193 205 1.0.0rc4 (**2010-10-12**)
194 206 ==========================
195 207
196 208 - fixed python2.5 missing simplejson imports (thanks to Jens BΓ€ckman)
197 209 - removed cache_manager settings from sqlalchemy meta
198 210 - added sqlalchemy cache settings to ini files
199 211 - validated password length and added second try of failure on paster setup-app
200 212 - fixed setup database destroy prompt even when there was no db
201 213
202 214
203 215 1.0.0rc3 (**2010-10-11**)
204 216 =========================
205 217
206 218 - fixed i18n during installation.
207 219
208 220 1.0.0rc2 (**2010-10-11**)
209 221 =========================
210 222
211 223 - Disabled dirsize in file browser, it's causing nasty bug when dir renames
212 224 occure. After vcs is fixed it'll be put back again.
213 225 - templating/css rewrites, optimized css. No newline at end of file
@@ -1,53 +1,56 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.__init__
4 4 ~~~~~~~~~~~~~~~~~~
5 5
6 6 RhodeCode, a web based repository management based on pylons
7 7 versioning implementation: http://semver.org/
8 8
9 9 :created_on: Apr 9, 2010
10 10 :author: marcink
11 11 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
12 12 :license: GPLv3, see COPYING for more details.
13 13 """
14 14 # This program is free software; you can redistribute it and/or
15 15 # modify it under the terms of the GNU General Public License
16 16 # as published by the Free Software Foundation; version 2
17 17 # of the License or (at your opinion) any later version of the license.
18 18 #
19 19 # This program is distributed in the hope that it will be useful,
20 20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
21 21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 22 # GNU General Public License for more details.
23 23 #
24 24 # You should have received a copy of the GNU General Public License
25 25 # along with this program; if not, write to the Free Software
26 26 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
27 27 # MA 02110-1301, USA.
28 28 import platform
29 29
30 VERSION = (1, 1, 5)
30 VERSION = (1, 1, 6)
31 31 __version__ = '.'.join((str(each) for each in VERSION[:4]))
32 32 __dbversion__ = 2 #defines current db version for migrations
33 33 __platform__ = platform.system()
34 34
35 PLATFORM_WIN = ('Windows',)
36 PLATFORM_OTHERS = ('Linux', 'Darwin', 'FreeBSD',)
37
35 38 try:
36 39 from rhodecode.lib.utils import get_current_revision
37 40 _rev = get_current_revision()
38 41 except ImportError:
39 42 #this is needed when doing some setup.py operations
40 43 _rev = False
41 44
42 45 if len(VERSION) > 3 and _rev:
43 46 __version__ += ' [rev:%s]' % _rev[0]
44 47
45 48 def get_version():
46 49 """Returns shorter version (digit parts only) as string."""
47 50
48 51 return '.'.join((str(each) for each in VERSION[:3]))
49 52
50 53 BACKENDS = {
51 54 'hg': 'Mercurial repository',
52 55 #'git': 'Git repository',
53 56 }
@@ -1,614 +1,614 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.lib.auth
4 4 ~~~~~~~~~~~~~~~~~~
5 5
6 6 authentication and permission libraries
7 7
8 8 :created_on: Apr 4, 2010
9 9 :copyright: (c) 2010 by marcink.
10 10 :license: LICENSE_NAME, see LICENSE_FILE for more details.
11 11 """
12 12 # This program is free software; you can redistribute it and/or
13 13 # modify it under the terms of the GNU General Public License
14 14 # as published by the Free Software Foundation; version 2
15 15 # of the License or (at your opinion) any later version of the license.
16 16 #
17 17 # This program is distributed in the hope that it will be useful,
18 18 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 19 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 20 # GNU General Public License for more details.
21 21 #
22 22 # You should have received a copy of the GNU General Public License
23 23 # along with this program; if not, write to the Free Software
24 24 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
25 25 # MA 02110-1301, USA.
26 26
27 27 import random
28 28 import logging
29 29 import traceback
30 30
31 31 from decorator import decorator
32 32
33 33 from pylons import config, session, url, request
34 34 from pylons.controllers.util import abort, redirect
35 35 from pylons.i18n.translation import _
36 36
37 from rhodecode import __platform__
37 from rhodecode import __platform__, PLATFORM_WIN, PLATFORM_OTHERS
38 38
39 if __platform__ == 'Windows':
39 if __platform__ in PLATFORM_WIN:
40 40 from hashlib import sha256
41 if __platform__ in ('Linux', 'Darwin'):
41 if __platform__ in PLATFORM_OTHERS:
42 42 import bcrypt
43 43
44 44 from rhodecode.lib import str2bool
45 45 from rhodecode.lib.exceptions import LdapPasswordError, LdapUsernameError
46 46 from rhodecode.lib.utils import get_repo_slug
47 47 from rhodecode.lib.auth_ldap import AuthLdap
48 48
49 49 from rhodecode.model import meta
50 50 from rhodecode.model.user import UserModel
51 51 from rhodecode.model.db import Permission, RepoToPerm, Repository, \
52 52 User, UserToPerm
53 53
54 54
55 55 log = logging.getLogger(__name__)
56 56
57 57 class PasswordGenerator(object):
58 58 """This is a simple class for generating password from
59 59 different sets of characters
60 60 usage:
61 61 passwd_gen = PasswordGenerator()
62 62 #print 8-letter password containing only big and small letters of alphabet
63 63 print passwd_gen.gen_password(8, passwd_gen.ALPHABETS_BIG_SMALL)
64 64 """
65 65 ALPHABETS_NUM = r'''1234567890'''#[0]
66 66 ALPHABETS_SMALL = r'''qwertyuiopasdfghjklzxcvbnm'''#[1]
67 67 ALPHABETS_BIG = r'''QWERTYUIOPASDFGHJKLZXCVBNM'''#[2]
68 68 ALPHABETS_SPECIAL = r'''`-=[]\;',./~!@#$%^&*()_+{}|:"<>?''' #[3]
69 69 ALPHABETS_FULL = ALPHABETS_BIG + ALPHABETS_SMALL + ALPHABETS_NUM + ALPHABETS_SPECIAL#[4]
70 70 ALPHABETS_ALPHANUM = ALPHABETS_BIG + ALPHABETS_SMALL + ALPHABETS_NUM#[5]
71 71 ALPHABETS_BIG_SMALL = ALPHABETS_BIG + ALPHABETS_SMALL
72 72 ALPHABETS_ALPHANUM_BIG = ALPHABETS_BIG + ALPHABETS_NUM#[6]
73 73 ALPHABETS_ALPHANUM_SMALL = ALPHABETS_SMALL + ALPHABETS_NUM#[7]
74 74
75 75 def __init__(self, passwd=''):
76 76 self.passwd = passwd
77 77
78 78 def gen_password(self, len, type):
79 79 self.passwd = ''.join([random.choice(type) for _ in xrange(len)])
80 80 return self.passwd
81 81
82 82 class RhodeCodeCrypto(object):
83 83
84 84 @classmethod
85 85 def hash_string(cls, str_):
86 86 """
87 87 Cryptographic function used for password hashing based on pybcrypt
88 88 or pycrypto in windows
89 89
90 90 :param password: password to hash
91 91 """
92 if __platform__ == 'Windows':
92 if __platform__ in PLATFORM_WIN:
93 93 return sha256(str_).hexdigest()
94 elif __platform__ in ('Linux', 'Darwin'):
94 elif __platform__ in PLATFORM_OTHERS:
95 95 return bcrypt.hashpw(str_, bcrypt.gensalt(10))
96 96 else:
97 97 raise Exception('Unknown or unsupported platform %s' % __platform__)
98 98
99 99 @classmethod
100 100 def hash_check(cls, password, hashed):
101 101 """
102 102 Checks matching password with it's hashed value, runs different
103 103 implementation based on platform it runs on
104 104
105 105 :param password: password
106 106 :param hashed: password in hashed form
107 107 """
108 108
109 109 if __platform__ == 'Windows':
110 110 return sha256(password).hexdigest() == hashed
111 111 elif __platform__ in ('Linux', 'Darwin'):
112 112 return bcrypt.hashpw(password, hashed) == hashed
113 113 else:
114 114 raise Exception('Unknown or unsupported platform %s' % __platform__)
115 115
116 116
117 117 def get_crypt_password(password):
118 118 return RhodeCodeCrypto.hash_string(password)
119 119
120 120 def check_password(password, hashed):
121 121 return RhodeCodeCrypto.hash_check(password, hashed)
122 122
123 123 def authfunc(environ, username, password):
124 124 """
125 125 Dummy authentication function used in Mercurial/Git/ and access control,
126 126
127 127 :param environ: needed only for using in Basic auth
128 128 """
129 129 return authenticate(username, password)
130 130
131 131
132 132 def authenticate(username, password):
133 133 """
134 134 Authentication function used for access control,
135 135 firstly checks for db authentication then if ldap is enabled for ldap
136 136 authentication, also creates ldap user if not in database
137 137
138 138 :param username: username
139 139 :param password: password
140 140 """
141 141 user_model = UserModel()
142 142 user = user_model.get_by_username(username, cache=False)
143 143
144 144 log.debug('Authenticating user using RhodeCode account')
145 145 if user is not None and user.is_ldap is False:
146 146 if user.active:
147 147
148 148 if user.username == 'default' and user.active:
149 149 log.info('user %s authenticated correctly as anonymous user',
150 150 username)
151 151 return True
152 152
153 153 elif user.username == username and check_password(password, user.password):
154 154 log.info('user %s authenticated correctly', username)
155 155 return True
156 156 else:
157 157 log.warning('user %s is disabled', username)
158 158
159 159 else:
160 160 log.debug('Regular authentication failed')
161 161 user_obj = user_model.get_by_username(username, cache=False,
162 162 case_insensitive=True)
163 163
164 164 if user_obj is not None and user_obj.is_ldap is False:
165 165 log.debug('this user already exists as non ldap')
166 166 return False
167 167
168 168 from rhodecode.model.settings import SettingsModel
169 169 ldap_settings = SettingsModel().get_ldap_settings()
170 170
171 171 #======================================================================
172 172 # FALLBACK TO LDAP AUTH IN ENABLE
173 173 #======================================================================
174 174 if str2bool(ldap_settings.get('ldap_active')):
175 175 log.debug("Authenticating user using ldap")
176 176 kwargs = {
177 177 'server':ldap_settings.get('ldap_host', ''),
178 178 'base_dn':ldap_settings.get('ldap_base_dn', ''),
179 179 'port':ldap_settings.get('ldap_port'),
180 180 'bind_dn':ldap_settings.get('ldap_dn_user'),
181 181 'bind_pass':ldap_settings.get('ldap_dn_pass'),
182 182 'use_ldaps':str2bool(ldap_settings.get('ldap_ldaps')),
183 183 'ldap_version':3,
184 184 }
185 185 log.debug('Checking for ldap authentication')
186 186 try:
187 187 aldap = AuthLdap(**kwargs)
188 188 res = aldap.authenticate_ldap(username, password)
189 189 log.debug('Got ldap response %s', res)
190 190
191 191 if user_model.create_ldap(username, password):
192 192 log.info('created new ldap user')
193 193
194 194 return True
195 195 except (LdapUsernameError, LdapPasswordError,):
196 196 pass
197 197 except (Exception,):
198 198 log.error(traceback.format_exc())
199 199 pass
200 200 return False
201 201
202 202 class AuthUser(object):
203 203 """
204 204 A simple object that handles a mercurial username for authentication
205 205 """
206 206 def __init__(self):
207 207 self.username = 'None'
208 208 self.name = ''
209 209 self.lastname = ''
210 210 self.email = ''
211 211 self.user_id = None
212 212 self.is_authenticated = False
213 213 self.is_admin = False
214 214 self.permissions = {}
215 215
216 216 def __repr__(self):
217 217 return "<AuthUser('id:%s:%s')>" % (self.user_id, self.username)
218 218
219 219 def set_available_permissions(config):
220 220 """
221 221 This function will propagate pylons globals with all available defined
222 222 permission given in db. We don't wannt to check each time from db for new
223 223 permissions since adding a new permission also requires application restart
224 224 ie. to decorate new views with the newly created permission
225 225 :param config:
226 226 """
227 227 log.info('getting information about all available permissions')
228 228 try:
229 229 sa = meta.Session()
230 230 all_perms = sa.query(Permission).all()
231 231 except:
232 232 pass
233 233 finally:
234 234 meta.Session.remove()
235 235
236 236 config['available_permissions'] = [x.permission_name for x in all_perms]
237 237
238 238 def set_base_path(config):
239 239 config['base_path'] = config['pylons.app_globals'].base_path
240 240
241 241
242 242 def fill_perms(user):
243 243 """
244 244 Fills user permission attribute with permissions taken from database
245 245 :param user:
246 246 """
247 247
248 248 sa = meta.Session()
249 249 user.permissions['repositories'] = {}
250 250 user.permissions['global'] = set()
251 251
252 252 #===========================================================================
253 253 # fetch default permissions
254 254 #===========================================================================
255 255 default_user = UserModel().get_by_username('default', cache=True)
256 256
257 257 default_perms = sa.query(RepoToPerm, Repository, Permission)\
258 258 .join((Repository, RepoToPerm.repository_id == Repository.repo_id))\
259 259 .join((Permission, RepoToPerm.permission_id == Permission.permission_id))\
260 260 .filter(RepoToPerm.user == default_user).all()
261 261
262 262 if user.is_admin:
263 263 #=======================================================================
264 264 # #admin have all default rights set to admin
265 265 #=======================================================================
266 266 user.permissions['global'].add('hg.admin')
267 267
268 268 for perm in default_perms:
269 269 p = 'repository.admin'
270 270 user.permissions['repositories'][perm.RepoToPerm.repository.repo_name] = p
271 271
272 272 else:
273 273 #=======================================================================
274 274 # set default permissions
275 275 #=======================================================================
276 276
277 277 #default global
278 278 default_global_perms = sa.query(UserToPerm)\
279 279 .filter(UserToPerm.user == sa.query(User)\
280 280 .filter(User.username == 'default').one())
281 281
282 282 for perm in default_global_perms:
283 283 user.permissions['global'].add(perm.permission.permission_name)
284 284
285 285 #default repositories
286 286 for perm in default_perms:
287 287 if perm.Repository.private and not perm.Repository.user_id == user.user_id:
288 288 #disable defaults for private repos,
289 289 p = 'repository.none'
290 290 elif perm.Repository.user_id == user.user_id:
291 291 #set admin if owner
292 292 p = 'repository.admin'
293 293 else:
294 294 p = perm.Permission.permission_name
295 295
296 296 user.permissions['repositories'][perm.RepoToPerm.repository.repo_name] = p
297 297
298 298 #=======================================================================
299 299 # #overwrite default with user permissions if any
300 300 #=======================================================================
301 301 user_perms = sa.query(RepoToPerm, Permission, Repository)\
302 302 .join((Repository, RepoToPerm.repository_id == Repository.repo_id))\
303 303 .join((Permission, RepoToPerm.permission_id == Permission.permission_id))\
304 304 .filter(RepoToPerm.user_id == user.user_id).all()
305 305
306 306 for perm in user_perms:
307 307 if perm.Repository.user_id == user.user_id:#set admin if owner
308 308 p = 'repository.admin'
309 309 else:
310 310 p = perm.Permission.permission_name
311 311 user.permissions['repositories'][perm.RepoToPerm.repository.repo_name] = p
312 312 meta.Session.remove()
313 313 return user
314 314
315 315 def get_user(session):
316 316 """
317 317 Gets user from session, and wraps permissions into user
318 318 :param session:
319 319 """
320 320 user = session.get('rhodecode_user', AuthUser())
321 321 #if the user is not logged in we check for anonymous access
322 322 #if user is logged and it's a default user check if we still have anonymous
323 323 #access enabled
324 324 if user.user_id is None or user.username == 'default':
325 325 anonymous_user = UserModel().get_by_username('default', cache=True)
326 326 if anonymous_user.active is True:
327 327 #then we set this user is logged in
328 328 user.is_authenticated = True
329 329 user.user_id = anonymous_user.user_id
330 330 else:
331 331 user.is_authenticated = False
332 332
333 333 if user.is_authenticated:
334 334 user = UserModel().fill_data(user)
335 335
336 336 user = fill_perms(user)
337 337 session['rhodecode_user'] = user
338 338 session.save()
339 339 return user
340 340
341 341 #===============================================================================
342 342 # CHECK DECORATORS
343 343 #===============================================================================
344 344 class LoginRequired(object):
345 345 """Must be logged in to execute this function else
346 346 redirect to login page"""
347 347
348 348 def __call__(self, func):
349 349 return decorator(self.__wrapper, func)
350 350
351 351 def __wrapper(self, func, *fargs, **fkwargs):
352 352 user = session.get('rhodecode_user', AuthUser())
353 353 log.debug('Checking login required for user:%s', user.username)
354 354 if user.is_authenticated:
355 355 log.debug('user %s is authenticated', user.username)
356 356 return func(*fargs, **fkwargs)
357 357 else:
358 358 log.warn('user %s not authenticated', user.username)
359 359
360 360 p = ''
361 361 if request.environ.get('SCRIPT_NAME') != '/':
362 362 p += request.environ.get('SCRIPT_NAME')
363 363
364 364 p += request.environ.get('PATH_INFO')
365 365 if request.environ.get('QUERY_STRING'):
366 366 p += '?' + request.environ.get('QUERY_STRING')
367 367
368 368 log.debug('redirecting to login page with %s', p)
369 369 return redirect(url('login_home', came_from=p))
370 370
371 371 class NotAnonymous(object):
372 372 """Must be logged in to execute this function else
373 373 redirect to login page"""
374 374
375 375 def __call__(self, func):
376 376 return decorator(self.__wrapper, func)
377 377
378 378 def __wrapper(self, func, *fargs, **fkwargs):
379 379 user = session.get('rhodecode_user', AuthUser())
380 380 log.debug('Checking if user is not anonymous')
381 381
382 382 anonymous = user.username == 'default'
383 383
384 384 if anonymous:
385 385 p = ''
386 386 if request.environ.get('SCRIPT_NAME') != '/':
387 387 p += request.environ.get('SCRIPT_NAME')
388 388
389 389 p += request.environ.get('PATH_INFO')
390 390 if request.environ.get('QUERY_STRING'):
391 391 p += '?' + request.environ.get('QUERY_STRING')
392 392 return redirect(url('login_home', came_from=p))
393 393 else:
394 394 return func(*fargs, **fkwargs)
395 395
396 396 class PermsDecorator(object):
397 397 """Base class for decorators"""
398 398
399 399 def __init__(self, *required_perms):
400 400 available_perms = config['available_permissions']
401 401 for perm in required_perms:
402 402 if perm not in available_perms:
403 403 raise Exception("'%s' permission is not defined" % perm)
404 404 self.required_perms = set(required_perms)
405 405 self.user_perms = None
406 406
407 407 def __call__(self, func):
408 408 return decorator(self.__wrapper, func)
409 409
410 410
411 411 def __wrapper(self, func, *fargs, **fkwargs):
412 412 # _wrapper.__name__ = func.__name__
413 413 # _wrapper.__dict__.update(func.__dict__)
414 414 # _wrapper.__doc__ = func.__doc__
415 415 self.user = session.get('rhodecode_user', AuthUser())
416 416 self.user_perms = self.user.permissions
417 417 log.debug('checking %s permissions %s for %s %s',
418 418 self.__class__.__name__, self.required_perms, func.__name__,
419 419 self.user)
420 420
421 421 if self.check_permissions():
422 422 log.debug('Permission granted for %s %s', func.__name__, self.user)
423 423
424 424 return func(*fargs, **fkwargs)
425 425
426 426 else:
427 427 log.warning('Permission denied for %s %s', func.__name__, self.user)
428 428 #redirect with forbidden ret code
429 429 return abort(403)
430 430
431 431
432 432
433 433 def check_permissions(self):
434 434 """Dummy function for overriding"""
435 435 raise Exception('You have to write this function in child class')
436 436
437 437 class HasPermissionAllDecorator(PermsDecorator):
438 438 """Checks for access permission for all given predicates. All of them
439 439 have to be meet in order to fulfill the request
440 440 """
441 441
442 442 def check_permissions(self):
443 443 if self.required_perms.issubset(self.user_perms.get('global')):
444 444 return True
445 445 return False
446 446
447 447
448 448 class HasPermissionAnyDecorator(PermsDecorator):
449 449 """Checks for access permission for any of given predicates. In order to
450 450 fulfill the request any of predicates must be meet
451 451 """
452 452
453 453 def check_permissions(self):
454 454 if self.required_perms.intersection(self.user_perms.get('global')):
455 455 return True
456 456 return False
457 457
458 458 class HasRepoPermissionAllDecorator(PermsDecorator):
459 459 """Checks for access permission for all given predicates for specific
460 460 repository. All of them have to be meet in order to fulfill the request
461 461 """
462 462
463 463 def check_permissions(self):
464 464 repo_name = get_repo_slug(request)
465 465 try:
466 466 user_perms = set([self.user_perms['repositories'][repo_name]])
467 467 except KeyError:
468 468 return False
469 469 if self.required_perms.issubset(user_perms):
470 470 return True
471 471 return False
472 472
473 473
474 474 class HasRepoPermissionAnyDecorator(PermsDecorator):
475 475 """Checks for access permission for any of given predicates for specific
476 476 repository. In order to fulfill the request any of predicates must be meet
477 477 """
478 478
479 479 def check_permissions(self):
480 480 repo_name = get_repo_slug(request)
481 481
482 482 try:
483 483 user_perms = set([self.user_perms['repositories'][repo_name]])
484 484 except KeyError:
485 485 return False
486 486 if self.required_perms.intersection(user_perms):
487 487 return True
488 488 return False
489 489 #===============================================================================
490 490 # CHECK FUNCTIONS
491 491 #===============================================================================
492 492
493 493 class PermsFunction(object):
494 494 """Base function for other check functions"""
495 495
496 496 def __init__(self, *perms):
497 497 available_perms = config['available_permissions']
498 498
499 499 for perm in perms:
500 500 if perm not in available_perms:
501 501 raise Exception("'%s' permission in not defined" % perm)
502 502 self.required_perms = set(perms)
503 503 self.user_perms = None
504 504 self.granted_for = ''
505 505 self.repo_name = None
506 506
507 507 def __call__(self, check_Location=''):
508 508 user = session.get('rhodecode_user', False)
509 509 if not user:
510 510 return False
511 511 self.user_perms = user.permissions
512 512 self.granted_for = user.username
513 513 log.debug('checking %s %s %s', self.__class__.__name__,
514 514 self.required_perms, user)
515 515
516 516 if self.check_permissions():
517 517 log.debug('Permission granted for %s @ %s %s', self.granted_for,
518 518 check_Location, user)
519 519 return True
520 520
521 521 else:
522 522 log.warning('Permission denied for %s @ %s %s', self.granted_for,
523 523 check_Location, user)
524 524 return False
525 525
526 526 def check_permissions(self):
527 527 """Dummy function for overriding"""
528 528 raise Exception('You have to write this function in child class')
529 529
530 530 class HasPermissionAll(PermsFunction):
531 531 def check_permissions(self):
532 532 if self.required_perms.issubset(self.user_perms.get('global')):
533 533 return True
534 534 return False
535 535
536 536 class HasPermissionAny(PermsFunction):
537 537 def check_permissions(self):
538 538 if self.required_perms.intersection(self.user_perms.get('global')):
539 539 return True
540 540 return False
541 541
542 542 class HasRepoPermissionAll(PermsFunction):
543 543
544 544 def __call__(self, repo_name=None, check_Location=''):
545 545 self.repo_name = repo_name
546 546 return super(HasRepoPermissionAll, self).__call__(check_Location)
547 547
548 548 def check_permissions(self):
549 549 if not self.repo_name:
550 550 self.repo_name = get_repo_slug(request)
551 551
552 552 try:
553 553 self.user_perms = set([self.user_perms['repositories']\
554 554 [self.repo_name]])
555 555 except KeyError:
556 556 return False
557 557 self.granted_for = self.repo_name
558 558 if self.required_perms.issubset(self.user_perms):
559 559 return True
560 560 return False
561 561
562 562 class HasRepoPermissionAny(PermsFunction):
563 563
564 564 def __call__(self, repo_name=None, check_Location=''):
565 565 self.repo_name = repo_name
566 566 return super(HasRepoPermissionAny, self).__call__(check_Location)
567 567
568 568 def check_permissions(self):
569 569 if not self.repo_name:
570 570 self.repo_name = get_repo_slug(request)
571 571
572 572 try:
573 573 self.user_perms = set([self.user_perms['repositories']\
574 574 [self.repo_name]])
575 575 except KeyError:
576 576 return False
577 577 self.granted_for = self.repo_name
578 578 if self.required_perms.intersection(self.user_perms):
579 579 return True
580 580 return False
581 581
582 582 #===============================================================================
583 583 # SPECIAL VERSION TO HANDLE MIDDLEWARE AUTH
584 584 #===============================================================================
585 585
586 586 class HasPermissionAnyMiddleware(object):
587 587 def __init__(self, *perms):
588 588 self.required_perms = set(perms)
589 589
590 590 def __call__(self, user, repo_name):
591 591 usr = AuthUser()
592 592 usr.user_id = user.user_id
593 593 usr.username = user.username
594 594 usr.is_admin = user.admin
595 595
596 596 try:
597 597 self.user_perms = set([fill_perms(usr)\
598 598 .permissions['repositories'][repo_name]])
599 599 except:
600 600 self.user_perms = set()
601 601 self.granted_for = ''
602 602 self.username = user.username
603 603 self.repo_name = repo_name
604 604 return self.check_permissions()
605 605
606 606 def check_permissions(self):
607 607 log.debug('checking mercurial protocol '
608 608 'permissions for user:%s repository:%s',
609 609 self.username, self.repo_name)
610 610 if self.required_perms.intersection(self.user_perms):
611 611 log.debug('permission granted')
612 612 return True
613 613 log.debug('permission denied')
614 614 return False
@@ -1,375 +1,375 b''
1 1 ## -*- coding: utf-8 -*-
2 2 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
3 <html xmlns="http://www.w3.org/1999/xhtml" id="mainhtml">
3 <html xmlns="http://www.w3.org/1999/xhtml">
4 4 <head>
5 5 <title>${next.title()}</title>
6 6 <link rel="icon" href="${h.url('/images/icons/database_gear.png')}" type="image/png" />
7 7 <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
8 8 <meta name="robots" content="index, nofollow"/>
9 9 <!-- stylesheets -->
10 10 ${self.css()}
11 11 <!-- scripts -->
12 12 ${self.js()}
13 13 </head>
14 14 <body>
15 15 <!-- header -->
16 16 <div id="header">
17 17 <!-- user -->
18 18 <ul id="logged-user">
19 19 <li class="first">
20 20 <div class="gravatar">
21 21 <img alt="gravatar" src="${h.gravatar_url(c.rhodecode_user.email,20)}" />
22 22 </div>
23 23 <div class="account">
24 24 %if c.rhodecode_user.username == 'default':
25 25 %if h.HasPermissionAny('hg.admin', 'hg.register.auto_activate', 'hg.register.manual_activate')():
26 26 ${h.link_to('anonymous',h.url('register'),title='%s %s'%(c.rhodecode_user.name,c.rhodecode_user.lastname))}
27 27 %else:
28 28 ${h.link_to('anonymous',h.url('#'),title='%s %s'%(c.rhodecode_user.name,c.rhodecode_user.lastname))}
29 29 %endif
30 30
31 31 %else:
32 32 ${h.link_to(c.rhodecode_user.username,h.url('admin_settings_my_account'),title='%s %s'%(c.rhodecode_user.name,c.rhodecode_user.lastname))}
33 33 %endif
34 34 </div>
35 35 </li>
36 36 <li>
37 37 <a href="${h.url('home')}">${_('Home')}</a>
38 38 </li>
39 39 %if c.rhodecode_user.username != 'default':
40 40 <li>
41 41 <a href="${h.url('journal')}">${_('Journal')}</a>
42 42 ##(${c.unread_journal})</a>
43 43 </li>
44 44 %endif
45 45 %if c.rhodecode_user.username == 'default':
46 46 <li class="last highlight">${h.link_to(u'Login',h.url('login_home'))}</li>
47 47 %else:
48 48 <li class="last highlight">${h.link_to(u'Log Out',h.url('logout_home'))}</li>
49 49 %endif
50 50 </ul>
51 51 <!-- end user -->
52 52 <div id="header-inner" class="title top-left-rounded-corner top-right-rounded-corner">
53 53 <!-- logo -->
54 54 <div id="logo">
55 55 <h1><a href="${h.url('home')}">${c.rhodecode_name}</a></h1>
56 56 </div>
57 57 <!-- end logo -->
58 58 <!-- menu -->
59 59 ${self.page_nav()}
60 60 <!-- quick -->
61 61 </div>
62 62 </div>
63 63 <!-- end header -->
64 64
65 65 <!-- CONTENT -->
66 66 <div id="content">
67 67 <div class="flash_msg">
68 68 <% messages = h.flash.pop_messages() %>
69 69 % if messages:
70 70 <ul id="flash-messages">
71 71 % for message in messages:
72 72 <li class="${message.category}_msg">${message}</li>
73 73 % endfor
74 74 </ul>
75 75 % endif
76 76 </div>
77 77 <div id="main">
78 78 ${next.main()}
79 79 </div>
80 80 </div>
81 81 <!-- END CONTENT -->
82 82
83 83 <!-- footer -->
84 84 <div id="footer">
85 85 <div id="footer-inner" class="title bottom-left-rounded-corner bottom-right-rounded-corner">
86 86 <div>
87 87 <p class="footer-link">${h.link_to(_('Submit a bug'),h.url('bugtracker'))}</p>
88 88 <p class="footer-link">${h.link_to(_('GPL license'),h.url('gpl_license'))}</p>
89 89 <p class="footer-link-right"><a href="${h.url('rhodecode_official')}">RhodeCode</a> ${c.rhodecode_version} &copy; 2010-2011 by Marcin Kuzminski</p>
90 90 </div>
91 91 </div>
92 92 <script type="text/javascript">
93 93 function tooltip_activate(){
94 94 ${h.tooltip.activate()}
95 95 }
96 96 tooltip_activate();
97 97 </script>
98 98 </div>
99 99 <!-- end footer -->
100 100 </body>
101 101
102 102 </html>
103 103
104 104 ### MAKO DEFS ###
105 105 <%def name="page_nav()">
106 106 ${self.menu()}
107 107 </%def>
108 108
109 109 <%def name="menu(current=None)">
110 110 <%
111 111 def is_current(selected):
112 112 if selected == current:
113 113 return h.literal('class="current"')
114 114 %>
115 115 %if current not in ['home','admin']:
116 116 ##REGULAR MENU
117 117 <ul id="quick">
118 118 <!-- repo switcher -->
119 119 <li>
120 120 <a id="repo_switcher" title="${_('Switch repository')}" href="#">
121 121 <span class="icon">
122 122 <img src="${h.url("/images/icons/database.png")}" alt="${_('Products')}" />
123 123 </span>
124 124 <span>&darr;</span>
125 125 </a>
126 126 <ul class="repo_switcher">
127 127 %for repo in c.cached_repo_list:
128 128
129 129 %if repo['repo'].dbrepo.private:
130 130 <li><img src="${h.url("/images/icons/lock.png")}" alt="${_('Private repository')}" class="repo_switcher_type"/>${h.link_to(repo['repo'].name,h.url('summary_home',repo_name=repo['repo'].name),class_="%s" % repo['repo'].dbrepo.repo_type)}</li>
131 131 %else:
132 132 <li><img src="${h.url("/images/icons/lock_open.png")}" alt="${_('Public repository')}" class="repo_switcher_type" />${h.link_to(repo['repo'].name,h.url('summary_home',repo_name=repo['repo'].name),class_="%s" % repo['repo'].dbrepo.repo_type)}</li>
133 133 %endif
134 134 %endfor
135 135 </ul>
136 136 </li>
137 137
138 138 <li ${is_current('summary')}>
139 139 <a title="${_('Summary')}" href="${h.url('summary_home',repo_name=c.repo_name)}">
140 140 <span class="icon">
141 141 <img src="${h.url("/images/icons/clipboard_16.png")}" alt="${_('Summary')}" />
142 142 </span>
143 143 <span>${_('Summary')}</span>
144 144 </a>
145 145 </li>
146 146 ##<li ${is_current('shortlog')}>
147 147 ## <a title="${_('Shortlog')}" href="${h.url('shortlog_home',repo_name=c.repo_name)}">
148 148 ## <span class="icon">
149 149 ## <img src="${h.url("/images/icons/application_view_list.png")}" alt="${_('Shortlog')}" />
150 150 ## </span>
151 151 ## <span>${_('Shortlog')}</span>
152 152 ## </a>
153 153 ##</li>
154 154 <li ${is_current('changelog')}>
155 155 <a title="${_('Changelog')}" href="${h.url('changelog_home',repo_name=c.repo_name)}">
156 156 <span class="icon">
157 157 <img src="${h.url("/images/icons/time.png")}" alt="${_('Changelog')}" />
158 158 </span>
159 159 <span>${_('Changelog')}</span>
160 160 </a>
161 161 </li>
162 162
163 163 <li ${is_current('switch_to')}>
164 164 <a title="${_('Switch to')}" href="#">
165 165 <span class="icon">
166 166 <img src="${h.url("/images/icons/arrow_switch.png")}" alt="${_('Switch to')}" />
167 167 </span>
168 168 <span>${_('Switch to')}</span>
169 169 </a>
170 170 <ul>
171 171 <li>
172 172 ${h.link_to('%s (%s)' % (_('branches'),len(c.repository_branches.values()),),h.url('branches_home',repo_name=c.repo_name),class_='branches childs')}
173 173 <ul>
174 174 %if c.repository_branches.values():
175 175 %for cnt,branch in enumerate(c.repository_branches.items()):
176 176 <li>${h.link_to('%s - %s' % (branch[0],h.short_id(branch[1])),h.url('files_home',repo_name=c.repo_name,revision=branch[1]))}</li>
177 177 %endfor
178 178 %else:
179 179 <li>${h.link_to(_('There are no branches yet'),'#')}</li>
180 180 %endif
181 181 </ul>
182 182 </li>
183 183 <li>
184 184 ${h.link_to('%s (%s)' % (_('tags'),len(c.repository_tags.values()),),h.url('tags_home',repo_name=c.repo_name),class_='tags childs')}
185 185 <ul>
186 186 %if c.repository_tags.values():
187 187 %for cnt,tag in enumerate(c.repository_tags.items()):
188 188 <li>${h.link_to('%s - %s' % (tag[0],h.short_id(tag[1])),h.url('files_home',repo_name=c.repo_name,revision=tag[1]))}</li>
189 189 %endfor
190 190 %else:
191 191 <li>${h.link_to(_('There are no tags yet'),'#')}</li>
192 192 %endif
193 193 </ul>
194 194 </li>
195 195 </ul>
196 196 </li>
197 197 <li ${is_current('files')}>
198 198 <a title="${_('Files')}" href="${h.url('files_home',repo_name=c.repo_name)}">
199 199 <span class="icon">
200 200 <img src="${h.url("/images/icons/file.png")}" alt="${_('Files')}" />
201 201 </span>
202 202 <span>${_('Files')}</span>
203 203 </a>
204 204 </li>
205 205
206 206 <li ${is_current('options')}>
207 207 <a title="${_('Options')}" href="#">
208 208 <span class="icon">
209 209 <img src="${h.url("/images/icons/table_gear.png")}" alt="${_('Admin')}" />
210 210 </span>
211 211 <span>${_('Options')}</span>
212 212 </a>
213 213 <ul>
214 214 %if h.HasRepoPermissionAll('repository.admin')(c.repo_name):
215 215 %if h.HasPermissionAll('hg.admin')('access settings on repository'):
216 216 <li>${h.link_to(_('settings'),h.url('edit_repo',repo_name=c.repo_name),class_='settings')}</li>
217 217 %else:
218 218 <li>${h.link_to(_('settings'),h.url('repo_settings_home',repo_name=c.repo_name),class_='settings')}</li>
219 219 %endif
220 220 %endif
221 221 <li>${h.link_to(_('fork'),h.url('repo_fork_home',repo_name=c.repo_name),class_='fork')}</li>
222 222 <li>${h.link_to(_('search'),h.url('search_repo',search_repo=c.repo_name),class_='search')}</li>
223 223
224 224 %if h.HasPermissionAll('hg.admin')('access admin main page'):
225 225 <li>
226 226 ${h.link_to(_('admin'),h.url('admin_home'),class_='admin')}
227 227 <%def name="admin_menu()">
228 228 <ul>
229 229 <li>${h.link_to(_('journal'),h.url('admin_home'),class_='journal')}</li>
230 230 <li>${h.link_to(_('repositories'),h.url('repos'),class_='repos')}</li>
231 231 <li>${h.link_to(_('users'),h.url('users'),class_='users')}</li>
232 232 <li>${h.link_to(_('permissions'),h.url('edit_permission',id='default'),class_='permissions')}</li>
233 233 <li>${h.link_to(_('ldap'),h.url('ldap_home'),class_='ldap')}</li>
234 234 <li class="last">${h.link_to(_('settings'),h.url('admin_settings'),class_='settings')}</li>
235 235 </ul>
236 236 </%def>
237 237
238 238 ${admin_menu()}
239 239 </li>
240 240 %endif
241 241
242 242 </ul>
243 243 </li>
244 244
245 245 <li>
246 246 <a title="${_('Followers')}" href="#">
247 247 <span class="icon_short">
248 248 <img src="${h.url("/images/icons/heart.png")}" alt="${_('Followers')}" />
249 249 </span>
250 250 <span class="short">${c.repository_followers}</span>
251 251 </a>
252 252 </li>
253 253 <li>
254 254 <a title="${_('Forks')}" href="#">
255 255 <span class="icon_short">
256 256 <img src="${h.url("/images/icons/arrow_divide.png")}" alt="${_('Forks')}" />
257 257 </span>
258 258 <span class="short">${c.repository_forks}</span>
259 259 </a>
260 260 </li>
261 261
262 262
263 263
264 264 </ul>
265 265 %else:
266 266 ##ROOT MENU
267 267 <ul id="quick">
268 268 <li>
269 269 <a title="${_('Home')}" href="${h.url('home')}">
270 270 <span class="icon">
271 271 <img src="${h.url("/images/icons/home_16.png")}" alt="${_('Home')}" />
272 272 </span>
273 273 <span>${_('Home')}</span>
274 274 </a>
275 275 </li>
276 276 %if c.rhodecode_user.username != 'default':
277 277 <li>
278 278 <a title="${_('Journal')}" href="${h.url('journal')}">
279 279 <span class="icon">
280 280 <img src="${h.url("/images/icons/book.png")}" alt="${_('Journal')}" />
281 281 </span>
282 282 <span>${_('Journal')}</span>
283 283 </a>
284 284 </li>
285 285 %endif
286 286 <li>
287 287 <a title="${_('Search')}" href="${h.url('search')}">
288 288 <span class="icon">
289 289 <img src="${h.url("/images/icons/search_16.png")}" alt="${_('Search')}" />
290 290 </span>
291 291 <span>${_('Search')}</span>
292 292 </a>
293 293 </li>
294 294
295 295 %if h.HasPermissionAll('hg.admin')('access admin main page'):
296 296 <li ${is_current('admin')}>
297 297 <a title="${_('Admin')}" href="${h.url('admin_home')}">
298 298 <span class="icon">
299 299 <img src="${h.url("/images/icons/cog_edit.png")}" alt="${_('Admin')}" />
300 300 </span>
301 301 <span>${_('Admin')}</span>
302 302 </a>
303 303 ${admin_menu()}
304 304 </li>
305 305 %endif
306 306 </ul>
307 307 %endif
308 308 </%def>
309 309
310 310
311 311 <%def name="css()">
312 312 <link rel="stylesheet" type="text/css" href="${h.url('/css/style.css')}" media="screen" />
313 313 <link rel="stylesheet" type="text/css" href="${h.url('/css/pygments.css')}" />
314 314 <link rel="stylesheet" type="text/css" href="${h.url('/css/diff.css')}" />
315 315 </%def>
316 316
317 317 <%def name="js()">
318 318 ##<script type="text/javascript" src="${h.url('/js/yui/utilities/utilities.js')}"></script>
319 319 ##<script type="text/javascript" src="${h.url('/js/yui/container/container.js')}"></script>
320 320 ##<script type="text/javascript" src="${h.url('/js/yui/datasource/datasource.js')}"></script>
321 321 ##<script type="text/javascript" src="${h.url('/js/yui/autocomplete/autocomplete.js')}"></script>
322 322 ##<script type="text/javascript" src="${h.url('/js/yui/selector/selector-min.js')}"></script>
323 323
324 324 <script type="text/javascript" src="${h.url('/js/yui2a.js')}"></script>
325 325 <!--[if IE]><script language="javascript" type="text/javascript" src="${h.url('/js/excanvas.min.js')}"></script><![endif]-->
326 326 <script type="text/javascript" src="${h.url('/js/yui.flot.js')}"></script>
327 327
328 328 <script type="text/javascript">
329 329 var base_url = "${h.url('toggle_following')}";
330 330 var YUC = YAHOO.util.Connect;
331 331 var YUD = YAHOO.util.Dom;
332 332 var YUE = YAHOO.util.Event;
333 333
334 334 function onSuccess(target){
335 335
336 336 var f = YUD.get(target.id);
337 337 if(f.getAttribute('class')=='follow'){
338 338 f.setAttribute('class','following');
339 339 f.setAttribute('title',"${_('Stop following this repository')}");
340 340 }
341 341 else{
342 342 f.setAttribute('class','follow');
343 343 f.setAttribute('title',"${_('Start following this repository')}");
344 344 }
345 345 }
346 346
347 347 function toggleFollowingUser(fallows_user_id,token){
348 348 args = 'follows_user_id='+fallows_user_id;
349 349 args+= '&amp;auth_token='+token;
350 350 YUC.asyncRequest('POST',base_url,{
351 351 success:function(o){
352 352 onSuccess();
353 353 }
354 354 },args); return false;
355 355 }
356 356
357 357 function toggleFollowingRepo(target,fallows_repo_id,token){
358 358
359 359 args = 'follows_repo_id='+fallows_repo_id;
360 360 args+= '&amp;auth_token='+token;
361 361 YUC.asyncRequest('POST',base_url,{
362 362 success:function(o){
363 363 onSuccess(target);
364 364 }
365 365 },args); return false;
366 366 }
367 367 </script>
368 368
369 369 </%def>
370 370
371 371 <%def name="breadcrumbs()">
372 372 <div class="breadcrumbs">
373 373 ${self.breadcrumbs_links()}
374 374 </div>
375 375 </%def> No newline at end of file
@@ -1,54 +1,54 b''
1 1 ## -*- coding: utf-8 -*-
2 2 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
3 <html xmlns="http://www.w3.org/1999/xhtml" id="mainhtml">
3 <html xmlns="http://www.w3.org/1999/xhtml">
4 4 <head>
5 5 <title>Error - ${c.error_message}</title>
6 6 <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
7 7 %if c.redirect_time:
8 8 <meta http-equiv="refresh" content="${c.redirect_time}; url=${c.url_redirect}"/>
9 9 %endif
10 10 <link rel="icon" href="${h.url('/images/icons/database_gear.png')}" type="image/png" />
11 11 <meta name="robots" content="index, nofollow"/>
12 12
13 13 <!-- stylesheets -->
14 14 <link rel="stylesheet" type="text/css" href="${h.url('/css/style.css')}" media="screen" />
15 15 <style type="text/css">
16 16 #main_div{
17 17 border: 0px solid #000;
18 18 width: 500px;
19 19 margin: auto;
20 20 text-align: center;
21 21 margin-top: 200px;
22 22 font-size: 1.6em;
23 23 }
24 24 .error_message{
25 25 text-align: center;
26 26 color:#003367;
27 27 font-size: 1.6em;
28 28 margin:10px;
29 29 }
30 30 </style>
31 31
32 32 </head>
33 33 <body>
34 34
35 35 <div id="login">
36 36 <div class="table">
37 37 <div id="main_div">
38 38 <div style="font-size:2.0em;margin: 10px">${c.rhodecode_name}</div>
39 39 <h1 class="error_message">${c.error_message}</h1>
40 40
41 41 <p>${c.error_explanation}</p>
42 42
43 43 %if c.redirect_time:
44 44 <p>${_('You will be redirected to %s in %s seconds') % (c.redirect_module,c.redirect_time)}</p>
45 45 %endif
46 46
47 47 </div>
48 48 </div>
49 49 <!-- end login -->
50 50 </div>
51 51 </body>
52 52
53 53 </html>
54 54
@@ -1,82 +1,82 b''
1 1 ## -*- coding: utf-8 -*-
2 2 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
3 <html xmlns="http://www.w3.org/1999/xhtml" id="mainhtml">
3 <html xmlns="http://www.w3.org/1999/xhtml">
4 4 <head>
5 5 <title>${_('Sign In')} - ${c.rhodecode_name}</title>
6 6 <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
7 7 <link rel="icon" href="${h.url("/images/icons/database_gear.png")}" type="image/png" />
8 8 <meta name="robots" content="index, nofollow"/>
9 9
10 10 <!-- stylesheets -->
11 11 <link rel="stylesheet" type="text/css" href="${h.url('/css/style.css')}" media="screen" />
12 12
13 13 </head>
14 14 <body>
15 15 <div id="login">
16 16 <div class="flash_msg">
17 17 <% messages = h.flash.pop_messages() %>
18 18 % if messages:
19 19 <ul id="flash-messages">
20 20 % for message in messages:
21 21 <li class="${message.category}_msg">${message}</li>
22 22 % endfor
23 23 </ul>
24 24 % endif
25 25 </div>
26 26 <!-- login -->
27 27 <div class="title top-left-rounded-corner top-right-rounded-corner">
28 28 <h5>${_('Sign In to')} ${c.rhodecode_name}</h5>
29 29 </div>
30 30 <div class="inner">
31 31 ${h.form(h.url.current(came_from=c.came_from))}
32 32 <div class="form">
33 33 <!-- fields -->
34 34
35 35 <div class="fields">
36 36 <div class="field">
37 37 <div class="label">
38 38 <label for="username">${_('Username')}:</label>
39 39 </div>
40 40 <div class="input">
41 41 ${h.text('username',class_='focus',size=40)}
42 42 </div>
43 43
44 44 </div>
45 45 <div class="field">
46 46 <div class="label">
47 47 <label for="password">${_('Password')}:</label>
48 48 </div>
49 49 <div class="input">
50 50 ${h.password('password',class_='focus',size=40)}
51 51 </div>
52 52
53 53 </div>
54 54 ##<div class="field">
55 55 ## <div class="checkbox">
56 56 ## <input type="checkbox" id="remember" name="remember" />
57 57 ## <label for="remember">Remember me</label>
58 58 ## </div>
59 59 ##</div>
60 60 <div class="buttons">
61 61 ${h.submit('sign_in','Sign In',class_="ui-button")}
62 62 </div>
63 63 </div>
64 64 <!-- end fields -->
65 65 <!-- links -->
66 66 <div class="links">
67 67 ${h.link_to(_('Forgot your password ?'),h.url('reset_password'))}
68 68 %if h.HasPermissionAny('hg.admin', 'hg.register.auto_activate', 'hg.register.manual_activate')():
69 69 /
70 70 ${h.link_to(_("Don't have an account ?"),h.url('register'))}
71 71 %endif
72 72 </div>
73 73
74 74 <!-- end links -->
75 75 </div>
76 76 ${h.end_form()}
77 77 </div>
78 78 <!-- end login -->
79 79 </div>
80 80 </body>
81 81 </html>
82 82
@@ -1,48 +1,48 b''
1 1 ## -*- coding: utf-8 -*-
2 2 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
3 <html xmlns="http://www.w3.org/1999/xhtml" id="mainhtml">
3 <html xmlns="http://www.w3.org/1999/xhtml">
4 4 <head>
5 5 <title>${_('Reset You password')} - ${c.rhodecode_name}</title>
6 6 <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
7 7 <link rel="icon" href="${h.url("/images/hgicon.png")}" type="image/png" />
8 8 <meta name="robots" content="index, nofollow"/>
9 9
10 10 <!-- stylesheets -->
11 11 <link rel="stylesheet" type="text/css" href="${h.url('/css/style.css')}" media="screen" />
12 12
13 13 </head>
14 14 <body>
15 15 <div id="register">
16 16
17 17 <div class="title top-left-rounded-corner top-right-rounded-corner">
18 18 <h5>${_('Reset You password to')} ${c.rhodecode_name}</h5>
19 19 </div>
20 20 <div class="inner">
21 21 ${h.form(url('password_reset'))}
22 22 <div class="form">
23 23 <!-- fields -->
24 24 <div class="fields">
25 25
26 26 <div class="field">
27 27 <div class="label">
28 28 <label for="email">${_('Email address')}:</label>
29 29 </div>
30 30 <div class="input">
31 31 ${h.text('email')}
32 32 </div>
33 33 </div>
34 34
35 35 <div class="buttons">
36 36 <div class="nohighlight">
37 37 ${h.submit('send','Reset my password',class_="ui-button")}
38 38 <div class="activation_msg">${_('Your new password will be send to matching email address')}</div>
39 39 </div>
40 40 </div>
41 41 </div>
42 42 </div>
43 43 ${h.end_form()}
44 44 </div>
45 45 </div>
46 46 </body>
47 47 </html>
48 48
@@ -1,96 +1,96 b''
1 1 ## -*- coding: utf-8 -*-
2 2 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
3 <html xmlns="http://www.w3.org/1999/xhtml" id="mainhtml">
3 <html xmlns="http://www.w3.org/1999/xhtml">
4 4 <head>
5 5 <title>${_('Sign Up')} - ${c.rhodecode_name}</title>
6 6 <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
7 7 <link rel="icon" href="${h.url("/images/hgicon.png")}" type="image/png" />
8 8 <meta name="robots" content="index, nofollow"/>
9 9
10 10 <!-- stylesheets -->
11 11 <link rel="stylesheet" type="text/css" href="${h.url('/css/style.css')}" media="screen" />
12 12
13 13 </head>
14 14 <body>
15 15 <div id="register">
16 16
17 17 <div class="title top-left-rounded-corner top-right-rounded-corner">
18 18 <h5>${_('Sign Up to')} ${c.rhodecode_name}</h5>
19 19 </div>
20 20 <div class="inner">
21 21 ${h.form(url('register'))}
22 22 <div class="form">
23 23 <!-- fields -->
24 24 <div class="fields">
25 25 <div class="field">
26 26 <div class="label">
27 27 <label for="username">${_('Username')}:</label>
28 28 </div>
29 29 <div class="input">
30 30 ${h.text('username',class_="medium")}
31 31 </div>
32 32 </div>
33 33
34 34 <div class="field">
35 35 <div class="label">
36 36 <label for="password">${_('Password')}:</label>
37 37 </div>
38 38 <div class="input">
39 39 ${h.password('password',class_="medium")}
40 40 </div>
41 41 </div>
42 42
43 43 <div class="field">
44 44 <div class="label">
45 45 <label for="password">${_('Re-enter password')}:</label>
46 46 </div>
47 47 <div class="input">
48 48 ${h.password('password_confirmation',class_="medium")}
49 49 </div>
50 50 </div>
51 51
52 52 <div class="field">
53 53 <div class="label">
54 54 <label for="name">${_('First Name')}:</label>
55 55 </div>
56 56 <div class="input">
57 57 ${h.text('name',class_="medium")}
58 58 </div>
59 59 </div>
60 60
61 61 <div class="field">
62 62 <div class="label">
63 63 <label for="lastname">${_('Last Name')}:</label>
64 64 </div>
65 65 <div class="input">
66 66 ${h.text('lastname',class_="medium")}
67 67 </div>
68 68 </div>
69 69
70 70 <div class="field">
71 71 <div class="label">
72 72 <label for="email">${_('Email')}:</label>
73 73 </div>
74 74 <div class="input">
75 75 ${h.text('email',class_="medium")}
76 76 </div>
77 77 </div>
78 78
79 79 <div class="buttons">
80 80 <div class="nohighlight">
81 81 ${h.submit('sign_up','Sign Up',class_="ui-button")}
82 82 %if c.auto_active:
83 83 <div class="activation_msg">${_('Your account will be activated right after registration')}</div>
84 84 %else:
85 85 <div class="activation_msg">${_('Your account must wait for activation by administrator')}</div>
86 86 %endif
87 87 </div>
88 88 </div>
89 89 </div>
90 90 </div>
91 91 ${h.end_form()}
92 92 </div>
93 93 </div>
94 94 </body>
95 95 </html>
96 96
@@ -1,113 +1,116 b''
1 1 import sys
2 2 from rhodecode import get_version
3 3 from rhodecode import __platform__
4 4
5 5 py_version = sys.version_info
6 6
7 if py_version < (2, 5):
8 raise Exception('RhodeCode requires python 2.5 or later')
9
7 10 requirements = [
8 11 "Pylons==1.0.0",
9 12 "WebHelpers==1.2",
10 13 "SQLAlchemy==0.6.6",
11 14 "Mako==0.4.0",
12 15 "vcs==0.1.11",
13 16 "pygments==1.4.0",
14 17 "mercurial==1.7.5",
15 18 "whoosh==1.3.4",
16 19 "celery==2.2.4",
17 20 "babel",
18 21 ]
19 22
20 23 classifiers = ['Development Status :: 5 - Production/Stable',
21 24 'Environment :: Web Environment',
22 25 'Framework :: Pylons',
23 26 'Intended Audience :: Developers',
24 27 'License :: OSI Approved :: BSD License',
25 28 'Operating System :: OS Independent',
26 29 'Programming Language :: Python', ]
27 30
28 31 if py_version < (2, 6):
29 32 requirements.append("simplejson")
30 33 requirements.append("pysqlite")
31 34
32 35 if __platform__ in ('Linux', 'Darwin'):
33 36 requirements.append("py-bcrypt")
34 37
35 38
36 39 #additional files from project that goes somewhere in the filesystem
37 40 #relative to sys.prefix
38 41 data_files = []
39 42
40 43 #additional files that goes into package itself
41 44 package_data = {'rhodecode': ['i18n/*/LC_MESSAGES/*.mo', ], }
42 45
43 46 description = ('Mercurial repository browser/management with '
44 47 'build in push/pull server and full text search')
45 48 keywords = ' '.join (['rhodecode', 'rhodiumcode', 'mercurial', 'git',
46 49 'repository management', 'hgweb replacement'
47 50 'hgwebdir', 'gitweb replacement', 'serving hgweb',
48 51 ])
49 52 #long description
50 53 try:
51 54 readme_file = 'README.rst'
52 55 changelog_file = 'docs/changelog.rst'
53 56 long_description = open(readme_file).read() + '\n\n' + \
54 57 open(changelog_file).read()
55 58
56 59 except IOError, err:
57 60 sys.stderr.write("[WARNING] Cannot find file specified as "
58 61 "long_description (%s)\n or changelog (%s) skipping that file" \
59 62 % (readme_file, changelog_file))
60 63 long_description = description
61 64
62 65
63 66 try:
64 67 from setuptools import setup, find_packages
65 68 except ImportError:
66 69 from ez_setup import use_setuptools
67 70 use_setuptools()
68 71 from setuptools import setup, find_packages
69 72 #packages
70 73 packages = find_packages(exclude=['ez_setup'])
71 74
72 75 setup(
73 76 name='RhodeCode',
74 77 version=get_version(),
75 78 description=description,
76 79 long_description=long_description,
77 80 keywords=keywords,
78 license='BSD',
81 license='GPLv3',
79 82 author='Marcin Kuzminski',
80 83 author_email='marcin@python-works.com',
81 84 url='http://rhodecode.org',
82 85 install_requires=requirements,
83 86 classifiers=classifiers,
84 87 setup_requires=["PasteScript>=1.6.3"],
85 88 data_files=data_files,
86 89 packages=packages,
87 90 include_package_data=True,
88 91 test_suite='nose.collector',
89 92 package_data=package_data,
90 93 message_extractors={'rhodecode': [
91 94 ('**.py', 'python', None),
92 95 ('templates/**.mako', 'mako', {'input_encoding': 'utf-8'}),
93 96 ('templates/**.html', 'mako', {'input_encoding': 'utf-8'}),
94 97 ('public/**', 'ignore', None)]},
95 98 zip_safe=False,
96 99 paster_plugins=['PasteScript', 'Pylons'],
97 100 entry_points="""
98 101 [paste.app_factory]
99 102 main = rhodecode.config.middleware:make_app
100 103
101 104 [paste.app_install]
102 105 main = pylons.util:PylonsInstaller
103 106
104 107 [paste.global_paster_command]
105 108 make-index = rhodecode.lib.indexers:MakeIndex
106 109 upgrade-db = rhodecode.lib.dbmigrate:UpgradeDb
107 110 celeryd=rhodecode.lib.celerypylons.commands:CeleryDaemonCommand
108 111 celerybeat=rhodecode.lib.celerypylons.commands:CeleryBeatCommand
109 112 camqadm=rhodecode.lib.celerypylons.commands:CAMQPAdminCommand
110 113 celeryev=rhodecode.lib.celerypylons.commands:CeleryEventCommand
111 114
112 115 """,
113 116 )
General Comments 0
You need to be logged in to leave comments. Login now