##// END OF EJS Templates
fixes for stable
marcink -
r1216:8363b0d2 default
parent child Browse files
Show More
@@ -1,132 +1,141 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 The latest source for RhodeCode can be obtained from official RhodeCode instance
34 The latest sources 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 bitbucket
37
38 MIRRORS:
39
40 Issue tracker and sources at bitbucket_
41
38 42 http://bitbucket.org/marcinkuzminski/rhodecode
39 43
44 Sources at github_
45
46 https://github.com/marcinkuzminski/rhodecode
47
40 48 Installation
41 49 ------------
42 50
43 51 Please visit http://packages.python.org/RhodeCode/installation.html
44 52
45 53
46 54 RhodeCode Features
47 55 ------------------
48 56
49 57 - Has it's own middleware to handle mercurial_ protocol requests.
50 58 Each request can be logged and authenticated.
51 - Runs on threads unlike hgweb. You can make multiple pulls/pushes simultaneous.
59 - Runs on threads unlike hgweb. You can make multiple pulls/pushes simultaneous.
52 60 Supports http/https and LDAP
53 61 - Full permissions (private/read/write/admin) and authentication per project.
54 62 One account for web interface and mercurial_ push/pull/clone operations.
55 63 - Mako templates let's you customize the look and feel of the application.
56 64 - Beautiful diffs, annotations and source code browsing all colored by pygments.
57 65 - Mercurial_ branch graph and yui-flot powered graphs with zooming and statistics
58 66 - Admin interface with user/permission management. Admin activity journal, logs
59 67 pulls, pushes, forks, registrations and other actions made by all users.
60 68 - Server side forks. It is possible to fork a project and modify it freely without
61 69 breaking the main repository.
62 70 - Full text search powered by Whoosh on the source files, and file names.
63 71 Build in indexing daemons, with optional incremental index build
64 72 (no external search servers required all in one application)
65 73 - Setup project descriptions and info inside built in db for easy, non
66 74 file-system operations
67 75 - Intelligent cache with invalidation after push or project change, provides high
68 76 performance and always up to date data.
69 77 - Rss / atom feeds, gravatar support, download sources as zip/tar/gz
70 78 - Async tasks for speed and performance using celery_ (works without them too)
71 79 - Backup scripts can do backup of whole app and send it over scp to desired
72 80 location
73 81 - Based on pylons / sqlalchemy / sqlite / whoosh / vcs
74 82
75 83
76 84 .. include:: ./docs/screenshots.rst
77 85
78 86
79 87 Incoming / Plans
80 88 ----------------
81 89
82 90 - Project grouping
83 91 - User groups/teams
84 92 - SSH based authentication with server side key management
85 93 - Code review (probably based on hg-review)
86 94 - Full git_ support, with push/pull server (currently in beta tests)
87 95 - Redmine integration
88 96 - Public accessible activity feeds
89 97 - Commit based built in wiki system
90 98 - Clone points and cloning from remote repositories into RhodeCode
91 99 - More statistics and graph (global annotation + some more statistics)
92 100 - Other advancements as development continues (or you can of course make additions and or requests)
93 101
94 102 License
95 103 -------
96 104
97 ``RhodeCode`` is released under the GPL_ license.
105 ``RhodeCode`` is released under the GPLv3 license.
98 106
99 107
100 108 Mailing group Q&A
101 109 -----------------
102 110
103 111 Join the `Google group <http://groups.google.com/group/rhodecode>`_
104 112
105 113 Open an issue at `issue tracker <http://bitbucket.org/marcinkuzminski/rhodecode/issues>`_
106 114
107 115 Join #rhodecode on FreeNode (irc.freenode.net)
108 116 or use http://webchat.freenode.net/?channels=rhodecode for web access to irc.
109 117
110 118 Online documentation
111 119 --------------------
112 120
113 121 Online documentation for the current version of RhodeCode is available at
114 122 http://packages.python.org/RhodeCode/.
115 123 You may also build the documentation for yourself - go into ``docs/`` and run::
116 124
117 125 make html
118 126
119 127 (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``)
128 have sphinx_ installed you can install it via the command:
129 ``easy_install sphinx``)
121 130
122 131 .. _virtualenv: http://pypi.python.org/pypi/virtualenv
123 132 .. _python: http://www.python.org/
124 133 .. _sphinx: http://sphinx.pocoo.org/
125 134 .. _mercurial: http://mercurial.selenic.com/
126 135 .. _bitbucket: http://bitbucket.org/
136 .. _github: http://github.com/
127 137 .. _subversion: http://subversion.tigris.org/
128 138 .. _git: http://git-scm.com/
129 139 .. _celery: http://celeryproject.org/
130 140 .. _Sphinx: http://sphinx.pocoo.org/
131 .. _GPL: http://www.gnu.org/licenses/gpl.html
132 141 .. _vcs: http://pypi.python.org/pypi/vcs No newline at end of file
@@ -1,249 +1,251 b''
1 1 .. _changelog:
2 2
3 3 Changelog
4 4 =========
5 5
6 6
7 1.1.7 (**2011-03-23**)
7 1.1.8 (**2011-04-XX**)
8 8 ======================
9 9
10 10 news
11 11 ----
12 12
13 13 fixes
14 14 -----
15 15
16 16 - fixed #140 freeze of python dateutil library, since new version is python2.x
17 17 incompatible
18 - setup-app will check for write permission in given path
19
18 20
19 21 1.1.7 (**2011-03-23**)
20 22 ======================
21 23
22 24 news
23 25 ----
24 26
25 27 fixes
26 28 -----
27 29
28 30 - fixed (again) #136 installation support for FreeBSD
29 31
30 32
31 33 1.1.6 (**2011-03-21**)
32 34 ======================
33 35
34 36 news
35 37 ----
36 38
37 39 fixes
38 40 -----
39 41
40 42 - fixed #136 installation support for FreeBSD
41 43 - RhodeCode will check for python version during installation
42 44
43 45 1.1.5 (**2011-03-17**)
44 46 ======================
45 47
46 48 news
47 49 ----
48 50
49 51 - basic windows support, by exchanging pybcrypt into sha256 for windows only
50 52 highly inspired by idea of mantis406
51 53
52 54 fixes
53 55 -----
54 56
55 57 - fixed sorting by author in main page
56 58 - fixed crashes with diffs on binary files
57 59 - fixed #131 problem with boolean values for LDAP
58 60 - fixed #122 mysql problems thanks to striker69
59 61 - fixed problem with errors on calling raw/raw_files/annotate functions
60 62 with unknown revisions
61 63 - fixed returned rawfiles attachment names with international character
62 64 - cleaned out docs, big thanks to Jason Harris
63 65
64 66 1.1.4 (**2011-02-19**)
65 67 ======================
66 68
67 69 news
68 70 ----
69 71
70 72 fixes
71 73 -----
72 74
73 75 - fixed formencode import problem on settings page, that caused server crash
74 76 when that page was accessed as first after server start
75 77 - journal fixes
76 78 - fixed option to access repository just by entering http://server/<repo_name>
77 79
78 80
79 81 1.1.3 (**2011-02-16**)
80 82 ======================
81 83
82 84 news
83 85 ----
84 86
85 87 - implemented #102 allowing the '.' character in username
86 88 - added option to access repository just by entering http://server/<repo_name>
87 89 - celery task ignores result for better performance
88 90
89 91 fixes
90 92 -----
91 93
92 94 - fixed ehlo command and non auth mail servers on smtp_lib. Thanks to
93 95 apollo13 and Johan Walles
94 96 - small fixes in journal
95 97 - fixed problems with getting setting for celery from .ini files
96 98 - registration, password reset and login boxes share the same title as main
97 99 application now
98 100 - fixed #113: to high permissions to fork repository
99 101 - fixed problem with '[' chars in commit messages in journal
100 102 - removed issue with space inside renamed repository after deletion
101 103 - db transaction fixes when filesystem repository creation failed
102 104 - fixed #106 relation issues on databases different than sqlite
103 105 - fixed static files paths links to use of url() method
104 106
105 107 1.1.2 (**2011-01-12**)
106 108 ======================
107 109
108 110 news
109 111 ----
110 112
111 113
112 114 fixes
113 115 -----
114 116
115 117 - fixes #98 protection against float division of percentage stats
116 118 - fixed graph bug
117 119 - forced webhelpers version since it was making troubles during installation
118 120
119 121 1.1.1 (**2011-01-06**)
120 122 ======================
121 123
122 124 news
123 125 ----
124 126
125 127 - added force https option into ini files for easier https usage (no need to
126 128 set server headers with this options)
127 129 - small css updates
128 130
129 131 fixes
130 132 -----
131 133
132 134 - fixed #96 redirect loop on files view on repositories without changesets
133 135 - fixed #97 unicode string passed into server header in special cases (mod_wsgi)
134 136 and server crashed with errors
135 137 - fixed large tooltips problems on main page
136 138 - fixed #92 whoosh indexer is more error proof
137 139
138 140 1.1.0 (**2010-12-18**)
139 141 ======================
140 142
141 143 news
142 144 ----
143 145
144 146 - rewrite of internals for vcs >=0.1.10
145 147 - uses mercurial 1.7 with dotencode disabled for maintaining compatibility
146 148 with older clients
147 149 - anonymous access, authentication via ldap
148 150 - performance upgrade for cached repos list - each repository has it's own
149 151 cache that's invalidated when needed.
150 152 - performance upgrades on repositories with large amount of commits (20K+)
151 153 - main page quick filter for filtering repositories
152 154 - user dashboards with ability to follow chosen repositories actions
153 155 - sends email to admin on new user registration
154 156 - added cache/statistics reset options into repository settings
155 157 - more detailed action logger (based on hooks) with pushed changesets lists
156 158 and options to disable those hooks from admin panel
157 159 - introduced new enhanced changelog for merges that shows more accurate results
158 160 - new improved and faster code stats (based on pygments lexers mapping tables,
159 161 showing up to 10 trending sources for each repository. Additionally stats
160 162 can be disabled in repository settings.
161 163 - gui optimizations, fixed application width to 1024px
162 164 - added cut off (for large files/changesets) limit into config files
163 165 - whoosh, celeryd, upgrade moved to paster command
164 166 - other than sqlite database backends can be used
165 167
166 168 fixes
167 169 -----
168 170
169 171 - fixes #61 forked repo was showing only after cache expired
170 172 - fixes #76 no confirmation on user deletes
171 173 - fixes #66 Name field misspelled
172 174 - fixes #72 block user removal when he owns repositories
173 175 - fixes #69 added password confirmation fields
174 176 - fixes #87 RhodeCode crashes occasionally on updating repository owner
175 177 - fixes #82 broken annotations on files with more than 1 blank line at the end
176 178 - a lot of fixes and tweaks for file browser
177 179 - fixed detached session issues
178 180 - fixed when user had no repos he would see all repos listed in my account
179 181 - fixed ui() instance bug when global hgrc settings was loaded for server
180 182 instance and all hgrc options were merged with our db ui() object
181 183 - numerous small bugfixes
182 184
183 185 (special thanks for TkSoh for detailed feedback)
184 186
185 187
186 188 1.0.2 (**2010-11-12**)
187 189 ======================
188 190
189 191 news
190 192 ----
191 193
192 194 - tested under python2.7
193 195 - bumped sqlalchemy and celery versions
194 196
195 197 fixes
196 198 -----
197 199
198 200 - fixed #59 missing graph.js
199 201 - fixed repo_size crash when repository had broken symlinks
200 202 - fixed python2.5 crashes.
201 203
202 204
203 205 1.0.1 (**2010-11-10**)
204 206 ======================
205 207
206 208 news
207 209 ----
208 210
209 211 - small css updated
210 212
211 213 fixes
212 214 -----
213 215
214 216 - fixed #53 python2.5 incompatible enumerate calls
215 217 - fixed #52 disable mercurial extension for web
216 218 - fixed #51 deleting repositories don't delete it's dependent objects
217 219
218 220
219 221 1.0.0 (**2010-11-02**)
220 222 ======================
221 223
222 224 - security bugfix simplehg wasn't checking for permissions on commands
223 225 other than pull or push.
224 226 - fixed doubled messages after push or pull in admin journal
225 227 - templating and css corrections, fixed repo switcher on chrome, updated titles
226 228 - admin menu accessible from options menu on repository view
227 229 - permissions cached queries
228 230
229 231 1.0.0rc4 (**2010-10-12**)
230 232 ==========================
231 233
232 234 - fixed python2.5 missing simplejson imports (thanks to Jens BΓ€ckman)
233 235 - removed cache_manager settings from sqlalchemy meta
234 236 - added sqlalchemy cache settings to ini files
235 237 - validated password length and added second try of failure on paster setup-app
236 238 - fixed setup database destroy prompt even when there was no db
237 239
238 240
239 241 1.0.0rc3 (**2010-10-11**)
240 242 =========================
241 243
242 244 - fixed i18n during installation.
243 245
244 246 1.0.0rc2 (**2010-10-11**)
245 247 =========================
246 248
247 249 - Disabled dirsize in file browser, it's causing nasty bug when dir renames
248 250 occure. After vcs is fixed it'll be put back again.
249 251 - templating/css rewrites, optimized css. No newline at end of file
@@ -1,477 +1,491 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.lib.db_manage
4 4 ~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 Database creation, and setup module for RhodeCode. Used for creation
7 7 of database as well as for migration operations
8
8
9 9 :created_on: Apr 10, 2010
10 10 :author: marcink
11 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
12 12 :license: GPLv3, see COPYING for more details.
13 13 """
14 # This program is free software; you can redistribute it and/or
15 # modify it under the terms of the GNU General Public License
16 # as published by the Free Software Foundation; version 2
17 # of the License or (at your opinion) any later version of the license.
18 #
14 # This program is free software: you can redistribute it and/or modify
15 # it under the terms of the GNU General Public License as published by
16 # the Free Software Foundation, either version 3 of the License, or
17 # (at your option) any later version.
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 # along with this program; if not, write to the Free Software
26 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
27 # MA 02110-1301, USA.
25 # along with this program. If not, see <http://www.gnu.org/licenses/>.
28 26
29 27 import os
30 28 import sys
31 29 import uuid
32 30 import logging
33 31 from os.path import dirname as dn, join as jn
34 32
35 33 from rhodecode import __dbversion__
36 34 from rhodecode.model import meta
37 35
38 36 from rhodecode.lib.auth import get_crypt_password
39 37 from rhodecode.lib.utils import ask_ok
40 38 from rhodecode.model import init_model
41 from rhodecode.model.db import User, Permission, RhodeCodeUi, RhodeCodeSettings, \
42 UserToPerm, DbMigrateVersion
39 from rhodecode.model.db import User, Permission, RhodeCodeUi, \
40 RhodeCodeSettings, UserToPerm, DbMigrateVersion
43 41
44 42 from sqlalchemy.engine import create_engine
45 43
46 44 log = logging.getLogger(__name__)
47 45
46
48 47 class DbManage(object):
49 48 def __init__(self, log_sql, dbconf, root, tests=False):
50 49 self.dbname = dbconf.split('/')[-1]
51 50 self.tests = tests
52 51 self.root = root
53 52 self.dburi = dbconf
54 53 self.log_sql = log_sql
55 54 self.db_exists = False
56 55 self.init_db()
57 56
58 57 def init_db(self):
59 58 engine = create_engine(self.dburi, echo=self.log_sql)
60 59 init_model(engine)
61 60 self.sa = meta.Session()
62 61
63 62 def create_tables(self, override=False):
64 63 """Create a auth database
65 64 """
66 65
67 66 log.info("Any existing database is going to be destroyed")
68 67 if self.tests:
69 68 destroy = True
70 69 else:
71 70 destroy = ask_ok('Are you sure to destroy old database ? [y/n]')
72 71 if not destroy:
73 72 sys.exit()
74 73 if destroy:
75 74 meta.Base.metadata.drop_all()
76 75
77 76 checkfirst = not override
78 77 meta.Base.metadata.create_all(checkfirst=checkfirst)
79 78 log.info('Created tables for %s', self.dbname)
80 79
81
82
83 80 def set_db_version(self):
84 81 try:
85 82 ver = DbMigrateVersion()
86 83 ver.version = __dbversion__
87 84 ver.repository_id = 'rhodecode_db_migrations'
88 85 ver.repository_path = 'versions'
89 86 self.sa.add(ver)
90 87 self.sa.commit()
91 88 except:
92 89 self.sa.rollback()
93 90 raise
94 91 log.info('db version set to: %s', __dbversion__)
95 92
93 def upgrade(self):
94 """Upgrades given database schema to given revision following
95 all needed steps, to perform the upgrade
96 96
97 def upgrade(self):
98 """Upgrades given database schema to given revision following
99 all needed steps, to perform the upgrade
100
101 :param revision: revision to upgrade to
102 97 """
103 98
104 99 from rhodecode.lib.dbmigrate.migrate.versioning import api
105 100 from rhodecode.lib.dbmigrate.migrate.exceptions import \
106 101 DatabaseNotControlledError
107 102
108 103 upgrade = ask_ok('You are about to perform database upgrade, make '
109 104 'sure You backed up your database before. '
110 105 'Continue ? [y/n]')
111 106 if not upgrade:
112 107 sys.exit('Nothing done')
113 108
114 109 repository_path = jn(dn(dn(dn(os.path.realpath(__file__)))),
115 110 'rhodecode/lib/dbmigrate')
116 111 db_uri = self.dburi
117 112
118 113 try:
119 114 curr_version = api.db_version(db_uri, repository_path)
120 115 msg = ('Found current database under version'
121 116 ' control with version %s' % curr_version)
122 117
123 118 except (RuntimeError, DatabaseNotControlledError), e:
124 119 curr_version = 1
125 120 msg = ('Current database is not under version control. Setting'
126 121 ' as version %s' % curr_version)
127 122 api.version_control(db_uri, repository_path, curr_version)
128 123
129 124 print (msg)
130 125
131 126 if curr_version == __dbversion__:
132 127 sys.exit('This database is already at the newest version')
133 128
134 129 #======================================================================
135 130 # UPGRADE STEPS
136 131 #======================================================================
137 132 class UpgradeSteps(object):
138 """Those steps follow schema versions so for example schema
133 """Those steps follow schema versions so for example schema
139 134 for example schema with seq 002 == step_2 and so on.
140 135 """
141 136
142 137 def __init__(self, klass):
143 138 self.klass = klass
144 139
145 140 def step_0(self):
146 141 #step 0 is the schema upgrade, and than follow proper upgrades
147 142 print ('attempting to do database upgrade to version %s' \
148 143 % __dbversion__)
149 144 api.upgrade(db_uri, repository_path, __dbversion__)
150 145 print ('Schema upgrade completed')
151 146
152 147 def step_1(self):
153 148 pass
154 149
155 150 def step_2(self):
156 151 print ('Patching repo paths for newer version of RhodeCode')
157 152 self.klass.fix_repo_paths()
158 153
159 154 print ('Patching default user of RhodeCode')
160 155 self.klass.fix_default_user()
161 156
162 157 log.info('Changing ui settings')
163 158 self.klass.create_ui_settings()
164 159
165
166 160 upgrade_steps = [0] + range(curr_version + 1, __dbversion__ + 1)
167 161
168 162 #CALL THE PROPER ORDER OF STEPS TO PERFORM FULL UPGRADE
169 163 for step in upgrade_steps:
170 164 print ('performing upgrade step %s' % step)
171 165 callable = getattr(UpgradeSteps(self), 'step_%s' % step)()
172 166
173
174
175 167 def fix_repo_paths(self):
176 168 """Fixes a old rhodecode version path into new one without a '*'
177 169 """
178 170
179 171 paths = self.sa.query(RhodeCodeUi)\
180 172 .filter(RhodeCodeUi.ui_key == '/')\
181 173 .scalar()
182 174
183 175 paths.ui_value = paths.ui_value.replace('*', '')
184 176
185 177 try:
186 178 self.sa.add(paths)
187 179 self.sa.commit()
188 180 except:
189 181 self.sa.rollback()
190 182 raise
191 183
192 184 def fix_default_user(self):
193 185 """Fixes a old default user with some 'nicer' default values,
194 186 used mostly for anonymous access
195 187 """
196 188 def_user = self.sa.query(User)\
197 189 .filter(User.username == 'default')\
198 190 .one()
199 191
200 192 def_user.name = 'Anonymous'
201 193 def_user.lastname = 'User'
202 194 def_user.email = 'anonymous@rhodecode.org'
203 195
204 196 try:
205 197 self.sa.add(def_user)
206 198 self.sa.commit()
207 199 except:
208 200 self.sa.rollback()
209 201 raise
210 202
211
212
213 203 def admin_prompt(self, second=False):
214 204 if not self.tests:
215 205 import getpass
216 206
217
218 207 def get_password():
219 password = getpass.getpass('Specify admin password (min 6 chars):')
208 password = getpass.getpass('Specify admin password '
209 '(min 6 chars):')
220 210 confirm = getpass.getpass('Confirm password:')
221 211
222 212 if password != confirm:
223 213 log.error('passwords mismatch')
224 214 return False
225 215 if len(password) < 6:
226 216 log.error('password is to short use at least 6 characters')
227 217 return False
228 218
229 219 return password
230 220
231 221 username = raw_input('Specify admin username:')
232 222
233 223 password = get_password()
234 224 if not password:
235 225 #second try
236 226 password = get_password()
237 227 if not password:
238 228 sys.exit()
239 229
240 230 email = raw_input('Specify admin email:')
241 231 self.create_user(username, password, email, True)
242 232 else:
243 233 log.info('creating admin and regular test users')
244 self.create_user('test_admin', 'test12', 'test_admin@mail.com', True)
245 self.create_user('test_regular', 'test12', 'test_regular@mail.com', False)
246 self.create_user('test_regular2', 'test12', 'test_regular2@mail.com', False)
234 self.create_user('test_admin', 'test12',
235 'test_admin@mail.com', True)
236 self.create_user('test_regular', 'test12',
237 'test_regular@mail.com', False)
238 self.create_user('test_regular2', 'test12',
239 'test_regular2@mail.com', False)
247 240
248 241 def create_ui_settings(self):
249 242 """Creates ui settings, fills out hooks
250 243 and disables dotencode
251
244
252 245 """
253 246 #HOOKS
254 247 hooks1_key = 'changegroup.update'
255 248 hooks1_ = self.sa.query(RhodeCodeUi)\
256 249 .filter(RhodeCodeUi.ui_key == hooks1_key).scalar()
257 250
258 251 hooks1 = RhodeCodeUi() if hooks1_ is None else hooks1_
259 252 hooks1.ui_section = 'hooks'
260 253 hooks1.ui_key = hooks1_key
261 254 hooks1.ui_value = 'hg update >&2'
262 255 hooks1.ui_active = False
263 256
264 257 hooks2_key = 'changegroup.repo_size'
265 258 hooks2_ = self.sa.query(RhodeCodeUi)\
266 259 .filter(RhodeCodeUi.ui_key == hooks2_key).scalar()
267 260
268 261 hooks2 = RhodeCodeUi() if hooks2_ is None else hooks2_
269 262 hooks2.ui_section = 'hooks'
270 263 hooks2.ui_key = hooks2_key
271 264 hooks2.ui_value = 'python:rhodecode.lib.hooks.repo_size'
272 265
273 266 hooks3 = RhodeCodeUi()
274 267 hooks3.ui_section = 'hooks'
275 268 hooks3.ui_key = 'pretxnchangegroup.push_logger'
276 269 hooks3.ui_value = 'python:rhodecode.lib.hooks.log_push_action'
277 270
278 271 hooks4 = RhodeCodeUi()
279 272 hooks4.ui_section = 'hooks'
280 273 hooks4.ui_key = 'preoutgoing.pull_logger'
281 274 hooks4.ui_value = 'python:rhodecode.lib.hooks.log_pull_action'
282 275
283 276 #For mercurial 1.7 set backward comapatibility with format
284 277 dotencode_disable = RhodeCodeUi()
285 278 dotencode_disable.ui_section = 'format'
286 279 dotencode_disable.ui_key = 'dotencode'
287 280 dotencode_disable.ui_value = 'false'
288 281
289 282 try:
290 283 self.sa.add(hooks1)
291 284 self.sa.add(hooks2)
292 285 self.sa.add(hooks3)
293 286 self.sa.add(hooks4)
294 287 self.sa.add(dotencode_disable)
295 288 self.sa.commit()
296 289 except:
297 290 self.sa.rollback()
298 291 raise
299 292
300
301 293 def create_ldap_options(self):
302 294 """Creates ldap settings"""
303 295
304 296 try:
305 297 for k, v in [('ldap_active', 'false'),
306 298 ('ldap_host', ''),
307 299 ('ldap_port', '389'),
308 300 ('ldap_ldaps', 'false'),
309 301 ('ldap_dn_user', ''), ('ldap_dn_pass', ''),
310 302 ('ldap_base_dn', '')]:
311 303
312 304 setting = RhodeCodeSettings(k, v)
313 305 self.sa.add(setting)
314 306 self.sa.commit()
315 307 except:
316 308 self.sa.rollback()
317 309 raise
318 310
319 def config_prompt(self, test_repo_path=''):
320 log.info('Setting up repositories config')
311 def config_prompt(self, test_repo_path='', retries=3):
312 if retries == 3:
313 log.info('Setting up repositories config')
321 314
322 315 if not self.tests and not test_repo_path:
323 316 path = raw_input('Specify valid full path to your repositories'
324 317 ' you can change this later in application settings:')
325 318 else:
326 319 path = test_repo_path
320 path_ok = True
327 321
322 #check proper dir
328 323 if not os.path.isdir(path):
329 log.error('You entered wrong path: %s', path)
324 path_ok = False
325 log.error('Entered path is not a valid directory: %s [%s/3]',
326 path, retries)
327
328 #check write access
329 if not os.access(path, os.W_OK):
330 path_ok = False
331
332 log.error('No write permission to given path: %s [%s/3]',
333 path, retries)
334
335 if retries == 0:
330 336 sys.exit()
337 if path_ok is False:
338 retries -= 1
339 return self.config_prompt(test_repo_path, retries)
340
341 return path
342
343 def create_settings(self, path):
331 344
332 345 self.create_ui_settings()
333 346
334 347 #HG UI OPTIONS
335 348 web1 = RhodeCodeUi()
336 349 web1.ui_section = 'web'
337 350 web1.ui_key = 'push_ssl'
338 351 web1.ui_value = 'false'
339 352
340 353 web2 = RhodeCodeUi()
341 354 web2.ui_section = 'web'
342 355 web2.ui_key = 'allow_archive'
343 356 web2.ui_value = 'gz zip bz2'
344 357
345 358 web3 = RhodeCodeUi()
346 359 web3.ui_section = 'web'
347 360 web3.ui_key = 'allow_push'
348 361 web3.ui_value = '*'
349 362
350 363 web4 = RhodeCodeUi()
351 364 web4.ui_section = 'web'
352 365 web4.ui_key = 'baseurl'
353 366 web4.ui_value = '/'
354 367
355 368 paths = RhodeCodeUi()
356 369 paths.ui_section = 'paths'
357 370 paths.ui_key = '/'
358 371 paths.ui_value = path
359 372
360
361 373 hgsettings1 = RhodeCodeSettings('realm', 'RhodeCode authentication')
362 374 hgsettings2 = RhodeCodeSettings('title', 'RhodeCode')
363 375
364
365 376 try:
366 377 self.sa.add(web1)
367 378 self.sa.add(web2)
368 379 self.sa.add(web3)
369 380 self.sa.add(web4)
370 381 self.sa.add(paths)
371 382 self.sa.add(hgsettings1)
372 383 self.sa.add(hgsettings2)
373 384
374 385 self.sa.commit()
375 386 except:
376 387 self.sa.rollback()
377 388 raise
378 389
379 390 self.create_ldap_options()
380 391
381 392 log.info('created ui config')
382 393
383 394 def create_user(self, username, password, email='', admin=False):
384 395 log.info('creating administrator user %s', username)
385 396 new_user = User()
386 397 new_user.username = username
387 398 new_user.password = get_crypt_password(password)
388 399 new_user.name = 'RhodeCode'
389 400 new_user.lastname = 'Admin'
390 401 new_user.email = email
391 402 new_user.admin = admin
392 403 new_user.active = True
393 404
394 405 try:
395 406 self.sa.add(new_user)
396 407 self.sa.commit()
397 408 except:
398 409 self.sa.rollback()
399 410 raise
400 411
401 412 def create_default_user(self):
402 413 log.info('creating default user')
403 414 #create default user for handling default permissions.
404 415 def_user = User()
405 416 def_user.username = 'default'
406 417 def_user.password = get_crypt_password(str(uuid.uuid1())[:8])
407 418 def_user.name = 'Anonymous'
408 419 def_user.lastname = 'User'
409 420 def_user.email = 'anonymous@rhodecode.org'
410 421 def_user.admin = False
411 422 def_user.active = False
412 423 try:
413 424 self.sa.add(def_user)
414 425 self.sa.commit()
415 426 except:
416 427 self.sa.rollback()
417 428 raise
418 429
419 430 def create_permissions(self):
420 431 #module.(access|create|change|delete)_[name]
421 432 #module.(read|write|owner)
422 433 perms = [('repository.none', 'Repository no access'),
423 434 ('repository.read', 'Repository read access'),
424 435 ('repository.write', 'Repository write access'),
425 436 ('repository.admin', 'Repository admin access'),
426 437 ('hg.admin', 'Hg Administrator'),
427 438 ('hg.create.repository', 'Repository create'),
428 439 ('hg.create.none', 'Repository creation disabled'),
429 440 ('hg.register.none', 'Register disabled'),
430 ('hg.register.manual_activate', 'Register new user with rhodecode without manual activation'),
431 ('hg.register.auto_activate', 'Register new user with rhodecode without auto activation'),
441 ('hg.register.manual_activate', ('Register new user with '
442 'RhodeCode without '
443 'manual activation')),
444 ('hg.register.auto_activate', ('Register new user with '
445 'RhodeCode without auto '
446 'activation')),
432 447 ]
433 448
434 449 for p in perms:
435 450 new_perm = Permission()
436 451 new_perm.permission_name = p[0]
437 452 new_perm.permission_longname = p[1]
438 453 try:
439 454 self.sa.add(new_perm)
440 455 self.sa.commit()
441 456 except:
442 457 self.sa.rollback()
443 458 raise
444 459
445 460 def populate_default_permissions(self):
446 461 log.info('creating default user permissions')
447 462
448 463 default_user = self.sa.query(User)\
449 464 .filter(User.username == 'default').scalar()
450 465
451 466 reg_perm = UserToPerm()
452 467 reg_perm.user = default_user
453 468 reg_perm.permission = self.sa.query(Permission)\
454 469 .filter(Permission.permission_name == 'hg.register.manual_activate')\
455 470 .scalar()
456 471
457 472 create_repo_perm = UserToPerm()
458 473 create_repo_perm.user = default_user
459 474 create_repo_perm.permission = self.sa.query(Permission)\
460 475 .filter(Permission.permission_name == 'hg.create.repository')\
461 476 .scalar()
462 477
463 478 default_repo_perm = UserToPerm()
464 479 default_repo_perm.user = default_user
465 480 default_repo_perm.permission = self.sa.query(Permission)\
466 481 .filter(Permission.permission_name == 'repository.read')\
467 482 .scalar()
468 483
469 484 try:
470 485 self.sa.add(reg_perm)
471 486 self.sa.add(create_repo_perm)
472 487 self.sa.add(default_repo_perm)
473 488 self.sa.commit()
474 489 except:
475 490 self.sa.rollback()
476 491 raise
477
@@ -1,378 +1,380 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.model.scm
4 4 ~~~~~~~~~~~~~~~~~~~
5 5
6 6 Scm model for RhodeCode
7 7
8 8 :created_on: Apr 9, 2010
9 9 :author: marcink
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 11 :license: GPLv3, see COPYING for more details.
12 12 """
13 # This program is free software; you can redistribute it and/or
14 # modify it under the terms of the GNU General Public License
15 # as published by the Free Software Foundation; version 2
16 # of the License or (at your opinion) any later version of the license.
17 #
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
17 #
18 18 # This program is distributed in the hope that it will be useful,
19 19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 21 # GNU General Public License for more details.
22 #
22 #
23 23 # You should have received a copy of the GNU General Public License
24 # along with this program; if not, write to the Free Software
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
26 # MA 02110-1301, USA.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
27 25 import os
28 26 import time
29 27 import traceback
30 28 import logging
31 29
32 30 from vcs import get_backend
33 31 from vcs.utils.helpers import get_scm
34 32 from vcs.exceptions import RepositoryError, VCSError
35 33 from vcs.utils.lazy import LazyProperty
36 34
37 35 from mercurial import ui
38 36
39 37 from beaker.cache import cache_region, region_invalidate
40 38
41 39 from rhodecode import BACKENDS
42 40 from rhodecode.lib import helpers as h
43 41 from rhodecode.lib.auth import HasRepoPermissionAny
44 42 from rhodecode.lib.utils import get_repos, make_ui, action_logger
45 43 from rhodecode.model import BaseModel
46 44 from rhodecode.model.user import UserModel
47 45
48 46 from rhodecode.model.db import Repository, RhodeCodeUi, CacheInvalidation, \
49 47 UserFollowing, UserLog
50 48 from rhodecode.model.caching_query import FromCache
51 49
52 50 from sqlalchemy.orm import joinedload
53 51 from sqlalchemy.orm.session import make_transient
54 52 from sqlalchemy.exc import DatabaseError
55 53
56 54 log = logging.getLogger(__name__)
57 55
58 56
59 57 class UserTemp(object):
60 58 def __init__(self, user_id):
61 59 self.user_id = user_id
60
61 def __repr__(self):
62 return "<%s('id:%s')>" % (self.__class__.__name__, self.user_id)
63
64
62 65 class RepoTemp(object):
63 66 def __init__(self, repo_id):
64 67 self.repo_id = repo_id
65 68
69 def __repr__(self):
70 return "<%s('id:%s')>" % (self.__class__.__name__, self.repo_id)
71
72
66 73 class ScmModel(BaseModel):
67 74 """Generic Scm Model
68 75 """
69 76
70 77 @LazyProperty
71 78 def repos_path(self):
72 79 """Get's the repositories root path from database
73 80 """
74 81
75 82 q = self.sa.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/').one()
76 83
77 84 return q.ui_value
78 85
79 86 def repo_scan(self, repos_path, baseui):
80 """Listing of repositories in given path. This path should not be a
87 """Listing of repositories in given path. This path should not be a
81 88 repository itself. Return a dictionary of repository objects
82
89
83 90 :param repos_path: path to directory containing repositories
84 91 :param baseui: baseui instance to instantiate MercurialRepostitory with
85 92 """
86 93
87 94 log.info('scanning for repositories in %s', repos_path)
88 95
89 96 if not isinstance(baseui, ui.ui):
90 97 baseui = make_ui('db')
91 98 repos_list = {}
92 99
93 100 for name, path in get_repos(repos_path):
94 101 try:
95 if repos_list.has_key(name):
102 if name in repos_list:
96 103 raise RepositoryError('Duplicate repository name %s '
97 104 'found in %s' % (name, path))
98 105 else:
99 106
100 107 klass = get_backend(path[0])
101 108
102 109 if path[0] == 'hg' and path[0] in BACKENDS.keys():
103 110 repos_list[name] = klass(path[1], baseui=baseui)
104 111
105 112 if path[0] == 'git' and path[0] in BACKENDS.keys():
106 113 repos_list[name] = klass(path[1])
107 114 except OSError:
108 115 continue
109 116
110 117 return repos_list
111 118
112 119 def get_repos(self, all_repos=None):
113 """Get all repos from db and for each repo create it's backend instance.
114 and fill that backed with information from database
115
120 """Get all repos from db and for each repo create it's
121 backend instance.and fill that backed with information from database
122
116 123 :param all_repos: give specific repositories list, good for filtering
117 124 """
118 125
119 126 if all_repos is None:
120 127 all_repos = self.sa.query(Repository)\
121 128 .order_by(Repository.repo_name).all()
122 129
123 130 #get the repositories that should be invalidated
124 131 invalidation_list = [str(x.cache_key) for x in \
125 132 self.sa.query(CacheInvalidation.cache_key)\
126 133 .filter(CacheInvalidation.cache_active == False)\
127 134 .all()]
128 135
129 136 for r in all_repos:
130 137
131 138 repo = self.get(r.repo_name, invalidation_list)
132 139
133 140 if repo is not None:
134 141 last_change = repo.last_change
135 142 tip = h.get_changeset_safe(repo, 'tip')
136 143
137 144 tmp_d = {}
138 145 tmp_d['name'] = repo.name
139 146 tmp_d['name_sort'] = tmp_d['name'].lower()
140 147 tmp_d['description'] = repo.dbrepo.description
141 148 tmp_d['description_sort'] = tmp_d['description']
142 149 tmp_d['last_change'] = last_change
143 tmp_d['last_change_sort'] = time.mktime(last_change.timetuple())
150 tmp_d['last_change_sort'] = time.mktime(last_change \
151 .timetuple())
144 152 tmp_d['tip'] = tip.raw_id
145 153 tmp_d['tip_sort'] = tip.revision
146 154 tmp_d['rev'] = tip.revision
147 155 tmp_d['contact'] = repo.dbrepo.user.full_contact
148 156 tmp_d['contact_sort'] = tmp_d['contact']
149 157 tmp_d['owner_sort'] = tmp_d['contact']
150 158 tmp_d['repo_archives'] = list(repo._get_archives())
151 159 tmp_d['last_msg'] = tip.message
152 160 tmp_d['repo'] = repo
153 161 yield tmp_d
154 162
155 163 def get_repo(self, repo_name):
156 164 return self.get(repo_name)
157 165
158 166 def get(self, repo_name, invalidation_list=None):
159 167 """Get's repository from given name, creates BackendInstance and
160 168 propagates it's data from database with all additional information
161
169
162 170 :param repo_name:
163 171 :param invalidation_list: if a invalidation list is given the get
164 method should not manually check if this repository needs
172 method should not manually check if this repository needs
165 173 invalidation and just invalidate the repositories in list
166
174
167 175 """
168 176 if not HasRepoPermissionAny('repository.read', 'repository.write',
169 177 'repository.admin')(repo_name, 'get repo check'):
170 178 return
171 179
172 180 #======================================================================
173 181 # CACHE FUNCTION
174 182 #======================================================================
175 183 @cache_region('long_term')
176 184 def _get_repo(repo_name):
177 185
178 186 repo_path = os.path.join(self.repos_path, repo_name)
179 187
180 188 try:
181 189 alias = get_scm(repo_path)[0]
182 190
183 191 log.debug('Creating instance of %s repository', alias)
184 192 backend = get_backend(alias)
185 193 except VCSError:
186 194 log.error(traceback.format_exc())
187 195 return
188 196
189 197 if alias == 'hg':
190 198 from pylons import app_globals as g
191 199 repo = backend(repo_path, create=False, baseui=g.baseui)
192 200 #skip hidden web repository
193 201 if repo._get_hidden():
194 202 return
195 203 else:
196 204 repo = backend(repo_path, create=False)
197 205
198 206 dbrepo = self.sa.query(Repository)\
199 207 .options(joinedload(Repository.fork))\
200 208 .options(joinedload(Repository.user))\
201 209 .filter(Repository.repo_name == repo_name)\
202 210 .scalar()
203 211
204 212 make_transient(dbrepo)
205 213 if dbrepo.user:
206 214 make_transient(dbrepo.user)
207 215 if dbrepo.fork:
208 216 make_transient(dbrepo.fork)
209 217
210 218 repo.dbrepo = dbrepo
211 219 return repo
212 220
213 221 pre_invalidate = True
214 222 if invalidation_list is not None:
215 223 pre_invalidate = repo_name in invalidation_list
216 224
217 225 if pre_invalidate:
218 226 invalidate = self._should_invalidate(repo_name)
219 227
220 228 if invalidate:
221 229 log.info('invalidating cache for repository %s', repo_name)
222 230 region_invalidate(_get_repo, None, repo_name)
223 231 self._mark_invalidated(invalidate)
224 232
225 233 return _get_repo(repo_name)
226 234
227
235 def mark_for_invalidation(self, repo_name):
236 """Puts cache invalidation task into db for
237 further global cache invalidation
228 238
229 def mark_for_invalidation(self, repo_name):
230 """Puts cache invalidation task into db for
231 further global cache invalidation
232
233 239 :param repo_name: this repo that should invalidation take place
234 240 """
235 241
236 242 log.debug('marking %s for invalidation', repo_name)
237 243 cache = self.sa.query(CacheInvalidation)\
238 244 .filter(CacheInvalidation.cache_key == repo_name).scalar()
239 245
240 246 if cache:
241 247 #mark this cache as inactive
242 248 cache.cache_active = False
243 249 else:
244 250 log.debug('cache key not found in invalidation db -> creating one')
245 251 cache = CacheInvalidation(repo_name)
246 252
247 253 try:
248 254 self.sa.add(cache)
249 255 self.sa.commit()
250 256 except (DatabaseError,):
251 257 log.error(traceback.format_exc())
252 258 self.sa.rollback()
253 259
254
255 260 def toggle_following_repo(self, follow_repo_id, user_id):
256 261
257 262 f = self.sa.query(UserFollowing)\
258 263 .filter(UserFollowing.follows_repo_id == follow_repo_id)\
259 264 .filter(UserFollowing.user_id == user_id).scalar()
260 265
261 266 if f is not None:
262 267
263 268 try:
264 269 self.sa.delete(f)
265 270 self.sa.commit()
266 271 action_logger(UserTemp(user_id),
267 272 'stopped_following_repo',
268 273 RepoTemp(follow_repo_id))
269 274 return
270 275 except:
271 276 log.error(traceback.format_exc())
272 277 self.sa.rollback()
273 278 raise
274 279
275
276 280 try:
277 281 f = UserFollowing()
278 282 f.user_id = user_id
279 283 f.follows_repo_id = follow_repo_id
280 284 self.sa.add(f)
281 285 self.sa.commit()
282 286 action_logger(UserTemp(user_id),
283 287 'started_following_repo',
284 288 RepoTemp(follow_repo_id))
285 289 except:
286 290 log.error(traceback.format_exc())
287 291 self.sa.rollback()
288 292 raise
289 293
290 def toggle_following_user(self, follow_user_id , user_id):
294 def toggle_following_user(self, follow_user_id, user_id):
291 295 f = self.sa.query(UserFollowing)\
292 296 .filter(UserFollowing.follows_user_id == follow_user_id)\
293 297 .filter(UserFollowing.user_id == user_id).scalar()
294 298
295 299 if f is not None:
296 300 try:
297 301 self.sa.delete(f)
298 302 self.sa.commit()
299 303 return
300 304 except:
301 305 log.error(traceback.format_exc())
302 306 self.sa.rollback()
303 307 raise
304 308
305 309 try:
306 310 f = UserFollowing()
307 311 f.user_id = user_id
308 312 f.follows_user_id = follow_user_id
309 313 self.sa.add(f)
310 314 self.sa.commit()
311 315 except:
312 316 log.error(traceback.format_exc())
313 317 self.sa.rollback()
314 318 raise
315 319
316 320 def is_following_repo(self, repo_name, user_id):
317 321 r = self.sa.query(Repository)\
318 322 .filter(Repository.repo_name == repo_name).scalar()
319 323
320 324 f = self.sa.query(UserFollowing)\
321 325 .filter(UserFollowing.follows_repository == r)\
322 326 .filter(UserFollowing.user_id == user_id).scalar()
323 327
324 328 return f is not None
325 329
326 330 def is_following_user(self, username, user_id):
327 331 u = UserModel(self.sa).get_by_username(username)
328 332
329 333 f = self.sa.query(UserFollowing)\
330 334 .filter(UserFollowing.follows_user == u)\
331 335 .filter(UserFollowing.user_id == user_id).scalar()
332 336
333 337 return f is not None
334 338
335 339 def get_followers(self, repo_id):
336 340 return self.sa.query(UserFollowing)\
337 341 .filter(UserFollowing.follows_repo_id == repo_id).count()
338 342
339 343 def get_forks(self, repo_id):
340 344 return self.sa.query(Repository)\
341 345 .filter(Repository.fork_id == repo_id).count()
342 346
343
344 347 def get_unread_journal(self):
345 348 return self.sa.query(UserLog).count()
346 349
347
348 350 def _should_invalidate(self, repo_name):
349 351 """Looks up database for invalidation signals for this repo_name
350
352
351 353 :param repo_name:
352 354 """
353 355
354 356 ret = self.sa.query(CacheInvalidation)\
355 357 .options(FromCache('sql_cache_short',
356 358 'get_invalidation_%s' % repo_name))\
357 359 .filter(CacheInvalidation.cache_key == repo_name)\
358 360 .filter(CacheInvalidation.cache_active == False)\
359 361 .scalar()
360 362
361 363 return ret
362 364
363 365 def _mark_invalidated(self, cache_key):
364 """ Marks all occurences of cache to invaldation as already invalidated
365
366 """ Marks all occurences of cache to invaldation as
367 already invalidated
368
366 369 :param cache_key:
367 370 """
368 371
369 372 if cache_key:
370 373 log.debug('marking %s as already invalidated', cache_key)
371 374 try:
372 375 cache_key.cache_active = True
373 376 self.sa.add(cache_key)
374 377 self.sa.commit()
375 378 except (DatabaseError,):
376 379 log.error(traceback.format_exc())
377 380 self.sa.rollback()
378
@@ -1,375 +1,374 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 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 <p class="footer-link">${h.link_to(_('GPL license'),h.url('gpl_license'))}</p>
89 88 <p class="footer-link-right"><a href="${h.url('rhodecode_official')}">RhodeCode</a> ${c.rhodecode_version} &copy; 2010-2011 by Marcin Kuzminski</p>
90 89 </div>
91 90 </div>
92 91 <script type="text/javascript">
93 92 function tooltip_activate(){
94 93 ${h.tooltip.activate()}
95 94 }
96 95 tooltip_activate();
97 96 </script>
98 97 </div>
99 98 <!-- end footer -->
100 99 </body>
101 100
102 101 </html>
103 102
104 103 ### MAKO DEFS ###
105 104 <%def name="page_nav()">
106 105 ${self.menu()}
107 106 </%def>
108 107
109 108 <%def name="menu(current=None)">
110 109 <%
111 110 def is_current(selected):
112 111 if selected == current:
113 112 return h.literal('class="current"')
114 113 %>
115 114 %if current not in ['home','admin']:
116 115 ##REGULAR MENU
117 116 <ul id="quick">
118 117 <!-- repo switcher -->
119 118 <li>
120 119 <a id="repo_switcher" title="${_('Switch repository')}" href="#">
121 120 <span class="icon">
122 121 <img src="${h.url("/images/icons/database.png")}" alt="${_('Products')}" />
123 122 </span>
124 123 <span>&darr;</span>
125 124 </a>
126 125 <ul class="repo_switcher">
127 126 %for repo in c.cached_repo_list:
128 127
129 128 %if repo['repo'].dbrepo.private:
130 129 <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 130 %else:
132 131 <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 132 %endif
134 133 %endfor
135 134 </ul>
136 135 </li>
137 136
138 137 <li ${is_current('summary')}>
139 138 <a title="${_('Summary')}" href="${h.url('summary_home',repo_name=c.repo_name)}">
140 139 <span class="icon">
141 140 <img src="${h.url("/images/icons/clipboard_16.png")}" alt="${_('Summary')}" />
142 141 </span>
143 142 <span>${_('Summary')}</span>
144 143 </a>
145 144 </li>
146 145 ##<li ${is_current('shortlog')}>
147 146 ## <a title="${_('Shortlog')}" href="${h.url('shortlog_home',repo_name=c.repo_name)}">
148 147 ## <span class="icon">
149 148 ## <img src="${h.url("/images/icons/application_view_list.png")}" alt="${_('Shortlog')}" />
150 149 ## </span>
151 150 ## <span>${_('Shortlog')}</span>
152 151 ## </a>
153 152 ##</li>
154 153 <li ${is_current('changelog')}>
155 154 <a title="${_('Changelog')}" href="${h.url('changelog_home',repo_name=c.repo_name)}">
156 155 <span class="icon">
157 156 <img src="${h.url("/images/icons/time.png")}" alt="${_('Changelog')}" />
158 157 </span>
159 158 <span>${_('Changelog')}</span>
160 159 </a>
161 160 </li>
162 161
163 162 <li ${is_current('switch_to')}>
164 163 <a title="${_('Switch to')}" href="#">
165 164 <span class="icon">
166 165 <img src="${h.url("/images/icons/arrow_switch.png")}" alt="${_('Switch to')}" />
167 166 </span>
168 167 <span>${_('Switch to')}</span>
169 168 </a>
170 169 <ul>
171 170 <li>
172 171 ${h.link_to('%s (%s)' % (_('branches'),len(c.repository_branches.values()),),h.url('branches_home',repo_name=c.repo_name),class_='branches childs')}
173 172 <ul>
174 173 %if c.repository_branches.values():
175 174 %for cnt,branch in enumerate(c.repository_branches.items()):
176 175 <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 176 %endfor
178 177 %else:
179 178 <li>${h.link_to(_('There are no branches yet'),'#')}</li>
180 179 %endif
181 180 </ul>
182 181 </li>
183 182 <li>
184 183 ${h.link_to('%s (%s)' % (_('tags'),len(c.repository_tags.values()),),h.url('tags_home',repo_name=c.repo_name),class_='tags childs')}
185 184 <ul>
186 185 %if c.repository_tags.values():
187 186 %for cnt,tag in enumerate(c.repository_tags.items()):
188 187 <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 188 %endfor
190 189 %else:
191 190 <li>${h.link_to(_('There are no tags yet'),'#')}</li>
192 191 %endif
193 192 </ul>
194 193 </li>
195 194 </ul>
196 195 </li>
197 196 <li ${is_current('files')}>
198 197 <a title="${_('Files')}" href="${h.url('files_home',repo_name=c.repo_name)}">
199 198 <span class="icon">
200 199 <img src="${h.url("/images/icons/file.png")}" alt="${_('Files')}" />
201 200 </span>
202 201 <span>${_('Files')}</span>
203 202 </a>
204 203 </li>
205 204
206 205 <li ${is_current('options')}>
207 206 <a title="${_('Options')}" href="#">
208 207 <span class="icon">
209 208 <img src="${h.url("/images/icons/table_gear.png")}" alt="${_('Admin')}" />
210 209 </span>
211 210 <span>${_('Options')}</span>
212 211 </a>
213 212 <ul>
214 213 %if h.HasRepoPermissionAll('repository.admin')(c.repo_name):
215 214 %if h.HasPermissionAll('hg.admin')('access settings on repository'):
216 215 <li>${h.link_to(_('settings'),h.url('edit_repo',repo_name=c.repo_name),class_='settings')}</li>
217 216 %else:
218 217 <li>${h.link_to(_('settings'),h.url('repo_settings_home',repo_name=c.repo_name),class_='settings')}</li>
219 218 %endif
220 219 %endif
221 220 <li>${h.link_to(_('fork'),h.url('repo_fork_home',repo_name=c.repo_name),class_='fork')}</li>
222 221 <li>${h.link_to(_('search'),h.url('search_repo',search_repo=c.repo_name),class_='search')}</li>
223 222
224 223 %if h.HasPermissionAll('hg.admin')('access admin main page'):
225 224 <li>
226 225 ${h.link_to(_('admin'),h.url('admin_home'),class_='admin')}
227 226 <%def name="admin_menu()">
228 227 <ul>
229 228 <li>${h.link_to(_('journal'),h.url('admin_home'),class_='journal')}</li>
230 229 <li>${h.link_to(_('repositories'),h.url('repos'),class_='repos')}</li>
231 230 <li>${h.link_to(_('users'),h.url('users'),class_='users')}</li>
232 231 <li>${h.link_to(_('permissions'),h.url('edit_permission',id='default'),class_='permissions')}</li>
233 232 <li>${h.link_to(_('ldap'),h.url('ldap_home'),class_='ldap')}</li>
234 233 <li class="last">${h.link_to(_('settings'),h.url('admin_settings'),class_='settings')}</li>
235 234 </ul>
236 235 </%def>
237 236
238 237 ${admin_menu()}
239 238 </li>
240 239 %endif
241 240
242 241 </ul>
243 242 </li>
244 243
245 244 <li>
246 245 <a title="${_('Followers')}" href="#">
247 246 <span class="icon_short">
248 247 <img src="${h.url("/images/icons/heart.png")}" alt="${_('Followers')}" />
249 248 </span>
250 249 <span class="short">${c.repository_followers}</span>
251 250 </a>
252 251 </li>
253 252 <li>
254 253 <a title="${_('Forks')}" href="#">
255 254 <span class="icon_short">
256 255 <img src="${h.url("/images/icons/arrow_divide.png")}" alt="${_('Forks')}" />
257 256 </span>
258 257 <span class="short">${c.repository_forks}</span>
259 258 </a>
260 259 </li>
261 260
262 261
263 262
264 263 </ul>
265 264 %else:
266 265 ##ROOT MENU
267 266 <ul id="quick">
268 267 <li>
269 268 <a title="${_('Home')}" href="${h.url('home')}">
270 269 <span class="icon">
271 270 <img src="${h.url("/images/icons/home_16.png")}" alt="${_('Home')}" />
272 271 </span>
273 272 <span>${_('Home')}</span>
274 273 </a>
275 274 </li>
276 275 %if c.rhodecode_user.username != 'default':
277 276 <li>
278 277 <a title="${_('Journal')}" href="${h.url('journal')}">
279 278 <span class="icon">
280 279 <img src="${h.url("/images/icons/book.png")}" alt="${_('Journal')}" />
281 280 </span>
282 281 <span>${_('Journal')}</span>
283 282 </a>
284 283 </li>
285 284 %endif
286 285 <li>
287 286 <a title="${_('Search')}" href="${h.url('search')}">
288 287 <span class="icon">
289 288 <img src="${h.url("/images/icons/search_16.png")}" alt="${_('Search')}" />
290 289 </span>
291 290 <span>${_('Search')}</span>
292 291 </a>
293 292 </li>
294 293
295 294 %if h.HasPermissionAll('hg.admin')('access admin main page'):
296 295 <li ${is_current('admin')}>
297 296 <a title="${_('Admin')}" href="${h.url('admin_home')}">
298 297 <span class="icon">
299 298 <img src="${h.url("/images/icons/cog_edit.png")}" alt="${_('Admin')}" />
300 299 </span>
301 300 <span>${_('Admin')}</span>
302 301 </a>
303 302 ${admin_menu()}
304 303 </li>
305 304 %endif
306 305 </ul>
307 306 %endif
308 307 </%def>
309 308
310 309
311 310 <%def name="css()">
312 311 <link rel="stylesheet" type="text/css" href="${h.url('/css/style.css')}" media="screen" />
313 312 <link rel="stylesheet" type="text/css" href="${h.url('/css/pygments.css')}" />
314 313 <link rel="stylesheet" type="text/css" href="${h.url('/css/diff.css')}" />
315 314 </%def>
316 315
317 316 <%def name="js()">
318 317 ##<script type="text/javascript" src="${h.url('/js/yui/utilities/utilities.js')}"></script>
319 318 ##<script type="text/javascript" src="${h.url('/js/yui/container/container.js')}"></script>
320 319 ##<script type="text/javascript" src="${h.url('/js/yui/datasource/datasource.js')}"></script>
321 320 ##<script type="text/javascript" src="${h.url('/js/yui/autocomplete/autocomplete.js')}"></script>
322 321 ##<script type="text/javascript" src="${h.url('/js/yui/selector/selector-min.js')}"></script>
323 322
324 323 <script type="text/javascript" src="${h.url('/js/yui2a.js')}"></script>
325 324 <!--[if IE]><script language="javascript" type="text/javascript" src="${h.url('/js/excanvas.min.js')}"></script><![endif]-->
326 325 <script type="text/javascript" src="${h.url('/js/yui.flot.js')}"></script>
327 326
328 327 <script type="text/javascript">
329 328 var base_url = "${h.url('toggle_following')}";
330 329 var YUC = YAHOO.util.Connect;
331 330 var YUD = YAHOO.util.Dom;
332 331 var YUE = YAHOO.util.Event;
333 332
334 333 function onSuccess(target){
335 334
336 335 var f = YUD.get(target.id);
337 336 if(f.getAttribute('class')=='follow'){
338 337 f.setAttribute('class','following');
339 338 f.setAttribute('title',"${_('Stop following this repository')}");
340 339 }
341 340 else{
342 341 f.setAttribute('class','follow');
343 342 f.setAttribute('title',"${_('Start following this repository')}");
344 343 }
345 344 }
346 345
347 346 function toggleFollowingUser(fallows_user_id,token){
348 347 args = 'follows_user_id='+fallows_user_id;
349 348 args+= '&amp;auth_token='+token;
350 349 YUC.asyncRequest('POST',base_url,{
351 350 success:function(o){
352 351 onSuccess();
353 352 }
354 353 },args); return false;
355 354 }
356 355
357 356 function toggleFollowingRepo(target,fallows_repo_id,token){
358 357
359 358 args = 'follows_repo_id='+fallows_repo_id;
360 359 args+= '&amp;auth_token='+token;
361 360 YUC.asyncRequest('POST',base_url,{
362 361 success:function(o){
363 362 onSuccess(target);
364 363 }
365 364 },args); return false;
366 365 }
367 366 </script>
368 367
369 368 </%def>
370 369
371 370 <%def name="breadcrumbs()">
372 371 <div class="breadcrumbs">
373 372 ${self.breadcrumbs_links()}
374 373 </div>
375 374 </%def> No newline at end of file
General Comments 0
You need to be logged in to leave comments. Login now