##// END OF EJS Templates
implemented #91,...
marcink -
r872:b956e6f4 beta
parent child Browse files
Show More
@@ -1,121 +1,132 b''
1 1 .. _changelog:
2 2
3 3 Changelog
4 4 =========
5 5
6 1.1.0 (**2010-XX-XX**)
6 1.2.0 (**2010-12-18**)
7 7 ----------------------
8 8
9 9 :status: in-progress
10 10 :branch: beta
11 11
12 12 news
13 13 ++++
14 14
15 - implemented #91 added nicer looking archive urls
16
17 fixes
18 ++++
19
20
21 1.1.0 (**2010-12-18**)
22 ----------------------
23
24 news
25 ++++
26
15 27 - rewrite of internals for vcs >=0.1.10
16 28 - uses mercurial 1.7 with dotencode disabled for maintaining compatibility
17 29 with older clients
18 30 - anonymous access, authentication via ldap
19 31 - performance upgrade for cached repos list - each repository has it's own
20 32 cache that's invalidated when needed.
21 33 - performance upgrades on repositories with large amount of commits (20K+)
22 34 - main page quick filter for filtering repositories
23 35 - user dashboards with ability to follow chosen repositories actions
24 36 - sends email to admin on new user registration
25 37 - added cache/statistics reset options into repository settings
26 38 - more detailed action logger (based on hooks) with pushed changesets lists
27 39 and options to disable those hooks from admin panel
28 40 - introduced new enhanced changelog for merges that shows more accurate results
29 41 - new improved and faster code stats (based on pygments lexers mapping tables,
30 42 showing up to 10 trending sources for each repository. Additionally stats
31 43 can be disabled in repository settings.
32 44 - gui optimizations, fixed application width to 1024px
33 45 - added cut off (for large files/changesets) limit into config files
34 46 - whoosh, celeryd, upgrade moved to paster command
35 47 - other than sqlite database backends can be used
36 48
37 49 fixes
38 50 +++++
39 51
40 52 - fixes #61 forked repo was showing only after cache expired
41 53 - fixes #76 no confirmation on user deletes
42 54 - fixes #66 Name field misspelled
43 55 - fixes #72 block user removal when he owns repositories
44 56 - fixes #69 added password confirmation fields
45 57 - fixes #87 RhodeCode crashes occasionally on updating repository owner
46 58 - fixes #82 broken annotations on files with more than 1 blank line at the end
47 59 - a lot of fixes and tweaks for file browser
48 60 - fixed detached session issues
49 61 - fixed when user had no repos he would see all repos listed in my account
50 62 - fixed ui() instance bug when global hgrc settings was loaded for server
51 63 instance and all hgrc options were merged with our db ui() object
52 64 - numerous small bugfixes
53 65
54 66 (special thanks for TkSoh for detailed feedback)
55 67
56 68
57 69 1.0.2 (**2010-11-12**)
58 70 ----------------------
59 71
60 72 news
61 73 ++++
62 74
63 75 - tested under python2.7
64 76 - bumped sqlalchemy and celery versions
65 77
66 78 fixes
67 79 +++++
68 80
69 81 - fixed #59 missing graph.js
70 82 - fixed repo_size crash when repository had broken symlinks
71 83 - fixed python2.5 crashes.
72 84
73 85
74 86 1.0.1 (**2010-11-10**)
75 87 ----------------------
76 88
77 89 news
78 90 ++++
79 91
80 92 - small css updated
81 93
82 94 fixes
83 95 +++++
84 96
85 97 - fixed #53 python2.5 incompatible enumerate calls
86 98 - fixed #52 disable mercurial extension for web
87 99 - fixed #51 deleting repositories don't delete it's dependent objects
88 100
89 101
90 102 1.0.0 (**2010-11-02**)
91 103 ----------------------
92 104
93 105 - security bugfix simplehg wasn't checking for permissions on commands
94 106 other than pull or push.
95 107 - fixed doubled messages after push or pull in admin journal
96 108 - templating and css corrections, fixed repo switcher on chrome, updated titles
97 109 - admin menu accessible from options menu on repository view
98 110 - permissions cached queries
99 111
100 112 1.0.0rc4 (**2010-10-12**)
101 113 --------------------------
102 114
103 115 - fixed python2.5 missing simplejson imports (thanks to Jens BΓ€ckman)
104 116 - removed cache_manager settings from sqlalchemy meta
105 117 - added sqlalchemy cache settings to ini files
106 118 - validated password length and added second try of failure on paster setup-app
107 119 - fixed setup database destroy prompt even when there was no db
108 120
109 121
110 122 1.0.0rc3 (**2010-10-11**)
111 123 -------------------------
112 124
113 125 - fixed i18n during installation.
114 126
115 127 1.0.0rc2 (**2010-10-11**)
116 128 -------------------------
117 129
118 130 - Disabled dirsize in file browser, it's causing nasty bug when dir renames
119 131 occure. After vcs is fixed it'll be put back again.
120 - templating/css rewrites, optimized css.
121
132 - templating/css rewrites, optimized css. No newline at end of file
@@ -1,212 +1,212 b''
1 1 """
2 2 Routes configuration
3 3
4 4 The more specific and detailed routes should be defined first so they
5 5 may take precedent over the more generic routes. For more information
6 6 refer to the routes manual at http://routes.groovie.org/docs/
7 7 """
8 8 from __future__ import with_statement
9 9 from routes import Mapper
10 10 from rhodecode.lib.utils import check_repo_fast as cr
11 11
12 12 def make_map(config):
13 13 """Create, configure and return the routes Mapper"""
14 14 map = Mapper(directory=config['pylons.paths']['controllers'],
15 15 always_scan=config['debug'])
16 16 map.minimization = False
17 17 map.explicit = False
18 18
19 19 def check_repo(environ, match_dict):
20 20 """
21 21 check for valid repository for proper 404 handling
22 22 :param environ:
23 23 :param match_dict:
24 24 """
25 25 repo_name = match_dict.get('repo_name')
26 26 return not cr(repo_name, config['base_path'])
27 27
28 28 # The ErrorController route (handles 404/500 error pages); it should
29 29 # likely stay at the top, ensuring it can always be resolved
30 30 map.connect('/error/{action}', controller='error')
31 31 map.connect('/error/{action}/{id}', controller='error')
32 32
33 33 #==========================================================================
34 34 # CUSTOM ROUTES HERE
35 35 #==========================================================================
36 36
37 37 #MAIN PAGE
38 38 map.connect('home', '/', controller='home', action='index')
39 39 map.connect('bugtracker', "http://bitbucket.org/marcinkuzminski/rhodecode/issues", _static=True)
40 40 map.connect('gpl_license', "http://www.gnu.org/licenses/gpl.html", _static=True)
41 41 #ADMIN REPOSITORY REST ROUTES
42 42 with map.submapper(path_prefix='/_admin', controller='admin/repos') as m:
43 43 m.connect("repos", "/repos",
44 44 action="create", conditions=dict(method=["POST"]))
45 45 m.connect("repos", "/repos",
46 46 action="index", conditions=dict(method=["GET"]))
47 47 m.connect("formatted_repos", "/repos.{format}",
48 48 action="index",
49 49 conditions=dict(method=["GET"]))
50 50 m.connect("new_repo", "/repos/new",
51 51 action="new", conditions=dict(method=["GET"]))
52 52 m.connect("formatted_new_repo", "/repos/new.{format}",
53 53 action="new", conditions=dict(method=["GET"]))
54 54 m.connect("/repos/{repo_name:.*}",
55 55 action="update", conditions=dict(method=["PUT"],
56 56 function=check_repo))
57 57 m.connect("/repos/{repo_name:.*}",
58 58 action="delete", conditions=dict(method=["DELETE"],
59 59 function=check_repo))
60 60 m.connect("edit_repo", "/repos/{repo_name:.*}/edit",
61 61 action="edit", conditions=dict(method=["GET"],
62 62 function=check_repo))
63 63 m.connect("formatted_edit_repo", "/repos/{repo_name:.*}.{format}/edit",
64 64 action="edit", conditions=dict(method=["GET"],
65 65 function=check_repo))
66 66 m.connect("repo", "/repos/{repo_name:.*}",
67 67 action="show", conditions=dict(method=["GET"],
68 68 function=check_repo))
69 69 m.connect("formatted_repo", "/repos/{repo_name:.*}.{format}",
70 70 action="show", conditions=dict(method=["GET"],
71 71 function=check_repo))
72 72 #ajax delete repo perm user
73 73 m.connect('delete_repo_user', "/repos_delete_user/{repo_name:.*}",
74 74 action="delete_perm_user", conditions=dict(method=["DELETE"],
75 75 function=check_repo))
76 76 #settings actions
77 77 m.connect('repo_stats', "/repos_stats/{repo_name:.*}",
78 78 action="repo_stats", conditions=dict(method=["DELETE"],
79 79 function=check_repo))
80 80 m.connect('repo_cache', "/repos_cache/{repo_name:.*}",
81 81 action="repo_cache", conditions=dict(method=["DELETE"],
82 82 function=check_repo))
83 83 #ADMIN USER REST ROUTES
84 84 map.resource('user', 'users', controller='admin/users', path_prefix='/_admin')
85 85
86 86 #ADMIN PERMISSIONS REST ROUTES
87 87 map.resource('permission', 'permissions', controller='admin/permissions', path_prefix='/_admin')
88 88
89 89
90 90 ##ADMIN LDAP SETTINGS
91 91 map.connect('ldap_settings', '/_admin/ldap', controller='admin/ldap_settings',
92 92 action='ldap_settings', conditions=dict(method=["POST"]))
93 93 map.connect('ldap_home', '/_admin/ldap', controller='admin/ldap_settings',)
94 94
95 95
96 96
97 97 #ADMIN SETTINGS REST ROUTES
98 98 with map.submapper(path_prefix='/_admin', controller='admin/settings') as m:
99 99 m.connect("admin_settings", "/settings",
100 100 action="create", conditions=dict(method=["POST"]))
101 101 m.connect("admin_settings", "/settings",
102 102 action="index", conditions=dict(method=["GET"]))
103 103 m.connect("formatted_admin_settings", "/settings.{format}",
104 104 action="index", conditions=dict(method=["GET"]))
105 105 m.connect("admin_new_setting", "/settings/new",
106 106 action="new", conditions=dict(method=["GET"]))
107 107 m.connect("formatted_admin_new_setting", "/settings/new.{format}",
108 108 action="new", conditions=dict(method=["GET"]))
109 109 m.connect("/settings/{setting_id}",
110 110 action="update", conditions=dict(method=["PUT"]))
111 111 m.connect("/settings/{setting_id}",
112 112 action="delete", conditions=dict(method=["DELETE"]))
113 113 m.connect("admin_edit_setting", "/settings/{setting_id}/edit",
114 114 action="edit", conditions=dict(method=["GET"]))
115 115 m.connect("formatted_admin_edit_setting", "/settings/{setting_id}.{format}/edit",
116 116 action="edit", conditions=dict(method=["GET"]))
117 117 m.connect("admin_setting", "/settings/{setting_id}",
118 118 action="show", conditions=dict(method=["GET"]))
119 119 m.connect("formatted_admin_setting", "/settings/{setting_id}.{format}",
120 120 action="show", conditions=dict(method=["GET"]))
121 121 m.connect("admin_settings_my_account", "/my_account",
122 122 action="my_account", conditions=dict(method=["GET"]))
123 123 m.connect("admin_settings_my_account_update", "/my_account_update",
124 124 action="my_account_update", conditions=dict(method=["PUT"]))
125 125 m.connect("admin_settings_create_repository", "/create_repository",
126 126 action="create_repository", conditions=dict(method=["GET"]))
127 127
128 128 #ADMIN MAIN PAGES
129 129 with map.submapper(path_prefix='/_admin', controller='admin/admin') as m:
130 130 m.connect('admin_home', '', action='index')#main page
131 131 m.connect('admin_add_repo', '/add_repo/{new_repo:[a-z0-9\. _-]*}',
132 132 action='add_repo')
133 133
134 134
135 135 #USER JOURNAL
136 136 map.connect('journal', '/_admin/journal', controller='journal',)
137 137 map.connect('toggle_following', '/_admin/toggle_following', controller='journal',
138 138 action='toggle_following', conditions=dict(method=["POST"]))
139 139
140 140
141 141 #SEARCH
142 142 map.connect('search', '/_admin/search', controller='search',)
143 143 map.connect('search_repo', '/_admin/search/{search_repo:.*}', controller='search')
144 144
145 145 #LOGIN/LOGOUT/REGISTER/SIGN IN
146 146 map.connect('login_home', '/_admin/login', controller='login')
147 147 map.connect('logout_home', '/_admin/logout', controller='login', action='logout')
148 148 map.connect('register', '/_admin/register', controller='login', action='register')
149 149 map.connect('reset_password', '/_admin/password_reset', controller='login', action='password_reset')
150 150
151 151 #FEEDS
152 152 map.connect('rss_feed_home', '/{repo_name:.*}/feed/rss',
153 153 controller='feed', action='rss',
154 154 conditions=dict(function=check_repo))
155 155 map.connect('atom_feed_home', '/{repo_name:.*}/feed/atom',
156 156 controller='feed', action='atom',
157 157 conditions=dict(function=check_repo))
158 158
159 159
160 160 #REPOSITORY ROUTES
161 161 map.connect('changeset_home', '/{repo_name:.*}/changeset/{revision}',
162 162 controller='changeset', revision='tip',
163 163 conditions=dict(function=check_repo))
164 164 map.connect('raw_changeset_home', '/{repo_name:.*}/raw-changeset/{revision}',
165 165 controller='changeset', action='raw_changeset', revision='tip',
166 166 conditions=dict(function=check_repo))
167 167 map.connect('summary_home', '/{repo_name:.*}/summary',
168 168 controller='summary', conditions=dict(function=check_repo))
169 169 map.connect('shortlog_home', '/{repo_name:.*}/shortlog',
170 170 controller='shortlog', conditions=dict(function=check_repo))
171 171 map.connect('branches_home', '/{repo_name:.*}/branches',
172 172 controller='branches', conditions=dict(function=check_repo))
173 173 map.connect('tags_home', '/{repo_name:.*}/tags',
174 174 controller='tags', conditions=dict(function=check_repo))
175 175 map.connect('changelog_home', '/{repo_name:.*}/changelog',
176 176 controller='changelog', conditions=dict(function=check_repo))
177 177 map.connect('files_home', '/{repo_name:.*}/files/{revision}/{f_path:.*}',
178 178 controller='files', revision='tip', f_path='',
179 179 conditions=dict(function=check_repo))
180 180 map.connect('files_diff_home', '/{repo_name:.*}/diff/{f_path:.*}',
181 181 controller='files', action='diff', revision='tip', f_path='',
182 182 conditions=dict(function=check_repo))
183 183 map.connect('files_rawfile_home', '/{repo_name:.*}/rawfile/{revision}/{f_path:.*}',
184 184 controller='files', action='rawfile', revision='tip', f_path='',
185 185 conditions=dict(function=check_repo))
186 186 map.connect('files_raw_home', '/{repo_name:.*}/raw/{revision}/{f_path:.*}',
187 187 controller='files', action='raw', revision='tip', f_path='',
188 188 conditions=dict(function=check_repo))
189 189 map.connect('files_annotate_home', '/{repo_name:.*}/annotate/{revision}/{f_path:.*}',
190 190 controller='files', action='annotate', revision='tip', f_path='',
191 191 conditions=dict(function=check_repo))
192 map.connect('files_archive_home', '/{repo_name:.*}/archive/{revision}/{fileformat}',
193 controller='files', action='archivefile', revision='tip',
192 map.connect('files_archive_home', '/{repo_name:.*}/archive/{fname}',
193 controller='files', action='archivefile',
194 194 conditions=dict(function=check_repo))
195 195 map.connect('repo_settings_delete', '/{repo_name:.*}/settings',
196 196 controller='settings', action="delete",
197 197 conditions=dict(method=["DELETE"], function=check_repo))
198 198 map.connect('repo_settings_update', '/{repo_name:.*}/settings',
199 199 controller='settings', action="update",
200 200 conditions=dict(method=["PUT"], function=check_repo))
201 201 map.connect('repo_settings_home', '/{repo_name:.*}/settings',
202 202 controller='settings', action='index',
203 203 conditions=dict(function=check_repo))
204 204
205 205 map.connect('repo_fork_create_home', '/{repo_name:.*}/fork',
206 206 controller='settings', action='fork_create',
207 207 conditions=dict(function=check_repo, method=["POST"]))
208 208 map.connect('repo_fork_home', '/{repo_name:.*}/fork',
209 209 controller='settings', action='fork',
210 210 conditions=dict(function=check_repo))
211 211
212 212 return map
@@ -1,253 +1,263 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.controllers.files
4 4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 Files controller for RhodeCode
7 7
8 8 :created_on: Apr 21, 2010
9 9 :author: marcink
10 10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
11 11 :license: GPLv3, see COPYING for more details.
12 12 """
13 13 # This program is free software; you can redistribute it and/or
14 14 # modify it under the terms of the GNU General Public License
15 15 # as published by the Free Software Foundation; version 2
16 16 # of the License or (at your opinion) any later version of the license.
17 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 24 # along with this program; if not, write to the Free Software
25 25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
26 26 # MA 02110-1301, USA.
27 27 import tempfile
28 28 import logging
29 29 import rhodecode.lib.helpers as h
30 30
31 31 from mercurial import archival
32 32
33 33 from pylons import request, response, session, tmpl_context as c, url
34 34 from pylons.i18n.translation import _
35 35 from pylons.controllers.util import redirect
36 36
37 37 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
38 38 from rhodecode.lib.base import BaseController, render
39 39 from rhodecode.lib.utils import EmptyChangeset
40 40 from rhodecode.model.scm import ScmModel
41 41
42 from vcs.exceptions import RepositoryError, ChangesetError
42 from vcs.exceptions import RepositoryError, ChangesetError, ChangesetDoesNotExistError
43 43 from vcs.nodes import FileNode
44 44 from vcs.utils import diffs as differ
45 45
46 46 log = logging.getLogger(__name__)
47 47
48 48 class FilesController(BaseController):
49 49
50 50 @LoginRequired()
51 51 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
52 52 'repository.admin')
53 53 def __before__(self):
54 54 super(FilesController, self).__before__()
55 55 c.cut_off_limit = self.cut_off_limit
56 56
57 57 def index(self, repo_name, revision, f_path):
58 58 hg_model = ScmModel()
59 59 c.repo = hg_model.get_repo(c.repo_name)
60 60 revision = request.POST.get('at_rev', None) or revision
61 61
62 62 def get_next_rev(cur):
63 63 max_rev = len(c.repo.revisions) - 1
64 64 r = cur + 1
65 65 if r > max_rev:
66 66 r = max_rev
67 67 return r
68 68
69 69 def get_prev_rev(cur):
70 70 r = cur - 1
71 71 return r
72 72
73 73 c.f_path = f_path
74 74
75 75
76 76 try:
77 77 c.changeset = c.repo.get_changeset(revision)
78 78 cur_rev = c.changeset.revision
79 79 prev_rev = c.repo.get_changeset(get_prev_rev(cur_rev)).raw_id
80 80 next_rev = c.repo.get_changeset(get_next_rev(cur_rev)).raw_id
81 81
82 82 c.url_prev = url('files_home', repo_name=c.repo_name,
83 83 revision=prev_rev, f_path=f_path)
84 84 c.url_next = url('files_home', repo_name=c.repo_name,
85 85 revision=next_rev, f_path=f_path)
86 86
87 87 try:
88 88 c.files_list = c.changeset.get_node(f_path)
89 89 c.file_history = self._get_history(c.repo, c.files_list, f_path)
90 90 except RepositoryError, e:
91 91 h.flash(str(e), category='warning')
92 92 redirect(h.url('files_home', repo_name=repo_name, revision=revision))
93 93
94 94 except RepositoryError, e:
95 95 h.flash(str(e), category='warning')
96 96 redirect(h.url('files_home', repo_name=repo_name, revision='tip'))
97 97
98 98
99 99
100 100 return render('files/files.html')
101 101
102 102 def rawfile(self, repo_name, revision, f_path):
103 103 hg_model = ScmModel()
104 104 c.repo = hg_model.get_repo(c.repo_name)
105 105 file_node = c.repo.get_changeset(revision).get_node(f_path)
106 106 response.content_type = file_node.mimetype
107 107 response.content_disposition = 'attachment; filename=%s' \
108 108 % f_path.split('/')[-1]
109 109 return file_node.content
110 110
111 111 def raw(self, repo_name, revision, f_path):
112 112 hg_model = ScmModel()
113 113 c.repo = hg_model.get_repo(c.repo_name)
114 114 file_node = c.repo.get_changeset(revision).get_node(f_path)
115 115 response.content_type = 'text/plain'
116 116
117 117 return file_node.content
118 118
119 119 def annotate(self, repo_name, revision, f_path):
120 120 hg_model = ScmModel()
121 121 c.repo = hg_model.get_repo(c.repo_name)
122 122
123 123 try:
124 124 c.cs = c.repo.get_changeset(revision)
125 125 c.file = c.cs.get_node(f_path)
126 126 except RepositoryError, e:
127 127 h.flash(str(e), category='warning')
128 128 redirect(h.url('files_home', repo_name=repo_name, revision=revision))
129 129
130 130 c.file_history = self._get_history(c.repo, c.file, f_path)
131 131
132 132 c.f_path = f_path
133 133
134 134 return render('files/files_annotate.html')
135 135
136 def archivefile(self, repo_name, revision, fileformat):
136 def archivefile(self, repo_name, fname):
137 info = fname.split('.')
138 revision, fileformat = info[0], '.' + '.'.join(info[1:])
137 139 archive_specs = {
138 140 '.tar.bz2': ('application/x-tar', 'tbz2'),
139 141 '.tar.gz': ('application/x-tar', 'tgz'),
140 142 '.zip': ('application/zip', 'zip'),
141 143 }
142 144 if not archive_specs.has_key(fileformat):
143 return 'Unknown archive type %s' % fileformat
145 return _('Unknown archive type %s') % fileformat
146
147 repo = ScmModel().get_repo(repo_name)
148
149 try:
150 repo.get_changeset(revision)
151 except ChangesetDoesNotExistError:
152 return _('Unknown revision %s') % revision
153
154 archive = tempfile.TemporaryFile()
155 localrepo = repo.repo
156 fname = '%s-%s%s' % (repo_name, revision, fileformat)
157 archival.archive(localrepo, archive, revision, archive_specs[fileformat][1],
158 prefix='%s-%s' % (repo_name, revision))
159 response.content_type = archive_specs[fileformat][0]
160 response.content_disposition = 'attachment; filename=%s' % fname
161 archive.seek(0)
144 162
145 163 def read_in_chunks(file_object, chunk_size=1024 * 40):
146 164 """Lazy function (generator) to read a file piece by piece.
147 165 Default chunk size: 40k."""
148 166 while True:
149 167 data = file_object.read(chunk_size)
150 168 if not data:
151 169 break
152 170 yield data
153 171
154 archive = tempfile.TemporaryFile()
155 repo = ScmModel().get_repo(repo_name).repo
156 fname = '%s-%s%s' % (repo_name, revision, fileformat)
157 archival.archive(repo, archive, revision, archive_specs[fileformat][1],
158 prefix='%s-%s' % (repo_name, revision))
159 response.content_type = archive_specs[fileformat][0]
160 response.content_disposition = 'attachment; filename=%s' % fname
161 archive.seek(0)
162 172 return read_in_chunks(archive)
163 173
164 174 def diff(self, repo_name, f_path):
165 175 hg_model = ScmModel()
166 176 diff1 = request.GET.get('diff1')
167 177 diff2 = request.GET.get('diff2')
168 178 c.action = request.GET.get('diff')
169 179 c.no_changes = diff1 == diff2
170 180 c.f_path = f_path
171 181 c.repo = hg_model.get_repo(c.repo_name)
172 182
173 183 try:
174 184 if diff1 not in ['', None, 'None', '0' * 12, '0' * 40]:
175 185 c.changeset_1 = c.repo.get_changeset(diff1)
176 186 node1 = c.changeset_1.get_node(f_path)
177 187 else:
178 188 c.changeset_1 = EmptyChangeset()
179 189 node1 = FileNode('.', '', changeset=c.changeset_1)
180 190
181 191 if diff2 not in ['', None, 'None', '0' * 12, '0' * 40]:
182 192 c.changeset_2 = c.repo.get_changeset(diff2)
183 193 node2 = c.changeset_2.get_node(f_path)
184 194 else:
185 195 c.changeset_2 = EmptyChangeset()
186 196 node2 = FileNode('.', '', changeset=c.changeset_2)
187 197 except RepositoryError:
188 198 return redirect(url('files_home',
189 199 repo_name=c.repo_name, f_path=f_path))
190 200
191 201 f_udiff = differ.get_udiff(node1, node2)
192 202 diff = differ.DiffProcessor(f_udiff)
193 203
194 204 if c.action == 'download':
195 205 diff_name = '%s_vs_%s.diff' % (diff1, diff2)
196 206 response.content_type = 'text/plain'
197 207 response.content_disposition = 'attachment; filename=%s' \
198 208 % diff_name
199 209 return diff.raw_diff()
200 210
201 211 elif c.action == 'raw':
202 212 response.content_type = 'text/plain'
203 213 return diff.raw_diff()
204 214
205 215 elif c.action == 'diff':
206 216 if node1.size > self.cut_off_limit or node2.size > self.cut_off_limit:
207 217 c.cur_diff = _('Diff is to big to display')
208 218 else:
209 219 c.cur_diff = diff.as_html()
210 220 else:
211 221 #default option
212 222 if node1.size > self.cut_off_limit or node2.size > self.cut_off_limit:
213 223 c.cur_diff = _('Diff is to big to display')
214 224 else:
215 225 c.cur_diff = diff.as_html()
216 226
217 227 if not c.cur_diff: c.no_changes = True
218 228 return render('files/file_diff.html')
219 229
220 230 def _get_history(self, repo, node, f_path):
221 231 from vcs.nodes import NodeKind
222 232 if not node.kind is NodeKind.FILE:
223 233 return []
224 234 changesets = node.history
225 235 hist_l = []
226 236
227 237 changesets_group = ([], _("Changesets"))
228 238 branches_group = ([], _("Branches"))
229 239 tags_group = ([], _("Tags"))
230 240
231 241 for chs in changesets:
232 242 n_desc = 'r%s:%s' % (chs.revision, chs.short_id)
233 243 changesets_group[0].append((chs.raw_id, n_desc,))
234 244
235 245 hist_l.append(changesets_group)
236 246
237 247 for name, chs in c.repository_branches.items():
238 248 #chs = chs.split(':')[-1]
239 249 branches_group[0].append((chs, name),)
240 250 hist_l.append(branches_group)
241 251
242 252 for name, chs in c.repository_tags.items():
243 253 #chs = chs.split(':')[-1]
244 254 tags_group[0].append((chs, name),)
245 255 hist_l.append(tags_group)
246 256
247 257 return hist_l
248 258
249 259 # [
250 260 # ([("u1", "User1"), ("u2", "User2")], "Users"),
251 261 # ([("g1", "Group1"), ("g2", "Group2")], "Groups")
252 262 # ]
253 263
@@ -1,643 +1,643 b''
1 1 <%inherit file="/base/base.html"/>
2 2
3 3 <%def name="title()">
4 4 ${c.repo_name} ${_('Summary')} - ${c.rhodecode_name}
5 5 </%def>
6 6
7 7 <%def name="breadcrumbs_links()">
8 8 ${h.link_to(u'Home',h.url('/'))}
9 9 &raquo;
10 10 ${h.link_to(c.repo_name,h.url('summary_home',repo_name=c.repo_name))}
11 11 &raquo;
12 12 ${_('summary')}
13 13 </%def>
14 14
15 15 <%def name="page_nav()">
16 16 ${self.menu('summary')}
17 17 </%def>
18 18
19 19 <%def name="main()">
20 20 <div class="box box-left">
21 21 <!-- box / title -->
22 22 <div class="title">
23 23 ${self.breadcrumbs()}
24 24 </div>
25 25 <!-- end box / title -->
26 26 <div class="form">
27 27 <div class="fields">
28 28
29 29 <div class="field">
30 30 <div class="label">
31 31 <label>${_('Name')}:</label>
32 32 </div>
33 33 <div class="input-short">
34 34 %if c.repo_info.dbrepo.repo_type =='hg':
35 35 <img style="margin-bottom:2px" class="icon" title="${_('Mercurial repository')}" alt="${_('Mercurial repository')}" src="/images/icons/hgicon.png"/>
36 36 %endif
37 37 %if c.repo_info.dbrepo.repo_type =='git':
38 38 <img style="margin-bottom:2px" class="icon" title="${_('Git repository')}" alt="${_('Git repository')}" src="/images/icons/giticon.png"/>
39 39 %endif
40 40
41 41 %if c.repo_info.dbrepo.private:
42 42 <img style="margin-bottom:2px" class="icon" title="${_('private repository')}" alt="${_('private repository')}" src="/images/icons/lock.png"/>
43 43 %else:
44 44 <img style="margin-bottom:2px" class="icon" title="${_('public repository')}" alt="${_('public repository')}" src="/images/icons/lock_open.png"/>
45 45 %endif
46 46 <span style="font-size: 1.6em;font-weight: bold;vertical-align: baseline;">${c.repo_info.name}</span>
47 47 %if c.rhodecode_user.username != 'default':
48 48 %if c.following:
49 49 <span id="follow_toggle" class="following" title="${_('Stop following this repository')}"
50 50 onclick="javascript:toggleFollowingRepo(${c.repo_info.dbrepo.repo_id},'${str(h.get_token())}')">
51 51 </span>
52 52 %else:
53 53 <span id="follow_toggle" class="follow" title="${_('Start following this repository')}"
54 54 onclick="javascript:toggleFollowingRepo(${c.repo_info.dbrepo.repo_id},'${str(h.get_token())}')">
55 55 </span>
56 56 %endif
57 57 %endif:
58 58 <br/>
59 59 %if c.repo_info.dbrepo.fork:
60 60 <span style="margin-top:5px">
61 61 <a href="${h.url('summary_home',repo_name=c.repo_info.dbrepo.fork.repo_name)}">
62 62 <img class="icon" alt="${_('public')}"
63 63 title="${_('Fork of')} ${c.repo_info.dbrepo.fork.repo_name}"
64 64 src="/images/icons/arrow_divide.png"/>
65 65 ${_('Fork of')} ${c.repo_info.dbrepo.fork.repo_name}
66 66 </a>
67 67 </span>
68 68 %endif
69 69 </div>
70 70 </div>
71 71
72 72
73 73 <div class="field">
74 74 <div class="label">
75 75 <label>${_('Description')}:</label>
76 76 </div>
77 77 <div class="input-short">
78 78 ${c.repo_info.dbrepo.description}
79 79 </div>
80 80 </div>
81 81
82 82
83 83 <div class="field">
84 84 <div class="label">
85 85 <label>${_('Contact')}:</label>
86 86 </div>
87 87 <div class="input-short">
88 88 <div class="gravatar">
89 89 <img alt="gravatar" src="${h.gravatar_url(c.repo_info.dbrepo.user.email)}"/>
90 90 </div>
91 91 ${_('Username')}: ${c.repo_info.dbrepo.user.username}<br/>
92 92 ${_('Name')}: ${c.repo_info.dbrepo.user.name} ${c.repo_info.dbrepo.user.lastname}<br/>
93 93 ${_('Email')}: <a href="mailto:${c.repo_info.dbrepo.user.email}">${c.repo_info.dbrepo.user.email}</a>
94 94 </div>
95 95 </div>
96 96
97 97 <div class="field">
98 98 <div class="label">
99 99 <label>${_('Last change')}:</label>
100 100 </div>
101 101 <div class="input-short">
102 102 ${h.age(c.repo_info.last_change)} - ${c.repo_info.last_change}
103 103 ${_('by')} ${h.get_changeset_safe(c.repo_info,'tip').author}
104 104
105 105 </div>
106 106 </div>
107 107
108 108 <div class="field">
109 109 <div class="label">
110 110 <label>${_('Clone url')}:</label>
111 111 </div>
112 112 <div class="input-short">
113 113 <input type="text" id="clone_url" readonly="readonly" value="hg clone ${c.clone_repo_url}" size="70"/>
114 114 </div>
115 115 </div>
116 116
117 117 <div class="field">
118 118 <div class="label">
119 119 <label>${_('Trending source files')}:</label>
120 120 </div>
121 121 <div class="input-short">
122 122 <div id="lang_stats">
123 123
124 124 </div>
125 125 <script type="text/javascript">
126 126 YUE.onDOMReady(function(e){
127 127 id = 'clone_url';
128 128 YUE.on(id,'click',function(e){
129 129 YUD.get('clone_url').select();
130 130 })
131 131 })
132 132 var data = ${c.trending_languages|n};
133 133 var total = 0;
134 134 var no_data = true;
135 135 for (k in data){
136 136 total += data[k];
137 137 no_data = false;
138 138 }
139 139 var tbl = document.createElement('table');
140 140 tbl.setAttribute('class','trending_language_tbl');
141 141 var cnt =0;
142 142 for (k in data){
143 143 cnt+=1;
144 144 var hide = cnt>2;
145 145 var tr = document.createElement('tr');
146 146 if (hide){
147 147 tr.setAttribute('style','display:none');
148 148 tr.setAttribute('class','stats_hidden');
149 149 }
150 150 var percentage = Math.round((data[k]/total*100),2);
151 151 var value = data[k];
152 152 var td1 = document.createElement('td');
153 153 td1.width=150;
154 154 var trending_language_label = document.createElement('div');
155 155 trending_language_label.innerHTML = k;
156 156 td1.appendChild(trending_language_label);
157 157
158 158 var td2 = document.createElement('td');
159 159 td2.setAttribute('style','padding-right:14px !important');
160 160 var trending_language = document.createElement('div');
161 161 var nr_files = value+" ${_('files')}";
162 162
163 163 trending_language.title = k+" "+nr_files;
164 164
165 165 if (percentage>20){
166 166 trending_language.innerHTML = "<b style='font-size:0.8em'>"+percentage+"% "+nr_files+ "</b>";
167 167 }
168 168 else{
169 169 trending_language.innerHTML = "<b style='font-size:0.8em'>"+percentage+"%</b>";
170 170 }
171 171
172 172 trending_language.setAttribute("class", 'trending_language top-right-rounded-corner bottom-right-rounded-corner');
173 173 trending_language.style.width=percentage+"%";
174 174 td2.appendChild(trending_language);
175 175
176 176 tr.appendChild(td1);
177 177 tr.appendChild(td2);
178 178 tbl.appendChild(tr);
179 179 if(cnt == 2){
180 180 var show_more = document.createElement('tr');
181 181 var td=document.createElement('td');
182 182 lnk = document.createElement('a');
183 183 lnk.href='#';
184 184 lnk.innerHTML = "${_("show more")}";
185 185 lnk.id='code_stats_show_more';
186 186 td.appendChild(lnk);
187 187 show_more.appendChild(td);
188 188 show_more.appendChild(document.createElement('td'));
189 189 tbl.appendChild(show_more);
190 190 }
191 191
192 192 }
193 193 if(no_data){
194 194 var tr = document.createElement('tr');
195 195 var td1 = document.createElement('td');
196 196 td1.innerHTML = "${c.no_data_msg}";
197 197 tr.appendChild(td1);
198 198 tbl.appendChild(tr);
199 199 }
200 200 YUD.get('lang_stats').appendChild(tbl);
201 201 YUE.on('code_stats_show_more','click',function(){
202 202 l = YUD.getElementsByClassName('stats_hidden')
203 203 for (e in l){
204 204 YUD.setStyle(l[e],'display','');
205 205 };
206 206 YUD.setStyle(YUD.get('code_stats_show_more'),
207 207 'display','none');
208 208 })
209 209
210 210 </script>
211 211
212 212 </div>
213 213 </div>
214 214
215 215 <div class="field">
216 216 <div class="label">
217 217 <label>${_('Download')}:</label>
218 218 </div>
219 219 <div class="input-short">
220 220 %for cnt,archive in enumerate(c.repo_info._get_archives()):
221 221 %if cnt >=1:
222 222 |
223 223 %endif
224 224 ${h.link_to(c.repo_info.name+'.'+archive['type'],
225 225 h.url('files_archive_home',repo_name=c.repo_info.name,
226 revision='tip',fileformat=archive['extension']),class_="archive_icon")}
226 fname='tip'+archive['extension']),class_="archive_icon")}
227 227 %endfor
228 228 </div>
229 229 </div>
230 230
231 231 <div class="field">
232 232 <div class="label">
233 233 <label>${_('Feeds')}:</label>
234 234 </div>
235 235 <div class="input-short">
236 236 ${h.link_to(_('RSS'),h.url('rss_feed_home',repo_name=c.repo_info.name),class_='rss_icon')}
237 237 ${h.link_to(_('Atom'),h.url('atom_feed_home',repo_name=c.repo_info.name),class_='atom_icon')}
238 238 </div>
239 239 </div>
240 240 </div>
241 241 </div>
242 242 </div>
243 243
244 244 <div class="box box-right" style="min-height:455px">
245 245 <!-- box / title -->
246 246 <div class="title">
247 247 <h5>${_('Commit activity by day / author')}</h5>
248 248 </div>
249 249
250 250 <div class="table">
251 251
252 252 %if c.no_data:
253 253 <div style="padding:0 10px 10px 15px;font-size: 1.2em;">${c.no_data_msg}</div>
254 254 %endif:
255 255 <div id="commit_history" style="width:460px;height:300px;float:left"></div>
256 256 <div style="clear: both;height: 10px"></div>
257 257 <div id="overview" style="width:460px;height:100px;float:left"></div>
258 258
259 259 <div id="legend_data" style="clear:both;margin-top:10px;">
260 260 <div id="legend_container"></div>
261 261 <div id="legend_choices">
262 262 <table id="legend_choices_tables" style="font-size:smaller;color:#545454"></table>
263 263 </div>
264 264 </div>
265 265 <script type="text/javascript">
266 266 /**
267 267 * Plots summary graph
268 268 *
269 269 * @class SummaryPlot
270 270 * @param {from} initial from for detailed graph
271 271 * @param {to} initial to for detailed graph
272 272 * @param {dataset}
273 273 * @param {overview_dataset}
274 274 */
275 275 function SummaryPlot(from,to,dataset,overview_dataset) {
276 276 var initial_ranges = {
277 277 "xaxis":{
278 278 "from":from,
279 279 "to":to,
280 280 },
281 281 };
282 282 var dataset = dataset;
283 283 var overview_dataset = [overview_dataset];
284 284 var choiceContainer = YUD.get("legend_choices");
285 285 var choiceContainerTable = YUD.get("legend_choices_tables");
286 286 var plotContainer = YUD.get('commit_history');
287 287 var overviewContainer = YUD.get('overview');
288 288
289 289 var plot_options = {
290 290 bars: {show:true,align:'center',lineWidth:4},
291 291 legend: {show:true, container:"legend_container"},
292 292 points: {show:true,radius:0,fill:false},
293 293 yaxis: {tickDecimals:0,},
294 294 xaxis: {
295 295 mode: "time",
296 296 timeformat: "%d/%m",
297 297 min:from,
298 298 max:to,
299 299 },
300 300 grid: {
301 301 hoverable: true,
302 302 clickable: true,
303 303 autoHighlight:true,
304 304 color: "#999"
305 305 },
306 306 //selection: {mode: "x"}
307 307 };
308 308 var overview_options = {
309 309 legend:{show:false},
310 310 bars: {show:true,barWidth: 2,},
311 311 shadowSize: 0,
312 312 xaxis: {mode: "time", timeformat: "%d/%m/%y",},
313 313 yaxis: {ticks: 3, min: 0,tickDecimals:0,},
314 314 grid: {color: "#999",},
315 315 selection: {mode: "x"}
316 316 };
317 317
318 318 /**
319 319 *get dummy data needed in few places
320 320 */
321 321 function getDummyData(label){
322 322 return {"label":label,
323 323 "data":[{"time":0,
324 324 "commits":0,
325 325 "added":0,
326 326 "changed":0,
327 327 "removed":0,
328 328 }],
329 329 "schema":["commits"],
330 330 "color":'#ffffff',
331 331 }
332 332 }
333 333
334 334 /**
335 335 * generate checkboxes accordindly to data
336 336 * @param keys
337 337 * @returns
338 338 */
339 339 function generateCheckboxes(data) {
340 340 //append checkboxes
341 341 var i = 0;
342 342 choiceContainerTable.innerHTML = '';
343 343 for(var pos in data) {
344 344
345 345 data[pos].color = i;
346 346 i++;
347 347 if(data[pos].label != ''){
348 348 choiceContainerTable.innerHTML += '<tr><td>'+
349 349 '<input type="checkbox" name="' + data[pos].label +'" checked="checked" />'
350 350 +data[pos].label+
351 351 '</td></tr>';
352 352 }
353 353 }
354 354 }
355 355
356 356 /**
357 357 * ToolTip show
358 358 */
359 359 function showTooltip(x, y, contents) {
360 360 var div=document.getElementById('tooltip');
361 361 if(!div) {
362 362 div = document.createElement('div');
363 363 div.id="tooltip";
364 364 div.style.position="absolute";
365 365 div.style.border='1px solid #fdd';
366 366 div.style.padding='2px';
367 367 div.style.backgroundColor='#fee';
368 368 document.body.appendChild(div);
369 369 }
370 370 YUD.setStyle(div, 'opacity', 0);
371 371 div.innerHTML = contents;
372 372 div.style.top=(y + 5) + "px";
373 373 div.style.left=(x + 5) + "px";
374 374
375 375 var anim = new YAHOO.util.Anim(div, {opacity: {to: 0.8}}, 0.2);
376 376 anim.animate();
377 377 }
378 378
379 379 /**
380 380 * This function will detect if selected period has some changesets
381 381 for this user if it does this data is then pushed for displaying
382 382 Additionally it will only display users that are selected by the checkbox
383 383 */
384 384 function getDataAccordingToRanges(ranges) {
385 385
386 386 var data = [];
387 387 var keys = [];
388 388 for(var key in dataset){
389 389 var push = false;
390 390
391 391 //method1 slow !!
392 392 //*
393 393 for(var ds in dataset[key].data){
394 394 commit_data = dataset[key].data[ds];
395 395 if (commit_data.time >= ranges.xaxis.from && commit_data.time <= ranges.xaxis.to){
396 396 push = true;
397 397 break;
398 398 }
399 399 }
400 400 //*/
401 401
402 402 /*//method2 sorted commit data !!!
403 403
404 404 var first_commit = dataset[key].data[0].time;
405 405 var last_commit = dataset[key].data[dataset[key].data.length-1].time;
406 406
407 407 if (first_commit >= ranges.xaxis.from && last_commit <= ranges.xaxis.to){
408 408 push = true;
409 409 }
410 410 //*/
411 411
412 412 if(push){
413 413 data.push(dataset[key]);
414 414 }
415 415 }
416 416 if(data.length >= 1){
417 417 return data;
418 418 }
419 419 else{
420 420 //just return dummy data for graph to plot itself
421 421 return [getDummyData('')];
422 422 }
423 423
424 424 }
425 425
426 426 /**
427 427 * redraw using new checkbox data
428 428 */
429 429 function plotchoiced(e,args){
430 430 var cur_data = args[0];
431 431 var cur_ranges = args[1];
432 432
433 433 var new_data = [];
434 434 var inputs = choiceContainer.getElementsByTagName("input");
435 435
436 436 //show only checked labels
437 437 for(var i=0; i<inputs.length; i++) {
438 438 var checkbox_key = inputs[i].name;
439 439
440 440 if(inputs[i].checked){
441 441 for(var d in cur_data){
442 442 if(cur_data[d].label == checkbox_key){
443 443 new_data.push(cur_data[d]);
444 444 }
445 445 }
446 446 }
447 447 else{
448 448 //push dummy data to not hide the label
449 449 new_data.push(getDummyData(checkbox_key));
450 450 }
451 451 }
452 452
453 453 var new_options = YAHOO.lang.merge(plot_options, {
454 454 xaxis: {
455 455 min: cur_ranges.xaxis.from,
456 456 max: cur_ranges.xaxis.to,
457 457 mode:"time",
458 458 timeformat: "%d/%m",
459 459 },
460 460 });
461 461 if (!new_data){
462 462 new_data = [[0,1]];
463 463 }
464 464 // do the zooming
465 465 plot = YAHOO.widget.Flot(plotContainer, new_data, new_options);
466 466
467 467 plot.subscribe("plotselected", plotselected);
468 468
469 469 //resubscribe plothover
470 470 plot.subscribe("plothover", plothover);
471 471
472 472 // don't fire event on the overview to prevent eternal loop
473 473 overview.setSelection(cur_ranges, true);
474 474
475 475 }
476 476
477 477 /**
478 478 * plot only selected items from overview
479 479 * @param ranges
480 480 * @returns
481 481 */
482 482 function plotselected(ranges,cur_data) {
483 483 //updates the data for new plot
484 484 data = getDataAccordingToRanges(ranges);
485 485 generateCheckboxes(data);
486 486
487 487 var new_options = YAHOO.lang.merge(plot_options, {
488 488 xaxis: {
489 489 min: ranges.xaxis.from,
490 490 max: ranges.xaxis.to,
491 491 mode:"time",
492 492 timeformat: "%d/%m",
493 493 },
494 494 yaxis: {
495 495 min: ranges.yaxis.from,
496 496 max: ranges.yaxis.to,
497 497 },
498 498
499 499 });
500 500 // do the zooming
501 501 plot = YAHOO.widget.Flot(plotContainer, data, new_options);
502 502
503 503 plot.subscribe("plotselected", plotselected);
504 504
505 505 //resubscribe plothover
506 506 plot.subscribe("plothover", plothover);
507 507
508 508 // don't fire event on the overview to prevent eternal loop
509 509 overview.setSelection(ranges, true);
510 510
511 511 //resubscribe choiced
512 512 YUE.on(choiceContainer.getElementsByTagName("input"), "click", plotchoiced, [data, ranges]);
513 513 }
514 514
515 515 var previousPoint = null;
516 516
517 517 function plothover(o) {
518 518 var pos = o.pos;
519 519 var item = o.item;
520 520
521 521 //YUD.get("x").innerHTML = pos.x.toFixed(2);
522 522 //YUD.get("y").innerHTML = pos.y.toFixed(2);
523 523 if (item) {
524 524 if (previousPoint != item.datapoint) {
525 525 previousPoint = item.datapoint;
526 526
527 527 var tooltip = YUD.get("tooltip");
528 528 if(tooltip) {
529 529 tooltip.parentNode.removeChild(tooltip);
530 530 }
531 531 var x = item.datapoint.x.toFixed(2);
532 532 var y = item.datapoint.y.toFixed(2);
533 533
534 534 if (!item.series.label){
535 535 item.series.label = 'commits';
536 536 }
537 537 var d = new Date(x*1000);
538 538 var fd = d.toDateString()
539 539 var nr_commits = parseInt(y);
540 540
541 541 var cur_data = dataset[item.series.label].data[item.dataIndex];
542 542 var added = cur_data.added;
543 543 var changed = cur_data.changed;
544 544 var removed = cur_data.removed;
545 545
546 546 var nr_commits_suffix = " ${_('commits')} ";
547 547 var added_suffix = " ${_('files added')} ";
548 548 var changed_suffix = " ${_('files changed')} ";
549 549 var removed_suffix = " ${_('files removed')} ";
550 550
551 551
552 552 if(nr_commits == 1){nr_commits_suffix = " ${_('commit')} ";}
553 553 if(added==1){added_suffix=" ${_('file added')} ";}
554 554 if(changed==1){changed_suffix=" ${_('file changed')} ";}
555 555 if(removed==1){removed_suffix=" ${_('file removed')} ";}
556 556
557 557 showTooltip(item.pageX, item.pageY, item.series.label + " on " + fd
558 558 +'<br/>'+
559 559 nr_commits + nr_commits_suffix+'<br/>'+
560 560 added + added_suffix +'<br/>'+
561 561 changed + changed_suffix + '<br/>'+
562 562 removed + removed_suffix + '<br/>');
563 563 }
564 564 }
565 565 else {
566 566 var tooltip = YUD.get("tooltip");
567 567
568 568 if(tooltip) {
569 569 tooltip.parentNode.removeChild(tooltip);
570 570 }
571 571 previousPoint = null;
572 572 }
573 573 }
574 574
575 575 /**
576 576 * MAIN EXECUTION
577 577 */
578 578
579 579 var data = getDataAccordingToRanges(initial_ranges);
580 580 generateCheckboxes(data);
581 581
582 582 //main plot
583 583 var plot = YAHOO.widget.Flot(plotContainer,data,plot_options);
584 584
585 585 //overview
586 586 var overview = YAHOO.widget.Flot(overviewContainer, overview_dataset, overview_options);
587 587
588 588 //show initial selection on overview
589 589 overview.setSelection(initial_ranges);
590 590
591 591 plot.subscribe("plotselected", plotselected);
592 592
593 593 overview.subscribe("plotselected", function (ranges) {
594 594 plot.setSelection(ranges);
595 595 });
596 596
597 597 plot.subscribe("plothover", plothover);
598 598
599 599 YUE.on(choiceContainer.getElementsByTagName("input"), "click", plotchoiced, [data, initial_ranges]);
600 600 }
601 601 SummaryPlot(${c.ts_min},${c.ts_max},${c.commit_data|n},${c.overview_data|n});
602 602 </script>
603 603
604 604 </div>
605 605 </div>
606 606
607 607 <div class="box">
608 608 <div class="title">
609 609 <div class="breadcrumbs">${h.link_to(_('Shortlog'),h.url('shortlog_home',repo_name=c.repo_name))}</div>
610 610 </div>
611 611 <div class="table">
612 612 <div id="shortlog_data">
613 613 <%include file='../shortlog/shortlog_data.html'/>
614 614 </div>
615 615 ##%if c.repo_changesets:
616 616 ## ${h.link_to(_('show more'),h.url('changelog_home',repo_name=c.repo_name))}
617 617 ##%endif
618 618 </div>
619 619 </div>
620 620 <div class="box">
621 621 <div class="title">
622 622 <div class="breadcrumbs">${h.link_to(_('Tags'),h.url('tags_home',repo_name=c.repo_name))}</div>
623 623 </div>
624 624 <div class="table">
625 625 <%include file='../tags/tags_data.html'/>
626 626 %if c.repo_changesets:
627 627 ${h.link_to(_('show more'),h.url('tags_home',repo_name=c.repo_name))}
628 628 %endif
629 629 </div>
630 630 </div>
631 631 <div class="box">
632 632 <div class="title">
633 633 <div class="breadcrumbs">${h.link_to(_('Branches'),h.url('branches_home',repo_name=c.repo_name))}</div>
634 634 </div>
635 635 <div class="table">
636 636 <%include file='../branches/branches_data.html'/>
637 637 %if c.repo_changesets:
638 638 ${h.link_to(_('show more'),h.url('branches_home',repo_name=c.repo_name))}
639 639 %endif
640 640 </div>
641 641 </div>
642 642
643 643 </%def> No newline at end of file
@@ -1,103 +1,103 b''
1 1 import sys
2 2 py_version = sys.version_info
3 3
4 4 from rhodecode import get_version
5 5
6 6 requirements = [
7 7 "Pylons==1.0.0",
8 "SQLAlchemy==0.6.5",
8 "SQLAlchemy>=0.6.5",
9 9 "Mako==0.3.6",
10 "vcs==0.1.10",
11 "pygments==1.3.1",
12 "mercurial==1.7.2",
13 "whoosh==1.3.4",
14 "celery==2.1.4",
10 "vcs=>0.1.10",
11 "pygments>=1.3.1",
12 "mercurial>=1.7.2",
13 "whoosh>=1.3.4",
14 "celery>=2.1.4",
15 15 "py-bcrypt",
16 16 "babel",
17 17 ]
18 18
19 19 classifiers = ['Development Status :: 4 - Beta',
20 20 'Environment :: Web Environment',
21 21 'Framework :: Pylons',
22 22 'Intended Audience :: Developers',
23 23 'License :: OSI Approved :: BSD License',
24 24 'Operating System :: OS Independent',
25 25 'Programming Language :: Python', ]
26 26
27 27 if sys.version_info < (2, 6):
28 28 requirements.append("simplejson")
29 29 requirements.append("pysqlite")
30 30
31 31 #additional files from project that goes somewhere in the filesystem
32 32 #relative to sys.prefix
33 33 data_files = []
34 34
35 35 #additional files that goes into package itself
36 36 package_data = {'rhodecode': ['i18n/*/LC_MESSAGES/*.mo', ], }
37 37
38 38 description = ('Mercurial repository browser/management with '
39 39 'build in push/pull server and full text search')
40 40 #long description
41 41 try:
42 42 readme_file = 'README.rst'
43 43 changelog_file = 'docs/changelog.rst'
44 44 long_description = open(readme_file).read() + '\n\n' + \
45 45 open(changelog_file).read()
46 46
47 47 except IOError, err:
48 48 sys.stderr.write("[WARNING] Cannot find file specified as "
49 49 "long_description (%s)\n or changelog (%s) skipping that file" \
50 50 % (readme_file, changelog_file))
51 51 long_description = description
52 52
53 53
54 54 try:
55 55 from setuptools import setup, find_packages
56 56 except ImportError:
57 57 from ez_setup import use_setuptools
58 58 use_setuptools()
59 59 from setuptools import setup, find_packages
60 60 #packages
61 61 packages = find_packages(exclude=['ez_setup'])
62 62
63 63 setup(
64 64 name='RhodeCode',
65 65 version=get_version(),
66 66 description=description,
67 67 long_description=long_description,
68 68 keywords='rhodiumcode mercurial web hgwebdir gitweb git replacement serving hgweb rhodecode',
69 69 license='BSD',
70 70 author='Marcin Kuzminski',
71 71 author_email='marcin@python-works.com',
72 72 url='http://hg.python-works.com',
73 73 install_requires=requirements,
74 74 classifiers=classifiers,
75 75 setup_requires=["PasteScript>=1.6.3"],
76 76 data_files=data_files,
77 77 packages=packages,
78 78 include_package_data=True,
79 79 test_suite='nose.collector',
80 80 package_data=package_data,
81 81 message_extractors={'rhodecode': [
82 82 ('**.py', 'python', None),
83 83 ('templates/**.mako', 'mako', {'input_encoding': 'utf-8'}),
84 84 ('public/**', 'ignore', None)]},
85 85 zip_safe=False,
86 86 paster_plugins=['PasteScript', 'Pylons'],
87 87 entry_points="""
88 88 [paste.app_factory]
89 89 main = rhodecode.config.middleware:make_app
90 90
91 91 [paste.app_install]
92 92 main = pylons.util:PylonsInstaller
93 93
94 94 [paste.global_paster_command]
95 95 make-index = rhodecode.lib.indexers:MakeIndex
96 96 upgrade-db = rhodecode.lib.dbmigrate:UpgradeDb
97 97 celeryd=rhodecode.lib.celerypylons.commands:CeleryDaemonCommand
98 98 celerybeat=rhodecode.lib.celerypylons.commands:CeleryBeatCommand
99 99 camqadm=rhodecode.lib.celerypylons.commands:CAMQPAdminCommand
100 100 celeryev=rhodecode.lib.celerypylons.commands:CeleryEventCommand
101 101
102 102 """,
103 103 )
General Comments 0
You need to be logged in to leave comments. Login now