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