##// END OF EJS Templates
fixes #59, notifications for user registrations + some changes to mailer
marcink -
r689:ecc566f8 beta
parent child Browse files
Show More
@@ -1,175 +1,176 b''
1 ################################################################################
1 ################################################################################
2 ################################################################################
2 ################################################################################
3 # rhodecode - Pylons environment configuration #
3 # rhodecode - Pylons environment configuration #
4 # #
4 # #
5 # The %(here)s variable will be replaced with the parent directory of this file#
5 # The %(here)s variable will be replaced with the parent directory of this file#
6 ################################################################################
6 ################################################################################
7
7
8 [DEFAULT]
8 [DEFAULT]
9 debug = true
9 debug = true
10 ################################################################################
10 ################################################################################
11 ## Uncomment and replace with the address which should receive ##
11 ## Uncomment and replace with the address which should receive ##
12 ## any error reports after application crash ##
12 ## any error reports after application crash ##
13 ## Additionally those settings will be used by rhodecode mailing system ##
13 ## Additionally those settings will be used by rhodecode mailing system ##
14 ################################################################################
14 ################################################################################
15 #email_to = admin@localhost
15 #email_to = admin@localhost
16 #error_email_from = paste_error@localhost
16 #error_email_from = paste_error@localhost
17 #app_email_from = rhodecode-noreply@localhost
17 #app_email_from = rhodecode-noreply@localhost
18 #error_message =
18 #error_message =
19
19
20 #smtp_server = mail.server.com
20 #smtp_server = mail.server.com
21 #smtp_username =
21 #smtp_username =
22 #smtp_password =
22 #smtp_password =
23 #smtp_port =
23 #smtp_port =
24 #smtp_use_tls = false
24 #smtp_use_tls = false
25 #smtp_use_ssl = true
25
26
26 [server:main]
27 [server:main]
27 ##nr of threads to spawn
28 ##nr of threads to spawn
28 threadpool_workers = 5
29 threadpool_workers = 5
29
30
30 ##max request before thread respawn
31 ##max request before thread respawn
31 threadpool_max_requests = 2
32 threadpool_max_requests = 2
32
33
33 ##option to use threads of process
34 ##option to use threads of process
34 use_threadpool = true
35 use_threadpool = true
35
36
36 use = egg:Paste#http
37 use = egg:Paste#http
37 host = 127.0.0.1
38 host = 127.0.0.1
38 port = 8001
39 port = 8001
39
40
40 [app:main]
41 [app:main]
41 use = egg:rhodecode
42 use = egg:rhodecode
42 full_stack = true
43 full_stack = true
43 static_files = false
44 static_files = false
44 lang=en
45 lang=en
45 cache_dir = %(here)s/data
46 cache_dir = %(here)s/data
46 index_dir = %(here)s/data/index
47 index_dir = %(here)s/data/index
47
48
48 ####################################
49 ####################################
49 ### BEAKER CACHE ####
50 ### BEAKER CACHE ####
50 ####################################
51 ####################################
51 beaker.cache.data_dir=/%(here)s/data/cache/data
52 beaker.cache.data_dir=/%(here)s/data/cache/data
52 beaker.cache.lock_dir=/%(here)s/data/cache/lock
53 beaker.cache.lock_dir=/%(here)s/data/cache/lock
53 beaker.cache.regions=super_short_term,short_term,long_term,sql_cache_short,sql_cache_med,sql_cache_long
54 beaker.cache.regions=super_short_term,short_term,long_term,sql_cache_short,sql_cache_med,sql_cache_long
54
55
55 beaker.cache.super_short_term.type=memory
56 beaker.cache.super_short_term.type=memory
56 beaker.cache.super_short_term.expire=10
57 beaker.cache.super_short_term.expire=10
57
58
58 beaker.cache.short_term.type=memory
59 beaker.cache.short_term.type=memory
59 beaker.cache.short_term.expire=60
60 beaker.cache.short_term.expire=60
60
61
61 beaker.cache.long_term.type=memory
62 beaker.cache.long_term.type=memory
62 beaker.cache.long_term.expire=36000
63 beaker.cache.long_term.expire=36000
63
64
64
65
65 beaker.cache.sql_cache_short.type=memory
66 beaker.cache.sql_cache_short.type=memory
66 beaker.cache.sql_cache_short.expire=5
67 beaker.cache.sql_cache_short.expire=5
67
68
68 beaker.cache.sql_cache_med.type=memory
69 beaker.cache.sql_cache_med.type=memory
69 beaker.cache.sql_cache_med.expire=360
70 beaker.cache.sql_cache_med.expire=360
70
71
71 beaker.cache.sql_cache_long.type=file
72 beaker.cache.sql_cache_long.type=file
72 beaker.cache.sql_cache_long.expire=3600
73 beaker.cache.sql_cache_long.expire=3600
73
74
74 ####################################
75 ####################################
75 ### BEAKER SESSION ####
76 ### BEAKER SESSION ####
76 ####################################
77 ####################################
77 ## Type of storage used for the session, current types are
78 ## Type of storage used for the session, current types are
78 ## dbm, file, memcached, database, and memory.
79 ## dbm, file, memcached, database, and memory.
79 ## The storage uses the Container API
80 ## The storage uses the Container API
80 ##that is also used by the cache system.
81 ##that is also used by the cache system.
81 beaker.session.type = file
82 beaker.session.type = file
82
83
83 beaker.session.key = rhodecode
84 beaker.session.key = rhodecode
84 beaker.session.secret = g654dcno0-9873jhgfreyu
85 beaker.session.secret = g654dcno0-9873jhgfreyu
85 beaker.session.timeout = 36000
86 beaker.session.timeout = 36000
86
87
87 ##auto save the session to not to use .save()
88 ##auto save the session to not to use .save()
88 beaker.session.auto = False
89 beaker.session.auto = False
89
90
90 ##true exire at browser close
91 ##true exire at browser close
91 #beaker.session.cookie_expires = 3600
92 #beaker.session.cookie_expires = 3600
92
93
93
94
94 ################################################################################
95 ################################################################################
95 ## WARNING: *THE LINE BELOW MUST BE UNCOMMENTED ON A PRODUCTION ENVIRONMENT* ##
96 ## WARNING: *THE LINE BELOW MUST BE UNCOMMENTED ON A PRODUCTION ENVIRONMENT* ##
96 ## Debug mode will enable the interactive debugging tool, allowing ANYONE to ##
97 ## Debug mode will enable the interactive debugging tool, allowing ANYONE to ##
97 ## execute malicious code after an exception is raised. ##
98 ## execute malicious code after an exception is raised. ##
98 ################################################################################
99 ################################################################################
99 set debug = false
100 set debug = false
100
101
101 ##################################
102 ##################################
102 ### LOGVIEW CONFIG ###
103 ### LOGVIEW CONFIG ###
103 ##################################
104 ##################################
104 logview.sqlalchemy = #faa
105 logview.sqlalchemy = #faa
105 logview.pylons.templating = #bfb
106 logview.pylons.templating = #bfb
106 logview.pylons.util = #eee
107 logview.pylons.util = #eee
107
108
108 #########################################################
109 #########################################################
109 ### DB CONFIGS - EACH DB WILL HAVE IT'S OWN CONFIG ###
110 ### DB CONFIGS - EACH DB WILL HAVE IT'S OWN CONFIG ###
110 #########################################################
111 #########################################################
111 sqlalchemy.db1.url = sqlite:///%(here)s/rhodecode.db
112 sqlalchemy.db1.url = sqlite:///%(here)s/rhodecode.db
112 #sqlalchemy.db1.echo = False
113 #sqlalchemy.db1.echo = False
113 #sqlalchemy.db1.pool_recycle = 3600
114 #sqlalchemy.db1.pool_recycle = 3600
114 sqlalchemy.convert_unicode = true
115 sqlalchemy.convert_unicode = true
115
116
116 ################################
117 ################################
117 ### LOGGING CONFIGURATION ####
118 ### LOGGING CONFIGURATION ####
118 ################################
119 ################################
119 [loggers]
120 [loggers]
120 keys = root, routes, rhodecode, sqlalchemy
121 keys = root, routes, rhodecode, sqlalchemy
121
122
122 [handlers]
123 [handlers]
123 keys = console
124 keys = console
124
125
125 [formatters]
126 [formatters]
126 keys = generic,color_formatter
127 keys = generic,color_formatter
127
128
128 #############
129 #############
129 ## LOGGERS ##
130 ## LOGGERS ##
130 #############
131 #############
131 [logger_root]
132 [logger_root]
132 level = INFO
133 level = INFO
133 handlers = console
134 handlers = console
134
135
135 [logger_routes]
136 [logger_routes]
136 level = INFO
137 level = INFO
137 handlers = console
138 handlers = console
138 qualname = routes.middleware
139 qualname = routes.middleware
139 # "level = DEBUG" logs the route matched and routing variables.
140 # "level = DEBUG" logs the route matched and routing variables.
140 propagate = 0
141 propagate = 0
141
142
142 [logger_rhodecode]
143 [logger_rhodecode]
143 level = DEBUG
144 level = DEBUG
144 handlers = console
145 handlers = console
145 qualname = rhodecode
146 qualname = rhodecode
146 propagate = 0
147 propagate = 0
147
148
148 [logger_sqlalchemy]
149 [logger_sqlalchemy]
149 level = ERROR
150 level = ERROR
150 handlers = console
151 handlers = console
151 qualname = sqlalchemy.engine
152 qualname = sqlalchemy.engine
152 propagate = 0
153 propagate = 0
153
154
154 ##############
155 ##############
155 ## HANDLERS ##
156 ## HANDLERS ##
156 ##############
157 ##############
157
158
158 [handler_console]
159 [handler_console]
159 class = StreamHandler
160 class = StreamHandler
160 args = (sys.stderr,)
161 args = (sys.stderr,)
161 level = NOTSET
162 level = NOTSET
162 formatter = color_formatter
163 formatter = color_formatter
163
164
164 ################
165 ################
165 ## FORMATTERS ##
166 ## FORMATTERS ##
166 ################
167 ################
167
168
168 [formatter_generic]
169 [formatter_generic]
169 format = %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
170 format = %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
170 datefmt = %Y-%m-%d %H:%M:%S
171 datefmt = %Y-%m-%d %H:%M:%S
171
172
172 [formatter_color_formatter]
173 [formatter_color_formatter]
173 class=rhodecode.lib.colored_formatter.ColorFormatter
174 class=rhodecode.lib.colored_formatter.ColorFormatter
174 format= %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
175 format= %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
175 datefmt = %Y-%m-%d %H:%M:%S No newline at end of file
176 datefmt = %Y-%m-%d %H:%M:%S
@@ -1,175 +1,176 b''
1 ################################################################################
1 ################################################################################
2 ################################################################################
2 ################################################################################
3 # rhodecode - Pylons environment configuration #
3 # rhodecode - Pylons environment configuration #
4 # #
4 # #
5 # The %(here)s variable will be replaced with the parent directory of this file#
5 # The %(here)s variable will be replaced with the parent directory of this file#
6 ################################################################################
6 ################################################################################
7
7
8 [DEFAULT]
8 [DEFAULT]
9 debug = true
9 debug = true
10 ################################################################################
10 ################################################################################
11 ## Uncomment and replace with the address which should receive ##
11 ## Uncomment and replace with the address which should receive ##
12 ## any error reports after application crash ##
12 ## any error reports after application crash ##
13 ## Additionally those settings will be used by rhodecode mailing system ##
13 ## Additionally those settings will be used by rhodecode mailing system ##
14 ################################################################################
14 ################################################################################
15 #email_to = admin@localhost
15 #email_to = admin@localhost
16 #error_email_from = paste_error@localhost
16 #error_email_from = paste_error@localhost
17 #app_email_from = rhodecode-noreply@localhost
17 #app_email_from = rhodecode-noreply@localhost
18 #error_message =
18 #error_message =
19
19
20 #smtp_server = mail.server.com
20 #smtp_server = mail.server.com
21 #smtp_username =
21 #smtp_username =
22 #smtp_password =
22 #smtp_password =
23 #smtp_port =
23 #smtp_port =
24 #smtp_use_tls = false
24 #smtp_use_tls = false
25 #smtp_use_ssl = true
25
26
26 [server:main]
27 [server:main]
27 ##nr of threads to spawn
28 ##nr of threads to spawn
28 threadpool_workers = 5
29 threadpool_workers = 5
29
30
30 ##max request before thread respawn
31 ##max request before thread respawn
31 threadpool_max_requests = 10
32 threadpool_max_requests = 10
32
33
33 ##option to use threads of process
34 ##option to use threads of process
34 use_threadpool = true
35 use_threadpool = true
35
36
36 use = egg:Paste#http
37 use = egg:Paste#http
37 host = 127.0.0.1
38 host = 127.0.0.1
38 port = 5000
39 port = 5000
39
40
40 [app:main]
41 [app:main]
41 use = egg:rhodecode
42 use = egg:rhodecode
42 full_stack = true
43 full_stack = true
43 static_files = true
44 static_files = true
44 lang=en
45 lang=en
45 cache_dir = %(here)s/data
46 cache_dir = %(here)s/data
46 index_dir = %(here)s/data/index
47 index_dir = %(here)s/data/index
47 app_instance_uuid = ${app_instance_uuid}
48 app_instance_uuid = ${app_instance_uuid}
48
49
49 ####################################
50 ####################################
50 ### BEAKER CACHE ####
51 ### BEAKER CACHE ####
51 ####################################
52 ####################################
52 beaker.cache.data_dir=/%(here)s/data/cache/data
53 beaker.cache.data_dir=/%(here)s/data/cache/data
53 beaker.cache.lock_dir=/%(here)s/data/cache/lock
54 beaker.cache.lock_dir=/%(here)s/data/cache/lock
54 beaker.cache.regions=super_short_term,short_term,long_term,sql_cache_short,sql_cache_med,sql_cache_long
55 beaker.cache.regions=super_short_term,short_term,long_term,sql_cache_short,sql_cache_med,sql_cache_long
55
56
56 beaker.cache.super_short_term.type=memory
57 beaker.cache.super_short_term.type=memory
57 beaker.cache.super_short_term.expire=10
58 beaker.cache.super_short_term.expire=10
58
59
59 beaker.cache.short_term.type=memory
60 beaker.cache.short_term.type=memory
60 beaker.cache.short_term.expire=60
61 beaker.cache.short_term.expire=60
61
62
62 beaker.cache.long_term.type=memory
63 beaker.cache.long_term.type=memory
63 beaker.cache.long_term.expire=36000
64 beaker.cache.long_term.expire=36000
64
65
65 beaker.cache.sql_cache_short.type=memory
66 beaker.cache.sql_cache_short.type=memory
66 beaker.cache.sql_cache_short.expire=5
67 beaker.cache.sql_cache_short.expire=5
67
68
68 beaker.cache.sql_cache_med.type=memory
69 beaker.cache.sql_cache_med.type=memory
69 beaker.cache.sql_cache_med.expire=360
70 beaker.cache.sql_cache_med.expire=360
70
71
71 beaker.cache.sql_cache_long.type=file
72 beaker.cache.sql_cache_long.type=file
72 beaker.cache.sql_cache_long.expire=3600
73 beaker.cache.sql_cache_long.expire=3600
73
74
74 ####################################
75 ####################################
75 ### BEAKER SESSION ####
76 ### BEAKER SESSION ####
76 ####################################
77 ####################################
77 ## Type of storage used for the session, current types are
78 ## Type of storage used for the session, current types are
78 ## dbm, file, memcached, database, and memory.
79 ## dbm, file, memcached, database, and memory.
79 ## The storage uses the Container API
80 ## The storage uses the Container API
80 ##that is also used by the cache system.
81 ##that is also used by the cache system.
81 beaker.session.type = file
82 beaker.session.type = file
82
83
83 beaker.session.key = rhodecode
84 beaker.session.key = rhodecode
84 beaker.session.secret = ${app_instance_secret}
85 beaker.session.secret = ${app_instance_secret}
85 beaker.session.timeout = 36000
86 beaker.session.timeout = 36000
86
87
87 ##auto save the session to not to use .save()
88 ##auto save the session to not to use .save()
88 beaker.session.auto = False
89 beaker.session.auto = False
89
90
90 ##true exire at browser close
91 ##true exire at browser close
91 #beaker.session.cookie_expires = 3600
92 #beaker.session.cookie_expires = 3600
92
93
93
94
94 ################################################################################
95 ################################################################################
95 ## WARNING: *THE LINE BELOW MUST BE UNCOMMENTED ON A PRODUCTION ENVIRONMENT* ##
96 ## WARNING: *THE LINE BELOW MUST BE UNCOMMENTED ON A PRODUCTION ENVIRONMENT* ##
96 ## Debug mode will enable the interactive debugging tool, allowing ANYONE to ##
97 ## Debug mode will enable the interactive debugging tool, allowing ANYONE to ##
97 ## execute malicious code after an exception is raised. ##
98 ## execute malicious code after an exception is raised. ##
98 ################################################################################
99 ################################################################################
99 set debug = false
100 set debug = false
100
101
101 ##################################
102 ##################################
102 ### LOGVIEW CONFIG ###
103 ### LOGVIEW CONFIG ###
103 ##################################
104 ##################################
104 logview.sqlalchemy = #faa
105 logview.sqlalchemy = #faa
105 logview.pylons.templating = #bfb
106 logview.pylons.templating = #bfb
106 logview.pylons.util = #eee
107 logview.pylons.util = #eee
107
108
108 #########################################################
109 #########################################################
109 ### DB CONFIGS - EACH DB WILL HAVE IT'S OWN CONFIG ###
110 ### DB CONFIGS - EACH DB WILL HAVE IT'S OWN CONFIG ###
110 #########################################################
111 #########################################################
111 sqlalchemy.db1.url = sqlite:///%(here)s/rhodecode.db
112 sqlalchemy.db1.url = sqlite:///%(here)s/rhodecode.db
112 #sqlalchemy.db1.echo = False
113 #sqlalchemy.db1.echo = False
113 #sqlalchemy.db1.pool_recycle = 3600
114 #sqlalchemy.db1.pool_recycle = 3600
114 sqlalchemy.convert_unicode = true
115 sqlalchemy.convert_unicode = true
115
116
116 ################################
117 ################################
117 ### LOGGING CONFIGURATION ####
118 ### LOGGING CONFIGURATION ####
118 ################################
119 ################################
119 [loggers]
120 [loggers]
120 keys = root, routes, rhodecode, sqlalchemy
121 keys = root, routes, rhodecode, sqlalchemy
121
122
122 [handlers]
123 [handlers]
123 keys = console
124 keys = console
124
125
125 [formatters]
126 [formatters]
126 keys = generic,color_formatter
127 keys = generic,color_formatter
127
128
128 #############
129 #############
129 ## LOGGERS ##
130 ## LOGGERS ##
130 #############
131 #############
131 [logger_root]
132 [logger_root]
132 level = INFO
133 level = INFO
133 handlers = console
134 handlers = console
134
135
135 [logger_routes]
136 [logger_routes]
136 level = INFO
137 level = INFO
137 handlers = console
138 handlers = console
138 qualname = routes.middleware
139 qualname = routes.middleware
139 # "level = DEBUG" logs the route matched and routing variables.
140 # "level = DEBUG" logs the route matched and routing variables.
140 propagate = 0
141 propagate = 0
141
142
142 [logger_rhodecode]
143 [logger_rhodecode]
143 level = DEBUG
144 level = DEBUG
144 handlers = console
145 handlers = console
145 qualname = rhodecode
146 qualname = rhodecode
146 propagate = 0
147 propagate = 0
147
148
148 [logger_sqlalchemy]
149 [logger_sqlalchemy]
149 level = ERROR
150 level = ERROR
150 handlers = console
151 handlers = console
151 qualname = sqlalchemy.engine
152 qualname = sqlalchemy.engine
152 propagate = 0
153 propagate = 0
153
154
154 ##############
155 ##############
155 ## HANDLERS ##
156 ## HANDLERS ##
156 ##############
157 ##############
157
158
158 [handler_console]
159 [handler_console]
159 class = StreamHandler
160 class = StreamHandler
160 args = (sys.stderr,)
161 args = (sys.stderr,)
161 level = NOTSET
162 level = NOTSET
162 formatter = color_formatter
163 formatter = color_formatter
163
164
164 ################
165 ################
165 ## FORMATTERS ##
166 ## FORMATTERS ##
166 ################
167 ################
167
168
168 [formatter_generic]
169 [formatter_generic]
169 format = %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
170 format = %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
170 datefmt = %Y-%m-%d %H:%M:%S
171 datefmt = %Y-%m-%d %H:%M:%S
171
172
172 [formatter_color_formatter]
173 [formatter_color_formatter]
173 class=rhodecode.lib.colored_formatter.ColorFormatter
174 class=rhodecode.lib.colored_formatter.ColorFormatter
174 format= %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
175 format= %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
175 datefmt = %Y-%m-%d %H:%M:%S No newline at end of file
176 datefmt = %Y-%m-%d %H:%M:%S
@@ -1,299 +1,315 b''
1 from celery.decorators import task
1 from celery.decorators import task
2
2
3 from operator import itemgetter
3 from operator import itemgetter
4 from pylons.i18n.translation import _
4 from pylons.i18n.translation import _
5 from rhodecode.lib.celerylib import run_task, locked_task
5 from rhodecode.lib.celerylib import run_task, locked_task
6 from rhodecode.lib.helpers import person
6 from rhodecode.lib.helpers import person
7 from rhodecode.lib.smtp_mailer import SmtpMailer
7 from rhodecode.lib.smtp_mailer import SmtpMailer
8 from rhodecode.lib.utils import OrderedDict
8 from rhodecode.lib.utils import OrderedDict
9 from time import mktime
9 from time import mktime
10 import os
10 import os
11 import traceback
11 import traceback
12 from vcs.backends import get_repo
12 from vcs.backends import get_repo
13 from rhodecode.model.hg import HgModel
13 from rhodecode.model.hg import HgModel
14 try:
14 try:
15 import json
15 import json
16 except ImportError:
16 except ImportError:
17 #python 2.5 compatibility
17 #python 2.5 compatibility
18 import simplejson as json
18 import simplejson as json
19
19
20 try:
20 try:
21 from celeryconfig import PYLONS_CONFIG as config
21 from celeryconfig import PYLONS_CONFIG as config
22 celery_on = True
22 celery_on = True
23 except ImportError:
23 except ImportError:
24 #if celeryconfig is not present let's just load our pylons
24 #if celeryconfig is not present let's just load our pylons
25 #config instead
25 #config instead
26 from pylons import config
26 from pylons import config
27 celery_on = False
27 celery_on = False
28
28
29
29
30 __all__ = ['whoosh_index', 'get_commits_stats',
30 __all__ = ['whoosh_index', 'get_commits_stats',
31 'reset_user_password', 'send_email']
31 'reset_user_password', 'send_email']
32
32
33 def get_session():
33 def get_session():
34 if celery_on:
34 if celery_on:
35 from sqlalchemy import engine_from_config
35 from sqlalchemy import engine_from_config
36 from sqlalchemy.orm import sessionmaker, scoped_session
36 from sqlalchemy.orm import sessionmaker, scoped_session
37 engine = engine_from_config(dict(config.items('app:main')),
37 engine = engine_from_config(dict(config.items('app:main')),
38 'sqlalchemy.db1.')
38 'sqlalchemy.db1.')
39 sa = scoped_session(sessionmaker(bind=engine))
39 sa = scoped_session(sessionmaker(bind=engine))
40 else:
40 else:
41 #If we don't use celery reuse our current application Session
41 #If we don't use celery reuse our current application Session
42 from rhodecode.model.meta import Session
42 from rhodecode.model.meta import Session
43 sa = Session()
43 sa = Session()
44
44
45 return sa
45 return sa
46
46
47 @task
47 @task
48 @locked_task
48 @locked_task
49 def whoosh_index(repo_location, full_index):
49 def whoosh_index(repo_location, full_index):
50 log = whoosh_index.get_logger()
50 log = whoosh_index.get_logger()
51 from rhodecode.lib.indexers.daemon import WhooshIndexingDaemon
51 from rhodecode.lib.indexers.daemon import WhooshIndexingDaemon
52 index_location = dict(config.items('app:main'))['index_dir']
52 index_location = dict(config.items('app:main'))['index_dir']
53 WhooshIndexingDaemon(index_location=index_location,
53 WhooshIndexingDaemon(index_location=index_location,
54 repo_location=repo_location).run(full_index=full_index)
54 repo_location=repo_location).run(full_index=full_index)
55
55
56 @task
56 @task
57 @locked_task
57 @locked_task
58 def get_commits_stats(repo_name, ts_min_y, ts_max_y):
58 def get_commits_stats(repo_name, ts_min_y, ts_max_y):
59 from rhodecode.model.db import Statistics, Repository
59 from rhodecode.model.db import Statistics, Repository
60 log = get_commits_stats.get_logger()
60 log = get_commits_stats.get_logger()
61 author_key_cleaner = lambda k: person(k).replace('"', "") #for js data compatibilty
61 author_key_cleaner = lambda k: person(k).replace('"', "") #for js data compatibilty
62
62
63 commits_by_day_author_aggregate = {}
63 commits_by_day_author_aggregate = {}
64 commits_by_day_aggregate = {}
64 commits_by_day_aggregate = {}
65 repos_path = HgModel().repos_path
65 repos_path = HgModel().repos_path
66 p = os.path.join(repos_path, repo_name)
66 p = os.path.join(repos_path, repo_name)
67 repo = get_repo(p)
67 repo = get_repo(p)
68
68
69 skip_date_limit = True
69 skip_date_limit = True
70 parse_limit = 250 #limit for single task changeset parsing optimal for
70 parse_limit = 250 #limit for single task changeset parsing optimal for
71 last_rev = 0
71 last_rev = 0
72 last_cs = None
72 last_cs = None
73 timegetter = itemgetter('time')
73 timegetter = itemgetter('time')
74
74
75 sa = get_session()
75 sa = get_session()
76
76
77 dbrepo = sa.query(Repository)\
77 dbrepo = sa.query(Repository)\
78 .filter(Repository.repo_name == repo_name).scalar()
78 .filter(Repository.repo_name == repo_name).scalar()
79 cur_stats = sa.query(Statistics)\
79 cur_stats = sa.query(Statistics)\
80 .filter(Statistics.repository == dbrepo).scalar()
80 .filter(Statistics.repository == dbrepo).scalar()
81 if cur_stats:
81 if cur_stats:
82 last_rev = cur_stats.stat_on_revision
82 last_rev = cur_stats.stat_on_revision
83 if not repo.revisions:
83 if not repo.revisions:
84 return True
84 return True
85
85
86 if last_rev == repo.revisions[-1] and len(repo.revisions) > 1:
86 if last_rev == repo.revisions[-1] and len(repo.revisions) > 1:
87 #pass silently without any work if we're not on first revision or current
87 #pass silently without any work if we're not on first revision or current
88 #state of parsing revision(from db marker) is the last revision
88 #state of parsing revision(from db marker) is the last revision
89 return True
89 return True
90
90
91 if cur_stats:
91 if cur_stats:
92 commits_by_day_aggregate = OrderedDict(
92 commits_by_day_aggregate = OrderedDict(
93 json.loads(
93 json.loads(
94 cur_stats.commit_activity_combined))
94 cur_stats.commit_activity_combined))
95 commits_by_day_author_aggregate = json.loads(cur_stats.commit_activity)
95 commits_by_day_author_aggregate = json.loads(cur_stats.commit_activity)
96
96
97 log.debug('starting parsing %s', parse_limit)
97 log.debug('starting parsing %s', parse_limit)
98 lmktime = mktime
98 lmktime = mktime
99
99
100 for cnt, rev in enumerate(repo.revisions[last_rev:]):
100 for cnt, rev in enumerate(repo.revisions[last_rev:]):
101 last_cs = cs = repo.get_changeset(rev)
101 last_cs = cs = repo.get_changeset(rev)
102 k = '%s-%s-%s' % (cs.date.timetuple()[0], cs.date.timetuple()[1],
102 k = '%s-%s-%s' % (cs.date.timetuple()[0], cs.date.timetuple()[1],
103 cs.date.timetuple()[2])
103 cs.date.timetuple()[2])
104 timetupple = [int(x) for x in k.split('-')]
104 timetupple = [int(x) for x in k.split('-')]
105 timetupple.extend([0 for _ in xrange(6)])
105 timetupple.extend([0 for _ in xrange(6)])
106 k = lmktime(timetupple)
106 k = lmktime(timetupple)
107 if commits_by_day_author_aggregate.has_key(author_key_cleaner(cs.author)):
107 if commits_by_day_author_aggregate.has_key(author_key_cleaner(cs.author)):
108 try:
108 try:
109 l = [timegetter(x) for x in commits_by_day_author_aggregate\
109 l = [timegetter(x) for x in commits_by_day_author_aggregate\
110 [author_key_cleaner(cs.author)]['data']]
110 [author_key_cleaner(cs.author)]['data']]
111 time_pos = l.index(k)
111 time_pos = l.index(k)
112 except ValueError:
112 except ValueError:
113 time_pos = False
113 time_pos = False
114
114
115 if time_pos >= 0 and time_pos is not False:
115 if time_pos >= 0 and time_pos is not False:
116
116
117 datadict = commits_by_day_author_aggregate\
117 datadict = commits_by_day_author_aggregate\
118 [author_key_cleaner(cs.author)]['data'][time_pos]
118 [author_key_cleaner(cs.author)]['data'][time_pos]
119
119
120 datadict["commits"] += 1
120 datadict["commits"] += 1
121 datadict["added"] += len(cs.added)
121 datadict["added"] += len(cs.added)
122 datadict["changed"] += len(cs.changed)
122 datadict["changed"] += len(cs.changed)
123 datadict["removed"] += len(cs.removed)
123 datadict["removed"] += len(cs.removed)
124
124
125 else:
125 else:
126 if k >= ts_min_y and k <= ts_max_y or skip_date_limit:
126 if k >= ts_min_y and k <= ts_max_y or skip_date_limit:
127
127
128 datadict = {"time":k,
128 datadict = {"time":k,
129 "commits":1,
129 "commits":1,
130 "added":len(cs.added),
130 "added":len(cs.added),
131 "changed":len(cs.changed),
131 "changed":len(cs.changed),
132 "removed":len(cs.removed),
132 "removed":len(cs.removed),
133 }
133 }
134 commits_by_day_author_aggregate\
134 commits_by_day_author_aggregate\
135 [author_key_cleaner(cs.author)]['data'].append(datadict)
135 [author_key_cleaner(cs.author)]['data'].append(datadict)
136
136
137 else:
137 else:
138 if k >= ts_min_y and k <= ts_max_y or skip_date_limit:
138 if k >= ts_min_y and k <= ts_max_y or skip_date_limit:
139 commits_by_day_author_aggregate[author_key_cleaner(cs.author)] = {
139 commits_by_day_author_aggregate[author_key_cleaner(cs.author)] = {
140 "label":author_key_cleaner(cs.author),
140 "label":author_key_cleaner(cs.author),
141 "data":[{"time":k,
141 "data":[{"time":k,
142 "commits":1,
142 "commits":1,
143 "added":len(cs.added),
143 "added":len(cs.added),
144 "changed":len(cs.changed),
144 "changed":len(cs.changed),
145 "removed":len(cs.removed),
145 "removed":len(cs.removed),
146 }],
146 }],
147 "schema":["commits"],
147 "schema":["commits"],
148 }
148 }
149
149
150 #gather all data by day
150 #gather all data by day
151 if commits_by_day_aggregate.has_key(k):
151 if commits_by_day_aggregate.has_key(k):
152 commits_by_day_aggregate[k] += 1
152 commits_by_day_aggregate[k] += 1
153 else:
153 else:
154 commits_by_day_aggregate[k] = 1
154 commits_by_day_aggregate[k] = 1
155
155
156 if cnt >= parse_limit:
156 if cnt >= parse_limit:
157 #don't fetch to much data since we can freeze application
157 #don't fetch to much data since we can freeze application
158 break
158 break
159 overview_data = []
159 overview_data = []
160 for k, v in commits_by_day_aggregate.items():
160 for k, v in commits_by_day_aggregate.items():
161 overview_data.append([k, v])
161 overview_data.append([k, v])
162 overview_data = sorted(overview_data, key=itemgetter(0))
162 overview_data = sorted(overview_data, key=itemgetter(0))
163 if not commits_by_day_author_aggregate:
163 if not commits_by_day_author_aggregate:
164 commits_by_day_author_aggregate[author_key_cleaner(repo.contact)] = {
164 commits_by_day_author_aggregate[author_key_cleaner(repo.contact)] = {
165 "label":author_key_cleaner(repo.contact),
165 "label":author_key_cleaner(repo.contact),
166 "data":[0, 1],
166 "data":[0, 1],
167 "schema":["commits"],
167 "schema":["commits"],
168 }
168 }
169
169
170 stats = cur_stats if cur_stats else Statistics()
170 stats = cur_stats if cur_stats else Statistics()
171 stats.commit_activity = json.dumps(commits_by_day_author_aggregate)
171 stats.commit_activity = json.dumps(commits_by_day_author_aggregate)
172 stats.commit_activity_combined = json.dumps(overview_data)
172 stats.commit_activity_combined = json.dumps(overview_data)
173
173
174 log.debug('last revison %s', last_rev)
174 log.debug('last revison %s', last_rev)
175 leftovers = len(repo.revisions[last_rev:])
175 leftovers = len(repo.revisions[last_rev:])
176 log.debug('revisions to parse %s', leftovers)
176 log.debug('revisions to parse %s', leftovers)
177
177
178 if last_rev == 0 or leftovers < parse_limit:
178 if last_rev == 0 or leftovers < parse_limit:
179 stats.languages = json.dumps(__get_codes_stats(repo_name))
179 stats.languages = json.dumps(__get_codes_stats(repo_name))
180
180
181 stats.repository = dbrepo
181 stats.repository = dbrepo
182 stats.stat_on_revision = last_cs.revision
182 stats.stat_on_revision = last_cs.revision
183
183
184 try:
184 try:
185 sa.add(stats)
185 sa.add(stats)
186 sa.commit()
186 sa.commit()
187 except:
187 except:
188 log.error(traceback.format_exc())
188 log.error(traceback.format_exc())
189 sa.rollback()
189 sa.rollback()
190 return False
190 return False
191 if len(repo.revisions) > 1:
191 if len(repo.revisions) > 1:
192 run_task(get_commits_stats, repo_name, ts_min_y, ts_max_y)
192 run_task(get_commits_stats, repo_name, ts_min_y, ts_max_y)
193
193
194 return True
194 return True
195
195
196 @task
196 @task
197 def reset_user_password(user_email):
197 def reset_user_password(user_email):
198 log = reset_user_password.get_logger()
198 log = reset_user_password.get_logger()
199 from rhodecode.lib import auth
199 from rhodecode.lib import auth
200 from rhodecode.model.db import User
200 from rhodecode.model.db import User
201
201
202 try:
202 try:
203 try:
203 try:
204 sa = get_session()
204 sa = get_session()
205 user = sa.query(User).filter(User.email == user_email).scalar()
205 user = sa.query(User).filter(User.email == user_email).scalar()
206 new_passwd = auth.PasswordGenerator().gen_password(8,
206 new_passwd = auth.PasswordGenerator().gen_password(8,
207 auth.PasswordGenerator.ALPHABETS_BIG_SMALL)
207 auth.PasswordGenerator.ALPHABETS_BIG_SMALL)
208 if user:
208 if user:
209 user.password = auth.get_crypt_password(new_passwd)
209 user.password = auth.get_crypt_password(new_passwd)
210 sa.add(user)
210 sa.add(user)
211 sa.commit()
211 sa.commit()
212 log.info('change password for %s', user_email)
212 log.info('change password for %s', user_email)
213 if new_passwd is None:
213 if new_passwd is None:
214 raise Exception('unable to generate new password')
214 raise Exception('unable to generate new password')
215
215
216 except:
216 except:
217 log.error(traceback.format_exc())
217 log.error(traceback.format_exc())
218 sa.rollback()
218 sa.rollback()
219
219
220 run_task(send_email, user_email,
220 run_task(send_email, user_email,
221 "Your new rhodecode password",
221 "Your new rhodecode password",
222 'Your new rhodecode password:%s' % (new_passwd))
222 'Your new rhodecode password:%s' % (new_passwd))
223 log.info('send new password mail to %s', user_email)
223 log.info('send new password mail to %s', user_email)
224
224
225
225
226 except:
226 except:
227 log.error('Failed to update user password')
227 log.error('Failed to update user password')
228 log.error(traceback.format_exc())
228 log.error(traceback.format_exc())
229 return True
229 return True
230
230
231 @task
231 @task
232 def send_email(recipients, subject, body):
232 def send_email(recipients, subject, body):
233 """
234 Sends an email with defined parameters from the .ini files.
235
236
237 :param recipients: list of recipients, it this is empty the defined email
238 address from field 'email_to' is used instead
239 :param subject: subject of the mail
240 :param body: body of the mail
241 """
233 log = send_email.get_logger()
242 log = send_email.get_logger()
234 email_config = dict(config.items('DEFAULT'))
243 email_config = dict(config.items('DEFAULT'))
244
245 if not recipients:
246 recipients = [email_config.get('email_to')]
247
248 def str2bool(v):
249 return v.lower() in ["yes", "true", "t", "1"]
250
235 mail_from = email_config.get('app_email_from')
251 mail_from = email_config.get('app_email_from')
236 user = email_config.get('smtp_username')
252 user = email_config.get('smtp_username')
237 passwd = email_config.get('smtp_password')
253 passwd = email_config.get('smtp_password')
238 mail_server = email_config.get('smtp_server')
254 mail_server = email_config.get('smtp_server')
239 mail_port = email_config.get('smtp_port')
255 mail_port = email_config.get('smtp_port')
240 tls = email_config.get('smtp_use_tls')
256 tls = str2bool(email_config.get('smtp_use_tls'))
241 ssl = False
257 ssl = str2bool(email_config.get('smtp_use_ssl'))
242
258
243 try:
259 try:
244 m = SmtpMailer(mail_from, user, passwd, mail_server,
260 m = SmtpMailer(mail_from, user, passwd, mail_server,
245 mail_port, ssl, tls)
261 mail_port, ssl, tls)
246 m.send(recipients, subject, body)
262 m.send(recipients, subject, body)
247 except:
263 except:
248 log.error('Mail sending failed')
264 log.error('Mail sending failed')
249 log.error(traceback.format_exc())
265 log.error(traceback.format_exc())
250 return False
266 return False
251 return True
267 return True
252
268
253 @task
269 @task
254 def create_repo_fork(form_data, cur_user):
270 def create_repo_fork(form_data, cur_user):
255 from rhodecode.model.repo import RepoModel
271 from rhodecode.model.repo import RepoModel
256 from vcs import get_backend
272 from vcs import get_backend
257 log = create_repo_fork.get_logger()
273 log = create_repo_fork.get_logger()
258 repo_model = RepoModel(get_session())
274 repo_model = RepoModel(get_session())
259 repo_model.create(form_data, cur_user, just_db=True, fork=True)
275 repo_model.create(form_data, cur_user, just_db=True, fork=True)
260 repo_name = form_data['repo_name']
276 repo_name = form_data['repo_name']
261 repos_path = HgModel().repos_path
277 repos_path = HgModel().repos_path
262 repo_path = os.path.join(repos_path, repo_name)
278 repo_path = os.path.join(repos_path, repo_name)
263 repo_fork_path = os.path.join(repos_path, form_data['fork_name'])
279 repo_fork_path = os.path.join(repos_path, form_data['fork_name'])
264 alias = form_data['repo_type']
280 alias = form_data['repo_type']
265
281
266 log.info('creating repo fork %s as %s', repo_name, repo_path)
282 log.info('creating repo fork %s as %s', repo_name, repo_path)
267 backend = get_backend(alias)
283 backend = get_backend(alias)
268 backend(str(repo_fork_path), create=True, src_url=str(repo_path))
284 backend(str(repo_fork_path), create=True, src_url=str(repo_path))
269
285
270 def __get_codes_stats(repo_name):
286 def __get_codes_stats(repo_name):
271 LANGUAGES_EXTENSIONS = ['action', 'adp', 'ashx', 'asmx',
287 LANGUAGES_EXTENSIONS = ['action', 'adp', 'ashx', 'asmx',
272 'aspx', 'asx', 'axd', 'c', 'cfg', 'cfm', 'cpp', 'cs', 'diff', 'do', 'el',
288 'aspx', 'asx', 'axd', 'c', 'cfg', 'cfm', 'cpp', 'cs', 'diff', 'do', 'el',
273 'erl', 'h', 'java', 'js', 'jsp', 'jspx', 'lisp', 'lua', 'm', 'mako', 'ml',
289 'erl', 'h', 'java', 'js', 'jsp', 'jspx', 'lisp', 'lua', 'm', 'mako', 'ml',
274 'pas', 'patch', 'php', 'php3', 'php4', 'phtml', 'pm', 'py', 'rb', 'rst',
290 'pas', 'patch', 'php', 'php3', 'php4', 'phtml', 'pm', 'py', 'rb', 'rst',
275 's', 'sh', 'tpl', 'txt', 'vim', 'wss', 'xhtml', 'xml', 'xsl', 'xslt', 'yaws']
291 's', 'sh', 'tpl', 'txt', 'vim', 'wss', 'xhtml', 'xml', 'xsl', 'xslt', 'yaws']
276
292
277
293
278 repos_path = HgModel().repos_path
294 repos_path = HgModel().repos_path
279 p = os.path.join(repos_path, repo_name)
295 p = os.path.join(repos_path, repo_name)
280 repo = get_repo(p)
296 repo = get_repo(p)
281 tip = repo.get_changeset()
297 tip = repo.get_changeset()
282 code_stats = {}
298 code_stats = {}
283
299
284 def aggregate(cs):
300 def aggregate(cs):
285 for f in cs[2]:
301 for f in cs[2]:
286 k = f.mimetype
302 k = f.mimetype
287 if f.extension in LANGUAGES_EXTENSIONS:
303 if f.extension in LANGUAGES_EXTENSIONS:
288 if code_stats.has_key(k):
304 if code_stats.has_key(k):
289 code_stats[k] += 1
305 code_stats[k] += 1
290 else:
306 else:
291 code_stats[k] = 1
307 code_stats[k] = 1
292
308
293 map(aggregate, tip.walk('/'))
309 map(aggregate, tip.walk('/'))
294
310
295 return code_stats or {}
311 return code_stats or {}
296
312
297
313
298
314
299
315
@@ -1,118 +1,118 b''
1 import logging
1 import logging
2 import smtplib
2 import smtplib
3 import mimetypes
3 import mimetypes
4 from email.mime.multipart import MIMEMultipart
4 from email.mime.multipart import MIMEMultipart
5 from email.mime.image import MIMEImage
5 from email.mime.image import MIMEImage
6 from email.mime.audio import MIMEAudio
6 from email.mime.audio import MIMEAudio
7 from email.mime.base import MIMEBase
7 from email.mime.base import MIMEBase
8 from email.mime.text import MIMEText
8 from email.mime.text import MIMEText
9 from email.utils import formatdate
9 from email.utils import formatdate
10 from email import encoders
10 from email import encoders
11
11
12 class SmtpMailer(object):
12 class SmtpMailer(object):
13 """simple smtp mailer class
13 """simple smtp mailer class
14
14
15 mailer = SmtpMailer(mail_from, user, passwd, mail_server, mail_port, ssl, tls)
15 mailer = SmtpMailer(mail_from, user, passwd, mail_server, mail_port, ssl, tls)
16 mailer.send(recipients, subject, body, attachment_files)
16 mailer.send(recipients, subject, body, attachment_files)
17
17
18 :param recipients might be a list of string or single string
18 :param recipients might be a list of string or single string
19 :param attachment_files is a dict of {filename:location}
19 :param attachment_files is a dict of {filename:location}
20 it tries to guess the mimetype and attach the file
20 it tries to guess the mimetype and attach the file
21 """
21 """
22
22
23 def __init__(self, mail_from, user, passwd, mail_server,
23 def __init__(self, mail_from, user, passwd, mail_server,
24 mail_port=None, ssl=False, tls=False):
24 mail_port=None, ssl=False, tls=False):
25
25
26 self.mail_from = mail_from
26 self.mail_from = mail_from
27 self.mail_server = mail_server
27 self.mail_server = mail_server
28 self.mail_port = mail_port
28 self.mail_port = mail_port
29 self.user = user
29 self.user = user
30 self.passwd = passwd
30 self.passwd = passwd
31 self.ssl = ssl
31 self.ssl = ssl
32 self.tls = tls
32 self.tls = tls
33 self.debug = False
33 self.debug = False
34
34
35 def send(self, recipients=[], subject='', body='', attachment_files={}):
35 def send(self, recipients=[], subject='', body='', attachment_files={}):
36
36
37 if isinstance(recipients, basestring):
37 if isinstance(recipients, basestring):
38 recipients = [recipients]
38 recipients = [recipients]
39 if self.ssl:
39 if self.ssl:
40 smtp_serv = smtplib.SMTP_SSL(self.mail_server, self.mail_port)
40 smtp_serv = smtplib.SMTP_SSL(self.mail_server, self.mail_port)
41 else:
41 else:
42 smtp_serv = smtplib.SMTP(self.mail_server, self.mail_port)
42 smtp_serv = smtplib.SMTP(self.mail_server, self.mail_port)
43
43
44 if self.tls:
44 if self.tls:
45 smtp_serv.starttls()
45 smtp_serv.starttls()
46
46
47 if self.debug:
47 if self.debug:
48 smtp_serv.set_debuglevel(1)
48 smtp_serv.set_debuglevel(1)
49
49
50 smtp_serv.ehlo("mailer")
50 smtp_serv.ehlo("rhodecode mailer")
51
51
52 #if server requires authorization you must provide login and password
52 #if server requires authorization you must provide login and password
53 smtp_serv.login(self.user, self.passwd)
53 smtp_serv.login(self.user, self.passwd)
54
54
55 date_ = formatdate(localtime=True)
55 date_ = formatdate(localtime=True)
56 msg = MIMEMultipart()
56 msg = MIMEMultipart()
57 msg['From'] = self.mail_from
57 msg['From'] = self.mail_from
58 msg['To'] = ','.join(recipients)
58 msg['To'] = ','.join(recipients)
59 msg['Date'] = date_
59 msg['Date'] = date_
60 msg['Subject'] = subject
60 msg['Subject'] = subject
61 msg.preamble = 'You will not see this in a MIME-aware mail reader.\n'
61 msg.preamble = 'You will not see this in a MIME-aware mail reader.\n'
62
62
63 msg.attach(MIMEText(body))
63 msg.attach(MIMEText(body))
64
64
65 if attachment_files:
65 if attachment_files:
66 self.__atach_files(msg, attachment_files)
66 self.__atach_files(msg, attachment_files)
67
67
68 smtp_serv.sendmail(self.mail_from, recipients, msg.as_string())
68 smtp_serv.sendmail(self.mail_from, recipients, msg.as_string())
69 logging.info('MAIL SEND TO: %s' % recipients)
69 logging.info('MAIL SEND TO: %s' % recipients)
70 smtp_serv.quit()
70 smtp_serv.quit()
71
71
72
72
73 def __atach_files(self, msg, attachment_files):
73 def __atach_files(self, msg, attachment_files):
74 if isinstance(attachment_files, dict):
74 if isinstance(attachment_files, dict):
75 for f_name, msg_file in attachment_files.items():
75 for f_name, msg_file in attachment_files.items():
76 ctype, encoding = mimetypes.guess_type(f_name)
76 ctype, encoding = mimetypes.guess_type(f_name)
77 logging.info("guessing file %s type based on %s" , ctype, f_name)
77 logging.info("guessing file %s type based on %s" , ctype, f_name)
78 if ctype is None or encoding is not None:
78 if ctype is None or encoding is not None:
79 # No guess could be made, or the file is encoded (compressed), so
79 # No guess could be made, or the file is encoded (compressed), so
80 # use a generic bag-of-bits type.
80 # use a generic bag-of-bits type.
81 ctype = 'application/octet-stream'
81 ctype = 'application/octet-stream'
82 maintype, subtype = ctype.split('/', 1)
82 maintype, subtype = ctype.split('/', 1)
83 if maintype == 'text':
83 if maintype == 'text':
84 # Note: we should handle calculating the charset
84 # Note: we should handle calculating the charset
85 file_part = MIMEText(self.get_content(msg_file),
85 file_part = MIMEText(self.get_content(msg_file),
86 _subtype=subtype)
86 _subtype=subtype)
87 elif maintype == 'image':
87 elif maintype == 'image':
88 file_part = MIMEImage(self.get_content(msg_file),
88 file_part = MIMEImage(self.get_content(msg_file),
89 _subtype=subtype)
89 _subtype=subtype)
90 elif maintype == 'audio':
90 elif maintype == 'audio':
91 file_part = MIMEAudio(self.get_content(msg_file),
91 file_part = MIMEAudio(self.get_content(msg_file),
92 _subtype=subtype)
92 _subtype=subtype)
93 else:
93 else:
94 file_part = MIMEBase(maintype, subtype)
94 file_part = MIMEBase(maintype, subtype)
95 file_part.set_payload(self.get_content(msg_file))
95 file_part.set_payload(self.get_content(msg_file))
96 # Encode the payload using Base64
96 # Encode the payload using Base64
97 encoders.encode_base64(msg)
97 encoders.encode_base64(msg)
98 # Set the filename parameter
98 # Set the filename parameter
99 file_part.add_header('Content-Disposition', 'attachment',
99 file_part.add_header('Content-Disposition', 'attachment',
100 filename=f_name)
100 filename=f_name)
101 file_part.add_header('Content-Type', ctype, name=f_name)
101 file_part.add_header('Content-Type', ctype, name=f_name)
102 msg.attach(file_part)
102 msg.attach(file_part)
103 else:
103 else:
104 raise Exception('Attachment files should be'
104 raise Exception('Attachment files should be'
105 'a dict in format {"filename":"filepath"}')
105 'a dict in format {"filename":"filepath"}')
106
106
107 def get_content(self, msg_file):
107 def get_content(self, msg_file):
108 '''
108 '''
109 Get content based on type, if content is a string do open first
109 Get content based on type, if content is a string do open first
110 else just read because it's a probably open file object
110 else just read because it's a probably open file object
111 :param msg_file:
111 :param msg_file:
112 '''
112 '''
113 if isinstance(msg_file, str):
113 if isinstance(msg_file, str):
114 return open(msg_file, "rb").read()
114 return open(msg_file, "rb").read()
115 else:
115 else:
116 #just for safe seek to 0
116 #just for safe seek to 0
117 msg_file.seek(0)
117 msg_file.seek(0)
118 return msg_file.read()
118 return msg_file.read()
@@ -1,553 +1,572 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 # Utilities for RhodeCode
3 # Utilities for RhodeCode
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 # This program is free software; you can redistribute it and/or
5 # This program is free software; you can redistribute it and/or
6 # modify it under the terms of the GNU General Public License
6 # modify it under the terms of the GNU General Public License
7 # as published by the Free Software Foundation; version 2
7 # as published by the Free Software Foundation; version 2
8 # of the License or (at your opinion) any later version of the license.
8 # of the License or (at your opinion) any later version of the license.
9 #
9 #
10 # This program is distributed in the hope that it will be useful,
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
13 # GNU General Public License for more details.
14 #
14 #
15 # You should have received a copy of the GNU General Public License
15 # You should have received a copy of the GNU General Public License
16 # along with this program; if not, write to the Free Software
16 # along with this program; if not, write to the Free Software
17 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
17 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18 # MA 02110-1301, USA.
18 # MA 02110-1301, USA.
19 """
19 """
20 Created on April 18, 2010
20 Created on April 18, 2010
21 Utilities for RhodeCode
21 Utilities for RhodeCode
22 @author: marcink
22 @author: marcink
23 """
23 """
24
24
25 from UserDict import DictMixin
25 from UserDict import DictMixin
26 from mercurial import ui, config, hg
26 from mercurial import ui, config, hg
27 from mercurial.error import RepoError
27 from mercurial.error import RepoError
28 from rhodecode.model import meta
28 from rhodecode.model import meta
29 from rhodecode.model.caching_query import FromCache
29 from rhodecode.model.caching_query import FromCache
30 from rhodecode.model.db import Repository, User, RhodeCodeUi, RhodeCodeSettings, \
30 from rhodecode.model.db import Repository, User, RhodeCodeUi, RhodeCodeSettings, \
31 UserLog
31 UserLog
32 from rhodecode.model.repo import RepoModel
32 from rhodecode.model.repo import RepoModel
33 from rhodecode.model.user import UserModel
33 from rhodecode.model.user import UserModel
34 from vcs.backends.base import BaseChangeset
34 from vcs.backends.base import BaseChangeset
35 from paste.script import command
35 from paste.script import command
36 import ConfigParser
36 import ConfigParser
37 from vcs.utils.lazy import LazyProperty
37 from vcs.utils.lazy import LazyProperty
38 import traceback
38 import traceback
39 import datetime
39 import datetime
40 import logging
40 import logging
41 import os
41 import os
42
42
43 log = logging.getLogger(__name__)
43 log = logging.getLogger(__name__)
44
44
45
45
46 def get_repo_slug(request):
46 def get_repo_slug(request):
47 return request.environ['pylons.routes_dict'].get('repo_name')
47 return request.environ['pylons.routes_dict'].get('repo_name')
48
48
49 def is_mercurial(environ):
49 def is_mercurial(environ):
50 """
50 """
51 Returns True if request's target is mercurial server - header
51 Returns True if request's target is mercurial server - header
52 ``HTTP_ACCEPT`` of such request would start with ``application/mercurial``.
52 ``HTTP_ACCEPT`` of such request would start with ``application/mercurial``.
53 """
53 """
54 http_accept = environ.get('HTTP_ACCEPT')
54 http_accept = environ.get('HTTP_ACCEPT')
55 if http_accept and http_accept.startswith('application/mercurial'):
55 if http_accept and http_accept.startswith('application/mercurial'):
56 return True
56 return True
57 return False
57 return False
58
58
59 def is_git(environ):
59 def is_git(environ):
60 """
60 """
61 Returns True if request's target is git server. ``HTTP_USER_AGENT`` would
61 Returns True if request's target is git server. ``HTTP_USER_AGENT`` would
62 then have git client version given.
62 then have git client version given.
63
63
64 :param environ:
64 :param environ:
65 """
65 """
66 http_user_agent = environ.get('HTTP_USER_AGENT')
66 http_user_agent = environ.get('HTTP_USER_AGENT')
67 if http_user_agent and http_user_agent.startswith('git'):
67 if http_user_agent and http_user_agent.startswith('git'):
68 return True
68 return True
69 return False
69 return False
70
70
71 def action_logger(user, action, repo, ipaddr, sa=None):
71 def action_logger(user, action, repo, ipaddr='', sa=None):
72 """
72 """
73 Action logger for various action made by users
73 Action logger for various action made by users
74
75 :param user: user that made this action, can be a string unique username or
76 object containing user_id attribute
77 :param action: action to log, should be on of predefined unique actions for
78 easy translations
79 :param repo: repository that action was made on
80 :param ipaddr: optional ip address from what the action was made
81 :param sa: optional sqlalchemy session
82
74 """
83 """
75
84
76 if not sa:
85 if not sa:
77 sa = meta.Session()
86 sa = meta.Session()
78
87
79 try:
88 try:
80 if hasattr(user, 'user_id'):
89 if hasattr(user, 'user_id'):
81 user_obj = user
90 user_obj = user
82 elif isinstance(user, basestring):
91 elif isinstance(user, basestring):
83 user_obj = UserModel(sa).get_by_username(user, cache=False)
92 user_obj = UserModel(sa).get_by_username(user, cache=False)
84 else:
93 else:
85 raise Exception('You have to provide user object or username')
94 raise Exception('You have to provide user object or username')
86
95
96
97 if repo:
87 repo_name = repo.lstrip('/')
98 repo_name = repo.lstrip('/')
99
100 repository = RepoModel(sa).get(repo_name, cache=False)
101 if not repository:
102 raise Exception('You have to provide valid repository')
103 else:
104 raise Exception('You have to provide repository to action logger')
105
106
88 user_log = UserLog()
107 user_log = UserLog()
89 user_log.user_id = user_obj.user_id
108 user_log.user_id = user_obj.user_id
90 user_log.action = action
109 user_log.action = action
91 user_log.repository_name = repo_name
110 user_log.repository_name = repo_name
92 user_log.repository = RepoModel(sa).get(repo_name, cache=False)
111 user_log.repository = repository
93 user_log.action_date = datetime.datetime.now()
112 user_log.action_date = datetime.datetime.now()
94 user_log.user_ip = ipaddr
113 user_log.user_ip = ipaddr
95 sa.add(user_log)
114 sa.add(user_log)
96 sa.commit()
115 sa.commit()
97
116
98 log.info('Adding user %s, action %s on %s',
117 log.info('Adding user %s, action %s on %s',
99 user_obj.username, action, repo)
118 user_obj.username, action, repo)
100 except:
119 except:
101 log.error(traceback.format_exc())
120 log.error(traceback.format_exc())
102 sa.rollback()
121 sa.rollback()
103
122
104 def get_repos(path, recursive=False, initial=False):
123 def get_repos(path, recursive=False, initial=False):
105 """
124 """
106 Scans given path for repos and return (name,(type,path)) tuple
125 Scans given path for repos and return (name,(type,path)) tuple
107 :param prefix:
126 :param prefix:
108 :param path:
127 :param path:
109 :param recursive:
128 :param recursive:
110 :param initial:
129 :param initial:
111 """
130 """
112 from vcs.utils.helpers import get_scm
131 from vcs.utils.helpers import get_scm
113 from vcs.exceptions import VCSError
132 from vcs.exceptions import VCSError
114
133
115 try:
134 try:
116 scm = get_scm(path)
135 scm = get_scm(path)
117 except:
136 except:
118 pass
137 pass
119 else:
138 else:
120 raise Exception('The given path %s should not be a repository got %s',
139 raise Exception('The given path %s should not be a repository got %s',
121 path, scm)
140 path, scm)
122
141
123 for dirpath in os.listdir(path):
142 for dirpath in os.listdir(path):
124 try:
143 try:
125 yield dirpath, get_scm(os.path.join(path, dirpath))
144 yield dirpath, get_scm(os.path.join(path, dirpath))
126 except VCSError:
145 except VCSError:
127 pass
146 pass
128
147
129 if __name__ == '__main__':
148 if __name__ == '__main__':
130 get_repos('', '/home/marcink/workspace-python')
149 get_repos('', '/home/marcink/workspace-python')
131
150
132
151
133 def check_repo_fast(repo_name, base_path):
152 def check_repo_fast(repo_name, base_path):
134 if os.path.isdir(os.path.join(base_path, repo_name)):return False
153 if os.path.isdir(os.path.join(base_path, repo_name)):return False
135 return True
154 return True
136
155
137 def check_repo(repo_name, base_path, verify=True):
156 def check_repo(repo_name, base_path, verify=True):
138
157
139 repo_path = os.path.join(base_path, repo_name)
158 repo_path = os.path.join(base_path, repo_name)
140
159
141 try:
160 try:
142 if not check_repo_fast(repo_name, base_path):
161 if not check_repo_fast(repo_name, base_path):
143 return False
162 return False
144 r = hg.repository(ui.ui(), repo_path)
163 r = hg.repository(ui.ui(), repo_path)
145 if verify:
164 if verify:
146 hg.verify(r)
165 hg.verify(r)
147 #here we hnow that repo exists it was verified
166 #here we hnow that repo exists it was verified
148 log.info('%s repo is already created', repo_name)
167 log.info('%s repo is already created', repo_name)
149 return False
168 return False
150 except RepoError:
169 except RepoError:
151 #it means that there is no valid repo there...
170 #it means that there is no valid repo there...
152 log.info('%s repo is free for creation', repo_name)
171 log.info('%s repo is free for creation', repo_name)
153 return True
172 return True
154
173
155 def ask_ok(prompt, retries=4, complaint='Yes or no, please!'):
174 def ask_ok(prompt, retries=4, complaint='Yes or no, please!'):
156 while True:
175 while True:
157 ok = raw_input(prompt)
176 ok = raw_input(prompt)
158 if ok in ('y', 'ye', 'yes'): return True
177 if ok in ('y', 'ye', 'yes'): return True
159 if ok in ('n', 'no', 'nop', 'nope'): return False
178 if ok in ('n', 'no', 'nop', 'nope'): return False
160 retries = retries - 1
179 retries = retries - 1
161 if retries < 0: raise IOError
180 if retries < 0: raise IOError
162 print complaint
181 print complaint
163
182
164 def get_hg_ui_cached():
183 def get_hg_ui_cached():
165 try:
184 try:
166 sa = meta.Session
185 sa = meta.Session
167 ret = sa.query(RhodeCodeUi)\
186 ret = sa.query(RhodeCodeUi)\
168 .options(FromCache("sql_cache_short", "get_hg_ui_settings"))\
187 .options(FromCache("sql_cache_short", "get_hg_ui_settings"))\
169 .all()
188 .all()
170 except:
189 except:
171 pass
190 pass
172 finally:
191 finally:
173 meta.Session.remove()
192 meta.Session.remove()
174 return ret
193 return ret
175
194
176
195
177 def get_hg_settings():
196 def get_hg_settings():
178 try:
197 try:
179 sa = meta.Session()
198 sa = meta.Session()
180 ret = sa.query(RhodeCodeSettings)\
199 ret = sa.query(RhodeCodeSettings)\
181 .options(FromCache("sql_cache_short", "get_hg_settings"))\
200 .options(FromCache("sql_cache_short", "get_hg_settings"))\
182 .all()
201 .all()
183 except:
202 except:
184 pass
203 pass
185 finally:
204 finally:
186 meta.Session.remove()
205 meta.Session.remove()
187
206
188 if not ret:
207 if not ret:
189 raise Exception('Could not get application settings !')
208 raise Exception('Could not get application settings !')
190 settings = {}
209 settings = {}
191 for each in ret:
210 for each in ret:
192 settings['rhodecode_' + each.app_settings_name] = each.app_settings_value
211 settings['rhodecode_' + each.app_settings_name] = each.app_settings_value
193
212
194 return settings
213 return settings
195
214
196 def get_hg_ui_settings():
215 def get_hg_ui_settings():
197 try:
216 try:
198 sa = meta.Session()
217 sa = meta.Session()
199 ret = sa.query(RhodeCodeUi).all()
218 ret = sa.query(RhodeCodeUi).all()
200 except:
219 except:
201 pass
220 pass
202 finally:
221 finally:
203 meta.Session.remove()
222 meta.Session.remove()
204
223
205 if not ret:
224 if not ret:
206 raise Exception('Could not get application ui settings !')
225 raise Exception('Could not get application ui settings !')
207 settings = {}
226 settings = {}
208 for each in ret:
227 for each in ret:
209 k = each.ui_key
228 k = each.ui_key
210 v = each.ui_value
229 v = each.ui_value
211 if k == '/':
230 if k == '/':
212 k = 'root_path'
231 k = 'root_path'
213
232
214 if k.find('.') != -1:
233 if k.find('.') != -1:
215 k = k.replace('.', '_')
234 k = k.replace('.', '_')
216
235
217 if each.ui_section == 'hooks':
236 if each.ui_section == 'hooks':
218 v = each.ui_active
237 v = each.ui_active
219
238
220 settings[each.ui_section + '_' + k] = v
239 settings[each.ui_section + '_' + k] = v
221
240
222 return settings
241 return settings
223
242
224 #propagated from mercurial documentation
243 #propagated from mercurial documentation
225 ui_sections = ['alias', 'auth',
244 ui_sections = ['alias', 'auth',
226 'decode/encode', 'defaults',
245 'decode/encode', 'defaults',
227 'diff', 'email',
246 'diff', 'email',
228 'extensions', 'format',
247 'extensions', 'format',
229 'merge-patterns', 'merge-tools',
248 'merge-patterns', 'merge-tools',
230 'hooks', 'http_proxy',
249 'hooks', 'http_proxy',
231 'smtp', 'patch',
250 'smtp', 'patch',
232 'paths', 'profiling',
251 'paths', 'profiling',
233 'server', 'trusted',
252 'server', 'trusted',
234 'ui', 'web', ]
253 'ui', 'web', ]
235
254
236 def make_ui(read_from='file', path=None, checkpaths=True):
255 def make_ui(read_from='file', path=None, checkpaths=True):
237 """
256 """
238 A function that will read python rc files or database
257 A function that will read python rc files or database
239 and make an mercurial ui object from read options
258 and make an mercurial ui object from read options
240
259
241 :param path: path to mercurial config file
260 :param path: path to mercurial config file
242 :param checkpaths: check the path
261 :param checkpaths: check the path
243 :param read_from: read from 'file' or 'db'
262 :param read_from: read from 'file' or 'db'
244 """
263 """
245
264
246 baseui = ui.ui()
265 baseui = ui.ui()
247
266
248 if read_from == 'file':
267 if read_from == 'file':
249 if not os.path.isfile(path):
268 if not os.path.isfile(path):
250 log.warning('Unable to read config file %s' % path)
269 log.warning('Unable to read config file %s' % path)
251 return False
270 return False
252 log.debug('reading hgrc from %s', path)
271 log.debug('reading hgrc from %s', path)
253 cfg = config.config()
272 cfg = config.config()
254 cfg.read(path)
273 cfg.read(path)
255 for section in ui_sections:
274 for section in ui_sections:
256 for k, v in cfg.items(section):
275 for k, v in cfg.items(section):
257 baseui.setconfig(section, k, v)
276 baseui.setconfig(section, k, v)
258 log.debug('settings ui from file[%s]%s:%s', section, k, v)
277 log.debug('settings ui from file[%s]%s:%s', section, k, v)
259
278
260 elif read_from == 'db':
279 elif read_from == 'db':
261 hg_ui = get_hg_ui_cached()
280 hg_ui = get_hg_ui_cached()
262 for ui_ in hg_ui:
281 for ui_ in hg_ui:
263 if ui_.ui_active:
282 if ui_.ui_active:
264 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section, ui_.ui_key, ui_.ui_value)
283 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section, ui_.ui_key, ui_.ui_value)
265 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
284 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
266
285
267
286
268 return baseui
287 return baseui
269
288
270
289
271 def set_rhodecode_config(config):
290 def set_rhodecode_config(config):
272 hgsettings = get_hg_settings()
291 hgsettings = get_hg_settings()
273
292
274 for k, v in hgsettings.items():
293 for k, v in hgsettings.items():
275 config[k] = v
294 config[k] = v
276
295
277 def invalidate_cache(name, *args):
296 def invalidate_cache(name, *args):
278 """
297 """
279 Puts cache invalidation task into db for
298 Puts cache invalidation task into db for
280 further global cache invalidation
299 further global cache invalidation
281 """
300 """
282 pass
301 pass
283
302
284 class EmptyChangeset(BaseChangeset):
303 class EmptyChangeset(BaseChangeset):
285 """
304 """
286 An dummy empty changeset. It's possible to pass hash when creating
305 An dummy empty changeset. It's possible to pass hash when creating
287 an EmptyChangeset
306 an EmptyChangeset
288 """
307 """
289
308
290 def __init__(self, cs='0' * 40):
309 def __init__(self, cs='0' * 40):
291 self._empty_cs = cs
310 self._empty_cs = cs
292 self.revision = -1
311 self.revision = -1
293 self.message = ''
312 self.message = ''
294 self.author = ''
313 self.author = ''
295 self.date = ''
314 self.date = ''
296
315
297 @LazyProperty
316 @LazyProperty
298 def raw_id(self):
317 def raw_id(self):
299 """
318 """
300 Returns raw string identifying this changeset, useful for web
319 Returns raw string identifying this changeset, useful for web
301 representation.
320 representation.
302 """
321 """
303 return self._empty_cs
322 return self._empty_cs
304
323
305 @LazyProperty
324 @LazyProperty
306 def short_id(self):
325 def short_id(self):
307 return self.raw_id[:12]
326 return self.raw_id[:12]
308
327
309 def get_file_changeset(self, path):
328 def get_file_changeset(self, path):
310 return self
329 return self
311
330
312 def get_file_content(self, path):
331 def get_file_content(self, path):
313 return u''
332 return u''
314
333
315 def get_file_size(self, path):
334 def get_file_size(self, path):
316 return 0
335 return 0
317
336
318 def repo2db_mapper(initial_repo_list, remove_obsolete=False):
337 def repo2db_mapper(initial_repo_list, remove_obsolete=False):
319 """
338 """
320 maps all found repositories into db
339 maps all found repositories into db
321 """
340 """
322
341
323 sa = meta.Session()
342 sa = meta.Session()
324 rm = RepoModel(sa)
343 rm = RepoModel(sa)
325 user = sa.query(User).filter(User.admin == True).first()
344 user = sa.query(User).filter(User.admin == True).first()
326
345
327 for name, repo in initial_repo_list.items():
346 for name, repo in initial_repo_list.items():
328 if not rm.get(name, cache=False):
347 if not rm.get(name, cache=False):
329 log.info('repository %s not found creating default', name)
348 log.info('repository %s not found creating default', name)
330
349
331 form_data = {
350 form_data = {
332 'repo_name':name,
351 'repo_name':name,
333 'repo_type':repo.alias,
352 'repo_type':repo.alias,
334 'description':repo.description \
353 'description':repo.description \
335 if repo.description != 'unknown' else \
354 if repo.description != 'unknown' else \
336 '%s repository' % name,
355 '%s repository' % name,
337 'private':False
356 'private':False
338 }
357 }
339 rm.create(form_data, user, just_db=True)
358 rm.create(form_data, user, just_db=True)
340
359
341 if remove_obsolete:
360 if remove_obsolete:
342 #remove from database those repositories that are not in the filesystem
361 #remove from database those repositories that are not in the filesystem
343 for repo in sa.query(Repository).all():
362 for repo in sa.query(Repository).all():
344 if repo.repo_name not in initial_repo_list.keys():
363 if repo.repo_name not in initial_repo_list.keys():
345 sa.delete(repo)
364 sa.delete(repo)
346 sa.commit()
365 sa.commit()
347
366
348 class OrderedDict(dict, DictMixin):
367 class OrderedDict(dict, DictMixin):
349
368
350 def __init__(self, *args, **kwds):
369 def __init__(self, *args, **kwds):
351 if len(args) > 1:
370 if len(args) > 1:
352 raise TypeError('expected at most 1 arguments, got %d' % len(args))
371 raise TypeError('expected at most 1 arguments, got %d' % len(args))
353 try:
372 try:
354 self.__end
373 self.__end
355 except AttributeError:
374 except AttributeError:
356 self.clear()
375 self.clear()
357 self.update(*args, **kwds)
376 self.update(*args, **kwds)
358
377
359 def clear(self):
378 def clear(self):
360 self.__end = end = []
379 self.__end = end = []
361 end += [None, end, end] # sentinel node for doubly linked list
380 end += [None, end, end] # sentinel node for doubly linked list
362 self.__map = {} # key --> [key, prev, next]
381 self.__map = {} # key --> [key, prev, next]
363 dict.clear(self)
382 dict.clear(self)
364
383
365 def __setitem__(self, key, value):
384 def __setitem__(self, key, value):
366 if key not in self:
385 if key not in self:
367 end = self.__end
386 end = self.__end
368 curr = end[1]
387 curr = end[1]
369 curr[2] = end[1] = self.__map[key] = [key, curr, end]
388 curr[2] = end[1] = self.__map[key] = [key, curr, end]
370 dict.__setitem__(self, key, value)
389 dict.__setitem__(self, key, value)
371
390
372 def __delitem__(self, key):
391 def __delitem__(self, key):
373 dict.__delitem__(self, key)
392 dict.__delitem__(self, key)
374 key, prev, next = self.__map.pop(key)
393 key, prev, next = self.__map.pop(key)
375 prev[2] = next
394 prev[2] = next
376 next[1] = prev
395 next[1] = prev
377
396
378 def __iter__(self):
397 def __iter__(self):
379 end = self.__end
398 end = self.__end
380 curr = end[2]
399 curr = end[2]
381 while curr is not end:
400 while curr is not end:
382 yield curr[0]
401 yield curr[0]
383 curr = curr[2]
402 curr = curr[2]
384
403
385 def __reversed__(self):
404 def __reversed__(self):
386 end = self.__end
405 end = self.__end
387 curr = end[1]
406 curr = end[1]
388 while curr is not end:
407 while curr is not end:
389 yield curr[0]
408 yield curr[0]
390 curr = curr[1]
409 curr = curr[1]
391
410
392 def popitem(self, last=True):
411 def popitem(self, last=True):
393 if not self:
412 if not self:
394 raise KeyError('dictionary is empty')
413 raise KeyError('dictionary is empty')
395 if last:
414 if last:
396 key = reversed(self).next()
415 key = reversed(self).next()
397 else:
416 else:
398 key = iter(self).next()
417 key = iter(self).next()
399 value = self.pop(key)
418 value = self.pop(key)
400 return key, value
419 return key, value
401
420
402 def __reduce__(self):
421 def __reduce__(self):
403 items = [[k, self[k]] for k in self]
422 items = [[k, self[k]] for k in self]
404 tmp = self.__map, self.__end
423 tmp = self.__map, self.__end
405 del self.__map, self.__end
424 del self.__map, self.__end
406 inst_dict = vars(self).copy()
425 inst_dict = vars(self).copy()
407 self.__map, self.__end = tmp
426 self.__map, self.__end = tmp
408 if inst_dict:
427 if inst_dict:
409 return (self.__class__, (items,), inst_dict)
428 return (self.__class__, (items,), inst_dict)
410 return self.__class__, (items,)
429 return self.__class__, (items,)
411
430
412 def keys(self):
431 def keys(self):
413 return list(self)
432 return list(self)
414
433
415 setdefault = DictMixin.setdefault
434 setdefault = DictMixin.setdefault
416 update = DictMixin.update
435 update = DictMixin.update
417 pop = DictMixin.pop
436 pop = DictMixin.pop
418 values = DictMixin.values
437 values = DictMixin.values
419 items = DictMixin.items
438 items = DictMixin.items
420 iterkeys = DictMixin.iterkeys
439 iterkeys = DictMixin.iterkeys
421 itervalues = DictMixin.itervalues
440 itervalues = DictMixin.itervalues
422 iteritems = DictMixin.iteritems
441 iteritems = DictMixin.iteritems
423
442
424 def __repr__(self):
443 def __repr__(self):
425 if not self:
444 if not self:
426 return '%s()' % (self.__class__.__name__,)
445 return '%s()' % (self.__class__.__name__,)
427 return '%s(%r)' % (self.__class__.__name__, self.items())
446 return '%s(%r)' % (self.__class__.__name__, self.items())
428
447
429 def copy(self):
448 def copy(self):
430 return self.__class__(self)
449 return self.__class__(self)
431
450
432 @classmethod
451 @classmethod
433 def fromkeys(cls, iterable, value=None):
452 def fromkeys(cls, iterable, value=None):
434 d = cls()
453 d = cls()
435 for key in iterable:
454 for key in iterable:
436 d[key] = value
455 d[key] = value
437 return d
456 return d
438
457
439 def __eq__(self, other):
458 def __eq__(self, other):
440 if isinstance(other, OrderedDict):
459 if isinstance(other, OrderedDict):
441 return len(self) == len(other) and self.items() == other.items()
460 return len(self) == len(other) and self.items() == other.items()
442 return dict.__eq__(self, other)
461 return dict.__eq__(self, other)
443
462
444 def __ne__(self, other):
463 def __ne__(self, other):
445 return not self == other
464 return not self == other
446
465
447
466
448 #===============================================================================
467 #===============================================================================
449 # TEST FUNCTIONS AND CREATORS
468 # TEST FUNCTIONS AND CREATORS
450 #===============================================================================
469 #===============================================================================
451 def create_test_index(repo_location, full_index):
470 def create_test_index(repo_location, full_index):
452 """Makes default test index
471 """Makes default test index
453 :param repo_location:
472 :param repo_location:
454 :param full_index:
473 :param full_index:
455 """
474 """
456 from rhodecode.lib.indexers.daemon import WhooshIndexingDaemon
475 from rhodecode.lib.indexers.daemon import WhooshIndexingDaemon
457 from rhodecode.lib.pidlock import DaemonLock, LockHeld
476 from rhodecode.lib.pidlock import DaemonLock, LockHeld
458 import shutil
477 import shutil
459
478
460 index_location = os.path.join(repo_location, 'index')
479 index_location = os.path.join(repo_location, 'index')
461 if os.path.exists(index_location):
480 if os.path.exists(index_location):
462 shutil.rmtree(index_location)
481 shutil.rmtree(index_location)
463
482
464 try:
483 try:
465 l = DaemonLock()
484 l = DaemonLock()
466 WhooshIndexingDaemon(index_location=index_location,
485 WhooshIndexingDaemon(index_location=index_location,
467 repo_location=repo_location)\
486 repo_location=repo_location)\
468 .run(full_index=full_index)
487 .run(full_index=full_index)
469 l.release()
488 l.release()
470 except LockHeld:
489 except LockHeld:
471 pass
490 pass
472
491
473 def create_test_env(repos_test_path, config):
492 def create_test_env(repos_test_path, config):
474 """Makes a fresh database and
493 """Makes a fresh database and
475 install test repository into tmp dir
494 install test repository into tmp dir
476 """
495 """
477 from rhodecode.lib.db_manage import DbManage
496 from rhodecode.lib.db_manage import DbManage
478 from rhodecode.tests import HG_REPO, GIT_REPO, NEW_HG_REPO, NEW_GIT_REPO, \
497 from rhodecode.tests import HG_REPO, GIT_REPO, NEW_HG_REPO, NEW_GIT_REPO, \
479 HG_FORK, GIT_FORK, TESTS_TMP_PATH
498 HG_FORK, GIT_FORK, TESTS_TMP_PATH
480 import tarfile
499 import tarfile
481 import shutil
500 import shutil
482 from os.path import dirname as dn, join as jn, abspath
501 from os.path import dirname as dn, join as jn, abspath
483
502
484 log = logging.getLogger('TestEnvCreator')
503 log = logging.getLogger('TestEnvCreator')
485 # create logger
504 # create logger
486 log.setLevel(logging.DEBUG)
505 log.setLevel(logging.DEBUG)
487 log.propagate = True
506 log.propagate = True
488 # create console handler and set level to debug
507 # create console handler and set level to debug
489 ch = logging.StreamHandler()
508 ch = logging.StreamHandler()
490 ch.setLevel(logging.DEBUG)
509 ch.setLevel(logging.DEBUG)
491
510
492 # create formatter
511 # create formatter
493 formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
512 formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
494
513
495 # add formatter to ch
514 # add formatter to ch
496 ch.setFormatter(formatter)
515 ch.setFormatter(formatter)
497
516
498 # add ch to logger
517 # add ch to logger
499 log.addHandler(ch)
518 log.addHandler(ch)
500
519
501 #PART ONE create db
520 #PART ONE create db
502 dbname = config['sqlalchemy.db1.url'].split('/')[-1]
521 dbname = config['sqlalchemy.db1.url'].split('/')[-1]
503 log.debug('making test db %s', dbname)
522 log.debug('making test db %s', dbname)
504
523
505 dbmanage = DbManage(log_sql=True, dbname=dbname, root=config['here'],
524 dbmanage = DbManage(log_sql=True, dbname=dbname, root=config['here'],
506 tests=True)
525 tests=True)
507 dbmanage.create_tables(override=True)
526 dbmanage.create_tables(override=True)
508 dbmanage.config_prompt(repos_test_path)
527 dbmanage.config_prompt(repos_test_path)
509 dbmanage.create_default_user()
528 dbmanage.create_default_user()
510 dbmanage.admin_prompt()
529 dbmanage.admin_prompt()
511 dbmanage.create_permissions()
530 dbmanage.create_permissions()
512 dbmanage.populate_default_permissions()
531 dbmanage.populate_default_permissions()
513
532
514 #PART TWO make test repo
533 #PART TWO make test repo
515 log.debug('making test vcs repositories')
534 log.debug('making test vcs repositories')
516
535
517 #remove old one from previos tests
536 #remove old one from previos tests
518 for r in [HG_REPO, GIT_REPO, NEW_HG_REPO, NEW_GIT_REPO, HG_FORK, GIT_FORK]:
537 for r in [HG_REPO, GIT_REPO, NEW_HG_REPO, NEW_GIT_REPO, HG_FORK, GIT_FORK]:
519
538
520 if os.path.isdir(jn(TESTS_TMP_PATH, r)):
539 if os.path.isdir(jn(TESTS_TMP_PATH, r)):
521 log.debug('removing %s', r)
540 log.debug('removing %s', r)
522 shutil.rmtree(jn(TESTS_TMP_PATH, r))
541 shutil.rmtree(jn(TESTS_TMP_PATH, r))
523
542
524 #CREATE DEFAULT HG REPOSITORY
543 #CREATE DEFAULT HG REPOSITORY
525 cur_dir = dn(dn(abspath(__file__)))
544 cur_dir = dn(dn(abspath(__file__)))
526 tar = tarfile.open(jn(cur_dir, 'tests', "vcs_test_hg.tar.gz"))
545 tar = tarfile.open(jn(cur_dir, 'tests', "vcs_test_hg.tar.gz"))
527 tar.extractall(jn(TESTS_TMP_PATH, HG_REPO))
546 tar.extractall(jn(TESTS_TMP_PATH, HG_REPO))
528 tar.close()
547 tar.close()
529
548
530 class UpgradeDb(command.Command):
549 class UpgradeDb(command.Command):
531 """Command used for paster to upgrade our database to newer version
550 """Command used for paster to upgrade our database to newer version
532 """
551 """
533
552
534 max_args = 1
553 max_args = 1
535 min_args = 1
554 min_args = 1
536
555
537 usage = "CONFIG_FILE"
556 usage = "CONFIG_FILE"
538 summary = "Upgrades current db to newer version given configuration file"
557 summary = "Upgrades current db to newer version given configuration file"
539 group_name = "RhodeCode"
558 group_name = "RhodeCode"
540
559
541 parser = command.Command.standard_parser(verbose=True)
560 parser = command.Command.standard_parser(verbose=True)
542
561
543 parser.add_option('--sql',
562 parser.add_option('--sql',
544 action='store_true',
563 action='store_true',
545 dest='just_sql',
564 dest='just_sql',
546 help="Prints upgrade sql for further investigation",
565 help="Prints upgrade sql for further investigation",
547 default=False)
566 default=False)
548 def command(self):
567 def command(self):
549 config_name = self.args[0]
568 config_name = self.args[0]
550 p = config_name.split('/')
569 p = config_name.split('/')
551 root = '.' if len(p) == 1 else '/'.join(p[:-1])
570 root = '.' if len(p) == 1 else '/'.join(p[:-1])
552 config = ConfigParser.ConfigParser({'here':root})
571 config = ConfigParser.ConfigParser({'here':root})
553 config.read(config_name)
572 config.read(config_name)
@@ -1,171 +1,180 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 # Model for users
3 # Model for users
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 #
5 #
6 # This program is free software; you can redistribute it and/or
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; version 2
8 # as published by the Free Software Foundation; version 2
9 # of the License or (at your opinion) any later version of the license.
9 # of the License or (at your opinion) any later version of the license.
10 #
10 #
11 # This program is distributed in the hope that it will be useful,
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
14 # GNU General Public License for more details.
15 #
15 #
16 # You should have received a copy of the GNU General Public License
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 # MA 02110-1301, USA.
19 # MA 02110-1301, USA.
20 """
20 """
21 Created on April 9, 2010
21 Created on April 9, 2010
22 Model for users
22 Model for users
23 :author: marcink
23 :author: marcink
24 """
24 """
25
25
26 from pylons.i18n.translation import _
26 from pylons.i18n.translation import _
27 from rhodecode.model.caching_query import FromCache
27 from rhodecode.model.caching_query import FromCache
28 from rhodecode.model.db import User
28 from rhodecode.model.db import User
29 from rhodecode.model.meta import Session
29 from rhodecode.model.meta import Session
30 import logging
30 import logging
31 import traceback
31 import traceback
32
32
33 log = logging.getLogger(__name__)
33 log = logging.getLogger(__name__)
34
34
35 class DefaultUserException(Exception):pass
35 class DefaultUserException(Exception):pass
36
36
37 class UserModel(object):
37 class UserModel(object):
38
38
39 def __init__(self, sa=None):
39 def __init__(self, sa=None):
40 if not sa:
40 if not sa:
41 self.sa = Session()
41 self.sa = Session()
42 else:
42 else:
43 self.sa = sa
43 self.sa = sa
44
44
45 def get(self, user_id, cache=False):
45 def get(self, user_id, cache=False):
46 user = self.sa.query(User)
46 user = self.sa.query(User)
47 if cache:
47 if cache:
48 user = user.options(FromCache("sql_cache_short",
48 user = user.options(FromCache("sql_cache_short",
49 "get_user_%s" % user_id))
49 "get_user_%s" % user_id))
50 return user.get(user_id)
50 return user.get(user_id)
51
51
52
52
53 def get_by_username(self, username, cache=False):
53 def get_by_username(self, username, cache=False):
54 user = self.sa.query(User)\
54 user = self.sa.query(User)\
55 .filter(User.username == username)
55 .filter(User.username == username)
56 if cache:
56 if cache:
57 user = user.options(FromCache("sql_cache_short",
57 user = user.options(FromCache("sql_cache_short",
58 "get_user_%s" % username))
58 "get_user_%s" % username))
59 return user.scalar()
59 return user.scalar()
60
60
61 def create(self, form_data):
61 def create(self, form_data):
62 try:
62 try:
63 new_user = User()
63 new_user = User()
64 for k, v in form_data.items():
64 for k, v in form_data.items():
65 setattr(new_user, k, v)
65 setattr(new_user, k, v)
66
66
67 self.sa.add(new_user)
67 self.sa.add(new_user)
68 self.sa.commit()
68 self.sa.commit()
69 except:
69 except:
70 log.error(traceback.format_exc())
70 log.error(traceback.format_exc())
71 self.sa.rollback()
71 self.sa.rollback()
72 raise
72 raise
73
73
74 def create_registration(self, form_data):
74 def create_registration(self, form_data):
75 from rhodecode.lib.celerylib import tasks, run_task
75 try:
76 try:
76 new_user = User()
77 new_user = User()
77 for k, v in form_data.items():
78 for k, v in form_data.items():
78 if k != 'admin':
79 if k != 'admin':
79 setattr(new_user, k, v)
80 setattr(new_user, k, v)
80
81
81 self.sa.add(new_user)
82 self.sa.add(new_user)
82 self.sa.commit()
83 self.sa.commit()
84 body = ('New user registration\n'
85 'username: %s\n'
86 'email: %s\n')
87 body = body % (form_data['username'], form_data['email'])
88
89 run_task(tasks.send_email, None,
90 _('[RhodeCode] New User registration'),
91 body)
83 except:
92 except:
84 log.error(traceback.format_exc())
93 log.error(traceback.format_exc())
85 self.sa.rollback()
94 self.sa.rollback()
86 raise
95 raise
87
96
88 def update(self, user_id, form_data):
97 def update(self, user_id, form_data):
89 try:
98 try:
90 new_user = self.get(user_id, cache=False)
99 new_user = self.get(user_id, cache=False)
91 if new_user.username == 'default':
100 if new_user.username == 'default':
92 raise DefaultUserException(
101 raise DefaultUserException(
93 _("You can't Edit this user since it's"
102 _("You can't Edit this user since it's"
94 " crucial for entire application"))
103 " crucial for entire application"))
95 for k, v in form_data.items():
104 for k, v in form_data.items():
96 if k == 'new_password' and v != '':
105 if k == 'new_password' and v != '':
97 new_user.password = v
106 new_user.password = v
98 else:
107 else:
99 setattr(new_user, k, v)
108 setattr(new_user, k, v)
100
109
101 self.sa.add(new_user)
110 self.sa.add(new_user)
102 self.sa.commit()
111 self.sa.commit()
103 except:
112 except:
104 log.error(traceback.format_exc())
113 log.error(traceback.format_exc())
105 self.sa.rollback()
114 self.sa.rollback()
106 raise
115 raise
107
116
108 def update_my_account(self, user_id, form_data):
117 def update_my_account(self, user_id, form_data):
109 try:
118 try:
110 new_user = self.get(user_id, cache=False)
119 new_user = self.get(user_id, cache=False)
111 if new_user.username == 'default':
120 if new_user.username == 'default':
112 raise DefaultUserException(
121 raise DefaultUserException(
113 _("You can't Edit this user since it's"
122 _("You can't Edit this user since it's"
114 " crucial for entire application"))
123 " crucial for entire application"))
115 for k, v in form_data.items():
124 for k, v in form_data.items():
116 if k == 'new_password' and v != '':
125 if k == 'new_password' and v != '':
117 new_user.password = v
126 new_user.password = v
118 else:
127 else:
119 if k not in ['admin', 'active']:
128 if k not in ['admin', 'active']:
120 setattr(new_user, k, v)
129 setattr(new_user, k, v)
121
130
122 self.sa.add(new_user)
131 self.sa.add(new_user)
123 self.sa.commit()
132 self.sa.commit()
124 except:
133 except:
125 log.error(traceback.format_exc())
134 log.error(traceback.format_exc())
126 self.sa.rollback()
135 self.sa.rollback()
127 raise
136 raise
128
137
129 def delete(self, user_id):
138 def delete(self, user_id):
130 try:
139 try:
131 user = self.get(user_id, cache=False)
140 user = self.get(user_id, cache=False)
132 if user.username == 'default':
141 if user.username == 'default':
133 raise DefaultUserException(
142 raise DefaultUserException(
134 _("You can't remove this user since it's"
143 _("You can't remove this user since it's"
135 " crucial for entire application"))
144 " crucial for entire application"))
136 self.sa.delete(user)
145 self.sa.delete(user)
137 self.sa.commit()
146 self.sa.commit()
138 except:
147 except:
139 log.error(traceback.format_exc())
148 log.error(traceback.format_exc())
140 self.sa.rollback()
149 self.sa.rollback()
141 raise
150 raise
142
151
143 def reset_password(self, data):
152 def reset_password(self, data):
144 from rhodecode.lib.celerylib import tasks, run_task
153 from rhodecode.lib.celerylib import tasks, run_task
145 run_task(tasks.reset_user_password, data['email'])
154 run_task(tasks.reset_user_password, data['email'])
146
155
147
156
148 def fill_data(self, user):
157 def fill_data(self, user):
149 """
158 """
150 Fills user data with those from database and log out user if not
159 Fills user data with those from database and log out user if not
151 present in database
160 present in database
152 :param user:
161 :param user:
153 """
162 """
154
163
155 if not hasattr(user, 'user_id') or user.user_id is None:
164 if not hasattr(user, 'user_id') or user.user_id is None:
156 raise Exception('passed in user has to have the user_id attribute')
165 raise Exception('passed in user has to have the user_id attribute')
157
166
158
167
159 log.debug('filling auth user data')
168 log.debug('filling auth user data')
160 try:
169 try:
161 dbuser = self.get(user.user_id)
170 dbuser = self.get(user.user_id)
162 user.username = dbuser.username
171 user.username = dbuser.username
163 user.is_admin = dbuser.admin
172 user.is_admin = dbuser.admin
164 user.name = dbuser.name
173 user.name = dbuser.name
165 user.lastname = dbuser.lastname
174 user.lastname = dbuser.lastname
166 user.email = dbuser.email
175 user.email = dbuser.email
167 except:
176 except:
168 log.error(traceback.format_exc())
177 log.error(traceback.format_exc())
169 user.is_authenticated = False
178 user.is_authenticated = False
170
179
171 return user
180 return user
General Comments 0
You need to be logged in to leave comments. Login now