##// END OF EJS Templates
fixed #597 commits in future get negative age.
marcink -
r2902:e2b2791d beta
parent child Browse files
Show More
@@ -1,820 +1,821 b''
1 .. _changelog:
1 .. _changelog:
2
2
3 =========
3 =========
4 Changelog
4 Changelog
5 =========
5 =========
6
6
7 1.4.4 (**2012-XX-XX**)
7 1.4.4 (**2012-XX-XX**)
8 ----------------------
8 ----------------------
9
9
10 :status: in-progress
10 :status: in-progress
11 :branch: beta
11 :branch: beta
12
12
13 news
13 news
14 ++++
14 ++++
15
15
16 - obfuscate db password in logs for engine connection string
16 - obfuscate db password in logs for engine connection string
17 - #574 Show pull request status also in shortlog (if any)
17 - #574 Show pull request status also in shortlog (if any)
18 - remember selected tab in my account page
18 - remember selected tab in my account page
19 - Bumped mercurial version to 2.3.2
19 - Bumped mercurial version to 2.3.2
20
20
21 fixes
21 fixes
22 +++++
22 +++++
23
23
24 - Add git version detection to warn users that Git used in system is to
24 - Add git version detection to warn users that Git used in system is to
25 old. Ref #588 - also show git version in system details in settings page
25 old. Ref #588 - also show git version in system details in settings page
26 - fixed files quick filter links
26 - fixed files quick filter links
27 - #590 Add GET flag that controls the way the diff are generated, for pull
27 - #590 Add GET flag that controls the way the diff are generated, for pull
28 requests we want to use non-bundle based diffs, That are far better for
28 requests we want to use non-bundle based diffs, That are far better for
29 doing code reviews. The /compare url still uses bundle compare for full
29 doing code reviews. The /compare url still uses bundle compare for full
30 comparison including the incoming changesets
30 comparison including the incoming changesets
31 - Fixed #585, checks for status of revision where to strict, and made
31 - Fixed #585, checks for status of revision where to strict, and made
32 opening pull request with those revision impossible due to previously set
32 opening pull request with those revision impossible due to previously set
33 status. Checks now are made also for the repository.
33 status. Checks now are made also for the repository.
34 - fixes #591 git backend was causing encoding errors when handling binary
34 - fixes #591 git backend was causing encoding errors when handling binary
35 files - added a test case for VCS lib tests
35 files - added a test case for VCS lib tests
36 - fixed #597 commits in future get negative age.
36
37
37 1.4.3 (**2012-09-28**)
38 1.4.3 (**2012-09-28**)
38 ----------------------
39 ----------------------
39
40
40 news
41 news
41 ++++
42 ++++
42
43
43 - #558 Added config file to hooks extra data
44 - #558 Added config file to hooks extra data
44 - bumped mercurial version to 2.3.1
45 - bumped mercurial version to 2.3.1
45 - #518 added possibility of specifying multiple patterns for issues
46 - #518 added possibility of specifying multiple patterns for issues
46 - update codemirror to latest version
47 - update codemirror to latest version
47
48
48 fixes
49 fixes
49 +++++
50 +++++
50
51
51 - fixed #570 explicit users group permissions can overwrite owner permissions
52 - fixed #570 explicit users group permissions can overwrite owner permissions
52 - fixed #578 set proper PATH with current Python for Git
53 - fixed #578 set proper PATH with current Python for Git
53 hooks to execute within same Python as RhodeCode
54 hooks to execute within same Python as RhodeCode
54 - fixed issue with Git bare repos that ends with .git in name
55 - fixed issue with Git bare repos that ends with .git in name
55
56
56 1.4.2 (**2012-09-12**)
57 1.4.2 (**2012-09-12**)
57 ----------------------
58 ----------------------
58
59
59 news
60 news
60 ++++
61 ++++
61
62
62 - added option to menu to quick lock/unlock repository for users that have
63 - added option to menu to quick lock/unlock repository for users that have
63 write access to
64 write access to
64 - Implemented permissions for writing to repo
65 - Implemented permissions for writing to repo
65 groups. Now only write access to group allows to create a repostiory
66 groups. Now only write access to group allows to create a repostiory
66 within that group
67 within that group
67 - #565 Add support for {netloc} and {scheme} to alternative_gravatar_url
68 - #565 Add support for {netloc} and {scheme} to alternative_gravatar_url
68 - updated translation for zh_CN
69 - updated translation for zh_CN
69
70
70 fixes
71 fixes
71 +++++
72 +++++
72
73
73 - fixed visual permissions check on repos groups inside groups
74 - fixed visual permissions check on repos groups inside groups
74 - fixed issues with non-ascii search terms in search, and indexers
75 - fixed issues with non-ascii search terms in search, and indexers
75 - fixed parsing of page number in GET parameters
76 - fixed parsing of page number in GET parameters
76 - fixed issues with generating pull-request overview for repos with
77 - fixed issues with generating pull-request overview for repos with
77 bookmarks and tags, also preview doesn't loose chosen revision from
78 bookmarks and tags, also preview doesn't loose chosen revision from
78 select dropdown
79 select dropdown
79
80
80 1.4.1 (**2012-09-07**)
81 1.4.1 (**2012-09-07**)
81 ----------------------
82 ----------------------
82
83
83 news
84 news
84 ++++
85 ++++
85
86
86 - always put a comment about code-review status change even if user send
87 - always put a comment about code-review status change even if user send
87 empty data
88 empty data
88 - modified_on column saves repository update and it's going to be used
89 - modified_on column saves repository update and it's going to be used
89 later for light version of main page ref #500
90 later for light version of main page ref #500
90 - pull request notifications send much nicer emails with details about pull
91 - pull request notifications send much nicer emails with details about pull
91 request
92 request
92 - #551 show breadcrumbs in summary view for repositories inside a group
93 - #551 show breadcrumbs in summary view for repositories inside a group
93
94
94 fixes
95 fixes
95 +++++
96 +++++
96
97
97 - fixed migrations of permissions that can lead to inconsistency.
98 - fixed migrations of permissions that can lead to inconsistency.
98 Some users sent feedback that after upgrading from older versions issues
99 Some users sent feedback that after upgrading from older versions issues
99 with updating default permissions occurred. RhodeCode detects that now and
100 with updating default permissions occurred. RhodeCode detects that now and
100 resets default user permission to initial state if there is a need for that.
101 resets default user permission to initial state if there is a need for that.
101 Also forces users to set the default value for new forking permission.
102 Also forces users to set the default value for new forking permission.
102 - #535 improved apache wsgi example configuration in docs
103 - #535 improved apache wsgi example configuration in docs
103 - fixes #550 mercurial repositories comparision failed when origin repo had
104 - fixes #550 mercurial repositories comparision failed when origin repo had
104 additional not-common changesets
105 additional not-common changesets
105 - fixed status of code-review in preview windows of pull request
106 - fixed status of code-review in preview windows of pull request
106 - git forks were not initialized at bare repos
107 - git forks were not initialized at bare repos
107 - fixes #555 fixes issues with comparing non-related repositories
108 - fixes #555 fixes issues with comparing non-related repositories
108 - fixes #557 follower counter always counts up
109 - fixes #557 follower counter always counts up
109 - fixed issue #560 require push ssl checkbox wasn't shown when option was
110 - fixed issue #560 require push ssl checkbox wasn't shown when option was
110 enabled
111 enabled
111 - fixed #559
112 - fixed #559
112 - fixed issue #559 fixed bug in routing that mapped repo names with <name>_<num> in name as
113 - fixed issue #559 fixed bug in routing that mapped repo names with <name>_<num> in name as
113 if it was a request to url by repository ID
114 if it was a request to url by repository ID
114
115
115 1.4.0 (**2012-09-03**)
116 1.4.0 (**2012-09-03**)
116 ----------------------
117 ----------------------
117
118
118 news
119 news
119 ++++
120 ++++
120
121
121 - new codereview system
122 - new codereview system
122 - email map, allowing users to have multiple email addresses mapped into
123 - email map, allowing users to have multiple email addresses mapped into
123 their accounts
124 their accounts
124 - improved git-hook system. Now all actions for git are logged into journal
125 - improved git-hook system. Now all actions for git are logged into journal
125 including pushed revisions, user and IP address
126 including pushed revisions, user and IP address
126 - changed setup-app into setup-rhodecode and added default options to it.
127 - changed setup-app into setup-rhodecode and added default options to it.
127 - new git repos are created as bare now by default
128 - new git repos are created as bare now by default
128 - #464 added links to groups in permission box
129 - #464 added links to groups in permission box
129 - #465 mentions autocomplete inside comments boxes
130 - #465 mentions autocomplete inside comments boxes
130 - #469 added --update-only option to whoosh to re-index only given list
131 - #469 added --update-only option to whoosh to re-index only given list
131 of repos in index
132 of repos in index
132 - rhodecode-api CLI client
133 - rhodecode-api CLI client
133 - new git http protocol replaced buggy dulwich implementation.
134 - new git http protocol replaced buggy dulwich implementation.
134 Now based on pygrack & gitweb
135 Now based on pygrack & gitweb
135 - Improved RSS/ATOM feeds. Discoverable by browsers using proper headers, and
136 - Improved RSS/ATOM feeds. Discoverable by browsers using proper headers, and
136 reformated based on user suggestions. Additional rss/atom feeds for user
137 reformated based on user suggestions. Additional rss/atom feeds for user
137 journal
138 journal
138 - various i18n improvements
139 - various i18n improvements
139 - #478 permissions overview for admin in user edit view
140 - #478 permissions overview for admin in user edit view
140 - File view now displays small gravatars off all authors of given file
141 - File view now displays small gravatars off all authors of given file
141 - Implemented landing revisions. Each repository will get landing_rev attribute
142 - Implemented landing revisions. Each repository will get landing_rev attribute
142 that defines 'default' revision/branch for generating readme files
143 that defines 'default' revision/branch for generating readme files
143 - Implemented #509, RhodeCode enforces SSL for push/pulling if requested at
144 - Implemented #509, RhodeCode enforces SSL for push/pulling if requested at
144 earliest possible call.
145 earliest possible call.
145 - Import remote svn repositories to mercurial using hgsubversion.
146 - Import remote svn repositories to mercurial using hgsubversion.
146 - Fixed #508 RhodeCode now has a option to explicitly set forking permissions
147 - Fixed #508 RhodeCode now has a option to explicitly set forking permissions
147 - RhodeCode can use alternative server for generating avatar icons
148 - RhodeCode can use alternative server for generating avatar icons
148 - implemented repositories locking. Pull locks, push unlocks. Also can be done
149 - implemented repositories locking. Pull locks, push unlocks. Also can be done
149 via API calls
150 via API calls
150 - #538 form for permissions can handle multiple users at once
151 - #538 form for permissions can handle multiple users at once
151
152
152 fixes
153 fixes
153 +++++
154 +++++
154
155
155 - improved translations
156 - improved translations
156 - fixes issue #455 Creating an archive generates an exception on Windows
157 - fixes issue #455 Creating an archive generates an exception on Windows
157 - fixes #448 Download ZIP archive keeps file in /tmp open and results
158 - fixes #448 Download ZIP archive keeps file in /tmp open and results
158 in out of disk space
159 in out of disk space
159 - fixes issue #454 Search results under Windows include proceeding
160 - fixes issue #454 Search results under Windows include proceeding
160 backslash
161 backslash
161 - fixed issue #450. Rhodecode no longer will crash when bad revision is
162 - fixed issue #450. Rhodecode no longer will crash when bad revision is
162 present in journal data.
163 present in journal data.
163 - fix for issue #417, git execution was broken on windows for certain
164 - fix for issue #417, git execution was broken on windows for certain
164 commands.
165 commands.
165 - fixed #413. Don't disable .git directory for bare repos on deleting
166 - fixed #413. Don't disable .git directory for bare repos on deleting
166 - fixed issue #459. Changed the way of obtaining logger in reindex task.
167 - fixed issue #459. Changed the way of obtaining logger in reindex task.
167 - fixed #453 added ID field in whoosh SCHEMA that solves the issue of
168 - fixed #453 added ID field in whoosh SCHEMA that solves the issue of
168 reindexing modified files
169 reindexing modified files
169 - fixed #481 rhodecode emails are sent without Date header
170 - fixed #481 rhodecode emails are sent without Date header
170 - fixed #458 wrong count when no repos are present
171 - fixed #458 wrong count when no repos are present
171 - fixed issue #492 missing `\ No newline at end of file` test at the end of
172 - fixed issue #492 missing `\ No newline at end of file` test at the end of
172 new chunk in html diff
173 new chunk in html diff
173 - full text search now works also for commit messages
174 - full text search now works also for commit messages
174
175
175 1.3.6 (**2012-05-17**)
176 1.3.6 (**2012-05-17**)
176 ----------------------
177 ----------------------
177
178
178 news
179 news
179 ++++
180 ++++
180
181
181 - chinese traditional translation
182 - chinese traditional translation
182 - changed setup-app into setup-rhodecode and added arguments for auto-setup
183 - changed setup-app into setup-rhodecode and added arguments for auto-setup
183 mode that doesn't need user interaction
184 mode that doesn't need user interaction
184
185
185 fixes
186 fixes
186 +++++
187 +++++
187
188
188 - fixed no scm found warning
189 - fixed no scm found warning
189 - fixed __future__ import error on rcextensions
190 - fixed __future__ import error on rcextensions
190 - made simplejson required lib for speedup on JSON encoding
191 - made simplejson required lib for speedup on JSON encoding
191 - fixes #449 bad regex could get more than revisions from parsing history
192 - fixes #449 bad regex could get more than revisions from parsing history
192 - don't clear DB session when CELERY_EAGER is turned ON
193 - don't clear DB session when CELERY_EAGER is turned ON
193
194
194 1.3.5 (**2012-05-10**)
195 1.3.5 (**2012-05-10**)
195 ----------------------
196 ----------------------
196
197
197 news
198 news
198 ++++
199 ++++
199
200
200 - use ext_json for json module
201 - use ext_json for json module
201 - unified annotation view with file source view
202 - unified annotation view with file source view
202 - notification improvements, better inbox + css
203 - notification improvements, better inbox + css
203 - #419 don't strip passwords for login forms, make rhodecode
204 - #419 don't strip passwords for login forms, make rhodecode
204 more compatible with LDAP servers
205 more compatible with LDAP servers
205 - Added HTTP_X_FORWARDED_FOR as another method of extracting
206 - Added HTTP_X_FORWARDED_FOR as another method of extracting
206 IP for pull/push logs. - moved all to base controller
207 IP for pull/push logs. - moved all to base controller
207 - #415: Adding comment to changeset causes reload.
208 - #415: Adding comment to changeset causes reload.
208 Comments are now added via ajax and doesn't reload the page
209 Comments are now added via ajax and doesn't reload the page
209 - #374 LDAP config is discarded when LDAP can't be activated
210 - #374 LDAP config is discarded when LDAP can't be activated
210 - limited push/pull operations are now logged for git in the journal
211 - limited push/pull operations are now logged for git in the journal
211 - bumped mercurial to 2.2.X series
212 - bumped mercurial to 2.2.X series
212 - added support for displaying submodules in file-browser
213 - added support for displaying submodules in file-browser
213 - #421 added bookmarks in changelog view
214 - #421 added bookmarks in changelog view
214
215
215 fixes
216 fixes
216 +++++
217 +++++
217
218
218 - fixed dev-version marker for stable when served from source codes
219 - fixed dev-version marker for stable when served from source codes
219 - fixed missing permission checks on show forks page
220 - fixed missing permission checks on show forks page
220 - #418 cast to unicode fixes in notification objects
221 - #418 cast to unicode fixes in notification objects
221 - #426 fixed mention extracting regex
222 - #426 fixed mention extracting regex
222 - fixed remote-pulling for git remotes remopositories
223 - fixed remote-pulling for git remotes remopositories
223 - fixed #434: Error when accessing files or changesets of a git repository
224 - fixed #434: Error when accessing files or changesets of a git repository
224 with submodules
225 with submodules
225 - fixed issue with empty APIKEYS for users after registration ref. #438
226 - fixed issue with empty APIKEYS for users after registration ref. #438
226 - fixed issue with getting README files from git repositories
227 - fixed issue with getting README files from git repositories
227
228
228 1.3.4 (**2012-03-28**)
229 1.3.4 (**2012-03-28**)
229 ----------------------
230 ----------------------
230
231
231 news
232 news
232 ++++
233 ++++
233
234
234 - Whoosh logging is now controlled by the .ini files logging setup
235 - Whoosh logging is now controlled by the .ini files logging setup
235 - added clone-url into edit form on /settings page
236 - added clone-url into edit form on /settings page
236 - added help text into repo add/edit forms
237 - added help text into repo add/edit forms
237 - created rcextensions module with additional mappings (ref #322) and
238 - created rcextensions module with additional mappings (ref #322) and
238 post push/pull/create repo hooks callbacks
239 post push/pull/create repo hooks callbacks
239 - implemented #377 Users view for his own permissions on account page
240 - implemented #377 Users view for his own permissions on account page
240 - #399 added inheritance of permissions for users group on repos groups
241 - #399 added inheritance of permissions for users group on repos groups
241 - #401 repository group is automatically pre-selected when adding repos
242 - #401 repository group is automatically pre-selected when adding repos
242 inside a repository group
243 inside a repository group
243 - added alternative HTTP 403 response when client failed to authenticate. Helps
244 - added alternative HTTP 403 response when client failed to authenticate. Helps
244 solving issues with Mercurial and LDAP
245 solving issues with Mercurial and LDAP
245 - #402 removed group prefix from repository name when listing repositories
246 - #402 removed group prefix from repository name when listing repositories
246 inside a group
247 inside a group
247 - added gravatars into permission view and permissions autocomplete
248 - added gravatars into permission view and permissions autocomplete
248 - #347 when running multiple RhodeCode instances, properly invalidates cache
249 - #347 when running multiple RhodeCode instances, properly invalidates cache
249 for all registered servers
250 for all registered servers
250
251
251 fixes
252 fixes
252 +++++
253 +++++
253
254
254 - fixed #390 cache invalidation problems on repos inside group
255 - fixed #390 cache invalidation problems on repos inside group
255 - fixed #385 clone by ID url was loosing proxy prefix in URL
256 - fixed #385 clone by ID url was loosing proxy prefix in URL
256 - fixed some unicode problems with waitress
257 - fixed some unicode problems with waitress
257 - fixed issue with escaping < and > in changeset commits
258 - fixed issue with escaping < and > in changeset commits
258 - fixed error occurring during recursive group creation in API
259 - fixed error occurring during recursive group creation in API
259 create_repo function
260 create_repo function
260 - fixed #393 py2.5 fixes for routes url generator
261 - fixed #393 py2.5 fixes for routes url generator
261 - fixed #397 Private repository groups shows up before login
262 - fixed #397 Private repository groups shows up before login
262 - fixed #396 fixed problems with revoking users in nested groups
263 - fixed #396 fixed problems with revoking users in nested groups
263 - fixed mysql unicode issues + specified InnoDB as default engine with
264 - fixed mysql unicode issues + specified InnoDB as default engine with
264 utf8 charset
265 utf8 charset
265 - #406 trim long branch/tag names in changelog to not break UI
266 - #406 trim long branch/tag names in changelog to not break UI
266
267
267 1.3.3 (**2012-03-02**)
268 1.3.3 (**2012-03-02**)
268 ----------------------
269 ----------------------
269
270
270 news
271 news
271 ++++
272 ++++
272
273
273
274
274 fixes
275 fixes
275 +++++
276 +++++
276
277
277 - fixed some python2.5 compatibility issues
278 - fixed some python2.5 compatibility issues
278 - fixed issues with removed repos was accidentally added as groups, after
279 - fixed issues with removed repos was accidentally added as groups, after
279 full rescan of paths
280 full rescan of paths
280 - fixes #376 Cannot edit user (using container auth)
281 - fixes #376 Cannot edit user (using container auth)
281 - fixes #378 Invalid image urls on changeset screen with proxy-prefix
282 - fixes #378 Invalid image urls on changeset screen with proxy-prefix
282 configuration
283 configuration
283 - fixed initial sorting of repos inside repo group
284 - fixed initial sorting of repos inside repo group
284 - fixes issue when user tried to resubmit same permission into user/user_groups
285 - fixes issue when user tried to resubmit same permission into user/user_groups
285 - bumped beaker version that fixes #375 leap error bug
286 - bumped beaker version that fixes #375 leap error bug
286 - fixed raw_changeset for git. It was generated with hg patch headers
287 - fixed raw_changeset for git. It was generated with hg patch headers
287 - fixed vcs issue with last_changeset for filenodes
288 - fixed vcs issue with last_changeset for filenodes
288 - fixed missing commit after hook delete
289 - fixed missing commit after hook delete
289 - fixed #372 issues with git operation detection that caused a security issue
290 - fixed #372 issues with git operation detection that caused a security issue
290 for git repos
291 for git repos
291
292
292 1.3.2 (**2012-02-28**)
293 1.3.2 (**2012-02-28**)
293 ----------------------
294 ----------------------
294
295
295 news
296 news
296 ++++
297 ++++
297
298
298
299
299 fixes
300 fixes
300 +++++
301 +++++
301
302
302 - fixed git protocol issues with repos-groups
303 - fixed git protocol issues with repos-groups
303 - fixed git remote repos validator that prevented from cloning remote git repos
304 - fixed git remote repos validator that prevented from cloning remote git repos
304 - fixes #370 ending slashes fixes for repo and groups
305 - fixes #370 ending slashes fixes for repo and groups
305 - fixes #368 improved git-protocol detection to handle other clients
306 - fixes #368 improved git-protocol detection to handle other clients
306 - fixes #366 When Setting Repository Group To Blank Repo Group Wont Be
307 - fixes #366 When Setting Repository Group To Blank Repo Group Wont Be
307 Moved To Root
308 Moved To Root
308 - fixes #371 fixed issues with beaker/sqlalchemy and non-ascii cache keys
309 - fixes #371 fixed issues with beaker/sqlalchemy and non-ascii cache keys
309 - fixed #373 missing cascade drop on user_group_to_perm table
310 - fixed #373 missing cascade drop on user_group_to_perm table
310
311
311 1.3.1 (**2012-02-27**)
312 1.3.1 (**2012-02-27**)
312 ----------------------
313 ----------------------
313
314
314 news
315 news
315 ++++
316 ++++
316
317
317
318
318 fixes
319 fixes
319 +++++
320 +++++
320
321
321 - redirection loop occurs when remember-me wasn't checked during login
322 - redirection loop occurs when remember-me wasn't checked during login
322 - fixes issues with git blob history generation
323 - fixes issues with git blob history generation
323 - don't fetch branch for git in file history dropdown. Causes unneeded slowness
324 - don't fetch branch for git in file history dropdown. Causes unneeded slowness
324
325
325 1.3.0 (**2012-02-26**)
326 1.3.0 (**2012-02-26**)
326 ----------------------
327 ----------------------
327
328
328 news
329 news
329 ++++
330 ++++
330
331
331 - code review, inspired by github code-comments
332 - code review, inspired by github code-comments
332 - #215 rst and markdown README files support
333 - #215 rst and markdown README files support
333 - #252 Container-based and proxy pass-through authentication support
334 - #252 Container-based and proxy pass-through authentication support
334 - #44 branch browser. Filtering of changelog by branches
335 - #44 branch browser. Filtering of changelog by branches
335 - mercurial bookmarks support
336 - mercurial bookmarks support
336 - new hover top menu, optimized to add maximum size for important views
337 - new hover top menu, optimized to add maximum size for important views
337 - configurable clone url template with possibility to specify protocol like
338 - configurable clone url template with possibility to specify protocol like
338 ssh:// or http:// and also manually alter other parts of clone_url.
339 ssh:// or http:// and also manually alter other parts of clone_url.
339 - enabled largefiles extension by default
340 - enabled largefiles extension by default
340 - optimized summary file pages and saved a lot of unused space in them
341 - optimized summary file pages and saved a lot of unused space in them
341 - #239 option to manually mark repository as fork
342 - #239 option to manually mark repository as fork
342 - #320 mapping of commit authors to RhodeCode users
343 - #320 mapping of commit authors to RhodeCode users
343 - #304 hashes are displayed using monospace font
344 - #304 hashes are displayed using monospace font
344 - diff configuration, toggle white lines and context lines
345 - diff configuration, toggle white lines and context lines
345 - #307 configurable diffs, whitespace toggle, increasing context lines
346 - #307 configurable diffs, whitespace toggle, increasing context lines
346 - sorting on branches, tags and bookmarks using YUI datatable
347 - sorting on branches, tags and bookmarks using YUI datatable
347 - improved file filter on files page
348 - improved file filter on files page
348 - implements #330 api method for listing nodes ar particular revision
349 - implements #330 api method for listing nodes ar particular revision
349 - #73 added linking issues in commit messages to chosen issue tracker url
350 - #73 added linking issues in commit messages to chosen issue tracker url
350 based on user defined regular expression
351 based on user defined regular expression
351 - added linking of changesets in commit messages
352 - added linking of changesets in commit messages
352 - new compact changelog with expandable commit messages
353 - new compact changelog with expandable commit messages
353 - firstname and lastname are optional in user creation
354 - firstname and lastname are optional in user creation
354 - #348 added post-create repository hook
355 - #348 added post-create repository hook
355 - #212 global encoding settings is now configurable from .ini files
356 - #212 global encoding settings is now configurable from .ini files
356 - #227 added repository groups permissions
357 - #227 added repository groups permissions
357 - markdown gets codehilite extensions
358 - markdown gets codehilite extensions
358 - new API methods, delete_repositories, grante/revoke permissions for groups
359 - new API methods, delete_repositories, grante/revoke permissions for groups
359 and repos
360 and repos
360
361
361
362
362 fixes
363 fixes
363 +++++
364 +++++
364
365
365 - rewrote dbsession management for atomic operations, and better error handling
366 - rewrote dbsession management for atomic operations, and better error handling
366 - fixed sorting of repo tables
367 - fixed sorting of repo tables
367 - #326 escape of special html entities in diffs
368 - #326 escape of special html entities in diffs
368 - normalized user_name => username in api attributes
369 - normalized user_name => username in api attributes
369 - fixes #298 ldap created users with mixed case emails created conflicts
370 - fixes #298 ldap created users with mixed case emails created conflicts
370 on saving a form
371 on saving a form
371 - fixes issue when owner of a repo couldn't revoke permissions for users
372 - fixes issue when owner of a repo couldn't revoke permissions for users
372 and groups
373 and groups
373 - fixes #271 rare JSON serialization problem with statistics
374 - fixes #271 rare JSON serialization problem with statistics
374 - fixes #337 missing validation check for conflicting names of a group with a
375 - fixes #337 missing validation check for conflicting names of a group with a
375 repositories group
376 repositories group
376 - #340 fixed session problem for mysql and celery tasks
377 - #340 fixed session problem for mysql and celery tasks
377 - fixed #331 RhodeCode mangles repository names if the a repository group
378 - fixed #331 RhodeCode mangles repository names if the a repository group
378 contains the "full path" to the repositories
379 contains the "full path" to the repositories
379 - #355 RhodeCode doesn't store encrypted LDAP passwords
380 - #355 RhodeCode doesn't store encrypted LDAP passwords
380
381
381 1.2.5 (**2012-01-28**)
382 1.2.5 (**2012-01-28**)
382 ----------------------
383 ----------------------
383
384
384 news
385 news
385 ++++
386 ++++
386
387
387 fixes
388 fixes
388 +++++
389 +++++
389
390
390 - #340 Celery complains about MySQL server gone away, added session cleanup
391 - #340 Celery complains about MySQL server gone away, added session cleanup
391 for celery tasks
392 for celery tasks
392 - #341 "scanning for repositories in None" log message during Rescan was missing
393 - #341 "scanning for repositories in None" log message during Rescan was missing
393 a parameter
394 a parameter
394 - fixed creating archives with subrepos. Some hooks were triggered during that
395 - fixed creating archives with subrepos. Some hooks were triggered during that
395 operation leading to crash.
396 operation leading to crash.
396 - fixed missing email in account page.
397 - fixed missing email in account page.
397 - Reverted Mercurial to 2.0.1 for windows due to bug in Mercurial that makes
398 - Reverted Mercurial to 2.0.1 for windows due to bug in Mercurial that makes
398 forking on windows impossible
399 forking on windows impossible
399
400
400 1.2.4 (**2012-01-19**)
401 1.2.4 (**2012-01-19**)
401 ----------------------
402 ----------------------
402
403
403 news
404 news
404 ++++
405 ++++
405
406
406 - RhodeCode is bundled with mercurial series 2.0.X by default, with
407 - RhodeCode is bundled with mercurial series 2.0.X by default, with
407 full support to largefiles extension. Enabled by default in new installations
408 full support to largefiles extension. Enabled by default in new installations
408 - #329 Ability to Add/Remove Groups to/from a Repository via AP
409 - #329 Ability to Add/Remove Groups to/from a Repository via AP
409 - added requires.txt file with requirements
410 - added requires.txt file with requirements
410
411
411 fixes
412 fixes
412 +++++
413 +++++
413
414
414 - fixes db session issues with celery when emailing admins
415 - fixes db session issues with celery when emailing admins
415 - #331 RhodeCode mangles repository names if the a repository group
416 - #331 RhodeCode mangles repository names if the a repository group
416 contains the "full path" to the repositories
417 contains the "full path" to the repositories
417 - #298 Conflicting e-mail addresses for LDAP and RhodeCode users
418 - #298 Conflicting e-mail addresses for LDAP and RhodeCode users
418 - DB session cleanup after hg protocol operations, fixes issues with
419 - DB session cleanup after hg protocol operations, fixes issues with
419 `mysql has gone away` errors
420 `mysql has gone away` errors
420 - #333 doc fixes for get_repo api function
421 - #333 doc fixes for get_repo api function
421 - #271 rare JSON serialization problem with statistics enabled
422 - #271 rare JSON serialization problem with statistics enabled
422 - #337 Fixes issues with validation of repository name conflicting with
423 - #337 Fixes issues with validation of repository name conflicting with
423 a group name. A proper message is now displayed.
424 a group name. A proper message is now displayed.
424 - #292 made ldap_dn in user edit readonly, to get rid of confusion that field
425 - #292 made ldap_dn in user edit readonly, to get rid of confusion that field
425 doesn't work
426 doesn't work
426 - #316 fixes issues with web description in hgrc files
427 - #316 fixes issues with web description in hgrc files
427
428
428 1.2.3 (**2011-11-02**)
429 1.2.3 (**2011-11-02**)
429 ----------------------
430 ----------------------
430
431
431 news
432 news
432 ++++
433 ++++
433
434
434 - added option to manage repos group for non admin users
435 - added option to manage repos group for non admin users
435 - added following API methods for get_users, create_user, get_users_groups,
436 - added following API methods for get_users, create_user, get_users_groups,
436 get_users_group, create_users_group, add_user_to_users_groups, get_repos,
437 get_users_group, create_users_group, add_user_to_users_groups, get_repos,
437 get_repo, create_repo, add_user_to_repo
438 get_repo, create_repo, add_user_to_repo
438 - implements #237 added password confirmation for my account
439 - implements #237 added password confirmation for my account
439 and admin edit user.
440 and admin edit user.
440 - implements #291 email notification for global events are now sent to all
441 - implements #291 email notification for global events are now sent to all
441 administrator users, and global config email.
442 administrator users, and global config email.
442
443
443 fixes
444 fixes
444 +++++
445 +++++
445
446
446 - added option for passing auth method for smtp mailer
447 - added option for passing auth method for smtp mailer
447 - #276 issue with adding a single user with id>10 to usergroups
448 - #276 issue with adding a single user with id>10 to usergroups
448 - #277 fixes windows LDAP settings in which missing values breaks the ldap auth
449 - #277 fixes windows LDAP settings in which missing values breaks the ldap auth
449 - #288 fixes managing of repos in a group for non admin user
450 - #288 fixes managing of repos in a group for non admin user
450
451
451 1.2.2 (**2011-10-17**)
452 1.2.2 (**2011-10-17**)
452 ----------------------
453 ----------------------
453
454
454 news
455 news
455 ++++
456 ++++
456
457
457 - #226 repo groups are available by path instead of numerical id
458 - #226 repo groups are available by path instead of numerical id
458
459
459 fixes
460 fixes
460 +++++
461 +++++
461
462
462 - #259 Groups with the same name but with different parent group
463 - #259 Groups with the same name but with different parent group
463 - #260 Put repo in group, then move group to another group -> repo becomes unavailable
464 - #260 Put repo in group, then move group to another group -> repo becomes unavailable
464 - #258 RhodeCode 1.2 assumes egg folder is writable (lockfiles problems)
465 - #258 RhodeCode 1.2 assumes egg folder is writable (lockfiles problems)
465 - #265 ldap save fails sometimes on converting attributes to booleans,
466 - #265 ldap save fails sometimes on converting attributes to booleans,
466 added getter and setter into model that will prevent from this on db model level
467 added getter and setter into model that will prevent from this on db model level
467 - fixed problems with timestamps issues #251 and #213
468 - fixed problems with timestamps issues #251 and #213
468 - fixes #266 RhodeCode allows to create repo with the same name and in
469 - fixes #266 RhodeCode allows to create repo with the same name and in
469 the same parent as group
470 the same parent as group
470 - fixes #245 Rescan of the repositories on Windows
471 - fixes #245 Rescan of the repositories on Windows
471 - fixes #248 cannot edit repos inside a group on windows
472 - fixes #248 cannot edit repos inside a group on windows
472 - fixes #219 forking problems on windows
473 - fixes #219 forking problems on windows
473
474
474 1.2.1 (**2011-10-08**)
475 1.2.1 (**2011-10-08**)
475 ----------------------
476 ----------------------
476
477
477 news
478 news
478 ++++
479 ++++
479
480
480
481
481 fixes
482 fixes
482 +++++
483 +++++
483
484
484 - fixed problems with basic auth and push problems
485 - fixed problems with basic auth and push problems
485 - gui fixes
486 - gui fixes
486 - fixed logger
487 - fixed logger
487
488
488 1.2.0 (**2011-10-07**)
489 1.2.0 (**2011-10-07**)
489 ----------------------
490 ----------------------
490
491
491 news
492 news
492 ++++
493 ++++
493
494
494 - implemented #47 repository groups
495 - implemented #47 repository groups
495 - implemented #89 Can setup google analytics code from settings menu
496 - implemented #89 Can setup google analytics code from settings menu
496 - implemented #91 added nicer looking archive urls with more download options
497 - implemented #91 added nicer looking archive urls with more download options
497 like tags, branches
498 like tags, branches
498 - implemented #44 into file browsing, and added follow branch option
499 - implemented #44 into file browsing, and added follow branch option
499 - implemented #84 downloads can be enabled/disabled for each repository
500 - implemented #84 downloads can be enabled/disabled for each repository
500 - anonymous repository can be cloned without having to pass default:default
501 - anonymous repository can be cloned without having to pass default:default
501 into clone url
502 into clone url
502 - fixed #90 whoosh indexer can index chooses repositories passed in command
503 - fixed #90 whoosh indexer can index chooses repositories passed in command
503 line
504 line
504 - extended journal with day aggregates and paging
505 - extended journal with day aggregates and paging
505 - implemented #107 source code lines highlight ranges
506 - implemented #107 source code lines highlight ranges
506 - implemented #93 customizable changelog on combined revision ranges -
507 - implemented #93 customizable changelog on combined revision ranges -
507 equivalent of githubs compare view
508 equivalent of githubs compare view
508 - implemented #108 extended and more powerful LDAP configuration
509 - implemented #108 extended and more powerful LDAP configuration
509 - implemented #56 users groups
510 - implemented #56 users groups
510 - major code rewrites optimized codes for speed and memory usage
511 - major code rewrites optimized codes for speed and memory usage
511 - raw and diff downloads are now in git format
512 - raw and diff downloads are now in git format
512 - setup command checks for write access to given path
513 - setup command checks for write access to given path
513 - fixed many issues with international characters and unicode. It uses utf8
514 - fixed many issues with international characters and unicode. It uses utf8
514 decode with replace to provide less errors even with non utf8 encoded strings
515 decode with replace to provide less errors even with non utf8 encoded strings
515 - #125 added API KEY access to feeds
516 - #125 added API KEY access to feeds
516 - #109 Repository can be created from external Mercurial link (aka. remote
517 - #109 Repository can be created from external Mercurial link (aka. remote
517 repository, and manually updated (via pull) from admin panel
518 repository, and manually updated (via pull) from admin panel
518 - beta git support - push/pull server + basic view for git repos
519 - beta git support - push/pull server + basic view for git repos
519 - added followers page and forks page
520 - added followers page and forks page
520 - server side file creation (with binary file upload interface)
521 - server side file creation (with binary file upload interface)
521 and edition with commits powered by codemirror
522 and edition with commits powered by codemirror
522 - #111 file browser file finder, quick lookup files on whole file tree
523 - #111 file browser file finder, quick lookup files on whole file tree
523 - added quick login sliding menu into main page
524 - added quick login sliding menu into main page
524 - changelog uses lazy loading of affected files details, in some scenarios
525 - changelog uses lazy loading of affected files details, in some scenarios
525 this can improve speed of changelog page dramatically especially for
526 this can improve speed of changelog page dramatically especially for
526 larger repositories.
527 larger repositories.
527 - implements #214 added support for downloading subrepos in download menu.
528 - implements #214 added support for downloading subrepos in download menu.
528 - Added basic API for direct operations on rhodecode via JSON
529 - Added basic API for direct operations on rhodecode via JSON
529 - Implemented advanced hook management
530 - Implemented advanced hook management
530
531
531 fixes
532 fixes
532 +++++
533 +++++
533
534
534 - fixed file browser bug, when switching into given form revision the url was
535 - fixed file browser bug, when switching into given form revision the url was
535 not changing
536 not changing
536 - fixed propagation to error controller on simplehg and simplegit middlewares
537 - fixed propagation to error controller on simplehg and simplegit middlewares
537 - fixed error when trying to make a download on empty repository
538 - fixed error when trying to make a download on empty repository
538 - fixed problem with '[' chars in commit messages in journal
539 - fixed problem with '[' chars in commit messages in journal
539 - fixed #99 Unicode errors, on file node paths with non utf-8 characters
540 - fixed #99 Unicode errors, on file node paths with non utf-8 characters
540 - journal fork fixes
541 - journal fork fixes
541 - removed issue with space inside renamed repository after deletion
542 - removed issue with space inside renamed repository after deletion
542 - fixed strange issue on formencode imports
543 - fixed strange issue on formencode imports
543 - fixed #126 Deleting repository on Windows, rename used incompatible chars.
544 - fixed #126 Deleting repository on Windows, rename used incompatible chars.
544 - #150 fixes for errors on repositories mapped in db but corrupted in
545 - #150 fixes for errors on repositories mapped in db but corrupted in
545 filesystem
546 filesystem
546 - fixed problem with ascendant characters in realm #181
547 - fixed problem with ascendant characters in realm #181
547 - fixed problem with sqlite file based database connection pool
548 - fixed problem with sqlite file based database connection pool
548 - whoosh indexer and code stats share the same dynamic extensions map
549 - whoosh indexer and code stats share the same dynamic extensions map
549 - fixes #188 - relationship delete of repo_to_perm entry on user removal
550 - fixes #188 - relationship delete of repo_to_perm entry on user removal
550 - fixes issue #189 Trending source files shows "show more" when no more exist
551 - fixes issue #189 Trending source files shows "show more" when no more exist
551 - fixes issue #197 Relative paths for pidlocks
552 - fixes issue #197 Relative paths for pidlocks
552 - fixes issue #198 password will require only 3 chars now for login form
553 - fixes issue #198 password will require only 3 chars now for login form
553 - fixes issue #199 wrong redirection for non admin users after creating a repository
554 - fixes issue #199 wrong redirection for non admin users after creating a repository
554 - fixes issues #202, bad db constraint made impossible to attach same group
555 - fixes issues #202, bad db constraint made impossible to attach same group
555 more than one time. Affects only mysql/postgres
556 more than one time. Affects only mysql/postgres
556 - fixes #218 os.kill patch for windows was missing sig param
557 - fixes #218 os.kill patch for windows was missing sig param
557 - improved rendering of dag (they are not trimmed anymore when number of
558 - improved rendering of dag (they are not trimmed anymore when number of
558 heads exceeds 5)
559 heads exceeds 5)
559
560
560 1.1.8 (**2011-04-12**)
561 1.1.8 (**2011-04-12**)
561 ----------------------
562 ----------------------
562
563
563 news
564 news
564 ++++
565 ++++
565
566
566 - improved windows support
567 - improved windows support
567
568
568 fixes
569 fixes
569 +++++
570 +++++
570
571
571 - fixed #140 freeze of python dateutil library, since new version is python2.x
572 - fixed #140 freeze of python dateutil library, since new version is python2.x
572 incompatible
573 incompatible
573 - setup-app will check for write permission in given path
574 - setup-app will check for write permission in given path
574 - cleaned up license info issue #149
575 - cleaned up license info issue #149
575 - fixes for issues #137,#116 and problems with unicode and accented characters.
576 - fixes for issues #137,#116 and problems with unicode and accented characters.
576 - fixes crashes on gravatar, when passed in email as unicode
577 - fixes crashes on gravatar, when passed in email as unicode
577 - fixed tooltip flickering problems
578 - fixed tooltip flickering problems
578 - fixed came_from redirection on windows
579 - fixed came_from redirection on windows
579 - fixed logging modules, and sql formatters
580 - fixed logging modules, and sql formatters
580 - windows fixes for os.kill issue #133
581 - windows fixes for os.kill issue #133
581 - fixes path splitting for windows issues #148
582 - fixes path splitting for windows issues #148
582 - fixed issue #143 wrong import on migration to 1.1.X
583 - fixed issue #143 wrong import on migration to 1.1.X
583 - fixed problems with displaying binary files, thanks to Thomas Waldmann
584 - fixed problems with displaying binary files, thanks to Thomas Waldmann
584 - removed name from archive files since it's breaking ui for long repo names
585 - removed name from archive files since it's breaking ui for long repo names
585 - fixed issue with archive headers sent to browser, thanks to Thomas Waldmann
586 - fixed issue with archive headers sent to browser, thanks to Thomas Waldmann
586 - fixed compatibility for 1024px displays, and larger dpi settings, thanks to
587 - fixed compatibility for 1024px displays, and larger dpi settings, thanks to
587 Thomas Waldmann
588 Thomas Waldmann
588 - fixed issue #166 summary pager was skipping 10 revisions on second page
589 - fixed issue #166 summary pager was skipping 10 revisions on second page
589
590
590
591
591 1.1.7 (**2011-03-23**)
592 1.1.7 (**2011-03-23**)
592 ----------------------
593 ----------------------
593
594
594 news
595 news
595 ++++
596 ++++
596
597
597 fixes
598 fixes
598 +++++
599 +++++
599
600
600 - fixed (again) #136 installation support for FreeBSD
601 - fixed (again) #136 installation support for FreeBSD
601
602
602
603
603 1.1.6 (**2011-03-21**)
604 1.1.6 (**2011-03-21**)
604 ----------------------
605 ----------------------
605
606
606 news
607 news
607 ++++
608 ++++
608
609
609 fixes
610 fixes
610 +++++
611 +++++
611
612
612 - fixed #136 installation support for FreeBSD
613 - fixed #136 installation support for FreeBSD
613 - RhodeCode will check for python version during installation
614 - RhodeCode will check for python version during installation
614
615
615 1.1.5 (**2011-03-17**)
616 1.1.5 (**2011-03-17**)
616 ----------------------
617 ----------------------
617
618
618 news
619 news
619 ++++
620 ++++
620
621
621 - basic windows support, by exchanging pybcrypt into sha256 for windows only
622 - basic windows support, by exchanging pybcrypt into sha256 for windows only
622 highly inspired by idea of mantis406
623 highly inspired by idea of mantis406
623
624
624 fixes
625 fixes
625 +++++
626 +++++
626
627
627 - fixed sorting by author in main page
628 - fixed sorting by author in main page
628 - fixed crashes with diffs on binary files
629 - fixed crashes with diffs on binary files
629 - fixed #131 problem with boolean values for LDAP
630 - fixed #131 problem with boolean values for LDAP
630 - fixed #122 mysql problems thanks to striker69
631 - fixed #122 mysql problems thanks to striker69
631 - fixed problem with errors on calling raw/raw_files/annotate functions
632 - fixed problem with errors on calling raw/raw_files/annotate functions
632 with unknown revisions
633 with unknown revisions
633 - fixed returned rawfiles attachment names with international character
634 - fixed returned rawfiles attachment names with international character
634 - cleaned out docs, big thanks to Jason Harris
635 - cleaned out docs, big thanks to Jason Harris
635
636
636 1.1.4 (**2011-02-19**)
637 1.1.4 (**2011-02-19**)
637 ----------------------
638 ----------------------
638
639
639 news
640 news
640 ++++
641 ++++
641
642
642 fixes
643 fixes
643 +++++
644 +++++
644
645
645 - fixed formencode import problem on settings page, that caused server crash
646 - fixed formencode import problem on settings page, that caused server crash
646 when that page was accessed as first after server start
647 when that page was accessed as first after server start
647 - journal fixes
648 - journal fixes
648 - fixed option to access repository just by entering http://server/<repo_name>
649 - fixed option to access repository just by entering http://server/<repo_name>
649
650
650 1.1.3 (**2011-02-16**)
651 1.1.3 (**2011-02-16**)
651 ----------------------
652 ----------------------
652
653
653 news
654 news
654 ++++
655 ++++
655
656
656 - implemented #102 allowing the '.' character in username
657 - implemented #102 allowing the '.' character in username
657 - added option to access repository just by entering http://server/<repo_name>
658 - added option to access repository just by entering http://server/<repo_name>
658 - celery task ignores result for better performance
659 - celery task ignores result for better performance
659
660
660 fixes
661 fixes
661 +++++
662 +++++
662
663
663 - fixed ehlo command and non auth mail servers on smtp_lib. Thanks to
664 - fixed ehlo command and non auth mail servers on smtp_lib. Thanks to
664 apollo13 and Johan Walles
665 apollo13 and Johan Walles
665 - small fixes in journal
666 - small fixes in journal
666 - fixed problems with getting setting for celery from .ini files
667 - fixed problems with getting setting for celery from .ini files
667 - registration, password reset and login boxes share the same title as main
668 - registration, password reset and login boxes share the same title as main
668 application now
669 application now
669 - fixed #113: to high permissions to fork repository
670 - fixed #113: to high permissions to fork repository
670 - fixed problem with '[' chars in commit messages in journal
671 - fixed problem with '[' chars in commit messages in journal
671 - removed issue with space inside renamed repository after deletion
672 - removed issue with space inside renamed repository after deletion
672 - db transaction fixes when filesystem repository creation failed
673 - db transaction fixes when filesystem repository creation failed
673 - fixed #106 relation issues on databases different than sqlite
674 - fixed #106 relation issues on databases different than sqlite
674 - fixed static files paths links to use of url() method
675 - fixed static files paths links to use of url() method
675
676
676 1.1.2 (**2011-01-12**)
677 1.1.2 (**2011-01-12**)
677 ----------------------
678 ----------------------
678
679
679 news
680 news
680 ++++
681 ++++
681
682
682
683
683 fixes
684 fixes
684 +++++
685 +++++
685
686
686 - fixes #98 protection against float division of percentage stats
687 - fixes #98 protection against float division of percentage stats
687 - fixed graph bug
688 - fixed graph bug
688 - forced webhelpers version since it was making troubles during installation
689 - forced webhelpers version since it was making troubles during installation
689
690
690 1.1.1 (**2011-01-06**)
691 1.1.1 (**2011-01-06**)
691 ----------------------
692 ----------------------
692
693
693 news
694 news
694 ++++
695 ++++
695
696
696 - added force https option into ini files for easier https usage (no need to
697 - added force https option into ini files for easier https usage (no need to
697 set server headers with this options)
698 set server headers with this options)
698 - small css updates
699 - small css updates
699
700
700 fixes
701 fixes
701 +++++
702 +++++
702
703
703 - fixed #96 redirect loop on files view on repositories without changesets
704 - fixed #96 redirect loop on files view on repositories without changesets
704 - fixed #97 unicode string passed into server header in special cases (mod_wsgi)
705 - fixed #97 unicode string passed into server header in special cases (mod_wsgi)
705 and server crashed with errors
706 and server crashed with errors
706 - fixed large tooltips problems on main page
707 - fixed large tooltips problems on main page
707 - fixed #92 whoosh indexer is more error proof
708 - fixed #92 whoosh indexer is more error proof
708
709
709 1.1.0 (**2010-12-18**)
710 1.1.0 (**2010-12-18**)
710 ----------------------
711 ----------------------
711
712
712 news
713 news
713 ++++
714 ++++
714
715
715 - rewrite of internals for vcs >=0.1.10
716 - rewrite of internals for vcs >=0.1.10
716 - uses mercurial 1.7 with dotencode disabled for maintaining compatibility
717 - uses mercurial 1.7 with dotencode disabled for maintaining compatibility
717 with older clients
718 with older clients
718 - anonymous access, authentication via ldap
719 - anonymous access, authentication via ldap
719 - performance upgrade for cached repos list - each repository has its own
720 - performance upgrade for cached repos list - each repository has its own
720 cache that's invalidated when needed.
721 cache that's invalidated when needed.
721 - performance upgrades on repositories with large amount of commits (20K+)
722 - performance upgrades on repositories with large amount of commits (20K+)
722 - main page quick filter for filtering repositories
723 - main page quick filter for filtering repositories
723 - user dashboards with ability to follow chosen repositories actions
724 - user dashboards with ability to follow chosen repositories actions
724 - sends email to admin on new user registration
725 - sends email to admin on new user registration
725 - added cache/statistics reset options into repository settings
726 - added cache/statistics reset options into repository settings
726 - more detailed action logger (based on hooks) with pushed changesets lists
727 - more detailed action logger (based on hooks) with pushed changesets lists
727 and options to disable those hooks from admin panel
728 and options to disable those hooks from admin panel
728 - introduced new enhanced changelog for merges that shows more accurate results
729 - introduced new enhanced changelog for merges that shows more accurate results
729 - new improved and faster code stats (based on pygments lexers mapping tables,
730 - new improved and faster code stats (based on pygments lexers mapping tables,
730 showing up to 10 trending sources for each repository. Additionally stats
731 showing up to 10 trending sources for each repository. Additionally stats
731 can be disabled in repository settings.
732 can be disabled in repository settings.
732 - gui optimizations, fixed application width to 1024px
733 - gui optimizations, fixed application width to 1024px
733 - added cut off (for large files/changesets) limit into config files
734 - added cut off (for large files/changesets) limit into config files
734 - whoosh, celeryd, upgrade moved to paster command
735 - whoosh, celeryd, upgrade moved to paster command
735 - other than sqlite database backends can be used
736 - other than sqlite database backends can be used
736
737
737 fixes
738 fixes
738 +++++
739 +++++
739
740
740 - fixes #61 forked repo was showing only after cache expired
741 - fixes #61 forked repo was showing only after cache expired
741 - fixes #76 no confirmation on user deletes
742 - fixes #76 no confirmation on user deletes
742 - fixes #66 Name field misspelled
743 - fixes #66 Name field misspelled
743 - fixes #72 block user removal when he owns repositories
744 - fixes #72 block user removal when he owns repositories
744 - fixes #69 added password confirmation fields
745 - fixes #69 added password confirmation fields
745 - fixes #87 RhodeCode crashes occasionally on updating repository owner
746 - fixes #87 RhodeCode crashes occasionally on updating repository owner
746 - fixes #82 broken annotations on files with more than 1 blank line at the end
747 - fixes #82 broken annotations on files with more than 1 blank line at the end
747 - a lot of fixes and tweaks for file browser
748 - a lot of fixes and tweaks for file browser
748 - fixed detached session issues
749 - fixed detached session issues
749 - fixed when user had no repos he would see all repos listed in my account
750 - fixed when user had no repos he would see all repos listed in my account
750 - fixed ui() instance bug when global hgrc settings was loaded for server
751 - fixed ui() instance bug when global hgrc settings was loaded for server
751 instance and all hgrc options were merged with our db ui() object
752 instance and all hgrc options were merged with our db ui() object
752 - numerous small bugfixes
753 - numerous small bugfixes
753
754
754 (special thanks for TkSoh for detailed feedback)
755 (special thanks for TkSoh for detailed feedback)
755
756
756
757
757 1.0.2 (**2010-11-12**)
758 1.0.2 (**2010-11-12**)
758 ----------------------
759 ----------------------
759
760
760 news
761 news
761 ++++
762 ++++
762
763
763 - tested under python2.7
764 - tested under python2.7
764 - bumped sqlalchemy and celery versions
765 - bumped sqlalchemy and celery versions
765
766
766 fixes
767 fixes
767 +++++
768 +++++
768
769
769 - fixed #59 missing graph.js
770 - fixed #59 missing graph.js
770 - fixed repo_size crash when repository had broken symlinks
771 - fixed repo_size crash when repository had broken symlinks
771 - fixed python2.5 crashes.
772 - fixed python2.5 crashes.
772
773
773
774
774 1.0.1 (**2010-11-10**)
775 1.0.1 (**2010-11-10**)
775 ----------------------
776 ----------------------
776
777
777 news
778 news
778 ++++
779 ++++
779
780
780 - small css updated
781 - small css updated
781
782
782 fixes
783 fixes
783 +++++
784 +++++
784
785
785 - fixed #53 python2.5 incompatible enumerate calls
786 - fixed #53 python2.5 incompatible enumerate calls
786 - fixed #52 disable mercurial extension for web
787 - fixed #52 disable mercurial extension for web
787 - fixed #51 deleting repositories don't delete it's dependent objects
788 - fixed #51 deleting repositories don't delete it's dependent objects
788
789
789
790
790 1.0.0 (**2010-11-02**)
791 1.0.0 (**2010-11-02**)
791 ----------------------
792 ----------------------
792
793
793 - security bugfix simplehg wasn't checking for permissions on commands
794 - security bugfix simplehg wasn't checking for permissions on commands
794 other than pull or push.
795 other than pull or push.
795 - fixed doubled messages after push or pull in admin journal
796 - fixed doubled messages after push or pull in admin journal
796 - templating and css corrections, fixed repo switcher on chrome, updated titles
797 - templating and css corrections, fixed repo switcher on chrome, updated titles
797 - admin menu accessible from options menu on repository view
798 - admin menu accessible from options menu on repository view
798 - permissions cached queries
799 - permissions cached queries
799
800
800 1.0.0rc4 (**2010-10-12**)
801 1.0.0rc4 (**2010-10-12**)
801 --------------------------
802 --------------------------
802
803
803 - fixed python2.5 missing simplejson imports (thanks to Jens BΓ€ckman)
804 - fixed python2.5 missing simplejson imports (thanks to Jens BΓ€ckman)
804 - removed cache_manager settings from sqlalchemy meta
805 - removed cache_manager settings from sqlalchemy meta
805 - added sqlalchemy cache settings to ini files
806 - added sqlalchemy cache settings to ini files
806 - validated password length and added second try of failure on paster setup-app
807 - validated password length and added second try of failure on paster setup-app
807 - fixed setup database destroy prompt even when there was no db
808 - fixed setup database destroy prompt even when there was no db
808
809
809
810
810 1.0.0rc3 (**2010-10-11**)
811 1.0.0rc3 (**2010-10-11**)
811 -------------------------
812 -------------------------
812
813
813 - fixed i18n during installation.
814 - fixed i18n during installation.
814
815
815 1.0.0rc2 (**2010-10-11**)
816 1.0.0rc2 (**2010-10-11**)
816 -------------------------
817 -------------------------
817
818
818 - Disabled dirsize in file browser, it's causing nasty bug when dir renames
819 - Disabled dirsize in file browser, it's causing nasty bug when dir renames
819 occure. After vcs is fixed it'll be put back again.
820 occure. After vcs is fixed it'll be put back again.
820 - templating/css rewrites, optimized css. No newline at end of file
821 - templating/css rewrites, optimized css.
@@ -1,507 +1,518 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.lib.utils
3 rhodecode.lib.utils
4 ~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~
5
5
6 Some simple helper functions
6 Some simple helper functions
7
7
8 :created_on: Jan 5, 2011
8 :created_on: Jan 5, 2011
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2011-2012 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2011-2012 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 modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
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, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25
25
26 import re
26 import re
27 import time
27 import time
28 import datetime
28 import datetime
29 from pylons.i18n.translation import _, ungettext
29 from pylons.i18n.translation import _, ungettext
30 from rhodecode.lib.vcs.utils.lazy import LazyProperty
30 from rhodecode.lib.vcs.utils.lazy import LazyProperty
31
31
32
32
33 def __get_lem():
33 def __get_lem():
34 """
34 """
35 Get language extension map based on what's inside pygments lexers
35 Get language extension map based on what's inside pygments lexers
36 """
36 """
37 from pygments import lexers
37 from pygments import lexers
38 from string import lower
38 from string import lower
39 from collections import defaultdict
39 from collections import defaultdict
40
40
41 d = defaultdict(lambda: [])
41 d = defaultdict(lambda: [])
42
42
43 def __clean(s):
43 def __clean(s):
44 s = s.lstrip('*')
44 s = s.lstrip('*')
45 s = s.lstrip('.')
45 s = s.lstrip('.')
46
46
47 if s.find('[') != -1:
47 if s.find('[') != -1:
48 exts = []
48 exts = []
49 start, stop = s.find('['), s.find(']')
49 start, stop = s.find('['), s.find(']')
50
50
51 for suffix in s[start + 1:stop]:
51 for suffix in s[start + 1:stop]:
52 exts.append(s[:s.find('[')] + suffix)
52 exts.append(s[:s.find('[')] + suffix)
53 return map(lower, exts)
53 return map(lower, exts)
54 else:
54 else:
55 return map(lower, [s])
55 return map(lower, [s])
56
56
57 for lx, t in sorted(lexers.LEXERS.items()):
57 for lx, t in sorted(lexers.LEXERS.items()):
58 m = map(__clean, t[-2])
58 m = map(__clean, t[-2])
59 if m:
59 if m:
60 m = reduce(lambda x, y: x + y, m)
60 m = reduce(lambda x, y: x + y, m)
61 for ext in m:
61 for ext in m:
62 desc = lx.replace('Lexer', '')
62 desc = lx.replace('Lexer', '')
63 d[ext].append(desc)
63 d[ext].append(desc)
64
64
65 return dict(d)
65 return dict(d)
66
66
67 def str2bool(_str):
67 def str2bool(_str):
68 """
68 """
69 returs True/False value from given string, it tries to translate the
69 returs True/False value from given string, it tries to translate the
70 string into boolean
70 string into boolean
71
71
72 :param _str: string value to translate into boolean
72 :param _str: string value to translate into boolean
73 :rtype: boolean
73 :rtype: boolean
74 :returns: boolean from given string
74 :returns: boolean from given string
75 """
75 """
76 if _str is None:
76 if _str is None:
77 return False
77 return False
78 if _str in (True, False):
78 if _str in (True, False):
79 return _str
79 return _str
80 _str = str(_str).strip().lower()
80 _str = str(_str).strip().lower()
81 return _str in ('t', 'true', 'y', 'yes', 'on', '1')
81 return _str in ('t', 'true', 'y', 'yes', 'on', '1')
82
82
83
83
84 def convert_line_endings(line, mode):
84 def convert_line_endings(line, mode):
85 """
85 """
86 Converts a given line "line end" accordingly to given mode
86 Converts a given line "line end" accordingly to given mode
87
87
88 Available modes are::
88 Available modes are::
89 0 - Unix
89 0 - Unix
90 1 - Mac
90 1 - Mac
91 2 - DOS
91 2 - DOS
92
92
93 :param line: given line to convert
93 :param line: given line to convert
94 :param mode: mode to convert to
94 :param mode: mode to convert to
95 :rtype: str
95 :rtype: str
96 :return: converted line according to mode
96 :return: converted line according to mode
97 """
97 """
98 from string import replace
98 from string import replace
99
99
100 if mode == 0:
100 if mode == 0:
101 line = replace(line, '\r\n', '\n')
101 line = replace(line, '\r\n', '\n')
102 line = replace(line, '\r', '\n')
102 line = replace(line, '\r', '\n')
103 elif mode == 1:
103 elif mode == 1:
104 line = replace(line, '\r\n', '\r')
104 line = replace(line, '\r\n', '\r')
105 line = replace(line, '\n', '\r')
105 line = replace(line, '\n', '\r')
106 elif mode == 2:
106 elif mode == 2:
107 line = re.sub("\r(?!\n)|(?<!\r)\n", "\r\n", line)
107 line = re.sub("\r(?!\n)|(?<!\r)\n", "\r\n", line)
108 return line
108 return line
109
109
110
110
111 def detect_mode(line, default):
111 def detect_mode(line, default):
112 """
112 """
113 Detects line break for given line, if line break couldn't be found
113 Detects line break for given line, if line break couldn't be found
114 given default value is returned
114 given default value is returned
115
115
116 :param line: str line
116 :param line: str line
117 :param default: default
117 :param default: default
118 :rtype: int
118 :rtype: int
119 :return: value of line end on of 0 - Unix, 1 - Mac, 2 - DOS
119 :return: value of line end on of 0 - Unix, 1 - Mac, 2 - DOS
120 """
120 """
121 if line.endswith('\r\n'):
121 if line.endswith('\r\n'):
122 return 2
122 return 2
123 elif line.endswith('\n'):
123 elif line.endswith('\n'):
124 return 0
124 return 0
125 elif line.endswith('\r'):
125 elif line.endswith('\r'):
126 return 1
126 return 1
127 else:
127 else:
128 return default
128 return default
129
129
130
130
131 def generate_api_key(username, salt=None):
131 def generate_api_key(username, salt=None):
132 """
132 """
133 Generates unique API key for given username, if salt is not given
133 Generates unique API key for given username, if salt is not given
134 it'll be generated from some random string
134 it'll be generated from some random string
135
135
136 :param username: username as string
136 :param username: username as string
137 :param salt: salt to hash generate KEY
137 :param salt: salt to hash generate KEY
138 :rtype: str
138 :rtype: str
139 :returns: sha1 hash from username+salt
139 :returns: sha1 hash from username+salt
140 """
140 """
141 from tempfile import _RandomNameSequence
141 from tempfile import _RandomNameSequence
142 import hashlib
142 import hashlib
143
143
144 if salt is None:
144 if salt is None:
145 salt = _RandomNameSequence().next()
145 salt = _RandomNameSequence().next()
146
146
147 return hashlib.sha1(username + salt).hexdigest()
147 return hashlib.sha1(username + salt).hexdigest()
148
148
149
149
150 def safe_int(val, default=None):
150 def safe_int(val, default=None):
151 """
151 """
152 Returns int() of val if val is not convertable to int use default
152 Returns int() of val if val is not convertable to int use default
153 instead
153 instead
154
154
155 :param val:
155 :param val:
156 :param default:
156 :param default:
157 """
157 """
158
158
159 try:
159 try:
160 val = int(val)
160 val = int(val)
161 except ValueError:
161 except ValueError:
162 val = default
162 val = default
163
163
164 return val
164 return val
165
165
166
166
167 def safe_unicode(str_, from_encoding=None):
167 def safe_unicode(str_, from_encoding=None):
168 """
168 """
169 safe unicode function. Does few trick to turn str_ into unicode
169 safe unicode function. Does few trick to turn str_ into unicode
170
170
171 In case of UnicodeDecode error we try to return it with encoding detected
171 In case of UnicodeDecode error we try to return it with encoding detected
172 by chardet library if it fails fallback to unicode with errors replaced
172 by chardet library if it fails fallback to unicode with errors replaced
173
173
174 :param str_: string to decode
174 :param str_: string to decode
175 :rtype: unicode
175 :rtype: unicode
176 :returns: unicode object
176 :returns: unicode object
177 """
177 """
178 if isinstance(str_, unicode):
178 if isinstance(str_, unicode):
179 return str_
179 return str_
180
180
181 if not from_encoding:
181 if not from_encoding:
182 import rhodecode
182 import rhodecode
183 DEFAULT_ENCODING = rhodecode.CONFIG.get('default_encoding','utf8')
183 DEFAULT_ENCODING = rhodecode.CONFIG.get('default_encoding','utf8')
184 from_encoding = DEFAULT_ENCODING
184 from_encoding = DEFAULT_ENCODING
185
185
186 try:
186 try:
187 return unicode(str_)
187 return unicode(str_)
188 except UnicodeDecodeError:
188 except UnicodeDecodeError:
189 pass
189 pass
190
190
191 try:
191 try:
192 return unicode(str_, from_encoding)
192 return unicode(str_, from_encoding)
193 except UnicodeDecodeError:
193 except UnicodeDecodeError:
194 pass
194 pass
195
195
196 try:
196 try:
197 import chardet
197 import chardet
198 encoding = chardet.detect(str_)['encoding']
198 encoding = chardet.detect(str_)['encoding']
199 if encoding is None:
199 if encoding is None:
200 raise Exception()
200 raise Exception()
201 return str_.decode(encoding)
201 return str_.decode(encoding)
202 except (ImportError, UnicodeDecodeError, Exception):
202 except (ImportError, UnicodeDecodeError, Exception):
203 return unicode(str_, from_encoding, 'replace')
203 return unicode(str_, from_encoding, 'replace')
204
204
205
205
206 def safe_str(unicode_, to_encoding=None):
206 def safe_str(unicode_, to_encoding=None):
207 """
207 """
208 safe str function. Does few trick to turn unicode_ into string
208 safe str function. Does few trick to turn unicode_ into string
209
209
210 In case of UnicodeEncodeError we try to return it with encoding detected
210 In case of UnicodeEncodeError we try to return it with encoding detected
211 by chardet library if it fails fallback to string with errors replaced
211 by chardet library if it fails fallback to string with errors replaced
212
212
213 :param unicode_: unicode to encode
213 :param unicode_: unicode to encode
214 :rtype: str
214 :rtype: str
215 :returns: str object
215 :returns: str object
216 """
216 """
217
217
218 # if it's not basestr cast to str
218 # if it's not basestr cast to str
219 if not isinstance(unicode_, basestring):
219 if not isinstance(unicode_, basestring):
220 return str(unicode_)
220 return str(unicode_)
221
221
222 if isinstance(unicode_, str):
222 if isinstance(unicode_, str):
223 return unicode_
223 return unicode_
224
224
225 if not to_encoding:
225 if not to_encoding:
226 import rhodecode
226 import rhodecode
227 DEFAULT_ENCODING = rhodecode.CONFIG.get('default_encoding','utf8')
227 DEFAULT_ENCODING = rhodecode.CONFIG.get('default_encoding','utf8')
228 to_encoding = DEFAULT_ENCODING
228 to_encoding = DEFAULT_ENCODING
229
229
230 try:
230 try:
231 return unicode_.encode(to_encoding)
231 return unicode_.encode(to_encoding)
232 except UnicodeEncodeError:
232 except UnicodeEncodeError:
233 pass
233 pass
234
234
235 try:
235 try:
236 import chardet
236 import chardet
237 encoding = chardet.detect(unicode_)['encoding']
237 encoding = chardet.detect(unicode_)['encoding']
238 if encoding is None:
238 if encoding is None:
239 raise UnicodeEncodeError()
239 raise UnicodeEncodeError()
240
240
241 return unicode_.encode(encoding)
241 return unicode_.encode(encoding)
242 except (ImportError, UnicodeEncodeError):
242 except (ImportError, UnicodeEncodeError):
243 return unicode_.encode(to_encoding, 'replace')
243 return unicode_.encode(to_encoding, 'replace')
244
244
245 return safe_str
245 return safe_str
246
246
247
247
248 def engine_from_config(configuration, prefix='sqlalchemy.', **kwargs):
248 def engine_from_config(configuration, prefix='sqlalchemy.', **kwargs):
249 """
249 """
250 Custom engine_from_config functions that makes sure we use NullPool for
250 Custom engine_from_config functions that makes sure we use NullPool for
251 file based sqlite databases. This prevents errors on sqlite. This only
251 file based sqlite databases. This prevents errors on sqlite. This only
252 applies to sqlalchemy versions < 0.7.0
252 applies to sqlalchemy versions < 0.7.0
253
253
254 """
254 """
255 import sqlalchemy
255 import sqlalchemy
256 from sqlalchemy import engine_from_config as efc
256 from sqlalchemy import engine_from_config as efc
257 import logging
257 import logging
258
258
259 if int(sqlalchemy.__version__.split('.')[1]) < 7:
259 if int(sqlalchemy.__version__.split('.')[1]) < 7:
260
260
261 # This solution should work for sqlalchemy < 0.7.0, and should use
261 # This solution should work for sqlalchemy < 0.7.0, and should use
262 # proxy=TimerProxy() for execution time profiling
262 # proxy=TimerProxy() for execution time profiling
263
263
264 from sqlalchemy.pool import NullPool
264 from sqlalchemy.pool import NullPool
265 url = configuration[prefix + 'url']
265 url = configuration[prefix + 'url']
266
266
267 if url.startswith('sqlite'):
267 if url.startswith('sqlite'):
268 kwargs.update({'poolclass': NullPool})
268 kwargs.update({'poolclass': NullPool})
269 return efc(configuration, prefix, **kwargs)
269 return efc(configuration, prefix, **kwargs)
270 else:
270 else:
271 import time
271 import time
272 from sqlalchemy import event
272 from sqlalchemy import event
273 from sqlalchemy.engine import Engine
273 from sqlalchemy.engine import Engine
274
274
275 log = logging.getLogger('sqlalchemy.engine')
275 log = logging.getLogger('sqlalchemy.engine')
276 BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = xrange(30, 38)
276 BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = xrange(30, 38)
277 engine = efc(configuration, prefix, **kwargs)
277 engine = efc(configuration, prefix, **kwargs)
278
278
279 def color_sql(sql):
279 def color_sql(sql):
280 COLOR_SEQ = "\033[1;%dm"
280 COLOR_SEQ = "\033[1;%dm"
281 COLOR_SQL = YELLOW
281 COLOR_SQL = YELLOW
282 normal = '\x1b[0m'
282 normal = '\x1b[0m'
283 return ''.join([COLOR_SEQ % COLOR_SQL, sql, normal])
283 return ''.join([COLOR_SEQ % COLOR_SQL, sql, normal])
284
284
285 if configuration['debug']:
285 if configuration['debug']:
286 #attach events only for debug configuration
286 #attach events only for debug configuration
287
287
288 def before_cursor_execute(conn, cursor, statement,
288 def before_cursor_execute(conn, cursor, statement,
289 parameters, context, executemany):
289 parameters, context, executemany):
290 context._query_start_time = time.time()
290 context._query_start_time = time.time()
291 log.info(color_sql(">>>>> STARTING QUERY >>>>>"))
291 log.info(color_sql(">>>>> STARTING QUERY >>>>>"))
292
292
293 def after_cursor_execute(conn, cursor, statement,
293 def after_cursor_execute(conn, cursor, statement,
294 parameters, context, executemany):
294 parameters, context, executemany):
295 total = time.time() - context._query_start_time
295 total = time.time() - context._query_start_time
296 log.info(color_sql("<<<<< TOTAL TIME: %f <<<<<" % total))
296 log.info(color_sql("<<<<< TOTAL TIME: %f <<<<<" % total))
297
297
298 event.listen(engine, "before_cursor_execute",
298 event.listen(engine, "before_cursor_execute",
299 before_cursor_execute)
299 before_cursor_execute)
300 event.listen(engine, "after_cursor_execute",
300 event.listen(engine, "after_cursor_execute",
301 after_cursor_execute)
301 after_cursor_execute)
302
302
303 return engine
303 return engine
304
304
305
305
306 def age(prevdate):
306 def age(prevdate):
307 """
307 """
308 turns a datetime into an age string.
308 turns a datetime into an age string.
309
309
310 :param prevdate: datetime object
310 :param prevdate: datetime object
311 :rtype: unicode
311 :rtype: unicode
312 :returns: unicode words describing age
312 :returns: unicode words describing age
313 """
313 """
314
314
315 order = ['year', 'month', 'day', 'hour', 'minute', 'second']
315 order = ['year', 'month', 'day', 'hour', 'minute', 'second']
316 deltas = {}
316 deltas = {}
317 future = False
317
318
318 # Get date parts deltas
319 # Get date parts deltas
319 now = datetime.datetime.now()
320 now = datetime.datetime.now()
321 if prevdate > now:
322 now, prevdate = prevdate, now
323 future = True
324
320 for part in order:
325 for part in order:
321 deltas[part] = getattr(now, part) - getattr(prevdate, part)
326 deltas[part] = getattr(now, part) - getattr(prevdate, part)
322
327
323 # Fix negative offsets (there is 1 second between 10:59:59 and 11:00:00,
328 # Fix negative offsets (there is 1 second between 10:59:59 and 11:00:00,
324 # not 1 hour, -59 minutes and -59 seconds)
329 # not 1 hour, -59 minutes and -59 seconds)
325
330
326 for num, length in [(5, 60), (4, 60), (3, 24)]: # seconds, minutes, hours
331 for num, length in [(5, 60), (4, 60), (3, 24)]: # seconds, minutes, hours
327 part = order[num]
332 part = order[num]
328 carry_part = order[num - 1]
333 carry_part = order[num - 1]
329
334
330 if deltas[part] < 0:
335 if deltas[part] < 0:
331 deltas[part] += length
336 deltas[part] += length
332 deltas[carry_part] -= 1
337 deltas[carry_part] -= 1
333
338
334 # Same thing for days except that the increment depends on the (variable)
339 # Same thing for days except that the increment depends on the (variable)
335 # number of days in the month
340 # number of days in the month
336 month_lengths = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
341 month_lengths = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
337 if deltas['day'] < 0:
342 if deltas['day'] < 0:
338 if prevdate.month == 2 and (prevdate.year % 4 == 0 and
343 if prevdate.month == 2 and (prevdate.year % 4 == 0 and
339 (prevdate.year % 100 != 0 or prevdate.year % 400 == 0)):
344 (prevdate.year % 100 != 0 or prevdate.year % 400 == 0)):
340 deltas['day'] += 29
345 deltas['day'] += 29
341 else:
346 else:
342 deltas['day'] += month_lengths[prevdate.month - 1]
347 deltas['day'] += month_lengths[prevdate.month - 1]
343
348
344 deltas['month'] -= 1
349 deltas['month'] -= 1
345
350
346 if deltas['month'] < 0:
351 if deltas['month'] < 0:
347 deltas['month'] += 12
352 deltas['month'] += 12
348 deltas['year'] -= 1
353 deltas['year'] -= 1
349
354
350 # Format the result
355 # Format the result
351 fmt_funcs = {
356 fmt_funcs = {
352 'year': lambda d: ungettext(u'%d year', '%d years', d) % d,
357 'year': lambda d: ungettext(u'%d year', '%d years', d) % d,
353 'month': lambda d: ungettext(u'%d month', '%d months', d) % d,
358 'month': lambda d: ungettext(u'%d month', '%d months', d) % d,
354 'day': lambda d: ungettext(u'%d day', '%d days', d) % d,
359 'day': lambda d: ungettext(u'%d day', '%d days', d) % d,
355 'hour': lambda d: ungettext(u'%d hour', '%d hours', d) % d,
360 'hour': lambda d: ungettext(u'%d hour', '%d hours', d) % d,
356 'minute': lambda d: ungettext(u'%d minute', '%d minutes', d) % d,
361 'minute': lambda d: ungettext(u'%d minute', '%d minutes', d) % d,
357 'second': lambda d: ungettext(u'%d second', '%d seconds', d) % d,
362 'second': lambda d: ungettext(u'%d second', '%d seconds', d) % d,
358 }
363 }
359
364
360 for i, part in enumerate(order):
365 for i, part in enumerate(order):
361 value = deltas[part]
366 value = deltas[part]
362 if value == 0:
367 if value == 0:
363 continue
368 continue
364
369
365 if i < 5:
370 if i < 5:
366 sub_part = order[i + 1]
371 sub_part = order[i + 1]
367 sub_value = deltas[sub_part]
372 sub_value = deltas[sub_part]
368 else:
373 else:
369 sub_value = 0
374 sub_value = 0
370
375
371 if sub_value == 0:
376 if sub_value == 0:
372 return _(u'%s ago') % fmt_funcs[part](value)
377 if future:
373
378 return _(u'in %s') % fmt_funcs[part](value)
374 return _(u'%s and %s ago') % (fmt_funcs[part](value),
379 else:
375 fmt_funcs[sub_part](sub_value))
380 return _(u'%s ago') % fmt_funcs[part](value)
381 if future:
382 return _(u'in %s and %s') % (fmt_funcs[part](value),
383 fmt_funcs[sub_part](sub_value))
384 else:
385 return _(u'%s and %s ago') % (fmt_funcs[part](value),
386 fmt_funcs[sub_part](sub_value))
376
387
377 return _(u'just now')
388 return _(u'just now')
378
389
379
390
380 def uri_filter(uri):
391 def uri_filter(uri):
381 """
392 """
382 Removes user:password from given url string
393 Removes user:password from given url string
383
394
384 :param uri:
395 :param uri:
385 :rtype: unicode
396 :rtype: unicode
386 :returns: filtered list of strings
397 :returns: filtered list of strings
387 """
398 """
388 if not uri:
399 if not uri:
389 return ''
400 return ''
390
401
391 proto = ''
402 proto = ''
392
403
393 for pat in ('https://', 'http://'):
404 for pat in ('https://', 'http://'):
394 if uri.startswith(pat):
405 if uri.startswith(pat):
395 uri = uri[len(pat):]
406 uri = uri[len(pat):]
396 proto = pat
407 proto = pat
397 break
408 break
398
409
399 # remove passwords and username
410 # remove passwords and username
400 uri = uri[uri.find('@') + 1:]
411 uri = uri[uri.find('@') + 1:]
401
412
402 # get the port
413 # get the port
403 cred_pos = uri.find(':')
414 cred_pos = uri.find(':')
404 if cred_pos == -1:
415 if cred_pos == -1:
405 host, port = uri, None
416 host, port = uri, None
406 else:
417 else:
407 host, port = uri[:cred_pos], uri[cred_pos + 1:]
418 host, port = uri[:cred_pos], uri[cred_pos + 1:]
408
419
409 return filter(None, [proto, host, port])
420 return filter(None, [proto, host, port])
410
421
411
422
412 def credentials_filter(uri):
423 def credentials_filter(uri):
413 """
424 """
414 Returns a url with removed credentials
425 Returns a url with removed credentials
415
426
416 :param uri:
427 :param uri:
417 """
428 """
418
429
419 uri = uri_filter(uri)
430 uri = uri_filter(uri)
420 #check if we have port
431 #check if we have port
421 if len(uri) > 2 and uri[2]:
432 if len(uri) > 2 and uri[2]:
422 uri[2] = ':' + uri[2]
433 uri[2] = ':' + uri[2]
423
434
424 return ''.join(uri)
435 return ''.join(uri)
425
436
426
437
427 def get_changeset_safe(repo, rev):
438 def get_changeset_safe(repo, rev):
428 """
439 """
429 Safe version of get_changeset if this changeset doesn't exists for a
440 Safe version of get_changeset if this changeset doesn't exists for a
430 repo it returns a Dummy one instead
441 repo it returns a Dummy one instead
431
442
432 :param repo:
443 :param repo:
433 :param rev:
444 :param rev:
434 """
445 """
435 from rhodecode.lib.vcs.backends.base import BaseRepository
446 from rhodecode.lib.vcs.backends.base import BaseRepository
436 from rhodecode.lib.vcs.exceptions import RepositoryError
447 from rhodecode.lib.vcs.exceptions import RepositoryError
437 from rhodecode.lib.vcs.backends.base import EmptyChangeset
448 from rhodecode.lib.vcs.backends.base import EmptyChangeset
438 if not isinstance(repo, BaseRepository):
449 if not isinstance(repo, BaseRepository):
439 raise Exception('You must pass an Repository '
450 raise Exception('You must pass an Repository '
440 'object as first argument got %s', type(repo))
451 'object as first argument got %s', type(repo))
441
452
442 try:
453 try:
443 cs = repo.get_changeset(rev)
454 cs = repo.get_changeset(rev)
444 except RepositoryError:
455 except RepositoryError:
445 cs = EmptyChangeset(requested_revision=rev)
456 cs = EmptyChangeset(requested_revision=rev)
446 return cs
457 return cs
447
458
448
459
449 def datetime_to_time(dt):
460 def datetime_to_time(dt):
450 if dt:
461 if dt:
451 return time.mktime(dt.timetuple())
462 return time.mktime(dt.timetuple())
452
463
453
464
454 def time_to_datetime(tm):
465 def time_to_datetime(tm):
455 if tm:
466 if tm:
456 if isinstance(tm, basestring):
467 if isinstance(tm, basestring):
457 try:
468 try:
458 tm = float(tm)
469 tm = float(tm)
459 except ValueError:
470 except ValueError:
460 return
471 return
461 return datetime.datetime.fromtimestamp(tm)
472 return datetime.datetime.fromtimestamp(tm)
462
473
463 MENTIONS_REGEX = r'(?:^@|\s@)([a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]+)(?:\s{1})'
474 MENTIONS_REGEX = r'(?:^@|\s@)([a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]+)(?:\s{1})'
464
475
465
476
466 def extract_mentioned_users(s):
477 def extract_mentioned_users(s):
467 """
478 """
468 Returns unique usernames from given string s that have @mention
479 Returns unique usernames from given string s that have @mention
469
480
470 :param s: string to get mentions
481 :param s: string to get mentions
471 """
482 """
472 usrs = set()
483 usrs = set()
473 for username in re.findall(MENTIONS_REGEX, s):
484 for username in re.findall(MENTIONS_REGEX, s):
474 usrs.add(username)
485 usrs.add(username)
475
486
476 return sorted(list(usrs), key=lambda k: k.lower())
487 return sorted(list(usrs), key=lambda k: k.lower())
477
488
478
489
479 class AttributeDict(dict):
490 class AttributeDict(dict):
480 def __getattr__(self, attr):
491 def __getattr__(self, attr):
481 return self.get(attr, None)
492 return self.get(attr, None)
482 __setattr__ = dict.__setitem__
493 __setattr__ = dict.__setitem__
483 __delattr__ = dict.__delitem__
494 __delattr__ = dict.__delitem__
484
495
485
496
486 def fix_PATH(os_=None):
497 def fix_PATH(os_=None):
487 """
498 """
488 Get current active python path, and append it to PATH variable to fix issues
499 Get current active python path, and append it to PATH variable to fix issues
489 of subprocess calls and different python versions
500 of subprocess calls and different python versions
490 """
501 """
491 import sys
502 import sys
492 if os_ is None:
503 if os_ is None:
493 import os
504 import os
494 else:
505 else:
495 os = os_
506 os = os_
496
507
497 cur_path = os.path.split(sys.executable)[0]
508 cur_path = os.path.split(sys.executable)[0]
498 if not os.environ['PATH'].startswith(cur_path):
509 if not os.environ['PATH'].startswith(cur_path):
499 os.environ['PATH'] = '%s:%s' % (cur_path, os.environ['PATH'])
510 os.environ['PATH'] = '%s:%s' % (cur_path, os.environ['PATH'])
500
511
501
512
502 def obfuscate_url_pw(engine):
513 def obfuscate_url_pw(engine):
503 from sqlalchemy.engine import url
514 from sqlalchemy.engine import url
504 url = url.make_url(engine)
515 url = url.make_url(engine)
505 if url.password:
516 if url.password:
506 url.password = 'XXXXX'
517 url.password = 'XXXXX'
507 return str(url) No newline at end of file
518 return str(url)
@@ -1,198 +1,213 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.tests.test_libs
3 rhodecode.tests.test_libs
4 ~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6
6
7 Package for testing various lib/helper functions in rhodecode
7 Package for testing various lib/helper functions in rhodecode
8
8
9 :created_on: Jun 9, 2011
9 :created_on: Jun 9, 2011
10 :copyright: (C) 2011-2012 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2011-2012 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 modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
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, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 from __future__ import with_statement
25 from __future__ import with_statement
26 import unittest
26 import unittest
27 import datetime
27 import datetime
28 import hashlib
28 import hashlib
29 import mock
29 import mock
30 from rhodecode.tests import *
30 from rhodecode.tests import *
31
31
32 proto = 'http'
32 proto = 'http'
33 TEST_URLS = [
33 TEST_URLS = [
34 ('%s://127.0.0.1' % proto, ['%s://' % proto, '127.0.0.1'],
34 ('%s://127.0.0.1' % proto, ['%s://' % proto, '127.0.0.1'],
35 '%s://127.0.0.1' % proto),
35 '%s://127.0.0.1' % proto),
36 ('%s://marcink@127.0.0.1' % proto, ['%s://' % proto, '127.0.0.1'],
36 ('%s://marcink@127.0.0.1' % proto, ['%s://' % proto, '127.0.0.1'],
37 '%s://127.0.0.1' % proto),
37 '%s://127.0.0.1' % proto),
38 ('%s://marcink:pass@127.0.0.1' % proto, ['%s://' % proto, '127.0.0.1'],
38 ('%s://marcink:pass@127.0.0.1' % proto, ['%s://' % proto, '127.0.0.1'],
39 '%s://127.0.0.1' % proto),
39 '%s://127.0.0.1' % proto),
40 ('%s://127.0.0.1:8080' % proto, ['%s://' % proto, '127.0.0.1', '8080'],
40 ('%s://127.0.0.1:8080' % proto, ['%s://' % proto, '127.0.0.1', '8080'],
41 '%s://127.0.0.1:8080' % proto),
41 '%s://127.0.0.1:8080' % proto),
42 ('%s://domain.org' % proto, ['%s://' % proto, 'domain.org'],
42 ('%s://domain.org' % proto, ['%s://' % proto, 'domain.org'],
43 '%s://domain.org' % proto),
43 '%s://domain.org' % proto),
44 ('%s://user:pass@domain.org:8080' % proto, ['%s://' % proto, 'domain.org',
44 ('%s://user:pass@domain.org:8080' % proto, ['%s://' % proto, 'domain.org',
45 '8080'],
45 '8080'],
46 '%s://domain.org:8080' % proto),
46 '%s://domain.org:8080' % proto),
47 ]
47 ]
48
48
49 proto = 'https'
49 proto = 'https'
50 TEST_URLS += [
50 TEST_URLS += [
51 ('%s://127.0.0.1' % proto, ['%s://' % proto, '127.0.0.1'],
51 ('%s://127.0.0.1' % proto, ['%s://' % proto, '127.0.0.1'],
52 '%s://127.0.0.1' % proto),
52 '%s://127.0.0.1' % proto),
53 ('%s://marcink@127.0.0.1' % proto, ['%s://' % proto, '127.0.0.1'],
53 ('%s://marcink@127.0.0.1' % proto, ['%s://' % proto, '127.0.0.1'],
54 '%s://127.0.0.1' % proto),
54 '%s://127.0.0.1' % proto),
55 ('%s://marcink:pass@127.0.0.1' % proto, ['%s://' % proto, '127.0.0.1'],
55 ('%s://marcink:pass@127.0.0.1' % proto, ['%s://' % proto, '127.0.0.1'],
56 '%s://127.0.0.1' % proto),
56 '%s://127.0.0.1' % proto),
57 ('%s://127.0.0.1:8080' % proto, ['%s://' % proto, '127.0.0.1', '8080'],
57 ('%s://127.0.0.1:8080' % proto, ['%s://' % proto, '127.0.0.1', '8080'],
58 '%s://127.0.0.1:8080' % proto),
58 '%s://127.0.0.1:8080' % proto),
59 ('%s://domain.org' % proto, ['%s://' % proto, 'domain.org'],
59 ('%s://domain.org' % proto, ['%s://' % proto, 'domain.org'],
60 '%s://domain.org' % proto),
60 '%s://domain.org' % proto),
61 ('%s://user:pass@domain.org:8080' % proto, ['%s://' % proto, 'domain.org',
61 ('%s://user:pass@domain.org:8080' % proto, ['%s://' % proto, 'domain.org',
62 '8080'],
62 '8080'],
63 '%s://domain.org:8080' % proto),
63 '%s://domain.org:8080' % proto),
64 ]
64 ]
65
65
66
66
67 class TestLibs(unittest.TestCase):
67 class TestLibs(unittest.TestCase):
68
68
69 def test_uri_filter(self):
69 def test_uri_filter(self):
70 from rhodecode.lib.utils2 import uri_filter
70 from rhodecode.lib.utils2 import uri_filter
71
71
72 for url in TEST_URLS:
72 for url in TEST_URLS:
73 self.assertEqual(uri_filter(url[0]), url[1])
73 self.assertEqual(uri_filter(url[0]), url[1])
74
74
75 def test_credentials_filter(self):
75 def test_credentials_filter(self):
76 from rhodecode.lib.utils2 import credentials_filter
76 from rhodecode.lib.utils2 import credentials_filter
77
77
78 for url in TEST_URLS:
78 for url in TEST_URLS:
79 self.assertEqual(credentials_filter(url[0]), url[2])
79 self.assertEqual(credentials_filter(url[0]), url[2])
80
80
81 def test_str2bool(self):
81 def test_str2bool(self):
82 from rhodecode.lib.utils2 import str2bool
82 from rhodecode.lib.utils2 import str2bool
83 test_cases = [
83 test_cases = [
84 ('t', True),
84 ('t', True),
85 ('true', True),
85 ('true', True),
86 ('y', True),
86 ('y', True),
87 ('yes', True),
87 ('yes', True),
88 ('on', True),
88 ('on', True),
89 ('1', True),
89 ('1', True),
90 ('Y', True),
90 ('Y', True),
91 ('yeS', True),
91 ('yeS', True),
92 ('Y', True),
92 ('Y', True),
93 ('TRUE', True),
93 ('TRUE', True),
94 ('T', True),
94 ('T', True),
95 ('False', False),
95 ('False', False),
96 ('F', False),
96 ('F', False),
97 ('FALSE', False),
97 ('FALSE', False),
98 ('0', False),
98 ('0', False),
99 ('-1', False),
99 ('-1', False),
100 ('', False), ]
100 ('', False), ]
101
101
102 for case in test_cases:
102 for case in test_cases:
103 self.assertEqual(str2bool(case[0]), case[1])
103 self.assertEqual(str2bool(case[0]), case[1])
104
104
105 def test_mention_extractor(self):
105 def test_mention_extractor(self):
106 from rhodecode.lib.utils2 import extract_mentioned_users
106 from rhodecode.lib.utils2 import extract_mentioned_users
107 sample = (
107 sample = (
108 "@first hi there @marcink here's my email marcin@email.com "
108 "@first hi there @marcink here's my email marcin@email.com "
109 "@lukaszb check @one_more22 it pls @ ttwelve @D[] @one@two@three "
109 "@lukaszb check @one_more22 it pls @ ttwelve @D[] @one@two@three "
110 "@MARCIN @maRCiN @2one_more22 @john please see this http://org.pl "
110 "@MARCIN @maRCiN @2one_more22 @john please see this http://org.pl "
111 "@marian.user just do it @marco-polo and next extract @marco_polo "
111 "@marian.user just do it @marco-polo and next extract @marco_polo "
112 "user.dot hej ! not-needed maril@domain.org"
112 "user.dot hej ! not-needed maril@domain.org"
113 )
113 )
114
114
115 s = sorted([
115 s = sorted([
116 'first', 'marcink', 'lukaszb', 'one_more22', 'MARCIN', 'maRCiN', 'john',
116 'first', 'marcink', 'lukaszb', 'one_more22', 'MARCIN', 'maRCiN', 'john',
117 'marian.user', 'marco-polo', 'marco_polo'
117 'marian.user', 'marco-polo', 'marco_polo'
118 ], key=lambda k: k.lower())
118 ], key=lambda k: k.lower())
119 self.assertEqual(s, extract_mentioned_users(sample))
119 self.assertEqual(s, extract_mentioned_users(sample))
120
120
121 def test_age(self):
121 def test_age(self):
122 import calendar
122 import calendar
123 from rhodecode.lib.utils2 import age
123 from rhodecode.lib.utils2 import age
124 n = datetime.datetime.now()
124 n = datetime.datetime.now()
125 delt = lambda *args, **kwargs: datetime.timedelta(*args, **kwargs)
125 delt = lambda *args, **kwargs: datetime.timedelta(*args, **kwargs)
126 self.assertEqual(age(n), u'just now')
126 self.assertEqual(age(n), u'just now')
127 self.assertEqual(age(n - delt(seconds=1)), u'1 second ago')
127 self.assertEqual(age(n - delt(seconds=1)), u'1 second ago')
128 self.assertEqual(age(n - delt(seconds=60 * 2)), u'2 minutes ago')
128 self.assertEqual(age(n - delt(seconds=60 * 2)), u'2 minutes ago')
129 self.assertEqual(age(n - delt(hours=1)), u'1 hour ago')
129 self.assertEqual(age(n - delt(hours=1)), u'1 hour ago')
130 self.assertEqual(age(n - delt(hours=24)), u'1 day ago')
130 self.assertEqual(age(n - delt(hours=24)), u'1 day ago')
131 self.assertEqual(age(n - delt(hours=24 * 5)), u'5 days ago')
131 self.assertEqual(age(n - delt(hours=24 * 5)), u'5 days ago')
132 self.assertEqual(age(n - delt(hours=24 * (calendar.mdays[n.month-1] + 2))),
132 self.assertEqual(age(n - delt(hours=24 * (calendar.mdays[n.month - 1] + 2))),
133 u'1 month and 2 days ago')
133 u'1 month and 2 days ago')
134 self.assertEqual(age(n - delt(hours=24 * 400)), u'1 year and 1 month ago')
134 self.assertEqual(age(n - delt(hours=24 * 400)), u'1 year and 1 month ago')
135
135
136 def test_age_in_future(self):
137 import calendar
138 from rhodecode.lib.utils2 import age
139 n = datetime.datetime.now()
140 delt = lambda *args, **kwargs: datetime.timedelta(*args, **kwargs)
141 self.assertEqual(age(n), u'just now')
142 self.assertEqual(age(n + delt(seconds=1)), u'in 1 second')
143 self.assertEqual(age(n + delt(seconds=60 * 2)), u'in 2 minutes')
144 self.assertEqual(age(n + delt(hours=1)), u'in 1 hour')
145 self.assertEqual(age(n + delt(hours=24)), u'in 1 day')
146 self.assertEqual(age(n + delt(hours=24 * 5)), u'in 5 days')
147 self.assertEqual(age(n + delt(hours=24 * (calendar.mdays[n.month - 1] + 2))),
148 u'in 1 month and 1 days')
149 self.assertEqual(age(n + delt(hours=24 * 400)), u'in 1 year and 1 month')
150
136 def test_tag_exctrator(self):
151 def test_tag_exctrator(self):
137 sample = (
152 sample = (
138 "hello pta[tag] gog [[]] [[] sda ero[or]d [me =>>< sa]"
153 "hello pta[tag] gog [[]] [[] sda ero[or]d [me =>>< sa]"
139 "[requires] [stale] [see<>=>] [see => http://url.com]"
154 "[requires] [stale] [see<>=>] [see => http://url.com]"
140 "[requires => url] [lang => python] [just a tag]"
155 "[requires => url] [lang => python] [just a tag]"
141 "[,d] [ => ULR ] [obsolete] [desc]]"
156 "[,d] [ => ULR ] [obsolete] [desc]]"
142 )
157 )
143 from rhodecode.lib.helpers import desc_stylize
158 from rhodecode.lib.helpers import desc_stylize
144 res = desc_stylize(sample)
159 res = desc_stylize(sample)
145 self.assertTrue('<div class="metatag" tag="tag">tag</div>' in res)
160 self.assertTrue('<div class="metatag" tag="tag">tag</div>' in res)
146 self.assertTrue('<div class="metatag" tag="obsolete">obsolete</div>' in res)
161 self.assertTrue('<div class="metatag" tag="obsolete">obsolete</div>' in res)
147 self.assertTrue('<div class="metatag" tag="stale">stale</div>' in res)
162 self.assertTrue('<div class="metatag" tag="stale">stale</div>' in res)
148 self.assertTrue('<div class="metatag" tag="lang">python</div>' in res)
163 self.assertTrue('<div class="metatag" tag="lang">python</div>' in res)
149 self.assertTrue('<div class="metatag" tag="requires">requires =&gt; <a href="/url">url</a></div>' in res)
164 self.assertTrue('<div class="metatag" tag="requires">requires =&gt; <a href="/url">url</a></div>' in res)
150 self.assertTrue('<div class="metatag" tag="tag">tag</div>' in res)
165 self.assertTrue('<div class="metatag" tag="tag">tag</div>' in res)
151
166
152 def test_alternative_gravatar(self):
167 def test_alternative_gravatar(self):
153 from rhodecode.lib.helpers import gravatar_url
168 from rhodecode.lib.helpers import gravatar_url
154 _md5 = lambda s: hashlib.md5(s).hexdigest()
169 _md5 = lambda s: hashlib.md5(s).hexdigest()
155
170
156 def fake_conf(**kwargs):
171 def fake_conf(**kwargs):
157 from pylons import config
172 from pylons import config
158 config['app_conf'] = {}
173 config['app_conf'] = {}
159 config['app_conf']['use_gravatar'] = True
174 config['app_conf']['use_gravatar'] = True
160 config['app_conf'].update(kwargs)
175 config['app_conf'].update(kwargs)
161 return config
176 return config
162
177
163 class fake_url():
178 class fake_url():
164 @classmethod
179 @classmethod
165 def current(cls, *args, **kwargs):
180 def current(cls, *args, **kwargs):
166 return 'https://server.com'
181 return 'https://server.com'
167
182
168 with mock.patch('pylons.url', fake_url):
183 with mock.patch('pylons.url', fake_url):
169 fake = fake_conf(alternative_gravatar_url='http://test.com/{email}')
184 fake = fake_conf(alternative_gravatar_url='http://test.com/{email}')
170 with mock.patch('pylons.config', fake):
185 with mock.patch('pylons.config', fake):
171 from pylons import url
186 from pylons import url
172 assert url.current() == 'https://server.com'
187 assert url.current() == 'https://server.com'
173 grav = gravatar_url(email_address='test@foo.com', size=24)
188 grav = gravatar_url(email_address='test@foo.com', size=24)
174 assert grav == 'http://test.com/test@foo.com'
189 assert grav == 'http://test.com/test@foo.com'
175
190
176 fake = fake_conf(alternative_gravatar_url='http://test.com/{email}')
191 fake = fake_conf(alternative_gravatar_url='http://test.com/{email}')
177 with mock.patch('pylons.config', fake):
192 with mock.patch('pylons.config', fake):
178 grav = gravatar_url(email_address='test@foo.com', size=24)
193 grav = gravatar_url(email_address='test@foo.com', size=24)
179 assert grav == 'http://test.com/test@foo.com'
194 assert grav == 'http://test.com/test@foo.com'
180
195
181 fake = fake_conf(alternative_gravatar_url='http://test.com/{md5email}')
196 fake = fake_conf(alternative_gravatar_url='http://test.com/{md5email}')
182 with mock.patch('pylons.config', fake):
197 with mock.patch('pylons.config', fake):
183 em = 'test@foo.com'
198 em = 'test@foo.com'
184 grav = gravatar_url(email_address=em, size=24)
199 grav = gravatar_url(email_address=em, size=24)
185 assert grav == 'http://test.com/%s' % (_md5(em))
200 assert grav == 'http://test.com/%s' % (_md5(em))
186
201
187 fake = fake_conf(alternative_gravatar_url='http://test.com/{md5email}/{size}')
202 fake = fake_conf(alternative_gravatar_url='http://test.com/{md5email}/{size}')
188 with mock.patch('pylons.config', fake):
203 with mock.patch('pylons.config', fake):
189 em = 'test@foo.com'
204 em = 'test@foo.com'
190 grav = gravatar_url(email_address=em, size=24)
205 grav = gravatar_url(email_address=em, size=24)
191 assert grav == 'http://test.com/%s/%s' % (_md5(em), 24)
206 assert grav == 'http://test.com/%s/%s' % (_md5(em), 24)
192
207
193 fake = fake_conf(alternative_gravatar_url='{scheme}://{netloc}/{md5email}/{size}')
208 fake = fake_conf(alternative_gravatar_url='{scheme}://{netloc}/{md5email}/{size}')
194 with mock.patch('pylons.config', fake):
209 with mock.patch('pylons.config', fake):
195 em = 'test@foo.com'
210 em = 'test@foo.com'
196 grav = gravatar_url(email_address=em, size=24)
211 grav = gravatar_url(email_address=em, size=24)
197 assert grav == 'https://server.com/%s/%s' % (_md5(em), 24)
212 assert grav == 'https://server.com/%s/%s' % (_md5(em), 24)
198
213
General Comments 0
You need to be logged in to leave comments. Login now