##// END OF EJS Templates
renamed hg_app to rhodecode
marcink -
r548:b75b77ef default
parent child Browse files
Show More
@@ -1,15 +1,15 b''
1 1
2 2 syntax: regexp
3 3 ^data$
4 4 syntax: regexp
5 5 ^\.settings$
6 6 syntax: regexp
7 7 ^\.project$
8 8 syntax: regexp
9 9 ^\.pydevproject$
10 10 syntax: regexp
11 ^hg_app\.db$
11 ^rhodecode\.db$
12 12 syntax: regexp
13 13 ^test\.db$
14 14 syntax: regexp
15 15 ^repositories\.config$
@@ -1,15 +1,15 b''
1 1 include COPYING
2 2 include README.rst
3 3 include rhodecode/config/deployment.ini_tmpl
4 4
5 include hg_app_daemon
6 include hg_app_daemon2
5 include rhodecode_daemon
6 include rhodecode_daemon2
7 7
8 8 recursive-include rhodecode/public/css *
9 9 recursive-include rhodecode/public/images *
10 10
11 11 include rhodecode/public/js/yui2.js
12 12 include rhodecode/public/js/excanvas.min.js
13 13 include rhodecode/public/js/yui.flot.js
14 14
15 15 recursive-include rhodecode/templates * No newline at end of file
@@ -1,160 +1,160 b''
1 1 ################################################################################
2 2 ################################################################################
3 3 # hg-app - Pylons environment configuration #
4 4 # #
5 5 # The %(here)s variable will be replaced with the parent directory of this file#
6 6 ################################################################################
7 7
8 8 [DEFAULT]
9 9 debug = true
10 10 ################################################################################
11 11 ## Uncomment and replace with the address which should receive ##
12 12 ## any error reports after application crash ##
13 13 ## Additionally those settings will be used by hg-app mailing system ##
14 14 ################################################################################
15 15 #email_to = admin@localhost
16 16 #error_email_from = paste_error@localhost
17 17 #app_email_from = hg-app-noreply@localhost
18 18 #error_message =
19 19
20 20 #smtp_server = mail.server.com
21 21 #smtp_username =
22 22 #smtp_password =
23 23 #smtp_port =
24 24 #smtp_use_tls =
25 25
26 26 [server:main]
27 27 ##nr of threads to spawn
28 28 threadpool_workers = 5
29 29
30 30 ##max request before
31 31 threadpool_max_requests = 6
32 32
33 33 ##option to use threads of process
34 34 use_threadpool = false
35 35
36 36 use = egg:Paste#http
37 37 host = 127.0.0.1
38 38 port = 5000
39 39
40 40 [app:main]
41 41 use = egg:rhodecode
42 42 full_stack = true
43 43 static_files = true
44 44 lang=en
45 45 cache_dir = %(here)s/data
46 46
47 47 ####################################
48 48 ### BEAKER CACHE ####
49 49 ####################################
50 50 beaker.cache.data_dir=/%(here)s/data/cache/data
51 51 beaker.cache.lock_dir=/%(here)s/data/cache/lock
52 52 beaker.cache.regions=super_short_term,short_term,long_term
53 53 beaker.cache.long_term.type=memory
54 54 beaker.cache.long_term.expire=36000
55 55 beaker.cache.short_term.type=memory
56 56 beaker.cache.short_term.expire=60
57 57 beaker.cache.super_short_term.type=memory
58 58 beaker.cache.super_short_term.expire=10
59 59
60 60 ####################################
61 61 ### BEAKER SESSION ####
62 62 ####################################
63 63 ## Type of storage used for the session, current types are
64 64 ## "dbm", "file", "memcached", "database", and "memory".
65 65 ## The storage uses the Container API
66 66 ##that is also used by the cache system.
67 67 beaker.session.type = file
68 68
69 69 beaker.session.key = hg-app
70 70 beaker.session.secret = g654dcno0-9873jhgfreyu
71 71 beaker.session.timeout = 36000
72 72
73 73 ##auto save the session to not to use .save()
74 74 beaker.session.auto = False
75 75
76 76 ##true exire at browser close
77 77 #beaker.session.cookie_expires = 3600
78 78
79 79
80 80 ################################################################################
81 81 ## WARNING: *THE LINE BELOW MUST BE UNCOMMENTED ON A PRODUCTION ENVIRONMENT* ##
82 82 ## Debug mode will enable the interactive debugging tool, allowing ANYONE to ##
83 83 ## execute malicious code after an exception is raised. ##
84 84 ################################################################################
85 85 #set debug = false
86 86
87 87 ##################################
88 88 ### LOGVIEW CONFIG ###
89 89 ##################################
90 90 logview.sqlalchemy = #faa
91 91 logview.pylons.templating = #bfb
92 92 logview.pylons.util = #eee
93 93
94 94 #########################################################
95 95 ### DB CONFIGS - EACH DB WILL HAVE IT'S OWN CONFIG ###
96 96 #########################################################
97 sqlalchemy.db1.url = sqlite:///%(here)s/hg_app.db
97 sqlalchemy.db1.url = sqlite:///%(here)s/rhodecode.db
98 98 #sqlalchemy.db1.echo = False
99 99 #sqlalchemy.db1.pool_recycle = 3600
100 100 sqlalchemy.convert_unicode = true
101 101
102 102 ################################
103 103 ### LOGGING CONFIGURATION ####
104 104 ################################
105 105 [loggers]
106 106 keys = root, routes, rhodecode, sqlalchemy
107 107
108 108 [handlers]
109 109 keys = console
110 110
111 111 [formatters]
112 112 keys = generic,color_formatter
113 113
114 114 #############
115 115 ## LOGGERS ##
116 116 #############
117 117 [logger_root]
118 118 level = NOTSET
119 119 handlers = console
120 120
121 121 [logger_routes]
122 122 level = DEBUG
123 123 handlers = console
124 124 qualname = routes.middleware
125 125 # "level = DEBUG" logs the route matched and routing variables.
126 126
127 127 [logger_rhodecode]
128 128 level = DEBUG
129 129 handlers = console
130 130 qualname = rhodecode
131 131 propagate = 0
132 132
133 133 [logger_sqlalchemy]
134 134 level = ERROR
135 135 handlers = console
136 136 qualname = sqlalchemy.engine
137 137 propagate = 0
138 138
139 139 ##############
140 140 ## HANDLERS ##
141 141 ##############
142 142
143 143 [handler_console]
144 144 class = StreamHandler
145 145 args = (sys.stderr,)
146 146 level = NOTSET
147 147 formatter = color_formatter
148 148
149 149 ################
150 150 ## FORMATTERS ##
151 151 ################
152 152
153 153 [formatter_generic]
154 154 format = %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
155 155 datefmt = %Y-%m-%d %H:%M:%S
156 156
157 157 [formatter_color_formatter]
158 158 class=rhodecode.lib.colored_formatter.ColorFormatter
159 159 format= %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
160 160 datefmt = %Y-%m-%d %H:%M:%S No newline at end of file
@@ -1,61 +1,61 b''
1 1 #!/sbin/runscript
2 2 ########################################
3 3 #### THIS IS AN GENTOO INIT.D SCRIPT####
4 4 ########################################
5 5
6 APP_NAME="hg_app"
6 APP_NAME="rhodecode"
7 7 APP_HOMEDIR="marcink/python_workspace"
8 8 APP_PATH="/home/$APP_HOMEDIR/$APP_NAME"
9 9
10 10 CONF_NAME="production.ini"
11 11
12 12 PID_PATH="$APP_PATH/$APP_NAME.pid"
13 13 LOG_PATH="$APP_PATH/$APP_NAME.log"
14 14
15 15 PYTHON_PATH="/home/$APP_HOMEDIR/v-env"
16 16
17 17 RUN_AS="marcink"
18 18
19 19 DAEMON="$PYTHON_PATH/bin/paster"
20 20
21 21 DAEMON_OPTS="serve --daemon \
22 22 --user=$RUN_AS \
23 23 --group=$RUN_AS \
24 24 --pid-file=$PID_PATH \
25 25 --log-file=$LOG_PATH $APP_PATH/$CONF_NAME"
26 26
27 27 #extra options
28 28 opts="${opts} restartdelay"
29 29
30 30 depend() {
31 31 need nginx
32 32 }
33 33
34 34 start() {
35 35 ebegin "Starting $APP_NAME"
36 36 start-stop-daemon -d $APP_PATH -e PYTHON_EGG_CACHE="/tmp" \
37 37 --start --quiet \
38 38 --pidfile $PID_PATH \
39 39 --user $RUN_AS \
40 40 --exec $DAEMON -- $DAEMON_OPTS
41 41 eend $?
42 42 }
43 43
44 44 stop() {
45 45 ebegin "Stopping $APP_NAME"
46 46 start-stop-daemon -d $APP_PATH \
47 47 --stop --quiet \
48 48 --pidfile $PID_PATH || echo "$APP_NAME - Not running!"
49 49 if [ -f $PID_PATH ]; then
50 50 rm $PID_PATH
51 51 fi
52 52 eend $?
53 53 }
54 54
55 55 restartdelay() {
56 56 #stop()
57 57 echo "sleep3"
58 58 sleep 3
59 59
60 60 #start()
61 61 }
@@ -1,77 +1,77 b''
1 1 #!/bin/sh -e
2 2 ########################################
3 3 #### THIS IS AN DEBIAN INIT.D SCRIPT####
4 4 ########################################
5 5
6 6 ### BEGIN INIT INFO
7 7 # Provides: hg-app
8 8 # Required-Start: $all
9 9 # Required-Stop: $all
10 10 # Default-Start: 2 3 4 5
11 11 # Default-Stop: 0 1 6
12 12 # Short-Description: starts instance of hg-app
13 13 # Description: starts instance of hg-app using start-stop-daemon
14 14 ### END INIT INFO
15 15
16 APP_NAME="hg_app"
16 APP_NAME="rhodecode"
17 17 APP_HOMEDIR="marcink/python_workspace"
18 18 APP_PATH="/home/$APP_HOMEDIR/$APP_NAME"
19 19
20 20 CONF_NAME="production.ini"
21 21
22 22 PID_PATH="$APP_PATH/$APP_NAME.pid"
23 23 LOG_PATH="$APP_PATH/$APP_NAME.log"
24 24
25 25 PYTHON_PATH="/home/$APP_HOMEDIR/v-env"
26 26
27 27 RUN_AS="marcink"
28 28
29 29 DAEMON="$PYTHON_PATH/bin/paster"
30 30
31 31 DAEMON_OPTS="serve --daemon \
32 32 --user=$RUN_AS \
33 33 --group=$RUN_AS \
34 34 --pid-file=$PID_PATH \
35 35 --log-file=$LOG_PATH $APP_PATH/$CONF_NAME"
36 36
37 37
38 38 case "$1" in
39 39 start)
40 40 echo "Starting $APP_NAME"
41 41 start-stop-daemon -d $APP_PATH -e PYTHON_EGG_CACHE="/tmp" \
42 42 --start --quiet \
43 43 --pidfile $PID_PATH \
44 44 --user $RUN_AS \
45 45 --exec $DAEMON -- $DAEMON_OPTS
46 46 ;;
47 47 stop)
48 48 echo "Stopping $APP_NAME"
49 49 start-stop-daemon -d $APP_PATH \
50 50 --stop --quiet \
51 51 --pidfile $PID_PATH || echo "$APP_NAME - Not running!"
52 52 if [ -f $PID_PATH ]; then
53 53 rm $PID_PATH
54 54 fi
55 55 ;;
56 56 restart)
57 57 echo "Restarting $APP_NAME"
58 58 ### stop ###
59 59 echo "Stopping $APP_NAME"
60 60 start-stop-daemon -d $APP_PATH \
61 61 --stop --quiet \
62 62 --pidfile $PID_PATH || echo "$APP_NAME - Not running!"
63 63 if [ -f $PID_PATH ]; then
64 64 rm $PID_PATH
65 65 fi
66 66 ### start ###
67 67 echo "Starting $APP_NAME"
68 68 start-stop-daemon -d $APP_PATH -e PYTHON_EGG_CACHE="/tmp" \
69 69 --start --quiet \
70 70 --pidfile $PID_PATH \
71 71 --user $RUN_AS \
72 72 --exec $DAEMON -- $DAEMON_OPTS
73 73 ;;
74 74 *)
75 75 echo "Usage: $0 {start|stop|restart}"
76 76 exit 1
77 77 esac No newline at end of file
@@ -1,160 +1,160 b''
1 1 ################################################################################
2 2 ################################################################################
3 3 # hg-app - Pylons environment configuration #
4 4 # #
5 5 # The %(here)s variable will be replaced with the parent directory of this file#
6 6 ################################################################################
7 7
8 8 [DEFAULT]
9 9 debug = true
10 10 ################################################################################
11 11 ## Uncomment and replace with the address which should receive ##
12 12 ## any error reports after application crash ##
13 13 ## Additionally those settings will be used by hg-app mailing system ##
14 14 ################################################################################
15 15 #email_to = admin@localhost
16 16 #error_email_from = paste_error@localhost
17 17 #app_email_from = hg-app-noreply@localhost
18 18 #error_message =
19 19
20 20 #smtp_server = mail.server.com
21 21 #smtp_username =
22 22 #smtp_password =
23 23 #smtp_port =
24 24 #smtp_use_tls = false
25 25
26 26 [server:main]
27 27 ##nr of threads to spawn
28 28 threadpool_workers = 5
29 29
30 30 ##max request before thread respawn
31 31 threadpool_max_requests = 2
32 32
33 33 ##option to use threads of process
34 34 use_threadpool = true
35 35
36 36 use = egg:Paste#http
37 37 host = 127.0.0.1
38 38 port = 8001
39 39
40 40 [app:main]
41 41 use = egg:rhodecode
42 42 full_stack = true
43 43 static_files = false
44 44 lang=en
45 45 cache_dir = %(here)s/data
46 46
47 47 ####################################
48 48 ### BEAKER CACHE ####
49 49 ####################################
50 50 beaker.cache.data_dir=/%(here)s/data/cache/data
51 51 beaker.cache.lock_dir=/%(here)s/data/cache/lock
52 52 beaker.cache.regions=super_short_term,short_term,long_term
53 53 beaker.cache.long_term.type=memory
54 54 beaker.cache.long_term.expire=36000
55 55 beaker.cache.short_term.type=memory
56 56 beaker.cache.short_term.expire=60
57 57 beaker.cache.super_short_term.type=memory
58 58 beaker.cache.super_short_term.expire=10
59 59
60 60 ####################################
61 61 ### BEAKER SESSION ####
62 62 ####################################
63 63 ## Type of storage used for the session, current types are
64 64 ## dbm, file, memcached, database, and memory.
65 65 ## The storage uses the Container API
66 66 ##that is also used by the cache system.
67 67 beaker.session.type = file
68 68
69 69 beaker.session.key = hg-app
70 70 beaker.session.secret = g654dcno0-9873jhgfreyu
71 71 beaker.session.timeout = 36000
72 72
73 73 ##auto save the session to not to use .save()
74 74 beaker.session.auto = False
75 75
76 76 ##true exire at browser close
77 77 #beaker.session.cookie_expires = 3600
78 78
79 79
80 80 ################################################################################
81 81 ## WARNING: *THE LINE BELOW MUST BE UNCOMMENTED ON A PRODUCTION ENVIRONMENT* ##
82 82 ## Debug mode will enable the interactive debugging tool, allowing ANYONE to ##
83 83 ## execute malicious code after an exception is raised. ##
84 84 ################################################################################
85 85 set debug = false
86 86
87 87 ##################################
88 88 ### LOGVIEW CONFIG ###
89 89 ##################################
90 90 logview.sqlalchemy = #faa
91 91 logview.pylons.templating = #bfb
92 92 logview.pylons.util = #eee
93 93
94 94 #########################################################
95 95 ### DB CONFIGS - EACH DB WILL HAVE IT'S OWN CONFIG ###
96 96 #########################################################
97 sqlalchemy.db1.url = sqlite:///%(here)s/hg_app.db
97 sqlalchemy.db1.url = sqlite:///%(here)s/rhodecode.db
98 98 #sqlalchemy.db1.echo = False
99 99 #sqlalchemy.db1.pool_recycle = 3600
100 100 sqlalchemy.convert_unicode = true
101 101
102 102 ################################
103 103 ### LOGGING CONFIGURATION ####
104 104 ################################
105 105 [loggers]
106 106 keys = root, routes, rhodecode, sqlalchemy
107 107
108 108 [handlers]
109 109 keys = console
110 110
111 111 [formatters]
112 112 keys = generic,color_formatter
113 113
114 114 #############
115 115 ## LOGGERS ##
116 116 #############
117 117 [logger_root]
118 118 level = INFO
119 119 handlers = console
120 120
121 121 [logger_routes]
122 122 level = INFO
123 123 handlers = console
124 124 qualname = routes.middleware
125 125 # "level = DEBUG" logs the route matched and routing variables.
126 126
127 127 [logger_rhodecode]
128 128 level = DEBUG
129 129 handlers = console
130 130 qualname = rhodecode
131 131 propagate = 0
132 132
133 133 [logger_sqlalchemy]
134 134 level = ERROR
135 135 handlers = console
136 136 qualname = sqlalchemy.engine
137 137 propagate = 0
138 138
139 139 ##############
140 140 ## HANDLERS ##
141 141 ##############
142 142
143 143 [handler_console]
144 144 class = StreamHandler
145 145 args = (sys.stderr,)
146 146 level = NOTSET
147 147 formatter = color_formatter
148 148
149 149 ################
150 150 ## FORMATTERS ##
151 151 ################
152 152
153 153 [formatter_generic]
154 154 format = %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
155 155 datefmt = %Y-%m-%d %H:%M:%S
156 156
157 157 [formatter_color_formatter]
158 158 class=rhodecode.lib.colored_formatter.ColorFormatter
159 159 format= %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
160 160 datefmt = %Y-%m-%d %H:%M:%S No newline at end of file
@@ -1,161 +1,161 b''
1 1 ################################################################################
2 2 ################################################################################
3 3 # hg-app - Pylons environment configuration #
4 4 # #
5 5 # The %(here)s variable will be replaced with the parent directory of this file#
6 6 ################################################################################
7 7
8 8 [DEFAULT]
9 9 debug = true
10 10 ################################################################################
11 11 ## Uncomment and replace with the address which should receive ##
12 12 ## any error reports after application crash ##
13 13 ## Additionally those settings will be used by hg-app mailing system ##
14 14 ################################################################################
15 15 #email_to = admin@localhost
16 16 #error_email_from = paste_error@localhost
17 17 #app_email_from = hg-app-noreply@localhost
18 18 #error_message =
19 19
20 20 #smtp_server = mail.server.com
21 21 #smtp_username =
22 22 #smtp_password =
23 23 #smtp_port =
24 24 #smtp_use_tls = false
25 25
26 26 [server:main]
27 27 ##nr of threads to spawn
28 28 threadpool_workers = 5
29 29
30 30 ##max request before thread respawn
31 31 threadpool_max_requests = 2
32 32
33 33 ##option to use threads of process
34 34 use_threadpool = true
35 35
36 36 use = egg:Paste#http
37 37 host = 127.0.0.1
38 38 port = 8001
39 39
40 40 [app:main]
41 41 use = egg:rhodecode
42 42 full_stack = true
43 43 static_files = false
44 44 lang=en
45 45 cache_dir = %(here)s/data
46 46 app_instance_uuid = ${app_instance_uuid}
47 47
48 48 ####################################
49 49 ### BEAKER CACHE ####
50 50 ####################################
51 51 beaker.cache.data_dir=/%(here)s/data/cache/data
52 52 beaker.cache.lock_dir=/%(here)s/data/cache/lock
53 53 beaker.cache.regions=super_short_term,short_term,long_term
54 54 beaker.cache.long_term.type=memory
55 55 beaker.cache.long_term.expire=36000
56 56 beaker.cache.short_term.type=memory
57 57 beaker.cache.short_term.expire=60
58 58 beaker.cache.super_short_term.type=memory
59 59 beaker.cache.super_short_term.expire=10
60 60
61 61 ####################################
62 62 ### BEAKER SESSION ####
63 63 ####################################
64 64 ## Type of storage used for the session, current types are
65 65 ## dbm, file, memcached, database, and memory.
66 66 ## The storage uses the Container API
67 67 ##that is also used by the cache system.
68 68 beaker.session.type = file
69 69
70 70 beaker.session.key = hg-app
71 71 beaker.session.secret = ${app_instance_secret}
72 72 beaker.session.timeout = 36000
73 73
74 74 ##auto save the session to not to use .save()
75 75 beaker.session.auto = False
76 76
77 77 ##true exire at browser close
78 78 #beaker.session.cookie_expires = 3600
79 79
80 80
81 81 ################################################################################
82 82 ## WARNING: *THE LINE BELOW MUST BE UNCOMMENTED ON A PRODUCTION ENVIRONMENT* ##
83 83 ## Debug mode will enable the interactive debugging tool, allowing ANYONE to ##
84 84 ## execute malicious code after an exception is raised. ##
85 85 ################################################################################
86 86 set debug = false
87 87
88 88 ##################################
89 89 ### LOGVIEW CONFIG ###
90 90 ##################################
91 91 logview.sqlalchemy = #faa
92 92 logview.pylons.templating = #bfb
93 93 logview.pylons.util = #eee
94 94
95 95 #########################################################
96 96 ### DB CONFIGS - EACH DB WILL HAVE IT'S OWN CONFIG ###
97 97 #########################################################
98 sqlalchemy.db1.url = sqlite:///%(here)s/hg_app.db
98 sqlalchemy.db1.url = sqlite:///%(here)s/rhodecode.db
99 99 #sqlalchemy.db1.echo = False
100 100 #sqlalchemy.db1.pool_recycle = 3600
101 101 sqlalchemy.convert_unicode = true
102 102
103 103 ################################
104 104 ### LOGGING CONFIGURATION ####
105 105 ################################
106 106 [loggers]
107 107 keys = root, routes, rhodecode, sqlalchemy
108 108
109 109 [handlers]
110 110 keys = console
111 111
112 112 [formatters]
113 113 keys = generic,color_formatter
114 114
115 115 #############
116 116 ## LOGGERS ##
117 117 #############
118 118 [logger_root]
119 119 level = INFO
120 120 handlers = console
121 121
122 122 [logger_routes]
123 123 level = INFO
124 124 handlers = console
125 125 qualname = routes.middleware
126 126 # "level = DEBUG" logs the route matched and routing variables.
127 127
128 128 [logger_rhodecode]
129 129 level = DEBUG
130 130 handlers = console
131 131 qualname = rhodecode
132 132 propagate = 0
133 133
134 134 [logger_sqlalchemy]
135 135 level = ERROR
136 136 handlers = console
137 137 qualname = sqlalchemy.engine
138 138 propagate = 0
139 139
140 140 ##############
141 141 ## HANDLERS ##
142 142 ##############
143 143
144 144 [handler_console]
145 145 class = StreamHandler
146 146 args = (sys.stderr,)
147 147 level = NOTSET
148 148 formatter = color_formatter
149 149
150 150 ################
151 151 ## FORMATTERS ##
152 152 ################
153 153
154 154 [formatter_generic]
155 155 format = %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
156 156 datefmt = %Y-%m-%d %H:%M:%S
157 157
158 158 [formatter_color_formatter]
159 159 class=rhodecode.lib.colored_formatter.ColorFormatter
160 160 format= %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
161 161 datefmt = %Y-%m-%d %H:%M:%S No newline at end of file
@@ -1,79 +1,79 b''
1 1 """Pylons environment configuration"""
2 2 from mako.lookup import TemplateLookup
3 3 from pylons.configuration import PylonsConfig
4 4 from pylons.error import handle_mako_error
5 5 from rhodecode.config.routing import make_map
6 6 from rhodecode.lib.auth import set_available_permissions, set_base_path
7 from rhodecode.lib.utils import repo2db_mapper, make_ui, set_hg_app_config
7 from rhodecode.lib.utils import repo2db_mapper, make_ui, set_rhodecode_config
8 8 from rhodecode.model import init_model
9 9 from rhodecode.model.hg_model import _get_repos_cached_initial
10 10 from sqlalchemy import engine_from_config
11 11 import logging
12 12 import os
13 13 import rhodecode.lib.app_globals as app_globals
14 14 import rhodecode.lib.helpers
15 15
16 16 log = logging.getLogger(__name__)
17 17
18 18 def load_environment(global_conf, app_conf, initial=False):
19 19 """Configure the Pylons environment via the ``pylons.config``
20 20 object
21 21 """
22 22 config = PylonsConfig()
23 23
24 24 # Pylons paths
25 25 root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
26 26 paths = dict(root=root,
27 27 controllers=os.path.join(root, 'controllers'),
28 28 static_files=os.path.join(root, 'public'),
29 29 templates=[os.path.join(root, 'templates')])
30 30
31 31 # Initialize config with the basic options
32 32 config.init_app(global_conf, app_conf, package='rhodecode', paths=paths)
33 33
34 34 config['routes.map'] = make_map(config)
35 35 config['pylons.app_globals'] = app_globals.Globals(config)
36 36 config['pylons.h'] = rhodecode.lib.helpers
37 37
38 38 # Setup cache object as early as possible
39 39 import pylons
40 40 pylons.cache._push_object(config['pylons.app_globals'].cache)
41 41
42 42 # Create the Mako TemplateLookup, with the default auto-escaping
43 43 config['pylons.app_globals'].mako_lookup = TemplateLookup(
44 44 directories=paths['templates'],
45 45 error_handler=handle_mako_error,
46 46 module_directory=os.path.join(app_conf['cache_dir'], 'templates'),
47 47 input_encoding='utf-8', default_filters=['escape'],
48 48 imports=['from webhelpers.html import escape'])
49 49
50 50 #sets the c attribute access when don't existing attribute are accessed
51 51 config['pylons.strict_tmpl_context'] = True
52 52 test = os.path.split(config['__file__'])[-1] == 'test.ini'
53 53 if test:
54 54 from rhodecode.lib.utils import create_test_env, create_test_index
55 55 create_test_env('/tmp', config)
56 56 create_test_index('/tmp/*', True)
57 57
58 58 #MULTIPLE DB configs
59 59 # Setup the SQLAlchemy database engine
60 60 if config['debug'] and not test:
61 61 #use query time debugging.
62 62 from rhodecode.lib.timerproxy import TimerProxy
63 63 sa_engine_db1 = engine_from_config(config, 'sqlalchemy.db1.',
64 64 proxy=TimerProxy())
65 65 else:
66 66 sa_engine_db1 = engine_from_config(config, 'sqlalchemy.db1.')
67 67
68 68 init_model(sa_engine_db1)
69 69 #init baseui
70 70 config['pylons.app_globals'].baseui = make_ui('db')
71 71
72 72 repo2db_mapper(_get_repos_cached_initial(config['pylons.app_globals'], initial))
73 73 set_available_permissions(config)
74 74 set_base_path(config)
75 set_hg_app_config(config)
75 set_rhodecode_config(config)
76 76 # CONFIGURATION OPTIONS HERE (note: all config options will override
77 77 # any Pylons config options)
78 78
79 79 return config
@@ -1,245 +1,245 b''
1 1 #!/usr/bin/env python
2 2 # encoding: utf-8
3 3 # repos controller for pylons
4 4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 5 #
6 6 # This program is free software; you can redistribute it and/or
7 7 # modify it under the terms of the GNU General Public License
8 8 # as published by the Free Software Foundation; version 2
9 9 # of the License or (at your opinion) any later version of the license.
10 10 #
11 11 # This program is distributed in the hope that it will be useful,
12 12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 14 # GNU General Public License for more details.
15 15 #
16 16 # You should have received a copy of the GNU General Public License
17 17 # along with this program; if not, write to the Free Software
18 18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 19 # MA 02110-1301, USA.
20 20 """
21 21 Created on April 7, 2010
22 22 admin controller for pylons
23 23 @author: marcink
24 24 """
25 25 from formencode import htmlfill
26 26 from operator import itemgetter
27 27 from paste.httpexceptions import HTTPInternalServerError
28 28 from pylons import request, response, session, tmpl_context as c, url
29 29 from pylons.controllers.util import abort, redirect
30 30 from pylons.i18n.translation import _
31 31 from rhodecode.lib import helpers as h
32 32 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator, \
33 33 HasPermissionAnyDecorator
34 34 from rhodecode.lib.base import BaseController, render
35 35 from rhodecode.lib.utils import invalidate_cache, action_logger
36 36 from rhodecode.model.db import User
37 37 from rhodecode.model.forms import RepoForm
38 38 from rhodecode.model.hg_model import HgModel
39 39 from rhodecode.model.repo_model import RepoModel
40 40 import formencode
41 41 import logging
42 42 import traceback
43 43
44 44 log = logging.getLogger(__name__)
45 45
46 46 class ReposController(BaseController):
47 47 """REST Controller styled on the Atom Publishing Protocol"""
48 48 # To properly map this controller, ensure your config/routing.py
49 49 # file has a resource setup:
50 50 # map.resource('repo', 'repos')
51 51
52 52 @LoginRequired()
53 53 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
54 54 def __before__(self):
55 55 c.admin_user = session.get('admin_user')
56 56 c.admin_username = session.get('admin_username')
57 57 super(ReposController, self).__before__()
58 58
59 59 @HasPermissionAllDecorator('hg.admin')
60 60 def index(self, format='html'):
61 61 """GET /repos: All items in the collection"""
62 62 # url('repos')
63 63 cached_repo_list = HgModel().get_repos()
64 64 c.repos_list = sorted(cached_repo_list, key=itemgetter('name_sort'))
65 65 return render('admin/repos/repos.html')
66 66
67 67 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
68 68 def create(self):
69 69 """POST /repos: Create a new item"""
70 70 # url('repos')
71 71 repo_model = RepoModel()
72 72 _form = RepoForm()()
73 73 form_result = {}
74 74 try:
75 75 form_result = _form.to_python(dict(request.POST))
76 repo_model.create(form_result, c.hg_app_user)
76 repo_model.create(form_result, c.rhodecode_user)
77 77 invalidate_cache('cached_repo_list')
78 78 h.flash(_('created repository %s') % form_result['repo_name'],
79 79 category='success')
80 80
81 81 if request.POST.get('user_created'):
82 action_logger(self.hg_app_user, 'user_created_repo',
82 action_logger(self.rhodecode_user, 'user_created_repo',
83 83 form_result['repo_name'], '', self.sa)
84 84 else:
85 action_logger(self.hg_app_user, 'admin_created_repo',
85 action_logger(self.rhodecode_user, 'admin_created_repo',
86 86 form_result['repo_name'], '', self.sa)
87 87
88 88 except formencode.Invalid as errors:
89 89 c.new_repo = errors.value['repo_name']
90 90
91 91 if request.POST.get('user_created'):
92 92 r = render('admin/repos/repo_add_create_repository.html')
93 93 else:
94 94 r = render('admin/repos/repo_add.html')
95 95
96 96 return htmlfill.render(
97 97 r,
98 98 defaults=errors.value,
99 99 errors=errors.error_dict or {},
100 100 prefix_error=False,
101 101 encoding="UTF-8")
102 102
103 103 except Exception:
104 104 log.error(traceback.format_exc())
105 105 msg = _('error occured during creation of repository %s') \
106 106 % form_result.get('repo_name')
107 107 h.flash(msg, category='error')
108 108 if request.POST.get('user_created'):
109 109 return redirect(url('hg_home'))
110 110 return redirect(url('repos'))
111 111
112 112 @HasPermissionAllDecorator('hg.admin')
113 113 def new(self, format='html'):
114 114 """GET /repos/new: Form to create a new item"""
115 115 new_repo = request.GET.get('repo', '')
116 116 c.new_repo = h.repo_name_slug(new_repo)
117 117
118 118 return render('admin/repos/repo_add.html')
119 119
120 120 @HasPermissionAllDecorator('hg.admin')
121 121 def update(self, repo_name):
122 122 """PUT /repos/repo_name: Update an existing item"""
123 123 # Forms posted to this method should contain a hidden field:
124 124 # <input type="hidden" name="_method" value="PUT" />
125 125 # Or using helpers:
126 126 # h.form(url('repo', repo_name=ID),
127 127 # method='put')
128 128 # url('repo', repo_name=ID)
129 129 repo_model = RepoModel()
130 130 changed_name = repo_name
131 131 _form = RepoForm(edit=True, old_data={'repo_name':repo_name})()
132 132
133 133 try:
134 134 form_result = _form.to_python(dict(request.POST))
135 135 repo_model.update(repo_name, form_result)
136 136 invalidate_cache('cached_repo_list')
137 137 h.flash(_('Repository %s updated succesfully' % repo_name),
138 138 category='success')
139 139 changed_name = form_result['repo_name']
140 140 except formencode.Invalid as errors:
141 141 c.repo_info = repo_model.get(repo_name)
142 142 c.users_array = repo_model.get_users_js()
143 143 errors.value.update({'user':c.repo_info.user.username})
144 144 return htmlfill.render(
145 145 render('admin/repos/repo_edit.html'),
146 146 defaults=errors.value,
147 147 errors=errors.error_dict or {},
148 148 prefix_error=False,
149 149 encoding="UTF-8")
150 150
151 151 except Exception:
152 152 log.error(traceback.format_exc())
153 153 h.flash(_('error occured during update of repository %s') \
154 154 % repo_name, category='error')
155 155
156 156 return redirect(url('edit_repo', repo_name=changed_name))
157 157
158 158 @HasPermissionAllDecorator('hg.admin')
159 159 def delete(self, repo_name):
160 160 """DELETE /repos/repo_name: Delete an existing item"""
161 161 # Forms posted to this method should contain a hidden field:
162 162 # <input type="hidden" name="_method" value="DELETE" />
163 163 # Or using helpers:
164 164 # h.form(url('repo', repo_name=ID),
165 165 # method='delete')
166 166 # url('repo', repo_name=ID)
167 167
168 168 repo_model = RepoModel()
169 169 repo = repo_model.get(repo_name)
170 170 if not repo:
171 171 h.flash(_('%s repository is not mapped to db perhaps'
172 172 ' it was moved or renamed from the filesystem'
173 173 ' please run the application again'
174 174 ' in order to rescan repositories') % repo_name,
175 175 category='error')
176 176
177 177 return redirect(url('repos'))
178 178 try:
179 action_logger(self.hg_app_user, 'admin_deleted_repo',
179 action_logger(self.rhodecode_user, 'admin_deleted_repo',
180 180 repo_name, '', self.sa)
181 181 repo_model.delete(repo)
182 182 invalidate_cache('cached_repo_list')
183 183 h.flash(_('deleted repository %s') % repo_name, category='success')
184 184
185 185 except Exception, e:
186 186 log.error(traceback.format_exc())
187 187 h.flash(_('An error occured during deletion of %s') % repo_name,
188 188 category='error')
189 189
190 190 return redirect(url('repos'))
191 191
192 192 @HasPermissionAllDecorator('hg.admin')
193 193 def delete_perm_user(self, repo_name):
194 194 """
195 195 DELETE an existing repository permission user
196 196 @param repo_name:
197 197 """
198 198
199 199 try:
200 200 repo_model = RepoModel()
201 201 repo_model.delete_perm_user(request.POST, repo_name)
202 202 except Exception as e:
203 203 h.flash(_('An error occured during deletion of repository user'),
204 204 category='error')
205 205 raise HTTPInternalServerError()
206 206
207 207 @HasPermissionAllDecorator('hg.admin')
208 208 def show(self, repo_name, format='html'):
209 209 """GET /repos/repo_name: Show a specific item"""
210 210 # url('repo', repo_name=ID)
211 211
212 212 @HasPermissionAllDecorator('hg.admin')
213 213 def edit(self, repo_name, format='html'):
214 214 """GET /repos/repo_name/edit: Form to edit an existing item"""
215 215 # url('edit_repo', repo_name=ID)
216 216 repo_model = RepoModel()
217 217 c.repo_info = repo = repo_model.get(repo_name)
218 218 if not repo:
219 219 h.flash(_('%s repository is not mapped to db perhaps'
220 220 ' it was created or renamed from the filesystem'
221 221 ' please run the application again'
222 222 ' in order to rescan repositories') % repo_name,
223 223 category='error')
224 224
225 225 return redirect(url('repos'))
226 226 defaults = c.repo_info.__dict__
227 227 if c.repo_info.user:
228 228 defaults.update({'user':c.repo_info.user.username})
229 229 else:
230 230 replacement_user = self.sa.query(User)\
231 231 .filter(User.admin == True).first().username
232 232 defaults.update({'user':replacement_user})
233 233
234 234 c.users_array = repo_model.get_users_js()
235 235
236 236 for p in c.repo_info.repo_to_perm:
237 237 defaults.update({'perm_%s' % p.user.username:
238 238 p.permission.permission_name})
239 239
240 240 return htmlfill.render(
241 241 render('admin/repos/repo_edit.html'),
242 242 defaults=defaults,
243 243 encoding="UTF-8",
244 244 force_defaults=False
245 245 )
@@ -1,298 +1,298 b''
1 1 #!/usr/bin/env python
2 2 # encoding: utf-8
3 3 # settings controller for pylons
4 4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 5 #
6 6 # This program is free software; you can redistribute it and/or
7 7 # modify it under the terms of the GNU General Public License
8 8 # as published by the Free Software Foundation; version 2
9 9 # of the License or (at your opinion) any later version of the license.
10 10 #
11 11 # This program is distributed in the hope that it will be useful,
12 12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 14 # GNU General Public License for more details.
15 15 #
16 16 # You should have received a copy of the GNU General Public License
17 17 # along with this program; if not, write to the Free Software
18 18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 19 # MA 02110-1301, USA.
20 20 """
21 21 Created on July 14, 2010
22 22 settings controller for pylons
23 23 @author: marcink
24 24 """
25 25 from formencode import htmlfill
26 26 from pylons import request, session, tmpl_context as c, url, app_globals as g, \
27 27 config
28 28 from pylons.controllers.util import abort, redirect
29 29 from pylons.i18n.translation import _
30 30 from rhodecode.lib import helpers as h
31 31 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator, \
32 32 HasPermissionAnyDecorator
33 33 from rhodecode.lib.base import BaseController, render
34 34 from rhodecode.lib.utils import repo2db_mapper, invalidate_cache, \
35 set_hg_app_config, get_hg_settings, get_hg_ui_settings, make_ui
35 set_rhodecode_config, get_hg_settings, get_hg_ui_settings, make_ui
36 36 from rhodecode.model.db import User, UserLog, HgAppSettings, HgAppUi
37 37 from rhodecode.model.forms import UserForm, ApplicationSettingsForm, \
38 38 ApplicationUiSettingsForm
39 39 from rhodecode.model.hg_model import HgModel
40 40 from rhodecode.model.user_model import UserModel
41 41 from rhodecode.lib.celerylib import tasks, run_task
42 42 import formencode
43 43 import logging
44 44 import traceback
45 45
46 46 log = logging.getLogger(__name__)
47 47
48 48
49 49 class SettingsController(BaseController):
50 50 """REST Controller styled on the Atom Publishing Protocol"""
51 51 # To properly map this controller, ensure your config/routing.py
52 52 # file has a resource setup:
53 53 # map.resource('setting', 'settings', controller='admin/settings',
54 54 # path_prefix='/admin', name_prefix='admin_')
55 55
56 56
57 57 @LoginRequired()
58 58 def __before__(self):
59 59 c.admin_user = session.get('admin_user')
60 60 c.admin_username = session.get('admin_username')
61 61 super(SettingsController, self).__before__()
62 62
63 63
64 64 @HasPermissionAllDecorator('hg.admin')
65 65 def index(self, format='html'):
66 66 """GET /admin/settings: All items in the collection"""
67 67 # url('admin_settings')
68 68
69 69 defaults = get_hg_settings()
70 70 defaults.update(get_hg_ui_settings())
71 71 return htmlfill.render(
72 72 render('admin/settings/settings.html'),
73 73 defaults=defaults,
74 74 encoding="UTF-8",
75 75 force_defaults=False
76 76 )
77 77
78 78 @HasPermissionAllDecorator('hg.admin')
79 79 def create(self):
80 80 """POST /admin/settings: Create a new item"""
81 81 # url('admin_settings')
82 82
83 83 @HasPermissionAllDecorator('hg.admin')
84 84 def new(self, format='html'):
85 85 """GET /admin/settings/new: Form to create a new item"""
86 86 # url('admin_new_setting')
87 87
88 88 @HasPermissionAllDecorator('hg.admin')
89 89 def update(self, setting_id):
90 90 """PUT /admin/settings/setting_id: Update an existing item"""
91 91 # Forms posted to this method should contain a hidden field:
92 92 # <input type="hidden" name="_method" value="PUT" />
93 93 # Or using helpers:
94 94 # h.form(url('admin_setting', setting_id=ID),
95 95 # method='put')
96 96 # url('admin_setting', setting_id=ID)
97 97 if setting_id == 'mapping':
98 98 rm_obsolete = request.POST.get('destroy', False)
99 99 log.debug('Rescanning directories with destroy=%s', rm_obsolete)
100 100
101 101 initial = HgModel.repo_scan(g.paths[0][0], g.paths[0][1], g.baseui)
102 102 repo2db_mapper(initial, rm_obsolete)
103 103 invalidate_cache('cached_repo_list')
104 104 h.flash(_('Repositories successfully rescanned'), category='success')
105 105
106 106 if setting_id == 'whoosh':
107 107 repo_location = get_hg_ui_settings()['paths_root_path']
108 108 full_index = request.POST.get('full_index', False)
109 109 task = run_task(tasks.whoosh_index, repo_location, full_index)
110 110
111 111 h.flash(_('Whoosh reindex task scheduled'), category='success')
112 112 if setting_id == 'global':
113 113
114 114 application_form = ApplicationSettingsForm()()
115 115 try:
116 116 form_result = application_form.to_python(dict(request.POST))
117 117
118 118 try:
119 119 hgsettings1 = self.sa.query(HgAppSettings)\
120 120 .filter(HgAppSettings.app_settings_name == 'title').one()
121 hgsettings1.app_settings_value = form_result['hg_app_title']
121 hgsettings1.app_settings_value = form_result['rhodecode_title']
122 122
123 123 hgsettings2 = self.sa.query(HgAppSettings)\
124 124 .filter(HgAppSettings.app_settings_name == 'realm').one()
125 hgsettings2.app_settings_value = form_result['hg_app_realm']
125 hgsettings2.app_settings_value = form_result['rhodecode_realm']
126 126
127 127
128 128 self.sa.add(hgsettings1)
129 129 self.sa.add(hgsettings2)
130 130 self.sa.commit()
131 set_hg_app_config(config)
131 set_rhodecode_config(config)
132 132 h.flash(_('Updated application settings'),
133 133 category='success')
134 134
135 135 except:
136 136 log.error(traceback.format_exc())
137 137 h.flash(_('error occurred during updating application settings'),
138 138 category='error')
139 139
140 140 self.sa.rollback()
141 141
142 142
143 143 except formencode.Invalid as errors:
144 144 return htmlfill.render(
145 145 render('admin/settings/settings.html'),
146 146 defaults=errors.value,
147 147 errors=errors.error_dict or {},
148 148 prefix_error=False,
149 149 encoding="UTF-8")
150 150
151 151 if setting_id == 'mercurial':
152 152 application_form = ApplicationUiSettingsForm()()
153 153 try:
154 154 form_result = application_form.to_python(dict(request.POST))
155 155
156 156 try:
157 157
158 158 hgsettings1 = self.sa.query(HgAppUi)\
159 159 .filter(HgAppUi.ui_key == 'push_ssl').one()
160 160 hgsettings1.ui_value = form_result['web_push_ssl']
161 161
162 162 hgsettings2 = self.sa.query(HgAppUi)\
163 163 .filter(HgAppUi.ui_key == '/').one()
164 164 hgsettings2.ui_value = form_result['paths_root_path']
165 165
166 166
167 167 #HOOKS
168 168 hgsettings3 = self.sa.query(HgAppUi)\
169 169 .filter(HgAppUi.ui_key == 'changegroup.update').one()
170 170 hgsettings3.ui_active = bool(form_result['hooks_changegroup_update'])
171 171
172 172 hgsettings4 = self.sa.query(HgAppUi)\
173 173 .filter(HgAppUi.ui_key == 'changegroup.repo_size').one()
174 174 hgsettings4.ui_active = bool(form_result['hooks_changegroup_repo_size'])
175 175
176 176
177 177
178 178
179 179 self.sa.add(hgsettings1)
180 180 self.sa.add(hgsettings2)
181 181 self.sa.add(hgsettings3)
182 182 self.sa.add(hgsettings4)
183 183 self.sa.commit()
184 184
185 185 h.flash(_('Updated mercurial settings'),
186 186 category='success')
187 187
188 188 except:
189 189 log.error(traceback.format_exc())
190 190 h.flash(_('error occurred during updating application settings'),
191 191 category='error')
192 192
193 193 self.sa.rollback()
194 194
195 195
196 196 except formencode.Invalid as errors:
197 197 return htmlfill.render(
198 198 render('admin/settings/settings.html'),
199 199 defaults=errors.value,
200 200 errors=errors.error_dict or {},
201 201 prefix_error=False,
202 202 encoding="UTF-8")
203 203
204 204
205 205
206 206 return redirect(url('admin_settings'))
207 207
208 208 @HasPermissionAllDecorator('hg.admin')
209 209 def delete(self, setting_id):
210 210 """DELETE /admin/settings/setting_id: Delete an existing item"""
211 211 # Forms posted to this method should contain a hidden field:
212 212 # <input type="hidden" name="_method" value="DELETE" />
213 213 # Or using helpers:
214 214 # h.form(url('admin_setting', setting_id=ID),
215 215 # method='delete')
216 216 # url('admin_setting', setting_id=ID)
217 217
218 218 @HasPermissionAllDecorator('hg.admin')
219 219 def show(self, setting_id, format='html'):
220 220 """GET /admin/settings/setting_id: Show a specific item"""
221 221 # url('admin_setting', setting_id=ID)
222 222
223 223 @HasPermissionAllDecorator('hg.admin')
224 224 def edit(self, setting_id, format='html'):
225 225 """GET /admin/settings/setting_id/edit: Form to edit an existing item"""
226 226 # url('admin_edit_setting', setting_id=ID)
227 227
228 228
229 229 def my_account(self):
230 230 """
231 231 GET /_admin/my_account Displays info about my account
232 232 """
233 233 # url('admin_settings_my_account')
234 c.user = self.sa.query(User).get(c.hg_app_user.user_id)
234 c.user = self.sa.query(User).get(c.rhodecode_user.user_id)
235 235 c.user_repos = []
236 236 for repo in c.cached_repo_list.values():
237 237 if repo.dbrepo.user.username == c.user.username:
238 238 c.user_repos.append(repo)
239 239
240 240 if c.user.username == 'default':
241 241 h.flash(_("You can't edit this user since it's"
242 242 " crucial for entire application"), category='warning')
243 243 return redirect(url('users'))
244 244
245 245 defaults = c.user.__dict__
246 246 return htmlfill.render(
247 247 render('admin/users/user_edit_my_account.html'),
248 248 defaults=defaults,
249 249 encoding="UTF-8",
250 250 force_defaults=False
251 251 )
252 252
253 253 def my_account_update(self):
254 254 """PUT /_admin/my_account_update: Update an existing item"""
255 255 # Forms posted to this method should contain a hidden field:
256 256 # <input type="hidden" name="_method" value="PUT" />
257 257 # Or using helpers:
258 258 # h.form(url('admin_settings_my_account_update'),
259 259 # method='put')
260 260 # url('admin_settings_my_account_update', id=ID)
261 261 user_model = UserModel()
262 uid = c.hg_app_user.user_id
262 uid = c.rhodecode_user.user_id
263 263 _form = UserForm(edit=True, old_data={'user_id':uid,
264 'email':c.hg_app_user.email})()
264 'email':c.rhodecode_user.email})()
265 265 form_result = {}
266 266 try:
267 267 form_result = _form.to_python(dict(request.POST))
268 268 user_model.update_my_account(uid, form_result)
269 269 h.flash(_('Your account was updated succesfully'),
270 270 category='success')
271 271
272 272 except formencode.Invalid as errors:
273 c.user = self.sa.query(User).get(c.hg_app_user.user_id)
273 c.user = self.sa.query(User).get(c.rhodecode_user.user_id)
274 274 c.user_repos = []
275 275 for repo in c.cached_repo_list.values():
276 276 if repo.dbrepo.user.username == c.user.username:
277 277 c.user_repos.append(repo)
278 278 return htmlfill.render(
279 279 render('admin/users/user_edit_my_account.html'),
280 280 defaults=errors.value,
281 281 errors=errors.error_dict or {},
282 282 prefix_error=False,
283 283 encoding="UTF-8")
284 284 except Exception:
285 285 log.error(traceback.format_exc())
286 286 h.flash(_('error occured during update of user %s') \
287 287 % form_result.get('username'), category='error')
288 288
289 289 return redirect(url('my_account'))
290 290
291 291 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
292 292 def create_repository(self):
293 293 """GET /_admin/create_repository: Form to create a new item"""
294 294 new_repo = request.GET.get('repo', '')
295 295 c.new_repo = h.repo_name_slug(new_repo)
296 296
297 297 return render('admin/repos/repo_add_create_repository.html')
298 298
@@ -1,168 +1,168 b''
1 1 #!/usr/bin/env python
2 2 # encoding: utf-8
3 3 # users controller for pylons
4 4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 5 #
6 6 # This program is free software; you can redistribute it and/or
7 7 # modify it under the terms of the GNU General Public License
8 8 # as published by the Free Software Foundation; version 2
9 9 # of the License or (at your opinion) any later version of the license.
10 10 #
11 11 # This program is distributed in the hope that it will be useful,
12 12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 14 # GNU General Public License for more details.
15 15 #
16 16 # You should have received a copy of the GNU General Public License
17 17 # along with this program; if not, write to the Free Software
18 18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 19 # MA 02110-1301, USA.
20 20 from rhodecode.lib.utils import action_logger
21 21 """
22 22 Created on April 4, 2010
23 23 users controller for pylons
24 24 @author: marcink
25 25 """
26 26
27 27 from formencode import htmlfill
28 28 from pylons import request, session, tmpl_context as c, url
29 29 from pylons.controllers.util import abort, redirect
30 30 from pylons.i18n.translation import _
31 31 from rhodecode.lib import helpers as h
32 32 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator
33 33 from rhodecode.lib.base import BaseController, render
34 34 from rhodecode.model.db import User, UserLog
35 35 from rhodecode.model.forms import UserForm
36 36 from rhodecode.model.user_model import UserModel, DefaultUserException
37 37 import formencode
38 38 import logging
39 39 import traceback
40 40
41 41 log = logging.getLogger(__name__)
42 42
43 43 class UsersController(BaseController):
44 44 """REST Controller styled on the Atom Publishing Protocol"""
45 45 # To properly map this controller, ensure your config/routing.py
46 46 # file has a resource setup:
47 47 # map.resource('user', 'users')
48 48
49 49 @LoginRequired()
50 50 @HasPermissionAllDecorator('hg.admin')
51 51 def __before__(self):
52 52 c.admin_user = session.get('admin_user')
53 53 c.admin_username = session.get('admin_username')
54 54 super(UsersController, self).__before__()
55 55
56 56
57 57 def index(self, format='html'):
58 58 """GET /users: All items in the collection"""
59 59 # url('users')
60 60
61 61 c.users_list = self.sa.query(User).all()
62 62 return render('admin/users/users.html')
63 63
64 64 def create(self):
65 65 """POST /users: Create a new item"""
66 66 # url('users')
67 67
68 68 user_model = UserModel()
69 69 login_form = UserForm()()
70 70 try:
71 71 form_result = login_form.to_python(dict(request.POST))
72 72 user_model.create(form_result)
73 73 h.flash(_('created user %s') % form_result['username'],
74 74 category='success')
75 #action_logger(self.hg_app_user, 'new_user', '', '', self.sa)
75 #action_logger(self.rhodecode_user, 'new_user', '', '', self.sa)
76 76 except formencode.Invalid as errors:
77 77 return htmlfill.render(
78 78 render('admin/users/user_add.html'),
79 79 defaults=errors.value,
80 80 errors=errors.error_dict or {},
81 81 prefix_error=False,
82 82 encoding="UTF-8")
83 83 except Exception:
84 84 log.error(traceback.format_exc())
85 85 h.flash(_('error occured during creation of user %s') \
86 86 % request.POST.get('username'), category='error')
87 87 return redirect(url('users'))
88 88
89 89 def new(self, format='html'):
90 90 """GET /users/new: Form to create a new item"""
91 91 # url('new_user')
92 92 return render('admin/users/user_add.html')
93 93
94 94 def update(self, id):
95 95 """PUT /users/id: Update an existing item"""
96 96 # Forms posted to this method should contain a hidden field:
97 97 # <input type="hidden" name="_method" value="PUT" />
98 98 # Or using helpers:
99 99 # h.form(url('user', id=ID),
100 100 # method='put')
101 101 # url('user', id=ID)
102 102 user_model = UserModel()
103 103 c.user = user_model.get_user(id)
104 104
105 105 _form = UserForm(edit=True, old_data={'user_id':id,
106 106 'email':c.user.email})()
107 107 form_result = {}
108 108 try:
109 109 form_result = _form.to_python(dict(request.POST))
110 110 user_model.update(id, form_result)
111 111 h.flash(_('User updated succesfully'), category='success')
112 112
113 113 except formencode.Invalid as errors:
114 114 return htmlfill.render(
115 115 render('admin/users/user_edit.html'),
116 116 defaults=errors.value,
117 117 errors=errors.error_dict or {},
118 118 prefix_error=False,
119 119 encoding="UTF-8")
120 120 except Exception:
121 121 log.error(traceback.format_exc())
122 122 h.flash(_('error occured during update of user %s') \
123 123 % form_result.get('username'), category='error')
124 124
125 125 return redirect(url('users'))
126 126
127 127 def delete(self, id):
128 128 """DELETE /users/id: Delete an existing item"""
129 129 # Forms posted to this method should contain a hidden field:
130 130 # <input type="hidden" name="_method" value="DELETE" />
131 131 # Or using helpers:
132 132 # h.form(url('user', id=ID),
133 133 # method='delete')
134 134 # url('user', id=ID)
135 135 user_model = UserModel()
136 136 try:
137 137 user_model.delete(id)
138 138 h.flash(_('sucessfully deleted user'), category='success')
139 139 except DefaultUserException as e:
140 140 h.flash(str(e), category='warning')
141 141 except Exception:
142 142 h.flash(_('An error occured during deletion of user'),
143 143 category='error')
144 144 return redirect(url('users'))
145 145
146 146 def show(self, id, format='html'):
147 147 """GET /users/id: Show a specific item"""
148 148 # url('user', id=ID)
149 149
150 150
151 151 def edit(self, id, format='html'):
152 152 """GET /users/id/edit: Form to edit an existing item"""
153 153 # url('edit_user', id=ID)
154 154 c.user = self.sa.query(User).get(id)
155 155 if not c.user:
156 156 return redirect(url('users'))
157 157 if c.user.username == 'default':
158 158 h.flash(_("You can't edit this user since it's"
159 159 " crucial for entire application"), category='warning')
160 160 return redirect(url('users'))
161 161
162 162 defaults = c.user.__dict__
163 163 return htmlfill.render(
164 164 render('admin/users/user_edit.html'),
165 165 defaults=defaults,
166 166 encoding="UTF-8",
167 167 force_defaults=False
168 168 )
@@ -1,144 +1,144 b''
1 1 #!/usr/bin/env python
2 2 # encoding: utf-8
3 3 # login controller for pylons
4 4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 5 #
6 6 # This program is free software; you can redistribute it and/or
7 7 # modify it under the terms of the GNU General Public License
8 8 # as published by the Free Software Foundation; version 2
9 9 # of the License or (at your opinion) any later version of the license.
10 10 #
11 11 # This program is distributed in the hope that it will be useful,
12 12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 14 # GNU General Public License for more details.
15 15 #
16 16 # You should have received a copy of the GNU General Public License
17 17 # along with this program; if not, write to the Free Software
18 18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 19 # MA 02110-1301, USA.
20 20
21 21 """
22 22 Created on April 22, 2010
23 23 login controller for pylons
24 24 @author: marcink
25 25 """
26 26 from formencode import htmlfill
27 27 from pylons import request, response, session, tmpl_context as c, url
28 28 from pylons.controllers.util import abort, redirect
29 29 from rhodecode.lib.auth import AuthUser, HasPermissionAnyDecorator
30 30 from rhodecode.lib.base import BaseController, render
31 31 import rhodecode.lib.helpers as h
32 32 from pylons.i18n.translation import _
33 33 from rhodecode.model.forms import LoginForm, RegisterForm, PasswordResetForm
34 34 from rhodecode.model.user_model import UserModel
35 35 import formencode
36 36 import logging
37 37
38 38 log = logging.getLogger(__name__)
39 39
40 40 class LoginController(BaseController):
41 41
42 42 def __before__(self):
43 43 super(LoginController, self).__before__()
44 44
45 45 def index(self):
46 46 #redirect if already logged in
47 47 c.came_from = request.GET.get('came_from', None)
48 48
49 if c.hg_app_user.is_authenticated:
49 if c.rhodecode_user.is_authenticated:
50 50 return redirect(url('hg_home'))
51 51
52 52 if request.POST:
53 53 #import Login Form validator class
54 54 login_form = LoginForm()
55 55 try:
56 56 c.form_result = login_form.to_python(dict(request.POST))
57 57 username = c.form_result['username']
58 58 user = UserModel().get_user_by_name(username)
59 59 auth_user = AuthUser()
60 60 auth_user.username = user.username
61 61 auth_user.is_authenticated = True
62 62 auth_user.is_admin = user.admin
63 63 auth_user.user_id = user.user_id
64 64 auth_user.name = user.name
65 65 auth_user.lastname = user.lastname
66 session['hg_app_user'] = auth_user
66 session['rhodecode_user'] = auth_user
67 67 session.save()
68 68 log.info('user %s is now authenticated', username)
69 69
70 70 user.update_lastlogin()
71 71
72 72 if c.came_from:
73 73 return redirect(c.came_from)
74 74 else:
75 75 return redirect(url('hg_home'))
76 76
77 77 except formencode.Invalid as errors:
78 78 return htmlfill.render(
79 79 render('/login.html'),
80 80 defaults=errors.value,
81 81 errors=errors.error_dict or {},
82 82 prefix_error=False,
83 83 encoding="UTF-8")
84 84
85 85 return render('/login.html')
86 86
87 87 @HasPermissionAnyDecorator('hg.admin', 'hg.register.auto_activate',
88 88 'hg.register.manual_activate')
89 89 def register(self):
90 90 user_model = UserModel()
91 91 c.auto_active = False
92 92 for perm in user_model.get_default().user_perms:
93 93 if perm.permission.permission_name == 'hg.register.auto_activate':
94 94 c.auto_active = True
95 95 break
96 96
97 97 if request.POST:
98 98
99 99 register_form = RegisterForm()()
100 100 try:
101 101 form_result = register_form.to_python(dict(request.POST))
102 102 form_result['active'] = c.auto_active
103 103 user_model.create_registration(form_result)
104 104 h.flash(_('You have successfully registered into hg-app'),
105 105 category='success')
106 106 return redirect(url('login_home'))
107 107
108 108 except formencode.Invalid as errors:
109 109 return htmlfill.render(
110 110 render('/register.html'),
111 111 defaults=errors.value,
112 112 errors=errors.error_dict or {},
113 113 prefix_error=False,
114 114 encoding="UTF-8")
115 115
116 116 return render('/register.html')
117 117
118 118 def password_reset(self):
119 119 user_model = UserModel()
120 120 if request.POST:
121 121
122 122 password_reset_form = PasswordResetForm()()
123 123 try:
124 124 form_result = password_reset_form.to_python(dict(request.POST))
125 125 user_model.reset_password(form_result)
126 126 h.flash(_('Your new password was sent'),
127 127 category='success')
128 128 return redirect(url('login_home'))
129 129
130 130 except formencode.Invalid as errors:
131 131 return htmlfill.render(
132 132 render('/password_reset.html'),
133 133 defaults=errors.value,
134 134 errors=errors.error_dict or {},
135 135 prefix_error=False,
136 136 encoding="UTF-8")
137 137
138 138 return render('/password_reset.html')
139 139
140 140 def logout(self):
141 session['hg_app_user'] = AuthUser()
141 session['rhodecode_user'] = AuthUser()
142 142 session.save()
143 143 log.info('Logging out and setting user as Empty')
144 144 redirect(url('hg_home'))
@@ -1,175 +1,175 b''
1 1 #!/usr/bin/env python
2 2 # encoding: utf-8
3 3 # settings controller for pylons
4 4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 5 #
6 6 # This program is free software; you can redistribute it and/or
7 7 # modify it under the terms of the GNU General Public License
8 8 # as published by the Free Software Foundation; version 2
9 9 # of the License or (at your opinion) any later version of the license.
10 10 #
11 11 # This program is distributed in the hope that it will be useful,
12 12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 14 # GNU General Public License for more details.
15 15 #
16 16 # You should have received a copy of the GNU General Public License
17 17 # along with this program; if not, write to the Free Software
18 18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 19 # MA 02110-1301, USA.
20 20 """
21 21 Created on June 30, 2010
22 22 settings controller for pylons
23 23 @author: marcink
24 24 """
25 25 from formencode import htmlfill
26 26 from pylons import tmpl_context as c, request, url
27 27 from pylons.controllers.util import redirect
28 28 from pylons.i18n.translation import _
29 29 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAllDecorator
30 30 from rhodecode.lib.base import BaseController, render
31 31 from rhodecode.lib.utils import invalidate_cache, action_logger
32 32 from rhodecode.model.forms import RepoSettingsForm, RepoForkForm
33 33 from rhodecode.model.repo_model import RepoModel
34 34 import formencode
35 35 import logging
36 36 import rhodecode.lib.helpers as h
37 37 import traceback
38 38
39 39 log = logging.getLogger(__name__)
40 40
41 41 class SettingsController(BaseController):
42 42
43 43 @LoginRequired()
44 44 @HasRepoPermissionAllDecorator('repository.admin')
45 45 def __before__(self):
46 46 super(SettingsController, self).__before__()
47 47
48 48 def index(self, repo_name):
49 49 repo_model = RepoModel()
50 50 c.repo_info = repo = repo_model.get(repo_name)
51 51 if not repo:
52 52 h.flash(_('%s repository is not mapped to db perhaps'
53 53 ' it was created or renamed from the filesystem'
54 54 ' please run the application again'
55 55 ' in order to rescan repositories') % repo_name,
56 56 category='error')
57 57
58 58 return redirect(url('hg_home'))
59 59 defaults = c.repo_info.__dict__
60 60 defaults.update({'user':c.repo_info.user.username})
61 61 c.users_array = repo_model.get_users_js()
62 62
63 63 for p in c.repo_info.repo_to_perm:
64 64 defaults.update({'perm_%s' % p.user.username:
65 65 p.permission.permission_name})
66 66
67 67 return htmlfill.render(
68 68 render('settings/repo_settings.html'),
69 69 defaults=defaults,
70 70 encoding="UTF-8",
71 71 force_defaults=False
72 72 )
73 73
74 74 def update(self, repo_name):
75 75 repo_model = RepoModel()
76 76 changed_name = repo_name
77 77 _form = RepoSettingsForm(edit=True, old_data={'repo_name':repo_name})()
78 78 try:
79 79 form_result = _form.to_python(dict(request.POST))
80 80 repo_model.update(repo_name, form_result)
81 81 invalidate_cache('cached_repo_list')
82 82 h.flash(_('Repository %s updated successfully' % repo_name),
83 83 category='success')
84 84 changed_name = form_result['repo_name']
85 85 except formencode.Invalid as errors:
86 86 c.repo_info = repo_model.get(repo_name)
87 87 c.users_array = repo_model.get_users_js()
88 88 errors.value.update({'user':c.repo_info.user.username})
89 89 return htmlfill.render(
90 90 render('settings/repo_settings.html'),
91 91 defaults=errors.value,
92 92 errors=errors.error_dict or {},
93 93 prefix_error=False,
94 94 encoding="UTF-8")
95 95 except Exception:
96 96 log.error(traceback.format_exc())
97 97 h.flash(_('error occured during update of repository %s') \
98 98 % repo_name, category='error')
99 99
100 100 return redirect(url('repo_settings_home', repo_name=changed_name))
101 101
102 102
103 103
104 104 def delete(self, repo_name):
105 105 """DELETE /repos/repo_name: Delete an existing item"""
106 106 # Forms posted to this method should contain a hidden field:
107 107 # <input type="hidden" name="_method" value="DELETE" />
108 108 # Or using helpers:
109 109 # h.form(url('repo_settings_delete', repo_name=ID),
110 110 # method='delete')
111 111 # url('repo_settings_delete', repo_name=ID)
112 112
113 113 repo_model = RepoModel()
114 114 repo = repo_model.get(repo_name)
115 115 if not repo:
116 116 h.flash(_('%s repository is not mapped to db perhaps'
117 117 ' it was moved or renamed from the filesystem'
118 118 ' please run the application again'
119 119 ' in order to rescan repositories') % repo_name,
120 120 category='error')
121 121
122 122 return redirect(url('hg_home'))
123 123 try:
124 action_logger(self.hg_app_user, 'user_deleted_repo',
124 action_logger(self.rhodecode_user, 'user_deleted_repo',
125 125 repo_name, '', self.sa)
126 126 repo_model.delete(repo)
127 127 invalidate_cache('cached_repo_list')
128 128 h.flash(_('deleted repository %s') % repo_name, category='success')
129 129 except Exception:
130 130 h.flash(_('An error occurred during deletion of %s') % repo_name,
131 131 category='error')
132 132
133 133 return redirect(url('hg_home'))
134 134
135 135 def fork(self, repo_name):
136 136 repo_model = RepoModel()
137 137 c.repo_info = repo = repo_model.get(repo_name)
138 138 if not repo:
139 139 h.flash(_('%s repository is not mapped to db perhaps'
140 140 ' it was created or renamed from the filesystem'
141 141 ' please run the application again'
142 142 ' in order to rescan repositories') % repo_name,
143 143 category='error')
144 144
145 145 return redirect(url('hg_home'))
146 146
147 147 return render('settings/repo_fork.html')
148 148
149 149
150 150
151 151 def fork_create(self, repo_name):
152 152 repo_model = RepoModel()
153 153 c.repo_info = repo_model.get(repo_name)
154 154 _form = RepoForkForm()()
155 155 form_result = {}
156 156 try:
157 157 form_result = _form.to_python(dict(request.POST))
158 158 form_result.update({'repo_name':repo_name})
159 repo_model.create_fork(form_result, c.hg_app_user)
159 repo_model.create_fork(form_result, c.rhodecode_user)
160 160 h.flash(_('fork %s repository as %s task added') \
161 161 % (repo_name, form_result['fork_name']),
162 162 category='success')
163 action_logger(self.hg_app_user, 'user_forked_repo',
163 action_logger(self.rhodecode_user, 'user_forked_repo',
164 164 repo_name, '', self.sa)
165 165 except formencode.Invalid as errors:
166 166 c.new_repo = errors.value['fork_name']
167 167 r = render('settings/repo_fork.html')
168 168
169 169 return htmlfill.render(
170 170 r,
171 171 defaults=errors.value,
172 172 errors=errors.error_dict or {},
173 173 prefix_error=False,
174 174 encoding="UTF-8")
175 175 return redirect(url('hg_home'))
@@ -1,104 +1,104 b''
1 1 #!/usr/bin/env python
2 2 # encoding: utf-8
3 3 # summary controller for pylons
4 4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 5 #
6 6 # This program is free software; you can redistribute it and/or
7 7 # modify it under the terms of the GNU General Public License
8 8 # as published by the Free Software Foundation; version 2
9 9 # of the License or (at your opinion) any later version of the license.
10 10 #
11 11 # This program is distributed in the hope that it will be useful,
12 12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 14 # GNU General Public License for more details.
15 15 #
16 16 # You should have received a copy of the GNU General Public License
17 17 # along with this program; if not, write to the Free Software
18 18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 19 # MA 02110-1301, USA.
20 20 """
21 21 Created on April 18, 2010
22 22 summary controller for pylons
23 23 @author: marcink
24 24 """
25 25 from pylons import tmpl_context as c, request, url
26 26 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
27 27 from rhodecode.lib.base import BaseController, render
28 28 from rhodecode.lib.utils import OrderedDict
29 29 from rhodecode.model.hg_model import HgModel
30 30 from rhodecode.model.db import Statistics
31 31 from webhelpers.paginate import Page
32 32 from rhodecode.lib.celerylib import run_task
33 33 from rhodecode.lib.celerylib.tasks import get_commits_stats
34 34 from datetime import datetime, timedelta
35 35 from time import mktime
36 36 import calendar
37 37 import logging
38 38 import json
39 39 log = logging.getLogger(__name__)
40 40
41 41 class SummaryController(BaseController):
42 42
43 43 @LoginRequired()
44 44 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
45 45 'repository.admin')
46 46 def __before__(self):
47 47 super(SummaryController, self).__before__()
48 48
49 49 def index(self):
50 50 hg_model = HgModel()
51 51 c.repo_info = hg_model.get_repo(c.repo_name)
52 52 c.repo_changesets = Page(list(c.repo_info[:10]), page=1, items_per_page=20)
53 53 e = request.environ
54 54
55 55 uri = u'%(protocol)s://%(user)s@%(host)s%(prefix)s/%(repo_name)s' % {
56 56 'protocol': e.get('wsgi.url_scheme'),
57 'user':str(c.hg_app_user.username),
57 'user':str(c.rhodecode_user.username),
58 58 'host':e.get('HTTP_HOST'),
59 59 'prefix':e.get('SCRIPT_NAME'),
60 60 'repo_name':c.repo_name, }
61 61 c.clone_repo_url = uri
62 62 c.repo_tags = OrderedDict()
63 63 for name, hash in c.repo_info.tags.items()[:10]:
64 64 c.repo_tags[name] = c.repo_info.get_changeset(hash)
65 65
66 66 c.repo_branches = OrderedDict()
67 67 for name, hash in c.repo_info.branches.items()[:10]:
68 68 c.repo_branches[name] = c.repo_info.get_changeset(hash)
69 69
70 70 td = datetime.today() + timedelta(days=1)
71 71 y, m, d = td.year, td.month, td.day
72 72
73 73 ts_min_y = mktime((y - 1, (td - timedelta(days=calendar.mdays[m])).month,
74 74 d, 0, 0, 0, 0, 0, 0,))
75 75 ts_min_m = mktime((y, (td - timedelta(days=calendar.mdays[m])).month,
76 76 d, 0, 0, 0, 0, 0, 0,))
77 77
78 78 ts_max_y = mktime((y, m, d, 0, 0, 0, 0, 0, 0,))
79 79
80 80 run_task(get_commits_stats, c.repo_info.name, ts_min_y, ts_max_y)
81 81 c.ts_min = ts_min_m
82 82 c.ts_max = ts_max_y
83 83
84 84 stats = self.sa.query(Statistics)\
85 85 .filter(Statistics.repository == c.repo_info.dbrepo)\
86 86 .scalar()
87 87
88 88
89 89 if stats and stats.languages:
90 90 lang_stats = json.loads(stats.languages)
91 91 c.commit_data = stats.commit_activity
92 92 c.overview_data = stats.commit_activity_combined
93 93 c.trending_languages = json.dumps(OrderedDict(
94 94 sorted(lang_stats.items(), reverse=True,
95 95 key=lambda k: k[1])[:2]
96 96 )
97 97 )
98 98 else:
99 99 c.commit_data = json.dumps({})
100 100 c.overview_data = json.dumps([[ts_min_y, 0], [ts_max_y, 0] ])
101 101 c.trending_languages = json.dumps({})
102 102
103 103 return render('summary/summary.html')
104 104
@@ -1,486 +1,486 b''
1 1 #!/usr/bin/env python
2 2 # encoding: utf-8
3 3 # authentication and permission libraries
4 4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 5 #
6 6 # This program is free software; you can redistribute it and/or
7 7 # modify it under the terms of the GNU General Public License
8 8 # as published by the Free Software Foundation; version 2
9 9 # of the License or (at your opinion) any later version of the license.
10 10 #
11 11 # This program is distributed in the hope that it will be useful,
12 12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 14 # GNU General Public License for more details.
15 15 #
16 16 # You should have received a copy of the GNU General Public License
17 17 # along with this program; if not, write to the Free Software
18 18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 19 # MA 02110-1301, USA.
20 20 """
21 21 Created on April 4, 2010
22 22
23 23 @author: marcink
24 24 """
25 25 from beaker.cache import cache_region
26 26 from pylons import config, session, url, request
27 27 from pylons.controllers.util import abort, redirect
28 28 from rhodecode.lib.utils import get_repo_slug
29 29 from rhodecode.model import meta
30 30 from rhodecode.model.db import User, RepoToPerm, Repository, Permission, \
31 31 UserToPerm
32 32 from sqlalchemy.exc import OperationalError
33 33 from sqlalchemy.orm.exc import NoResultFound, MultipleResultsFound
34 34 import bcrypt
35 35 from decorator import decorator
36 36 import logging
37 37 import random
38 38
39 39 log = logging.getLogger(__name__)
40 40
41 41 class PasswordGenerator(object):
42 42 """This is a simple class for generating password from
43 43 different sets of characters
44 44 usage:
45 45 passwd_gen = PasswordGenerator()
46 46 #print 8-letter password containing only big and small letters of alphabet
47 47 print passwd_gen.gen_password(8, passwd_gen.ALPHABETS_BIG_SMALL)
48 48 """
49 49 ALPHABETS_NUM = r'''1234567890'''#[0]
50 50 ALPHABETS_SMALL = r'''qwertyuiopasdfghjklzxcvbnm'''#[1]
51 51 ALPHABETS_BIG = r'''QWERTYUIOPASDFGHJKLZXCVBNM'''#[2]
52 52 ALPHABETS_SPECIAL = r'''`-=[]\;',./~!@#$%^&*()_+{}|:"<>?''' #[3]
53 53 ALPHABETS_FULL = ALPHABETS_BIG + ALPHABETS_SMALL + ALPHABETS_NUM + ALPHABETS_SPECIAL#[4]
54 54 ALPHABETS_ALPHANUM = ALPHABETS_BIG + ALPHABETS_SMALL + ALPHABETS_NUM#[5]
55 55 ALPHABETS_BIG_SMALL = ALPHABETS_BIG + ALPHABETS_SMALL
56 56 ALPHABETS_ALPHANUM_BIG = ALPHABETS_BIG + ALPHABETS_NUM#[6]
57 57 ALPHABETS_ALPHANUM_SMALL = ALPHABETS_SMALL + ALPHABETS_NUM#[7]
58 58
59 59 def __init__(self, passwd=''):
60 60 self.passwd = passwd
61 61
62 62 def gen_password(self, len, type):
63 63 self.passwd = ''.join([random.choice(type) for _ in xrange(len)])
64 64 return self.passwd
65 65
66 66
67 67 def get_crypt_password(password):
68 68 """Cryptographic function used for password hashing based on sha1
69 69 @param password: password to hash
70 70 """
71 71 return bcrypt.hashpw(password, bcrypt.gensalt(10))
72 72
73 73 def check_password(password, hashed):
74 74 return bcrypt.hashpw(password, hashed) == hashed
75 75
76 76 @cache_region('super_short_term', 'cached_user')
77 77 def get_user_cached(username):
78 78 sa = meta.Session
79 79 try:
80 80 user = sa.query(User).filter(User.username == username).one()
81 81 finally:
82 82 meta.Session.remove()
83 83 return user
84 84
85 85 def authfunc(environ, username, password):
86 86 try:
87 87 user = get_user_cached(username)
88 88 except (NoResultFound, MultipleResultsFound, OperationalError) as e:
89 89 log.error(e)
90 90 user = None
91 91
92 92 if user:
93 93 if user.active:
94 94 if user.username == username and check_password(password, user.password):
95 95 log.info('user %s authenticated correctly', username)
96 96 return True
97 97 else:
98 98 log.error('user %s is disabled', username)
99 99
100 100 return False
101 101
102 102 class AuthUser(object):
103 103 """
104 104 A simple object that handles a mercurial username for authentication
105 105 """
106 106 def __init__(self):
107 107 self.username = 'None'
108 108 self.name = ''
109 109 self.lastname = ''
110 110 self.email = ''
111 111 self.user_id = None
112 112 self.is_authenticated = False
113 113 self.is_admin = False
114 114 self.permissions = {}
115 115
116 116
117 117 def set_available_permissions(config):
118 118 """
119 119 This function will propagate pylons globals with all available defined
120 120 permission given in db. We don't wannt to check each time from db for new
121 121 permissions since adding a new permission also requires application restart
122 122 ie. to decorate new views with the newly created permission
123 123 @param config:
124 124 """
125 125 log.info('getting information about all available permissions')
126 126 try:
127 127 sa = meta.Session
128 128 all_perms = sa.query(Permission).all()
129 129 finally:
130 130 meta.Session.remove()
131 131
132 132 config['available_permissions'] = [x.permission_name for x in all_perms]
133 133
134 134 def set_base_path(config):
135 135 config['base_path'] = config['pylons.app_globals'].base_path
136 136
137 137 def fill_data(user):
138 138 """
139 139 Fills user data with those from database and log out user if not present
140 140 in database
141 141 @param user:
142 142 """
143 143 sa = meta.Session
144 144 dbuser = sa.query(User).get(user.user_id)
145 145 if dbuser:
146 146 user.username = dbuser.username
147 147 user.is_admin = dbuser.admin
148 148 user.name = dbuser.name
149 149 user.lastname = dbuser.lastname
150 150 user.email = dbuser.email
151 151 else:
152 152 user.is_authenticated = False
153 153 meta.Session.remove()
154 154 return user
155 155
156 156 def fill_perms(user):
157 157 """
158 158 Fills user permission attribute with permissions taken from database
159 159 @param user:
160 160 """
161 161
162 162 sa = meta.Session
163 163 user.permissions['repositories'] = {}
164 164 user.permissions['global'] = set()
165 165
166 166 #===========================================================================
167 167 # fetch default permissions
168 168 #===========================================================================
169 169 default_perms = sa.query(RepoToPerm, Repository, Permission)\
170 170 .join((Repository, RepoToPerm.repository_id == Repository.repo_id))\
171 171 .join((Permission, RepoToPerm.permission_id == Permission.permission_id))\
172 172 .filter(RepoToPerm.user == sa.query(User).filter(User.username ==
173 173 'default').scalar()).all()
174 174
175 175 if user.is_admin:
176 176 #=======================================================================
177 177 # #admin have all default rights set to admin
178 178 #=======================================================================
179 179 user.permissions['global'].add('hg.admin')
180 180
181 181 for perm in default_perms:
182 182 p = 'repository.admin'
183 183 user.permissions['repositories'][perm.RepoToPerm.repository.repo_name] = p
184 184
185 185 else:
186 186 #=======================================================================
187 187 # set default permissions
188 188 #=======================================================================
189 189
190 190 #default global
191 191 default_global_perms = sa.query(UserToPerm)\
192 192 .filter(UserToPerm.user == sa.query(User).filter(User.username ==
193 193 'default').one())
194 194
195 195 for perm in default_global_perms:
196 196 user.permissions['global'].add(perm.permission.permission_name)
197 197
198 198 #default repositories
199 199 for perm in default_perms:
200 200 if perm.Repository.private and not perm.Repository.user_id == user.user_id:
201 201 #disable defaults for private repos,
202 202 p = 'repository.none'
203 203 elif perm.Repository.user_id == user.user_id:
204 204 #set admin if owner
205 205 p = 'repository.admin'
206 206 else:
207 207 p = perm.Permission.permission_name
208 208
209 209 user.permissions['repositories'][perm.RepoToPerm.repository.repo_name] = p
210 210
211 211 #=======================================================================
212 212 # #overwrite default with user permissions if any
213 213 #=======================================================================
214 214 user_perms = sa.query(RepoToPerm, Permission, Repository)\
215 215 .join((Repository, RepoToPerm.repository_id == Repository.repo_id))\
216 216 .join((Permission, RepoToPerm.permission_id == Permission.permission_id))\
217 217 .filter(RepoToPerm.user_id == user.user_id).all()
218 218
219 219 for perm in user_perms:
220 220 if perm.Repository.user_id == user.user_id:#set admin if owner
221 221 p = 'repository.admin'
222 222 else:
223 223 p = perm.Permission.permission_name
224 224 user.permissions['repositories'][perm.RepoToPerm.repository.repo_name] = p
225 225 meta.Session.remove()
226 226 return user
227 227
228 228 def get_user(session):
229 229 """
230 230 Gets user from session, and wraps permissions into user
231 231 @param session:
232 232 """
233 user = session.get('hg_app_user', AuthUser())
233 user = session.get('rhodecode_user', AuthUser())
234 234 if user.is_authenticated:
235 235 user = fill_data(user)
236 236 user = fill_perms(user)
237 session['hg_app_user'] = user
237 session['rhodecode_user'] = user
238 238 session.save()
239 239 return user
240 240
241 241 #===============================================================================
242 242 # CHECK DECORATORS
243 243 #===============================================================================
244 244 class LoginRequired(object):
245 245 """Must be logged in to execute this function else redirect to login page"""
246 246
247 247 def __call__(self, func):
248 248 return decorator(self.__wrapper, func)
249 249
250 250 def __wrapper(self, func, *fargs, **fkwargs):
251 user = session.get('hg_app_user', AuthUser())
251 user = session.get('rhodecode_user', AuthUser())
252 252 log.debug('Checking login required for user:%s', user.username)
253 253 if user.is_authenticated:
254 254 log.debug('user %s is authenticated', user.username)
255 255 return func(*fargs, **fkwargs)
256 256 else:
257 257 log.warn('user %s not authenticated', user.username)
258 258
259 259 p = ''
260 260 if request.environ.get('SCRIPT_NAME') != '/':
261 261 p += request.environ.get('SCRIPT_NAME')
262 262
263 263 p += request.environ.get('PATH_INFO')
264 264 if request.environ.get('QUERY_STRING'):
265 265 p += '?' + request.environ.get('QUERY_STRING')
266 266
267 267 log.debug('redirecting to login page with %s', p)
268 268 return redirect(url('login_home', came_from=p))
269 269
270 270 class PermsDecorator(object):
271 271 """Base class for decorators"""
272 272
273 273 def __init__(self, *required_perms):
274 274 available_perms = config['available_permissions']
275 275 for perm in required_perms:
276 276 if perm not in available_perms:
277 277 raise Exception("'%s' permission is not defined" % perm)
278 278 self.required_perms = set(required_perms)
279 279 self.user_perms = None
280 280
281 281 def __call__(self, func):
282 282 return decorator(self.__wrapper, func)
283 283
284 284
285 285 def __wrapper(self, func, *fargs, **fkwargs):
286 286 # _wrapper.__name__ = func.__name__
287 287 # _wrapper.__dict__.update(func.__dict__)
288 288 # _wrapper.__doc__ = func.__doc__
289 289
290 self.user_perms = session.get('hg_app_user', AuthUser()).permissions
290 self.user_perms = session.get('rhodecode_user', AuthUser()).permissions
291 291 log.debug('checking %s permissions %s for %s',
292 292 self.__class__.__name__, self.required_perms, func.__name__)
293 293
294 294 if self.check_permissions():
295 295 log.debug('Permission granted for %s', func.__name__)
296 296
297 297 return func(*fargs, **fkwargs)
298 298
299 299 else:
300 300 log.warning('Permission denied for %s', func.__name__)
301 301 #redirect with forbidden ret code
302 302 return abort(403)
303 303
304 304
305 305
306 306 def check_permissions(self):
307 307 """Dummy function for overriding"""
308 308 raise Exception('You have to write this function in child class')
309 309
310 310 class HasPermissionAllDecorator(PermsDecorator):
311 311 """Checks for access permission for all given predicates. All of them
312 312 have to be meet in order to fulfill the request
313 313 """
314 314
315 315 def check_permissions(self):
316 316 if self.required_perms.issubset(self.user_perms.get('global')):
317 317 return True
318 318 return False
319 319
320 320
321 321 class HasPermissionAnyDecorator(PermsDecorator):
322 322 """Checks for access permission for any of given predicates. In order to
323 323 fulfill the request any of predicates must be meet
324 324 """
325 325
326 326 def check_permissions(self):
327 327 if self.required_perms.intersection(self.user_perms.get('global')):
328 328 return True
329 329 return False
330 330
331 331 class HasRepoPermissionAllDecorator(PermsDecorator):
332 332 """Checks for access permission for all given predicates for specific
333 333 repository. All of them have to be meet in order to fulfill the request
334 334 """
335 335
336 336 def check_permissions(self):
337 337 repo_name = get_repo_slug(request)
338 338 try:
339 339 user_perms = set([self.user_perms['repositories'][repo_name]])
340 340 except KeyError:
341 341 return False
342 342 if self.required_perms.issubset(user_perms):
343 343 return True
344 344 return False
345 345
346 346
347 347 class HasRepoPermissionAnyDecorator(PermsDecorator):
348 348 """Checks for access permission for any of given predicates for specific
349 349 repository. In order to fulfill the request any of predicates must be meet
350 350 """
351 351
352 352 def check_permissions(self):
353 353 repo_name = get_repo_slug(request)
354 354
355 355 try:
356 356 user_perms = set([self.user_perms['repositories'][repo_name]])
357 357 except KeyError:
358 358 return False
359 359 if self.required_perms.intersection(user_perms):
360 360 return True
361 361 return False
362 362 #===============================================================================
363 363 # CHECK FUNCTIONS
364 364 #===============================================================================
365 365
366 366 class PermsFunction(object):
367 367 """Base function for other check functions"""
368 368
369 369 def __init__(self, *perms):
370 370 available_perms = config['available_permissions']
371 371
372 372 for perm in perms:
373 373 if perm not in available_perms:
374 374 raise Exception("'%s' permission in not defined" % perm)
375 375 self.required_perms = set(perms)
376 376 self.user_perms = None
377 377 self.granted_for = ''
378 378 self.repo_name = None
379 379
380 380 def __call__(self, check_Location=''):
381 user = session.get('hg_app_user', False)
381 user = session.get('rhodecode_user', False)
382 382 if not user:
383 383 return False
384 384 self.user_perms = user.permissions
385 385 self.granted_for = user.username
386 386 log.debug('checking %s %s', self.__class__.__name__, self.required_perms)
387 387
388 388 if self.check_permissions():
389 389 log.debug('Permission granted for %s @%s', self.granted_for,
390 390 check_Location)
391 391 return True
392 392
393 393 else:
394 394 log.warning('Permission denied for %s @%s', self.granted_for,
395 395 check_Location)
396 396 return False
397 397
398 398 def check_permissions(self):
399 399 """Dummy function for overriding"""
400 400 raise Exception('You have to write this function in child class')
401 401
402 402 class HasPermissionAll(PermsFunction):
403 403 def check_permissions(self):
404 404 if self.required_perms.issubset(self.user_perms.get('global')):
405 405 return True
406 406 return False
407 407
408 408 class HasPermissionAny(PermsFunction):
409 409 def check_permissions(self):
410 410 if self.required_perms.intersection(self.user_perms.get('global')):
411 411 return True
412 412 return False
413 413
414 414 class HasRepoPermissionAll(PermsFunction):
415 415
416 416 def __call__(self, repo_name=None, check_Location=''):
417 417 self.repo_name = repo_name
418 418 return super(HasRepoPermissionAll, self).__call__(check_Location)
419 419
420 420 def check_permissions(self):
421 421 if not self.repo_name:
422 422 self.repo_name = get_repo_slug(request)
423 423
424 424 try:
425 425 self.user_perms = set([self.user_perms['repositories']\
426 426 [self.repo_name]])
427 427 except KeyError:
428 428 return False
429 429 self.granted_for = self.repo_name
430 430 if self.required_perms.issubset(self.user_perms):
431 431 return True
432 432 return False
433 433
434 434 class HasRepoPermissionAny(PermsFunction):
435 435
436 436 def __call__(self, repo_name=None, check_Location=''):
437 437 self.repo_name = repo_name
438 438 return super(HasRepoPermissionAny, self).__call__(check_Location)
439 439
440 440 def check_permissions(self):
441 441 if not self.repo_name:
442 442 self.repo_name = get_repo_slug(request)
443 443
444 444 try:
445 445 self.user_perms = set([self.user_perms['repositories']\
446 446 [self.repo_name]])
447 447 except KeyError:
448 448 return False
449 449 self.granted_for = self.repo_name
450 450 if self.required_perms.intersection(self.user_perms):
451 451 return True
452 452 return False
453 453
454 454 #===============================================================================
455 455 # SPECIAL VERSION TO HANDLE MIDDLEWARE AUTH
456 456 #===============================================================================
457 457
458 458 class HasPermissionAnyMiddleware(object):
459 459 def __init__(self, *perms):
460 460 self.required_perms = set(perms)
461 461
462 462 def __call__(self, user, repo_name):
463 463 usr = AuthUser()
464 464 usr.user_id = user.user_id
465 465 usr.username = user.username
466 466 usr.is_admin = user.admin
467 467
468 468 try:
469 469 self.user_perms = set([fill_perms(usr)\
470 470 .permissions['repositories'][repo_name]])
471 471 except:
472 472 self.user_perms = set()
473 473 self.granted_for = ''
474 474 self.username = user.username
475 475 self.repo_name = repo_name
476 476 return self.check_permissions()
477 477
478 478 def check_permissions(self):
479 479 log.debug('checking mercurial protocol '
480 480 'permissions for user:%s repository:%s',
481 481 self.username, self.repo_name)
482 482 if self.required_perms.intersection(self.user_perms):
483 483 log.debug('permission granted')
484 484 return True
485 485 log.debug('permission denied')
486 486 return False
@@ -1,46 +1,46 b''
1 1 """The base Controller API
2 2
3 3 Provides the BaseController class for subclassing.
4 4 """
5 5 from pylons import config, tmpl_context as c, request, session
6 6 from pylons.controllers import WSGIController
7 7 from pylons.templating import render_mako as render
8 8 from rhodecode import __version__
9 9 from rhodecode.lib import auth
10 10 from rhodecode.lib.utils import get_repo_slug
11 11 from rhodecode.model import meta
12 12 from rhodecode.model.hg_model import _get_repos_cached, \
13 13 _get_repos_switcher_cached
14 14
15 15 class BaseController(WSGIController):
16 16
17 17 def __before__(self):
18 c.hg_app_version = __version__
19 c.hg_app_name = config['hg_app_title']
18 c.rhodecode_version = __version__
19 c.rhodecode_name = config['rhodecode_title']
20 20 c.repo_name = get_repo_slug(request)
21 21 c.cached_repo_list = _get_repos_cached()
22 22 c.repo_switcher_list = _get_repos_switcher_cached(c.cached_repo_list)
23 23
24 24 if c.repo_name:
25 25 cached_repo = c.cached_repo_list.get(c.repo_name)
26 26
27 27 if cached_repo:
28 28 c.repository_tags = cached_repo.tags
29 29 c.repository_branches = cached_repo.branches
30 30 else:
31 31 c.repository_tags = {}
32 32 c.repository_branches = {}
33 33
34 34 self.sa = meta.Session
35 35
36 36 def __call__(self, environ, start_response):
37 37 """Invoke the Controller"""
38 38 # WSGIController.__call__ dispatches to the Controller method
39 39 # the request is routed to. This routing information is
40 40 # available in environ['pylons.routes_dict']
41 41 try:
42 42 #putting this here makes sure that we update permissions every time
43 self.hg_app_user = c.hg_app_user = auth.get_user(session)
43 self.rhodecode_user = c.rhodecode_user = auth.get_user(session)
44 44 return WSGIController.__call__(self, environ, start_response)
45 45 finally:
46 46 meta.Session.remove()
@@ -1,316 +1,316 b''
1 1 from celery.decorators import task
2 2 from celery.task.sets import subtask
3 3 from celeryconfig import PYLONS_CONFIG as config
4 4 from operator import itemgetter
5 5 from pylons.i18n.translation import _
6 6 from rhodecode.lib.celerylib import run_task, locked_task
7 7 from rhodecode.lib.helpers import person
8 8 from rhodecode.lib.smtp_mailer import SmtpMailer
9 9 from rhodecode.lib.utils import OrderedDict
10 10 from time import mktime
11 11 from vcs.backends.hg import MercurialRepository
12 12 import json
13 13 import traceback
14 14
15 15 __all__ = ['whoosh_index', 'get_commits_stats',
16 16 'reset_user_password', 'send_email']
17 17
18 18 def get_session():
19 19 from sqlalchemy import engine_from_config
20 20 from sqlalchemy.orm import sessionmaker, scoped_session
21 21 engine = engine_from_config(dict(config.items('app:main')), 'sqlalchemy.db1.')
22 22 sa = scoped_session(sessionmaker(bind=engine))
23 23 return sa
24 24
25 25 def get_hg_settings():
26 26 from rhodecode.model.db import HgAppSettings
27 27 try:
28 28 sa = get_session()
29 29 ret = sa.query(HgAppSettings).all()
30 30 finally:
31 31 sa.remove()
32 32
33 33 if not ret:
34 34 raise Exception('Could not get application settings !')
35 35 settings = {}
36 36 for each in ret:
37 settings['hg_app_' + each.app_settings_name] = each.app_settings_value
37 settings['rhodecode_' + each.app_settings_name] = each.app_settings_value
38 38
39 39 return settings
40 40
41 41 def get_hg_ui_settings():
42 42 from rhodecode.model.db import HgAppUi
43 43 try:
44 44 sa = get_session()
45 45 ret = sa.query(HgAppUi).all()
46 46 finally:
47 47 sa.remove()
48 48
49 49 if not ret:
50 50 raise Exception('Could not get application ui settings !')
51 51 settings = {}
52 52 for each in ret:
53 53 k = each.ui_key
54 54 v = each.ui_value
55 55 if k == '/':
56 56 k = 'root_path'
57 57
58 58 if k.find('.') != -1:
59 59 k = k.replace('.', '_')
60 60
61 61 if each.ui_section == 'hooks':
62 62 v = each.ui_active
63 63
64 64 settings[each.ui_section + '_' + k] = v
65 65
66 66 return settings
67 67
68 68 @task
69 69 @locked_task
70 70 def whoosh_index(repo_location, full_index):
71 71 log = whoosh_index.get_logger()
72 72 from rhodecode.lib.indexers.daemon import WhooshIndexingDaemon
73 73 WhooshIndexingDaemon(repo_location=repo_location).run(full_index=full_index)
74 74
75 75 @task
76 76 @locked_task
77 77 def get_commits_stats(repo_name, ts_min_y, ts_max_y):
78 78 from rhodecode.model.db import Statistics, Repository
79 79 log = get_commits_stats.get_logger()
80 80 author_key_cleaner = lambda k: person(k).replace('"', "") #for js data compatibilty
81 81
82 82 commits_by_day_author_aggregate = {}
83 83 commits_by_day_aggregate = {}
84 84 repos_path = get_hg_ui_settings()['paths_root_path'].replace('*', '')
85 85 repo = MercurialRepository(repos_path + repo_name)
86 86
87 87 skip_date_limit = True
88 88 parse_limit = 350 #limit for single task changeset parsing optimal for
89 89 last_rev = 0
90 90 last_cs = None
91 91 timegetter = itemgetter('time')
92 92
93 93 sa = get_session()
94 94
95 95 dbrepo = sa.query(Repository)\
96 96 .filter(Repository.repo_name == repo_name).scalar()
97 97 cur_stats = sa.query(Statistics)\
98 98 .filter(Statistics.repository == dbrepo).scalar()
99 99 if cur_stats:
100 100 last_rev = cur_stats.stat_on_revision
101 101 if not repo.revisions:
102 102 return True
103 103
104 104 if last_rev == repo.revisions[-1] and len(repo.revisions) > 1:
105 105 #pass silently without any work if we're not on first revision or current
106 106 #state of parsing revision(from db marker) is the last revision
107 107 return True
108 108
109 109 if cur_stats:
110 110 commits_by_day_aggregate = OrderedDict(
111 111 json.loads(
112 112 cur_stats.commit_activity_combined))
113 113 commits_by_day_author_aggregate = json.loads(cur_stats.commit_activity)
114 114
115 115 log.debug('starting parsing %s', parse_limit)
116 116 for cnt, rev in enumerate(repo.revisions[last_rev:]):
117 117 last_cs = cs = repo.get_changeset(rev)
118 118 k = '%s-%s-%s' % (cs.date.timetuple()[0], cs.date.timetuple()[1],
119 119 cs.date.timetuple()[2])
120 120 timetupple = [int(x) for x in k.split('-')]
121 121 timetupple.extend([0 for _ in xrange(6)])
122 122 k = mktime(timetupple)
123 123 if commits_by_day_author_aggregate.has_key(author_key_cleaner(cs.author)):
124 124 try:
125 125 l = [timegetter(x) for x in commits_by_day_author_aggregate\
126 126 [author_key_cleaner(cs.author)]['data']]
127 127 time_pos = l.index(k)
128 128 except ValueError:
129 129 time_pos = False
130 130
131 131 if time_pos >= 0 and time_pos is not False:
132 132
133 133 datadict = commits_by_day_author_aggregate\
134 134 [author_key_cleaner(cs.author)]['data'][time_pos]
135 135
136 136 datadict["commits"] += 1
137 137 datadict["added"] += len(cs.added)
138 138 datadict["changed"] += len(cs.changed)
139 139 datadict["removed"] += len(cs.removed)
140 140 #print datadict
141 141
142 142 else:
143 143 #print 'ELSE !!!!'
144 144 if k >= ts_min_y and k <= ts_max_y or skip_date_limit:
145 145
146 146 datadict = {"time":k,
147 147 "commits":1,
148 148 "added":len(cs.added),
149 149 "changed":len(cs.changed),
150 150 "removed":len(cs.removed),
151 151 }
152 152 commits_by_day_author_aggregate\
153 153 [author_key_cleaner(cs.author)]['data'].append(datadict)
154 154
155 155 else:
156 156 #print k, 'nokey ADDING'
157 157 if k >= ts_min_y and k <= ts_max_y or skip_date_limit:
158 158 commits_by_day_author_aggregate[author_key_cleaner(cs.author)] = {
159 159 "label":author_key_cleaner(cs.author),
160 160 "data":[{"time":k,
161 161 "commits":1,
162 162 "added":len(cs.added),
163 163 "changed":len(cs.changed),
164 164 "removed":len(cs.removed),
165 165 }],
166 166 "schema":["commits"],
167 167 }
168 168
169 169 # #gather all data by day
170 170 if commits_by_day_aggregate.has_key(k):
171 171 commits_by_day_aggregate[k] += 1
172 172 else:
173 173 commits_by_day_aggregate[k] = 1
174 174
175 175 if cnt >= parse_limit:
176 176 #don't fetch to much data since we can freeze application
177 177 break
178 178
179 179 overview_data = []
180 180 for k, v in commits_by_day_aggregate.items():
181 181 overview_data.append([k, v])
182 182 overview_data = sorted(overview_data, key=itemgetter(0))
183 183
184 184 if not commits_by_day_author_aggregate:
185 185 commits_by_day_author_aggregate[author_key_cleaner(repo.contact)] = {
186 186 "label":author_key_cleaner(repo.contact),
187 187 "data":[0, 1],
188 188 "schema":["commits"],
189 189 }
190 190
191 191 stats = cur_stats if cur_stats else Statistics()
192 192 stats.commit_activity = json.dumps(commits_by_day_author_aggregate)
193 193 stats.commit_activity_combined = json.dumps(overview_data)
194 194
195 195 log.debug('last revison %s', last_rev)
196 196 leftovers = len(repo.revisions[last_rev:])
197 197 log.debug('revisions to parse %s', leftovers)
198 198
199 199 if last_rev == 0 or leftovers < parse_limit:
200 200 stats.languages = json.dumps(__get_codes_stats(repo_name))
201 201
202 202 stats.repository = dbrepo
203 203 stats.stat_on_revision = last_cs.revision
204 204
205 205 try:
206 206 sa.add(stats)
207 207 sa.commit()
208 208 except:
209 209 log.error(traceback.format_exc())
210 210 sa.rollback()
211 211 return False
212 212 if len(repo.revisions) > 1:
213 213 run_task(get_commits_stats, repo_name, ts_min_y, ts_max_y)
214 214
215 215 return True
216 216
217 217 @task
218 218 def reset_user_password(user_email):
219 219 log = reset_user_password.get_logger()
220 220 from rhodecode.lib import auth
221 221 from rhodecode.model.db import User
222 222
223 223 try:
224 224 try:
225 225 sa = get_session()
226 226 user = sa.query(User).filter(User.email == user_email).scalar()
227 227 new_passwd = auth.PasswordGenerator().gen_password(8,
228 228 auth.PasswordGenerator.ALPHABETS_BIG_SMALL)
229 229 if user:
230 230 user.password = auth.get_crypt_password(new_passwd)
231 231 sa.add(user)
232 232 sa.commit()
233 233 log.info('change password for %s', user_email)
234 234 if new_passwd is None:
235 235 raise Exception('unable to generate new password')
236 236
237 237 except:
238 238 log.error(traceback.format_exc())
239 239 sa.rollback()
240 240
241 241 run_task(send_email, user_email,
242 242 "Your new hg-app password",
243 243 'Your new hg-app password:%s' % (new_passwd))
244 244 log.info('send new password mail to %s', user_email)
245 245
246 246
247 247 except:
248 248 log.error('Failed to update user password')
249 249 log.error(traceback.format_exc())
250 250 return True
251 251
252 252 @task
253 253 def send_email(recipients, subject, body):
254 254 log = send_email.get_logger()
255 255 email_config = dict(config.items('DEFAULT'))
256 256 mail_from = email_config.get('app_email_from')
257 257 user = email_config.get('smtp_username')
258 258 passwd = email_config.get('smtp_password')
259 259 mail_server = email_config.get('smtp_server')
260 260 mail_port = email_config.get('smtp_port')
261 261 tls = email_config.get('smtp_use_tls')
262 262 ssl = False
263 263
264 264 try:
265 265 m = SmtpMailer(mail_from, user, passwd, mail_server,
266 266 mail_port, ssl, tls)
267 267 m.send(recipients, subject, body)
268 268 except:
269 269 log.error('Mail sending failed')
270 270 log.error(traceback.format_exc())
271 271 return False
272 272 return True
273 273
274 274 @task
275 275 def create_repo_fork(form_data, cur_user):
276 276 import os
277 277 from rhodecode.model.repo_model import RepoModel
278 278 sa = get_session()
279 279 rm = RepoModel(sa)
280 280
281 281 rm.create(form_data, cur_user, just_db=True, fork=True)
282 282
283 283 repos_path = get_hg_ui_settings()['paths_root_path'].replace('*', '')
284 284 repo_path = os.path.join(repos_path, form_data['repo_name'])
285 285 repo_fork_path = os.path.join(repos_path, form_data['fork_name'])
286 286
287 287 MercurialRepository(str(repo_fork_path), True, clone_url=str(repo_path))
288 288
289 289
290 290 def __get_codes_stats(repo_name):
291 291 LANGUAGES_EXTENSIONS = ['action', 'adp', 'ashx', 'asmx', 'aspx', 'asx', 'axd', 'c',
292 292 'cfg', 'cfm', 'cpp', 'cs', 'diff', 'do', 'el', 'erl',
293 293 'h', 'java', 'js', 'jsp', 'jspx', 'lisp',
294 294 'lua', 'm', 'mako', 'ml', 'pas', 'patch', 'php', 'php3',
295 295 'php4', 'phtml', 'pm', 'py', 'rb', 'rst', 's', 'sh',
296 296 'tpl', 'txt', 'vim', 'wss', 'xhtml', 'xml', 'xsl', 'xslt',
297 297 'yaws']
298 298 repos_path = get_hg_ui_settings()['paths_root_path'].replace('*', '')
299 299 repo = MercurialRepository(repos_path + repo_name)
300 300
301 301 code_stats = {}
302 302 for topnode, dirs, files in repo.walk('/', 'tip'):
303 303 for f in files:
304 304 k = f.mimetype
305 305 if f.extension in LANGUAGES_EXTENSIONS:
306 306 if code_stats.has_key(k):
307 307 code_stats[k] += 1
308 308 else:
309 309 code_stats[k] = 1
310 310
311 311 return code_stats or {}
312 312
313 313
314 314
315 315
316 316
@@ -1,221 +1,221 b''
1 1 #!/usr/bin/env python
2 2 # encoding: utf-8
3 3 # middleware to handle mercurial api calls
4 4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 5 #
6 6 # This program is free software; you can redistribute it and/or
7 7 # modify it under the terms of the GNU General Public License
8 8 # as published by the Free Software Foundation; version 2
9 9 # of the License or (at your opinion) any later version of the license.
10 10 #
11 11 # This program is distributed in the hope that it will be useful,
12 12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 14 # GNU General Public License for more details.
15 15 #
16 16 # You should have received a copy of the GNU General Public License
17 17 # along with this program; if not, write to the Free Software
18 18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 19 # MA 02110-1301, USA.
20 20 """
21 21 Created on 2010-04-28
22 22
23 23 @author: marcink
24 24 SimpleHG middleware for handling mercurial protocol request (push/clone etc.)
25 25 It's implemented with basic auth function
26 26 """
27 27 from itertools import chain
28 28 from mercurial.error import RepoError
29 29 from mercurial.hgweb import hgweb
30 30 from mercurial.hgweb.request import wsgiapplication
31 31 from paste.auth.basic import AuthBasicAuthenticator
32 32 from paste.httpheaders import REMOTE_USER, AUTH_TYPE
33 33 from rhodecode.lib.auth import authfunc, HasPermissionAnyMiddleware, \
34 34 get_user_cached
35 35 from rhodecode.lib.utils import is_mercurial, make_ui, invalidate_cache, \
36 36 check_repo_fast, ui_sections
37 37 from webob.exc import HTTPNotFound, HTTPForbidden, HTTPInternalServerError
38 38 from rhodecode.lib.utils import action_logger
39 39 import logging
40 40 import os
41 41 import traceback
42 42
43 43 log = logging.getLogger(__name__)
44 44
45 45 class SimpleHg(object):
46 46
47 47 def __init__(self, application, config):
48 48 self.application = application
49 49 self.config = config
50 50 #authenticate this mercurial request using
51 51 self.authenticate = AuthBasicAuthenticator('', authfunc)
52 52
53 53 def __call__(self, environ, start_response):
54 54 if not is_mercurial(environ):
55 55 return self.application(environ, start_response)
56 56
57 57 #===================================================================
58 58 # AUTHENTICATE THIS MERCURIAL REQUEST
59 59 #===================================================================
60 60 username = REMOTE_USER(environ)
61 61 if not username:
62 self.authenticate.realm = self.config['hg_app_realm']
62 self.authenticate.realm = self.config['rhodecode_realm']
63 63 result = self.authenticate(environ)
64 64 if isinstance(result, str):
65 65 AUTH_TYPE.update(environ, 'basic')
66 66 REMOTE_USER.update(environ, result)
67 67 else:
68 68 return result.wsgi_application(environ, start_response)
69 69
70 70 try:
71 71 repo_name = '/'.join(environ['PATH_INFO'].split('/')[1:])
72 72 if repo_name.endswith('/'):
73 73 repo_name = repo_name.rstrip('/')
74 74 except:
75 75 log.error(traceback.format_exc())
76 76 return HTTPInternalServerError()(environ, start_response)
77 77
78 78 #===================================================================
79 79 # CHECK PERMISSIONS FOR THIS REQUEST
80 80 #===================================================================
81 81 action = self.__get_action(environ)
82 82 if action:
83 83 username = self.__get_environ_user(environ)
84 84 try:
85 85 user = self.__get_user(username)
86 86 except:
87 87 log.error(traceback.format_exc())
88 88 return HTTPInternalServerError()(environ, start_response)
89 89 #check permissions for this repository
90 90 if action == 'pull':
91 91 if not HasPermissionAnyMiddleware('repository.read',
92 92 'repository.write',
93 93 'repository.admin')\
94 94 (user, repo_name):
95 95 return HTTPForbidden()(environ, start_response)
96 96 if action == 'push':
97 97 if not HasPermissionAnyMiddleware('repository.write',
98 98 'repository.admin')\
99 99 (user, repo_name):
100 100 return HTTPForbidden()(environ, start_response)
101 101
102 102 #log action
103 103 proxy_key = 'HTTP_X_REAL_IP'
104 104 def_key = 'REMOTE_ADDR'
105 105 ipaddr = environ.get(proxy_key, environ.get(def_key, '0.0.0.0'))
106 106 self.__log_user_action(user, action, repo_name, ipaddr)
107 107
108 108 #===================================================================
109 109 # MERCURIAL REQUEST HANDLING
110 110 #===================================================================
111 111 environ['PATH_INFO'] = '/'#since we wrap into hgweb, reset the path
112 112 self.baseui = make_ui('db')
113 113 self.basepath = self.config['base_path']
114 114 self.repo_path = os.path.join(self.basepath, repo_name)
115 115
116 116 #quick check if that dir exists...
117 117 if check_repo_fast(repo_name, self.basepath):
118 118 return HTTPNotFound()(environ, start_response)
119 119 try:
120 120 app = wsgiapplication(self.__make_app)
121 121 except RepoError, e:
122 122 if str(e).find('not found') != -1:
123 123 return HTTPNotFound()(environ, start_response)
124 124 except Exception:
125 125 log.error(traceback.format_exc())
126 126 return HTTPInternalServerError()(environ, start_response)
127 127
128 128 #invalidate cache on push
129 129 if action == 'push':
130 130 self.__invalidate_cache(repo_name)
131 131 messages = []
132 132 messages.append('thank you for using hg-app')
133 133
134 134 return self.msg_wrapper(app, environ, start_response, messages)
135 135 else:
136 136 return app(environ, start_response)
137 137
138 138
139 139 def msg_wrapper(self, app, environ, start_response, messages=[]):
140 140 """
141 141 Wrapper for custom messages that come out of mercurial respond messages
142 142 is a list of messages that the user will see at the end of response
143 143 from merurial protocol actions that involves remote answers
144 144 @param app:
145 145 @param environ:
146 146 @param start_response:
147 147 """
148 148 def custom_messages(msg_list):
149 149 for msg in msg_list:
150 150 yield msg + '\n'
151 151 org_response = app(environ, start_response)
152 152 return chain(org_response, custom_messages(messages))
153 153
154 154 def __make_app(self):
155 155 hgserve = hgweb(str(self.repo_path), baseui=self.baseui)
156 156 return self.__load_web_settings(hgserve)
157 157
158 158 def __get_environ_user(self, environ):
159 159 return environ.get('REMOTE_USER')
160 160
161 161 def __get_user(self, username):
162 162 return get_user_cached(username)
163 163
164 164 def __get_action(self, environ):
165 165 """
166 166 Maps mercurial request commands into a pull or push command.
167 167 @param environ:
168 168 """
169 169 mapping = {'changegroup': 'pull',
170 170 'changegroupsubset': 'pull',
171 171 'stream_out': 'pull',
172 172 'listkeys': 'pull',
173 173 'unbundle': 'push',
174 174 'pushkey': 'push', }
175 175
176 176 for qry in environ['QUERY_STRING'].split('&'):
177 177 if qry.startswith('cmd'):
178 178 cmd = qry.split('=')[-1]
179 179 if mapping.has_key(cmd):
180 180 return mapping[cmd]
181 181
182 182 def __log_user_action(self, user, action, repo, ipaddr):
183 183 action_logger(user, action, repo, ipaddr)
184 184
185 185 def __invalidate_cache(self, repo_name):
186 186 """we know that some change was made to repositories and we should
187 187 invalidate the cache to see the changes right away but only for
188 188 push requests"""
189 189 invalidate_cache('cached_repo_list')
190 190 invalidate_cache('full_changelog', repo_name)
191 191
192 192
193 193 def __load_web_settings(self, hgserve):
194 194 #set the global ui for hgserve
195 195 hgserve.repo.ui = self.baseui
196 196
197 197 hgrc = os.path.join(self.repo_path, '.hg', 'hgrc')
198 198 repoui = make_ui('file', hgrc, False)
199 199
200 200
201 201 if repoui:
202 202 #overwrite our ui instance with the section from hgrc file
203 203 for section in ui_sections:
204 204 for k, v in repoui.configitems(section):
205 205 hgserve.repo.ui.setconfig(section, k, v)
206 206
207 207 return hgserve
208 208
209 209
210 210
211 211
212 212
213 213
214 214
215 215
216 216
217 217
218 218
219 219
220 220
221 221
@@ -1,488 +1,488 b''
1 1 #!/usr/bin/env python
2 2 # encoding: utf-8
3 3 # Utilities for hg app
4 4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 5 # This program is free software; you can redistribute it and/or
6 6 # modify it under the terms of the GNU General Public License
7 7 # as published by the Free Software Foundation; version 2
8 8 # of the License or (at your opinion) any later version of the license.
9 9 #
10 10 # This program is distributed in the hope that it will be useful,
11 11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 13 # GNU General Public License for more details.
14 14 #
15 15 # You should have received a copy of the GNU General Public License
16 16 # along with this program; if not, write to the Free Software
17 17 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18 18 # MA 02110-1301, USA.
19 19
20 20 """
21 21 Created on April 18, 2010
22 22 Utilities for hg app
23 23 @author: marcink
24 24 """
25 25 from beaker.cache import cache_region
26 26 from mercurial import ui, config, hg
27 27 from mercurial.error import RepoError
28 28 from rhodecode.model import meta
29 29 from rhodecode.model.db import Repository, User, HgAppUi, HgAppSettings, UserLog
30 30 from vcs.backends.base import BaseChangeset
31 31 from vcs.utils.lazy import LazyProperty
32 32 import logging
33 33 import datetime
34 34 import os
35 35
36 36 log = logging.getLogger(__name__)
37 37
38 38
39 39 def get_repo_slug(request):
40 40 return request.environ['pylons.routes_dict'].get('repo_name')
41 41
42 42 def is_mercurial(environ):
43 43 """
44 44 Returns True if request's target is mercurial server - header
45 45 ``HTTP_ACCEPT`` of such request would start with ``application/mercurial``.
46 46 """
47 47 http_accept = environ.get('HTTP_ACCEPT')
48 48 if http_accept and http_accept.startswith('application/mercurial'):
49 49 return True
50 50 return False
51 51
52 52 def action_logger(user, action, repo, ipaddr, sa=None):
53 53 """
54 54 Action logger for various action made by users
55 55 """
56 56
57 57 if not sa:
58 58 sa = meta.Session
59 59
60 60 try:
61 61 if hasattr(user, 'user_id'):
62 62 user_id = user.user_id
63 63 elif isinstance(user, basestring):
64 64 user_id = sa.query(User).filter(User.username == user).one()
65 65 else:
66 66 raise Exception('You have to provide user object or username')
67 67
68 68 repo_name = repo.lstrip('/')
69 69 user_log = UserLog()
70 70 user_log.user_id = user_id
71 71 user_log.action = action
72 72 user_log.repository_name = repo_name
73 73 user_log.repository = sa.query(Repository)\
74 74 .filter(Repository.repo_name == repo_name).one()
75 75 user_log.action_date = datetime.datetime.now()
76 76 user_log.user_ip = ipaddr
77 77 sa.add(user_log)
78 78 sa.commit()
79 79 log.info('Adding user %s, action %s on %s',
80 80 user.username, action, repo)
81 81 except Exception, e:
82 82 raise
83 83 sa.rollback()
84 84 log.error('could not log user action:%s', str(e))
85 85
86 86 def check_repo_dir(paths):
87 87 repos_path = paths[0][1].split('/')
88 88 if repos_path[-1] in ['*', '**']:
89 89 repos_path = repos_path[:-1]
90 90 if repos_path[0] != '/':
91 91 repos_path[0] = '/'
92 92 if not os.path.isdir(os.path.join(*repos_path)):
93 93 raise Exception('Not a valid repository in %s' % paths[0][1])
94 94
95 95 def check_repo_fast(repo_name, base_path):
96 96 if os.path.isdir(os.path.join(base_path, repo_name)):return False
97 97 return True
98 98
99 99 def check_repo(repo_name, base_path, verify=True):
100 100
101 101 repo_path = os.path.join(base_path, repo_name)
102 102
103 103 try:
104 104 if not check_repo_fast(repo_name, base_path):
105 105 return False
106 106 r = hg.repository(ui.ui(), repo_path)
107 107 if verify:
108 108 hg.verify(r)
109 109 #here we hnow that repo exists it was verified
110 110 log.info('%s repo is already created', repo_name)
111 111 return False
112 112 except RepoError:
113 113 #it means that there is no valid repo there...
114 114 log.info('%s repo is free for creation', repo_name)
115 115 return True
116 116
117 117 def ask_ok(prompt, retries=4, complaint='Yes or no, please!'):
118 118 while True:
119 119 ok = raw_input(prompt)
120 120 if ok in ('y', 'ye', 'yes'): return True
121 121 if ok in ('n', 'no', 'nop', 'nope'): return False
122 122 retries = retries - 1
123 123 if retries < 0: raise IOError
124 124 print complaint
125 125
126 126 @cache_region('super_short_term', 'cached_hg_ui')
127 127 def get_hg_ui_cached():
128 128 try:
129 129 sa = meta.Session
130 130 ret = sa.query(HgAppUi).all()
131 131 finally:
132 132 meta.Session.remove()
133 133 return ret
134 134
135 135
136 136 def get_hg_settings():
137 137 try:
138 138 sa = meta.Session
139 139 ret = sa.query(HgAppSettings).all()
140 140 finally:
141 141 meta.Session.remove()
142 142
143 143 if not ret:
144 144 raise Exception('Could not get application settings !')
145 145 settings = {}
146 146 for each in ret:
147 settings['hg_app_' + each.app_settings_name] = each.app_settings_value
147 settings['rhodecode_' + each.app_settings_name] = each.app_settings_value
148 148
149 149 return settings
150 150
151 151 def get_hg_ui_settings():
152 152 try:
153 153 sa = meta.Session
154 154 ret = sa.query(HgAppUi).all()
155 155 finally:
156 156 meta.Session.remove()
157 157
158 158 if not ret:
159 159 raise Exception('Could not get application ui settings !')
160 160 settings = {}
161 161 for each in ret:
162 162 k = each.ui_key
163 163 v = each.ui_value
164 164 if k == '/':
165 165 k = 'root_path'
166 166
167 167 if k.find('.') != -1:
168 168 k = k.replace('.', '_')
169 169
170 170 if each.ui_section == 'hooks':
171 171 v = each.ui_active
172 172
173 173 settings[each.ui_section + '_' + k] = v
174 174
175 175 return settings
176 176
177 177 #propagated from mercurial documentation
178 178 ui_sections = ['alias', 'auth',
179 179 'decode/encode', 'defaults',
180 180 'diff', 'email',
181 181 'extensions', 'format',
182 182 'merge-patterns', 'merge-tools',
183 183 'hooks', 'http_proxy',
184 184 'smtp', 'patch',
185 185 'paths', 'profiling',
186 186 'server', 'trusted',
187 187 'ui', 'web', ]
188 188
189 189 def make_ui(read_from='file', path=None, checkpaths=True):
190 190 """
191 191 A function that will read python rc files or database
192 192 and make an mercurial ui object from read options
193 193
194 194 @param path: path to mercurial config file
195 195 @param checkpaths: check the path
196 196 @param read_from: read from 'file' or 'db'
197 197 """
198 198
199 199 baseui = ui.ui()
200 200
201 201 if read_from == 'file':
202 202 if not os.path.isfile(path):
203 203 log.warning('Unable to read config file %s' % path)
204 204 return False
205 205 log.debug('reading hgrc from %s', path)
206 206 cfg = config.config()
207 207 cfg.read(path)
208 208 for section in ui_sections:
209 209 for k, v in cfg.items(section):
210 210 baseui.setconfig(section, k, v)
211 211 log.debug('settings ui from file[%s]%s:%s', section, k, v)
212 212 if checkpaths:check_repo_dir(cfg.items('paths'))
213 213
214 214
215 215 elif read_from == 'db':
216 216 hg_ui = get_hg_ui_cached()
217 217 for ui_ in hg_ui:
218 218 if ui_.ui_active:
219 219 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section, ui_.ui_key, ui_.ui_value)
220 220 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
221 221
222 222
223 223 return baseui
224 224
225 225
226 def set_hg_app_config(config):
226 def set_rhodecode_config(config):
227 227 hgsettings = get_hg_settings()
228 228
229 229 for k, v in hgsettings.items():
230 230 config[k] = v
231 231
232 232 def invalidate_cache(name, *args):
233 233 """Invalidates given name cache"""
234 234
235 235 from beaker.cache import region_invalidate
236 236 log.info('INVALIDATING CACHE FOR %s', name)
237 237
238 238 """propagate our arguments to make sure invalidation works. First
239 239 argument has to be the name of cached func name give to cache decorator
240 240 without that the invalidation would not work"""
241 241 tmp = [name]
242 242 tmp.extend(args)
243 243 args = tuple(tmp)
244 244
245 245 if name == 'cached_repo_list':
246 246 from rhodecode.model.hg_model import _get_repos_cached
247 247 region_invalidate(_get_repos_cached, None, *args)
248 248
249 249 if name == 'full_changelog':
250 250 from rhodecode.model.hg_model import _full_changelog_cached
251 251 region_invalidate(_full_changelog_cached, None, *args)
252 252
253 253 class EmptyChangeset(BaseChangeset):
254 254 """
255 255 An dummy empty changeset.
256 256 """
257 257
258 258 revision = -1
259 259 message = ''
260 260 author = ''
261 261 date = ''
262 262 @LazyProperty
263 263 def raw_id(self):
264 264 """
265 265 Returns raw string identifing this changeset, useful for web
266 266 representation.
267 267 """
268 268 return '0' * 40
269 269
270 270 @LazyProperty
271 271 def short_id(self):
272 272 return self.raw_id[:12]
273 273
274 274 def get_file_changeset(self, path):
275 275 return self
276 276
277 277 def get_file_content(self, path):
278 278 return u''
279 279
280 280 def get_file_size(self, path):
281 281 return 0
282 282
283 283 def repo2db_mapper(initial_repo_list, remove_obsolete=False):
284 284 """
285 285 maps all found repositories into db
286 286 """
287 287 from rhodecode.model.repo_model import RepoModel
288 288
289 289 sa = meta.Session
290 290 user = sa.query(User).filter(User.admin == True).first()
291 291
292 292 rm = RepoModel()
293 293
294 294 for name, repo in initial_repo_list.items():
295 295 if not sa.query(Repository).filter(Repository.repo_name == name).scalar():
296 296 log.info('repository %s not found creating default', name)
297 297
298 298 form_data = {
299 299 'repo_name':name,
300 300 'description':repo.description if repo.description != 'unknown' else \
301 301 'auto description for %s' % name,
302 302 'private':False
303 303 }
304 304 rm.create(form_data, user, just_db=True)
305 305
306 306
307 307 if remove_obsolete:
308 308 #remove from database those repositories that are not in the filesystem
309 309 for repo in sa.query(Repository).all():
310 310 if repo.repo_name not in initial_repo_list.keys():
311 311 sa.delete(repo)
312 312 sa.commit()
313 313
314 314
315 315 meta.Session.remove()
316 316
317 317 from UserDict import DictMixin
318 318
319 319 class OrderedDict(dict, DictMixin):
320 320
321 321 def __init__(self, *args, **kwds):
322 322 if len(args) > 1:
323 323 raise TypeError('expected at most 1 arguments, got %d' % len(args))
324 324 try:
325 325 self.__end
326 326 except AttributeError:
327 327 self.clear()
328 328 self.update(*args, **kwds)
329 329
330 330 def clear(self):
331 331 self.__end = end = []
332 332 end += [None, end, end] # sentinel node for doubly linked list
333 333 self.__map = {} # key --> [key, prev, next]
334 334 dict.clear(self)
335 335
336 336 def __setitem__(self, key, value):
337 337 if key not in self:
338 338 end = self.__end
339 339 curr = end[1]
340 340 curr[2] = end[1] = self.__map[key] = [key, curr, end]
341 341 dict.__setitem__(self, key, value)
342 342
343 343 def __delitem__(self, key):
344 344 dict.__delitem__(self, key)
345 345 key, prev, next = self.__map.pop(key)
346 346 prev[2] = next
347 347 next[1] = prev
348 348
349 349 def __iter__(self):
350 350 end = self.__end
351 351 curr = end[2]
352 352 while curr is not end:
353 353 yield curr[0]
354 354 curr = curr[2]
355 355
356 356 def __reversed__(self):
357 357 end = self.__end
358 358 curr = end[1]
359 359 while curr is not end:
360 360 yield curr[0]
361 361 curr = curr[1]
362 362
363 363 def popitem(self, last=True):
364 364 if not self:
365 365 raise KeyError('dictionary is empty')
366 366 if last:
367 367 key = reversed(self).next()
368 368 else:
369 369 key = iter(self).next()
370 370 value = self.pop(key)
371 371 return key, value
372 372
373 373 def __reduce__(self):
374 374 items = [[k, self[k]] for k in self]
375 375 tmp = self.__map, self.__end
376 376 del self.__map, self.__end
377 377 inst_dict = vars(self).copy()
378 378 self.__map, self.__end = tmp
379 379 if inst_dict:
380 380 return (self.__class__, (items,), inst_dict)
381 381 return self.__class__, (items,)
382 382
383 383 def keys(self):
384 384 return list(self)
385 385
386 386 setdefault = DictMixin.setdefault
387 387 update = DictMixin.update
388 388 pop = DictMixin.pop
389 389 values = DictMixin.values
390 390 items = DictMixin.items
391 391 iterkeys = DictMixin.iterkeys
392 392 itervalues = DictMixin.itervalues
393 393 iteritems = DictMixin.iteritems
394 394
395 395 def __repr__(self):
396 396 if not self:
397 397 return '%s()' % (self.__class__.__name__,)
398 398 return '%s(%r)' % (self.__class__.__name__, self.items())
399 399
400 400 def copy(self):
401 401 return self.__class__(self)
402 402
403 403 @classmethod
404 404 def fromkeys(cls, iterable, value=None):
405 405 d = cls()
406 406 for key in iterable:
407 407 d[key] = value
408 408 return d
409 409
410 410 def __eq__(self, other):
411 411 if isinstance(other, OrderedDict):
412 412 return len(self) == len(other) and self.items() == other.items()
413 413 return dict.__eq__(self, other)
414 414
415 415 def __ne__(self, other):
416 416 return not self == other
417 417
418 418
419 419 #===============================================================================
420 420 # TEST FUNCTIONS
421 421 #===============================================================================
422 422 def create_test_index(repo_location, full_index):
423 423 """Makes default test index
424 424 @param repo_location:
425 425 @param full_index:
426 426 """
427 427 from rhodecode.lib.indexers.daemon import WhooshIndexingDaemon
428 428 from rhodecode.lib.pidlock import DaemonLock, LockHeld
429 429 from rhodecode.lib.indexers import IDX_LOCATION
430 430 import shutil
431 431
432 432 if os.path.exists(IDX_LOCATION):
433 433 shutil.rmtree(IDX_LOCATION)
434 434
435 435 try:
436 436 l = DaemonLock()
437 437 WhooshIndexingDaemon(repo_location=repo_location)\
438 438 .run(full_index=full_index)
439 439 l.release()
440 440 except LockHeld:
441 441 pass
442 442
443 443 def create_test_env(repos_test_path, config):
444 444 """Makes a fresh database and
445 445 install test repository into tmp dir
446 446 """
447 447 from rhodecode.lib.db_manage import DbManage
448 448 import tarfile
449 449 import shutil
450 450 from os.path import dirname as dn, join as jn, abspath
451 451
452 452 log = logging.getLogger('TestEnvCreator')
453 453 # create logger
454 454 log.setLevel(logging.DEBUG)
455 455 log.propagate = True
456 456 # create console handler and set level to debug
457 457 ch = logging.StreamHandler()
458 458 ch.setLevel(logging.DEBUG)
459 459
460 460 # create formatter
461 461 formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
462 462
463 463 # add formatter to ch
464 464 ch.setFormatter(formatter)
465 465
466 466 # add ch to logger
467 467 log.addHandler(ch)
468 468
469 469 #PART ONE create db
470 470 log.debug('making test db')
471 471 dbname = config['sqlalchemy.db1.url'].split('/')[-1]
472 472 dbmanage = DbManage(log_sql=True, dbname=dbname, tests=True)
473 473 dbmanage.create_tables(override=True)
474 474 dbmanage.config_prompt(repos_test_path)
475 475 dbmanage.create_default_user()
476 476 dbmanage.admin_prompt()
477 477 dbmanage.create_permissions()
478 478 dbmanage.populate_default_permissions()
479 479
480 480 #PART TWO make test repo
481 481 log.debug('making test vcs repo')
482 482 if os.path.isdir('/tmp/vcs_test'):
483 483 shutil.rmtree('/tmp/vcs_test')
484 484
485 485 cur_dir = dn(dn(abspath(__file__)))
486 486 tar = tarfile.open(jn(cur_dir, 'tests', "vcs_test.tar.gz"))
487 487 tar.extractall('/tmp')
488 488 tar.close()
@@ -1,139 +1,139 b''
1 1 from rhodecode.model.meta import Base
2 2 from sqlalchemy import *
3 3 from sqlalchemy.orm import relation, backref
4 4 from sqlalchemy.orm.session import Session
5 5 from vcs.utils.lazy import LazyProperty
6 6 import logging
7 7
8 8 log = logging.getLogger(__name__)
9 9
10 10 class HgAppSettings(Base):
11 __tablename__ = 'hg_app_settings'
11 __tablename__ = 'rhodecode_settings'
12 12 __table_args__ = (UniqueConstraint('app_settings_name'), {'useexisting':True})
13 13 app_settings_id = Column("app_settings_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=True)
14 14 app_settings_name = Column("app_settings_name", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
15 15 app_settings_value = Column("app_settings_value", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
16 16
17 17 class HgAppUi(Base):
18 __tablename__ = 'hg_app_ui'
18 __tablename__ = 'rhodecode_ui'
19 19 __table_args__ = {'useexisting':True}
20 20 ui_id = Column("ui_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=True)
21 21 ui_section = Column("ui_section", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
22 22 ui_key = Column("ui_key", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
23 23 ui_value = Column("ui_value", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
24 24 ui_active = Column("ui_active", BOOLEAN(), nullable=True, unique=None, default=True)
25 25
26 26
27 27 class User(Base):
28 28 __tablename__ = 'users'
29 29 __table_args__ = (UniqueConstraint('username'), UniqueConstraint('email'), {'useexisting':True})
30 30 user_id = Column("user_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=True)
31 31 username = Column("username", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
32 32 password = Column("password", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
33 33 active = Column("active", BOOLEAN(), nullable=True, unique=None, default=None)
34 34 admin = Column("admin", BOOLEAN(), nullable=True, unique=None, default=False)
35 35 name = Column("name", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
36 36 lastname = Column("lastname", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
37 37 email = Column("email", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
38 38 last_login = Column("last_login", DATETIME(timezone=False), nullable=True, unique=None, default=None)
39 39
40 40 user_log = relation('UserLog')
41 41 user_perms = relation('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id")
42 42
43 43 @LazyProperty
44 44 def full_contact(self):
45 45 return '%s %s <%s>' % (self.name, self.lastname, self.email)
46 46
47 47 def __repr__(self):
48 48 return "<User('id:%s:%s')>" % (self.user_id, self.username)
49 49
50 50 def update_lastlogin(self):
51 51 """Update user lastlogin"""
52 52 import datetime
53 53
54 54 try:
55 55 session = Session.object_session(self)
56 56 self.last_login = datetime.datetime.now()
57 57 session.add(self)
58 58 session.commit()
59 59 log.debug('updated user %s lastlogin', self.username)
60 60 except Exception:
61 61 session.rollback()
62 62
63 63
64 64 class UserLog(Base):
65 65 __tablename__ = 'user_logs'
66 66 __table_args__ = {'useexisting':True}
67 67 user_log_id = Column("user_log_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=True)
68 68 user_id = Column("user_id", INTEGER(), ForeignKey(u'users.user_id'), nullable=False, unique=None, default=None)
69 69 repository_id = Column("repository_id", INTEGER(length=None, convert_unicode=False, assert_unicode=None), ForeignKey(u'repositories.repo_id'), nullable=False, unique=None, default=None)
70 70 repository_name = Column("repository_name", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
71 71 user_ip = Column("user_ip", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
72 72 action = Column("action", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
73 73 action_date = Column("action_date", DATETIME(timezone=False), nullable=True, unique=None, default=None)
74 74 revision = Column('revision', TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
75 75
76 76 user = relation('User')
77 77 repository = relation('Repository')
78 78
79 79 class Repository(Base):
80 80 __tablename__ = 'repositories'
81 81 __table_args__ = (UniqueConstraint('repo_name'), {'useexisting':True},)
82 82 repo_id = Column("repo_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=True)
83 83 repo_name = Column("repo_name", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
84 84 user_id = Column("user_id", INTEGER(), ForeignKey(u'users.user_id'), nullable=False, unique=False, default=None)
85 85 private = Column("private", BOOLEAN(), nullable=True, unique=None, default=None)
86 86 description = Column("description", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
87 87 fork_id = Column("fork_id", INTEGER(), ForeignKey(u'repositories.repo_id'), nullable=True, unique=False, default=None)
88 88
89 89 user = relation('User')
90 90 fork = relation('Repository', remote_side=repo_id)
91 91 repo_to_perm = relation('RepoToPerm', cascade='all')
92 92
93 93 def __repr__(self):
94 94 return "<Repository('id:%s:%s')>" % (self.repo_id, self.repo_name)
95 95
96 96 class Permission(Base):
97 97 __tablename__ = 'permissions'
98 98 __table_args__ = {'useexisting':True}
99 99 permission_id = Column("permission_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=True)
100 100 permission_name = Column("permission_name", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
101 101 permission_longname = Column("permission_longname", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
102 102
103 103 def __repr__(self):
104 104 return "<Permission('%s:%s')>" % (self.permission_id, self.permission_name)
105 105
106 106 class RepoToPerm(Base):
107 107 __tablename__ = 'repo_to_perm'
108 108 __table_args__ = (UniqueConstraint('user_id', 'repository_id'), {'useexisting':True})
109 109 repo_to_perm_id = Column("repo_to_perm_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=True)
110 110 user_id = Column("user_id", INTEGER(), ForeignKey(u'users.user_id'), nullable=False, unique=None, default=None)
111 111 permission_id = Column("permission_id", INTEGER(), ForeignKey(u'permissions.permission_id'), nullable=False, unique=None, default=None)
112 112 repository_id = Column("repository_id", INTEGER(), ForeignKey(u'repositories.repo_id'), nullable=False, unique=None, default=None)
113 113
114 114 user = relation('User')
115 115 permission = relation('Permission')
116 116 repository = relation('Repository')
117 117
118 118 class UserToPerm(Base):
119 119 __tablename__ = 'user_to_perm'
120 120 __table_args__ = (UniqueConstraint('user_id', 'permission_id'), {'useexisting':True})
121 121 user_to_perm_id = Column("user_to_perm_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=True)
122 122 user_id = Column("user_id", INTEGER(), ForeignKey(u'users.user_id'), nullable=False, unique=None, default=None)
123 123 permission_id = Column("permission_id", INTEGER(), ForeignKey(u'permissions.permission_id'), nullable=False, unique=None, default=None)
124 124
125 125 user = relation('User')
126 126 permission = relation('Permission')
127 127
128 128 class Statistics(Base):
129 129 __tablename__ = 'statistics'
130 130 __table_args__ = (UniqueConstraint('repository_id'), {'useexisting':True})
131 131 stat_id = Column("stat_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=True)
132 132 repository_id = Column("repository_id", INTEGER(), ForeignKey(u'repositories.repo_id'), nullable=False, unique=True, default=None)
133 133 stat_on_revision = Column("stat_on_revision", INTEGER(), nullable=False)
134 134 commit_activity = Column("commit_activity", BLOB(), nullable=False)#JSON data
135 135 commit_activity_combined = Column("commit_activity_combined", BLOB(), nullable=False)#JSON data
136 136 languages = Column("languages", BLOB(), nullable=False)#JSON data
137 137
138 138 repository = relation('Repository')
139 139
@@ -1,361 +1,361 b''
1 1 """ this is forms validation classes
2 2 http://formencode.org/module-formencode.validators.html
3 3 for list off all availible validators
4 4
5 5 we can create our own validators
6 6
7 7 The table below outlines the options which can be used in a schema in addition to the validators themselves
8 8 pre_validators [] These validators will be applied before the schema
9 9 chained_validators [] These validators will be applied after the schema
10 10 allow_extra_fields False If True, then it is not an error when keys that aren't associated with a validator are present
11 11 filter_extra_fields False If True, then keys that aren't associated with a validator are removed
12 12 if_key_missing NoDefault If this is given, then any keys that aren't available but are expected will be replaced with this value (and then validated). This does not override a present .if_missing attribute on validators. NoDefault is a special FormEncode class to mean that no default values has been specified and therefore missing keys shouldn't take a default value.
13 13 ignore_key_missing False If True, then missing keys will be missing in the result, if the validator doesn't have .if_missing on it already
14 14
15 15
16 16 <name> = formencode.validators.<name of validator>
17 17 <name> must equal form name
18 18 list=[1,2,3,4,5]
19 19 for SELECT use formencode.All(OneOf(list), Int())
20 20
21 21 """
22 22 from formencode import All
23 23 from formencode.validators import UnicodeString, OneOf, Int, Number, Regex, \
24 24 Email, Bool, StringBoolean
25 25 from pylons import session
26 26 from pylons.i18n.translation import _
27 27 from rhodecode.lib.auth import check_password, get_crypt_password
28 28 from rhodecode.model import meta
29 29 from rhodecode.model.user_model import UserModel
30 30 from rhodecode.model.db import User, Repository
31 31 from sqlalchemy.exc import OperationalError
32 32 from sqlalchemy.orm.exc import NoResultFound, MultipleResultsFound
33 33 from webhelpers.pylonslib.secure_form import authentication_token
34 34 import formencode
35 35 import logging
36 36 import os
37 37 import rhodecode.lib.helpers as h
38 38 log = logging.getLogger(__name__)
39 39
40 40
41 41 #this is needed to translate the messages using _() in validators
42 42 class State_obj(object):
43 43 _ = staticmethod(_)
44 44
45 45 #===============================================================================
46 46 # VALIDATORS
47 47 #===============================================================================
48 48 class ValidAuthToken(formencode.validators.FancyValidator):
49 49 messages = {'invalid_token':_('Token mismatch')}
50 50
51 51 def validate_python(self, value, state):
52 52
53 53 if value != authentication_token():
54 54 raise formencode.Invalid(self.message('invalid_token', state,
55 55 search_number=value), value, state)
56 56
57 57 def ValidUsername(edit, old_data):
58 58 class _ValidUsername(formencode.validators.FancyValidator):
59 59
60 60 def validate_python(self, value, state):
61 61 if value in ['default', 'new_user']:
62 62 raise formencode.Invalid(_('Invalid username'), value, state)
63 63 #check if user is uniq
64 64 sa = meta.Session
65 65 old_un = None
66 66 if edit:
67 67 old_un = sa.query(User).get(old_data.get('user_id')).username
68 68
69 69 if old_un != value or not edit:
70 70 if sa.query(User).filter(User.username == value).scalar():
71 71 raise formencode.Invalid(_('This username already exists') ,
72 72 value, state)
73 73 meta.Session.remove()
74 74
75 75 return _ValidUsername
76 76
77 77 class ValidPassword(formencode.validators.FancyValidator):
78 78
79 79 def to_python(self, value, state):
80 80 if value:
81 81 return get_crypt_password(value)
82 82
83 83 class ValidAuth(formencode.validators.FancyValidator):
84 84 messages = {
85 85 'invalid_password':_('invalid password'),
86 86 'invalid_login':_('invalid user name'),
87 87 'disabled_account':_('Your acccount is disabled')
88 88
89 89 }
90 90 #error mapping
91 91 e_dict = {'username':messages['invalid_login'],
92 92 'password':messages['invalid_password']}
93 93 e_dict_disable = {'username':messages['disabled_account']}
94 94
95 95 def validate_python(self, value, state):
96 96 password = value['password']
97 97 username = value['username']
98 98 user = UserModel().get_user_by_name(username)
99 99 if user is None:
100 100 raise formencode.Invalid(self.message('invalid_password',
101 101 state=State_obj), value, state,
102 102 error_dict=self.e_dict)
103 103 if user:
104 104 if user.active:
105 105 if user.username == username and check_password(password,
106 106 user.password):
107 107 return value
108 108 else:
109 109 log.warning('user %s not authenticated', username)
110 110 raise formencode.Invalid(self.message('invalid_password',
111 111 state=State_obj), value, state,
112 112 error_dict=self.e_dict)
113 113 else:
114 114 log.warning('user %s is disabled', username)
115 115 raise formencode.Invalid(self.message('disabled_account',
116 116 state=State_obj),
117 117 value, state,
118 118 error_dict=self.e_dict_disable)
119 119
120 120 class ValidRepoUser(formencode.validators.FancyValidator):
121 121
122 122 def to_python(self, value, state):
123 123 try:
124 124 self.user_db = meta.Session.query(User)\
125 125 .filter(User.active == True)\
126 126 .filter(User.username == value).one()
127 127 except Exception:
128 128 raise formencode.Invalid(_('This username is not valid'),
129 129 value, state)
130 130 finally:
131 131 meta.Session.remove()
132 132
133 133 return self.user_db.user_id
134 134
135 135 def ValidRepoName(edit, old_data):
136 136 class _ValidRepoName(formencode.validators.FancyValidator):
137 137
138 138 def to_python(self, value, state):
139 139 slug = h.repo_name_slug(value)
140 140 if slug in ['_admin']:
141 141 raise formencode.Invalid(_('This repository name is disallowed'),
142 142 value, state)
143 143 if old_data.get('repo_name') != value or not edit:
144 144 sa = meta.Session
145 145 if sa.query(Repository).filter(Repository.repo_name == slug).scalar():
146 146 raise formencode.Invalid(_('This repository already exists') ,
147 147 value, state)
148 148 meta.Session.remove()
149 149 return slug
150 150
151 151
152 152 return _ValidRepoName
153 153
154 154 class ValidPerms(formencode.validators.FancyValidator):
155 155 messages = {'perm_new_user_name':_('This username is not valid')}
156 156
157 157 def to_python(self, value, state):
158 158 perms_update = []
159 159 perms_new = []
160 160 #build a list of permission to update and new permission to create
161 161 for k, v in value.items():
162 162 if k.startswith('perm_'):
163 163 if k.startswith('perm_new_user'):
164 164 new_perm = value.get('perm_new_user', False)
165 165 new_user = value.get('perm_new_user_name', False)
166 166 if new_user and new_perm:
167 167 if (new_user, new_perm) not in perms_new:
168 168 perms_new.append((new_user, new_perm))
169 169 else:
170 170 usr = k[5:]
171 171 if usr == 'default':
172 172 if value['private']:
173 173 #set none for default when updating to private repo
174 174 v = 'repository.none'
175 175 perms_update.append((usr, v))
176 176 value['perms_updates'] = perms_update
177 177 value['perms_new'] = perms_new
178 178 sa = meta.Session
179 179 for k, v in perms_new:
180 180 try:
181 181 self.user_db = sa.query(User)\
182 182 .filter(User.active == True)\
183 183 .filter(User.username == k).one()
184 184 except Exception:
185 185 msg = self.message('perm_new_user_name',
186 186 state=State_obj)
187 187 raise formencode.Invalid(msg, value, state, error_dict={'perm_new_user_name':msg})
188 188 return value
189 189
190 190 class ValidSettings(formencode.validators.FancyValidator):
191 191
192 192 def to_python(self, value, state):
193 193 #settings form can't edit user
194 194 if value.has_key('user'):
195 195 del['value']['user']
196 196
197 197 return value
198 198
199 199 class ValidPath(formencode.validators.FancyValidator):
200 200 def to_python(self, value, state):
201 201 isdir = os.path.isdir(value.replace('*', ''))
202 202 if (value.endswith('/*') or value.endswith('/**')) and isdir:
203 203 return value
204 204 elif not isdir:
205 205 msg = _('This is not a valid path')
206 206 else:
207 207 msg = _('You need to specify * or ** at the end of path (ie. /tmp/*)')
208 208
209 209 raise formencode.Invalid(msg, value, state,
210 210 error_dict={'paths_root_path':msg})
211 211
212 212 def UniqSystemEmail(old_data):
213 213 class _UniqSystemEmail(formencode.validators.FancyValidator):
214 214 def to_python(self, value, state):
215 215 if old_data.get('email') != value:
216 216 sa = meta.Session
217 217 try:
218 218 user = sa.query(User).filter(User.email == value).scalar()
219 219 if user:
220 220 raise formencode.Invalid(_("That e-mail address is already taken") ,
221 221 value, state)
222 222 finally:
223 223 meta.Session.remove()
224 224
225 225 return value
226 226
227 227 return _UniqSystemEmail
228 228
229 229 class ValidSystemEmail(formencode.validators.FancyValidator):
230 230 def to_python(self, value, state):
231 231 sa = meta.Session
232 232 try:
233 233 user = sa.query(User).filter(User.email == value).scalar()
234 234 if user is None:
235 235 raise formencode.Invalid(_("That e-mail address doesn't exist.") ,
236 236 value, state)
237 237 finally:
238 238 meta.Session.remove()
239 239
240 240 return value
241 241
242 242 #===============================================================================
243 243 # FORMS
244 244 #===============================================================================
245 245 class LoginForm(formencode.Schema):
246 246 allow_extra_fields = True
247 247 filter_extra_fields = True
248 248 username = UnicodeString(
249 249 strip=True,
250 250 min=1,
251 251 not_empty=True,
252 252 messages={
253 253 'empty':_('Please enter a login'),
254 254 'tooShort':_('Enter a value %(min)i characters long or more')}
255 255 )
256 256
257 257 password = UnicodeString(
258 258 strip=True,
259 259 min=6,
260 260 not_empty=True,
261 261 messages={
262 262 'empty':_('Please enter a password'),
263 263 'tooShort':_('Enter a value %(min)i characters long or more')}
264 264 )
265 265
266 266
267 267 #chained validators have access to all data
268 268 chained_validators = [ValidAuth]
269 269
270 270 def UserForm(edit=False, old_data={}):
271 271 class _UserForm(formencode.Schema):
272 272 allow_extra_fields = True
273 273 filter_extra_fields = True
274 274 username = All(UnicodeString(strip=True, min=1, not_empty=True), ValidUsername(edit, old_data))
275 275 if edit:
276 276 new_password = All(UnicodeString(strip=True, min=6, not_empty=False), ValidPassword)
277 277 admin = StringBoolean(if_missing=False)
278 278 else:
279 279 password = All(UnicodeString(strip=True, min=6, not_empty=True), ValidPassword)
280 280 active = StringBoolean(if_missing=False)
281 281 name = UnicodeString(strip=True, min=1, not_empty=True)
282 282 lastname = UnicodeString(strip=True, min=1, not_empty=True)
283 283 email = All(Email(not_empty=True), UniqSystemEmail(old_data))
284 284
285 285 return _UserForm
286 286
287 287 RegisterForm = UserForm
288 288
289 289 def PasswordResetForm():
290 290 class _PasswordResetForm(formencode.Schema):
291 291 allow_extra_fields = True
292 292 filter_extra_fields = True
293 293 email = All(ValidSystemEmail(), Email(not_empty=True))
294 294 return _PasswordResetForm
295 295
296 296 def RepoForm(edit=False, old_data={}):
297 297 class _RepoForm(formencode.Schema):
298 298 allow_extra_fields = True
299 299 filter_extra_fields = False
300 300 repo_name = All(UnicodeString(strip=True, min=1, not_empty=True), ValidRepoName(edit, old_data))
301 301 description = UnicodeString(strip=True, min=1, not_empty=True)
302 302 private = StringBoolean(if_missing=False)
303 303
304 304 if edit:
305 305 user = All(Int(not_empty=True), ValidRepoUser)
306 306
307 307 chained_validators = [ValidPerms]
308 308 return _RepoForm
309 309
310 310 def RepoForkForm(edit=False, old_data={}):
311 311 class _RepoForkForm(formencode.Schema):
312 312 allow_extra_fields = True
313 313 filter_extra_fields = False
314 314 fork_name = All(UnicodeString(strip=True, min=1, not_empty=True), ValidRepoName(edit, old_data))
315 315 description = UnicodeString(strip=True, min=1, not_empty=True)
316 316 private = StringBoolean(if_missing=False)
317 317
318 318 return _RepoForkForm
319 319
320 320 def RepoSettingsForm(edit=False, old_data={}):
321 321 class _RepoForm(formencode.Schema):
322 322 allow_extra_fields = True
323 323 filter_extra_fields = False
324 324 repo_name = All(UnicodeString(strip=True, min=1, not_empty=True), ValidRepoName(edit, old_data))
325 325 description = UnicodeString(strip=True, min=1, not_empty=True)
326 326 private = StringBoolean(if_missing=False)
327 327
328 328 chained_validators = [ValidPerms, ValidSettings]
329 329 return _RepoForm
330 330
331 331
332 332 def ApplicationSettingsForm():
333 333 class _ApplicationSettingsForm(formencode.Schema):
334 334 allow_extra_fields = True
335 335 filter_extra_fields = False
336 hg_app_title = UnicodeString(strip=True, min=1, not_empty=True)
337 hg_app_realm = UnicodeString(strip=True, min=1, not_empty=True)
336 rhodecode_title = UnicodeString(strip=True, min=1, not_empty=True)
337 rhodecode_realm = UnicodeString(strip=True, min=1, not_empty=True)
338 338
339 339 return _ApplicationSettingsForm
340 340
341 341 def ApplicationUiSettingsForm():
342 342 class _ApplicationUiSettingsForm(formencode.Schema):
343 343 allow_extra_fields = True
344 344 filter_extra_fields = False
345 345 web_push_ssl = OneOf(['true', 'false'], if_missing='false')
346 346 paths_root_path = All(ValidPath(), UnicodeString(strip=True, min=1, not_empty=True))
347 347 hooks_changegroup_update = OneOf(['True', 'False'], if_missing=False)
348 348 hooks_changegroup_repo_size = OneOf(['True', 'False'], if_missing=False)
349 349
350 350 return _ApplicationUiSettingsForm
351 351
352 352 def DefaultPermissionsForm(perms_choices, register_choices, create_choices):
353 353 class _DefaultPermissionsForm(formencode.Schema):
354 354 allow_extra_fields = True
355 355 filter_extra_fields = True
356 356 overwrite_default = OneOf(['true', 'false'], if_missing='false')
357 357 default_perm = OneOf(perms_choices)
358 358 default_register = OneOf(register_choices)
359 359 default_create = OneOf(create_choices)
360 360
361 361 return _DefaultPermissionsForm
@@ -1,170 +1,170 b''
1 1 ## -*- coding: utf-8 -*-
2 2 <%inherit file="/base/base.html"/>
3 3
4 4 <%def name="title()">
5 5 ${_('Settings administration')}
6 6 </%def>
7 7
8 8 <%def name="breadcrumbs_links()">
9 9 ${h.link_to(_('Admin'),h.url('admin_home'))} &raquo; ${_('Settings')}
10 10 </%def>
11 11
12 12 <%def name="page_nav()">
13 13 ${self.menu('admin')}
14 14 </%def>
15 15
16 16 <%def name="main()">
17 17 <div class="box">
18 18 <!-- box / title -->
19 19 <div class="title">
20 20 ${self.breadcrumbs()}
21 21 </div>
22 22 <!-- end box / title -->
23 23
24 24 <h3>${_('Remap and rescan repositories')}</h3>
25 25 ${h.form(url('admin_setting', setting_id='mapping'),method='put')}
26 26 <div class="form">
27 27 <!-- fields -->
28 28
29 29 <div class="fields">
30 30 <div class="field">
31 31 <div class="label label-checkbox">
32 32 <label for="destroy">${_('rescan option')}:</label>
33 33 </div>
34 34 <div class="checkboxes">
35 35 <div class="checkbox">
36 36 ${h.checkbox('destroy',True)}
37 37 <label for="checkbox-1">
38 38 <span class="tooltip" tooltip_title="${h.tooltip(_('In case a repository was deleted from filesystem and there are leftovers in the database check this option to scan obsolete data in database and remove it.'))}">
39 39 ${_('destroy old data')}</span> </label>
40 40 </div>
41 41 </div>
42 42 </div>
43 43
44 44 <div class="buttons">
45 45 ${h.submit('rescan','rescan repositories',class_="ui-button ui-widget ui-state-default ui-corner-all")}
46 46 </div>
47 47 </div>
48 48 </div>
49 49 ${h.end_form()}
50 50
51 51 <h3>${_('Whoosh indexing')}</h3>
52 52 ${h.form(url('admin_setting', setting_id='whoosh'),method='put')}
53 53 <div class="form">
54 54 <!-- fields -->
55 55
56 56 <div class="fields">
57 57 <div class="field">
58 58 <div class="label label-checkbox">
59 59 <label for="destroy">${_('index build option')}:</label>
60 60 </div>
61 61 <div class="checkboxes">
62 62 <div class="checkbox">
63 63 ${h.checkbox('full_index',True)}
64 64 <label for="checkbox-1">${_('build from scratch')}</label>
65 65 </div>
66 66 </div>
67 67 </div>
68 68
69 69 <div class="buttons">
70 70 ${h.submit('reindex','reindex',class_="ui-button ui-widget ui-state-default ui-corner-all")}
71 71 </div>
72 72 </div>
73 73 </div>
74 74 ${h.end_form()}
75 75
76 76 <h3>${_('Global application settings')}</h3>
77 77 ${h.form(url('admin_setting', setting_id='global'),method='put')}
78 78 <div class="form">
79 79 <!-- fields -->
80 80
81 81 <div class="fields">
82 82
83 83 <div class="field">
84 84 <div class="label">
85 <label for="hg_app_title">${_('Application name')}:</label>
85 <label for="rhodecode_title">${_('Application name')}:</label>
86 86 </div>
87 87 <div class="input">
88 ${h.text('hg_app_title',size=30)}
88 ${h.text('rhodecode_title',size=30)}
89 89 </div>
90 90 </div>
91 91
92 92 <div class="field">
93 93 <div class="label">
94 <label for="hg_app_realm">${_('Realm text')}:</label>
94 <label for="rhodecode_realm">${_('Realm text')}:</label>
95 95 </div>
96 96 <div class="input">
97 ${h.text('hg_app_realm',size=30)}
97 ${h.text('rhodecode_realm',size=30)}
98 98 </div>
99 99 </div>
100 100
101 101 <div class="buttons">
102 102 ${h.submit('save','save settings',class_="ui-button ui-widget ui-state-default ui-corner-all")}
103 103 </div>
104 104 </div>
105 105 </div>
106 106 ${h.end_form()}
107 107
108 108 <h3>${_('Mercurial settings')}</h3>
109 109 ${h.form(url('admin_setting', setting_id='mercurial'),method='put')}
110 110 <div class="form">
111 111 <!-- fields -->
112 112
113 113 <div class="fields">
114 114
115 115 <div class="field">
116 116 <div class="label label-checkbox">
117 117 <label for="web_push_ssl">${_('Web')}:</label>
118 118 </div>
119 119 <div class="checkboxes">
120 120 <div class="checkbox">
121 121 ${h.checkbox('web_push_ssl','true')}
122 122 <label for="web_push_ssl">${_('require ssl for pushing')}</label>
123 123 </div>
124 124 </div>
125 125 </div>
126 126
127 127 <div class="field">
128 128 <div class="label label-checkbox">
129 129 <label for="web_push_ssl">${_('Hooks')}:</label>
130 130 </div>
131 131 <div class="checkboxes">
132 132 <div class="checkbox">
133 133 ${h.checkbox('hooks_changegroup_update','True')}
134 134 <label for="hooks_changegroup_update">${_('Update repository after push (hg update)')}</label>
135 135 </div>
136 136 <div class="checkbox">
137 137 ${h.checkbox('hooks_changegroup_repo_size','True')}
138 138 <label for="hooks_changegroup_repo_size">${_('Show repository size after push')}</label>
139 139 </div>
140 140 </div>
141 141 </div>
142 142
143 143 <div class="field">
144 144 <div class="label">
145 145 <label for="paths_root_path">${_('Repositories location')}:</label>
146 146 </div>
147 147 <div class="input">
148 148 ${h.text('paths_root_path',size=30,readonly="readonly")}
149 149 <span id="path_unlock" class="tooltip"
150 150 tooltip_title="${h.tooltip(_('This a crucial application setting. If You really sure you need to change this, you must restart application in order to make this settings take effect. Click this label to unlock.'))}">
151 151 ${_('unlock')}</span>
152 152 </div>
153 153 </div>
154 154
155 155 <div class="buttons">
156 156 ${h.submit('save','save settings',class_="ui-button ui-widget ui-state-default ui-corner-all")}
157 157 </div>
158 158 </div>
159 159 </div>
160 160 ${h.end_form()}
161 161
162 162 <script type="text/javascript">
163 163 YAHOO.util.Event.onDOMReady(function(){
164 164 YAHOO.util.Event.addListener('path_unlock','click',function(){
165 165 YAHOO.util.Dom.get('paths_root_path').removeAttribute('readonly');
166 166 });
167 167 });
168 168 </script>
169 169 </div>
170 170 </%def>
@@ -1,130 +1,130 b''
1 1 ## -*- coding: utf-8 -*-
2 2 <%inherit file="/base/base.html"/>
3 3
4 4 <%def name="title()">
5 ${c.hg_app_user.username} ${_('account')}
5 ${c.rhodecode_user.username} ${_('account')}
6 6 </%def>
7 7
8 8 <%def name="breadcrumbs_links()">
9 9 ${_('My Account')}
10 10 </%def>
11 11
12 12 <%def name="page_nav()">
13 13 ${self.menu('admin')}
14 14 </%def>
15 15
16 16 <%def name="main()">
17 17
18 18 <div class="box box-left">
19 19 <!-- box / title -->
20 20 <div class="title">
21 21 ${self.breadcrumbs()}
22 22 </div>
23 23 <!-- end box / title -->
24 24 <div class="ui-tabs-panel ui-widget-content ui-corner-bottom">
25 25 ${h.form(url('admin_settings_my_account_update'),method='put')}
26 26 <div class="form">
27 27 <div class="fields">
28 28 <div class="field">
29 29 <div class="label">
30 30 <label for="username">${_('Username')}:</label>
31 31 </div>
32 32 <div class="input">
33 33 ${h.text('username')}
34 34 </div>
35 35 </div>
36 36
37 37 <div class="field">
38 38 <div class="label">
39 39 <label for="new_password">${_('New password')}:</label>
40 40 </div>
41 41 <div class="input">
42 42 ${h.password('new_password')}
43 43 </div>
44 44 </div>
45 45
46 46 <div class="field">
47 47 <div class="label">
48 48 <label for="name">${_('Name')}:</label>
49 49 </div>
50 50 <div class="input">
51 51 ${h.text('name')}
52 52 </div>
53 53 </div>
54 54
55 55 <div class="field">
56 56 <div class="label">
57 57 <label for="lastname">${_('Lastname')}:</label>
58 58 </div>
59 59 <div class="input">
60 60 ${h.text('lastname')}
61 61 </div>
62 62 </div>
63 63
64 64 <div class="field">
65 65 <div class="label">
66 66 <label for="email">${_('Email')}:</label>
67 67 </div>
68 68 <div class="input">
69 69 ${h.text('email')}
70 70 </div>
71 71 </div>
72 72
73 73 <div class="buttons">
74 74 ${h.submit('save','save',class_="ui-button ui-widget ui-state-default ui-corner-all")}
75 75 </div>
76 76 </div>
77 77 </div>
78 78 ${h.end_form()}
79 79 </div>
80 80 </div>
81 81
82 82 <div class="box box-right">
83 83 <!-- box / title -->
84 84 <div class="title">
85 85 <h5>${_('My repositories')}</h5>
86 86 </div>
87 87 <!-- end box / title -->
88 88 <div class="table">
89 89 <table>
90 90 <tbody>
91 91 %if c.user_repos:
92 92 %for repo in c.user_repos:
93 93 <tr>
94 94 <td>
95 95 %if repo.dbrepo.private:
96 96 <img class="icon" alt="${_('private')}" src="/images/icons/lock.png"/>
97 97 %else:
98 98 <img class="icon" alt="${_('public')}" src="/images/icons/lock_open.png"/>
99 99 %endif
100 100
101 101 ${h.link_to(repo.name, h.url('summary_home',repo_name=repo.name))}
102 102 %if repo.dbrepo.fork:
103 103 <a href="${h.url('summary_home',repo_name=repo.dbrepo.fork.repo_name)}">
104 104 <img class="icon" alt="${_('public')}"
105 105 title="${_('Fork of')} ${repo.dbrepo.fork.repo_name}"
106 106 src="/images/icons/arrow_divide.png"/></a>
107 107 %endif
108 108 </td>
109 109 <td>${_('revision')}: ${h.get_changeset_safe(repo,'tip').revision}</td>
110 110 <td>${_('last changed')}: ${h.age(repo.last_change)}</td>
111 111 <td><img class="icon" alt="${_('private')}" src="/images/icons/application_form_edit.png"/> ${h.link_to(_('edit'),h.url('repo_settings_home',repo_name=repo.name))}</td>
112 112 <td>
113 113 ${h.form(url('repo_settings_delete', repo_name=repo.name),method='delete')}
114 114 ${h.submit('remove_%s' % repo.name,'delete',class_="delete_icon action_button",onclick="return confirm('Confirm to delete this repository');")}
115 115 ${h.end_form()}
116 116 </td>
117 117 </tr>
118 118 %endfor
119 119 %else:
120 120 ${_('No repositories yet')}
121 121 %if h.HasPermissionAny('hg.admin','hg.create.repository')():
122 122 ${h.link_to(_('create one now'),h.url('admin_settings_create_repository'))}
123 123 %endif
124 124 %endif
125 125 </tbody>
126 126 </table>
127 127 </div>
128 128
129 129 </div>
130 130 </%def> No newline at end of file
@@ -1,269 +1,269 b''
1 1 ## -*- coding: utf-8 -*-
2 2 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
3 3 <html xmlns="http://www.w3.org/1999/xhtml" id="mainhtml">
4 4 <head>
5 5 <title>${next.title()}</title>
6 6 <link rel="icon" href="/images/hgicon.png" type="image/png" />
7 7 <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
8 8 <meta name="robots" content="index, nofollow"/>
9 9 <!-- stylesheets -->
10 10 ${self.css()}
11 11 <!-- scripts -->
12 12 ${self.js()}
13 13 </head>
14 14 <body>
15 15 <!-- header -->
16 16 <div id="header">
17 17 <!-- user -->
18 18 <ul id="logged-user">
19 19 <li class="first">
20 20 <div class="gravatar">
21 <img alt="gravatar" src="${h.gravatar_url(c.hg_app_user.email,24)}" />
21 <img alt="gravatar" src="${h.gravatar_url(c.rhodecode_user.email,24)}" />
22 22 </div>
23 23 <div class="account">
24 ${h.link_to('%s %s'%(c.hg_app_user.name,c.hg_app_user.lastname),h.url('admin_settings_my_account'))}<br/>
25 ${h.link_to(c.hg_app_user.username,h.url('admin_settings_my_account'))}
24 ${h.link_to('%s %s'%(c.rhodecode_user.name,c.rhodecode_user.lastname),h.url('admin_settings_my_account'))}<br/>
25 ${h.link_to(c.rhodecode_user.username,h.url('admin_settings_my_account'))}
26 26 </div>
27 27 </li>
28 28 <li class="last highlight">${h.link_to(u'Logout',h.url('logout_home'))}</li>
29 29 </ul>
30 30 <!-- end user -->
31 31 <div id="header-inner">
32 32 <div id="home">
33 33 <a href="${h.url('hg_home')}"></a>
34 34 </div>
35 35 <!-- logo -->
36 36 <div id="logo">
37 <h1><a href="${h.url('hg_home')}">${c.hg_app_name}</a></h1>
37 <h1><a href="${h.url('hg_home')}">${c.rhodecode_name}</a></h1>
38 38 </div>
39 39 <!-- end logo -->
40 40 <!-- quick menu -->
41 41 ${self.page_nav()}
42 42 <!-- end quick -->
43 43 <div class="corner tl"></div>
44 44 <div class="corner tr"></div>
45 45 </div>
46 46 </div>
47 47 <!-- end header -->
48 48
49 49 <!-- CONTENT -->
50 50 <div id="content">
51 51 <div class="flash_msg">
52 52 <% messages = h.flash.pop_messages() %>
53 53 % if messages:
54 54 <ul id="flash-messages">
55 55 % for message in messages:
56 56 <li class="${message.category}_msg">${message}</li>
57 57 % endfor
58 58 </ul>
59 59 % endif
60 60 </div>
61 61 <div id="main">
62 62 ${next.main()}
63 63 </div>
64 64 </div>
65 65 <!-- END CONTENT -->
66 66
67 67 <!-- footer -->
68 68 <div id="footer">
69 <p>Hg App ${c.hg_app_version} &copy; 2010 by Marcin Kuzminski</p>
69 <p>Hg App ${c.rhodecode_version} &copy; 2010 by Marcin Kuzminski</p>
70 70 <script type="text/javascript">${h.tooltip.activate()}</script>
71 71 </div>
72 72 <!-- end footer -->
73 73 </body>
74 74
75 75 </html>
76 76
77 77 ### MAKO DEFS ###
78 78 <%def name="page_nav()">
79 79 ${self.menu()}
80 80 </%def>
81 81
82 82 <%def name="menu(current=None)">
83 83 <%
84 84 def is_current(selected):
85 85 if selected == current:
86 86 return h.literal('class="current"')
87 87 %>
88 88 %if current not in ['home','admin']:
89 89 ##REGULAR MENU
90 90 <ul id="quick">
91 91 <!-- repo switcher -->
92 92 <li>
93 93 <a id="repo_switcher" title="${_('Switch repository')}" href="#">
94 94 <span class="icon">
95 95 <img src="/images/icons/database.png" alt="${_('Products')}" />
96 96 </span>
97 97 <span>&darr;</span>
98 98 </a>
99 99 <ul class="repo_switcher">
100 100 %for repo,private in c.repo_switcher_list:
101 101 %if private:
102 102 <li>${h.link_to(repo,h.url('summary_home',repo_name=repo),class_="private_repo")}</li>
103 103 %else:
104 104 <li>${h.link_to(repo,h.url('summary_home',repo_name=repo),class_="public_repo")}</li>
105 105 %endif
106 106 %endfor
107 107 </ul>
108 108 </li>
109 109
110 110 <li ${is_current('summary')}>
111 111 <a title="${_('Summary')}" href="${h.url('summary_home',repo_name=c.repo_name)}">
112 112 <span class="icon">
113 113 <img src="/images/icons/clipboard_16.png" alt="${_('Summary')}" />
114 114 </span>
115 115 <span>${_('Summary')}</span>
116 116 </a>
117 117 </li>
118 118 <li ${is_current('shortlog')}>
119 119 <a title="${_('Shortlog')}" href="${h.url('shortlog_home',repo_name=c.repo_name)}">
120 120 <span class="icon">
121 121 <img src="/images/icons/application_double.png" alt="${_('Shortlog')}" />
122 122 </span>
123 123 <span>${_('Shortlog')}</span>
124 124 </a>
125 125 </li>
126 126 <li ${is_current('changelog')}>
127 127 <a title="${_('Changelog')}" href="${h.url('changelog_home',repo_name=c.repo_name)}">
128 128 <span class="icon">
129 129 <img src="/images/icons/time.png" alt="${_('Changelog')}" />
130 130 </span>
131 131 <span>${_('Changelog')}</span>
132 132 </a>
133 133 </li>
134 134
135 135 <li ${is_current('switch_to')}>
136 136 <a title="${_('Switch to')}" href="#">
137 137 <span class="icon">
138 138 <img src="/images/icons/arrow_switch.png" alt="${_('Switch to')}" />
139 139 </span>
140 140 <span>${_('Switch to')}</span>
141 141 </a>
142 142 <ul>
143 143 <li>
144 144 ${h.link_to(_('branches'),h.url('branches_home',repo_name=c.repo_name),class_='branches childs')}
145 145 <ul>
146 146 %if c.repository_branches.values():
147 147 %for cnt,branch in enumerate(c.repository_branches.items()):
148 148 <li>${h.link_to('%s - %s' % (branch[0],branch[1]),h.url('files_home',repo_name=c.repo_name,revision=branch[1]))}</li>
149 149 %endfor
150 150 %else:
151 151 <li>${h.link_to(_('There are no branches yet'),'#')}</li>
152 152 %endif
153 153 </ul>
154 154 </li>
155 155 <li>
156 156 ${h.link_to(_('tags'),h.url('tags_home',repo_name=c.repo_name),class_='tags childs')}
157 157 <ul>
158 158 %if c.repository_tags.values():
159 159 %for cnt,tag in enumerate(c.repository_tags.items()):
160 160 <li>${h.link_to('%s - %s' % (tag[0],tag[1]),h.url('files_home',repo_name=c.repo_name,revision=tag[1]))}</li>
161 161 %endfor
162 162 %else:
163 163 <li>${h.link_to(_('There are no tags yet'),'#')}</li>
164 164 %endif
165 165 </ul>
166 166 </li>
167 167 </ul>
168 168 </li>
169 169 <li ${is_current('files')}>
170 170 <a title="${_('Files')}" href="${h.url('files_home',repo_name=c.repo_name)}">
171 171 <span class="icon">
172 172 <img src="/images/icons/file.png" alt="${_('Files')}" />
173 173 </span>
174 174 <span>${_('Files')}</span>
175 175 </a>
176 176 </li>
177 177
178 178 <li ${is_current('options')}>
179 179 <a title="${_('Options')}" href="#">
180 180 <span class="icon">
181 181 <img src="/images/icons/table_gear.png" alt="${_('Admin')}" />
182 182 </span>
183 183 <span>${_('Options')}</span>
184 184 </a>
185 185 <ul>
186 186 %if h.HasRepoPermissionAll('repository.admin')(c.repo_name):
187 187 <li>${h.link_to(_('settings'),h.url('repo_settings_home',repo_name=c.repo_name),class_='settings')}</li>
188 188 %endif
189 189 <li>${h.link_to(_('fork'),h.url('repo_fork_home',repo_name=c.repo_name),class_='fork')}</li>
190 190 <li>${h.link_to(_('search'),h.url('search_repo',search_repo=c.repo_name),class_='search')}</li>
191 191 ## %if h.HasRepoPermissionAll('repository.admin')(c.repo_name):
192 192 ## <li class="last">
193 193 ## ${h.link_to(_('delete'),'#',class_='delete')}
194 194 ## ${h.form(url('repo_settings_delete', repo_name=c.repo_name),method='delete')}
195 195 ## ${h.submit('remove_%s' % c.repo_name,'delete',class_="delete_icon action_button",onclick="return confirm('Confirm to delete this repository');")}
196 196 ## ${h.end_form()}
197 197 ## </li>
198 198 ## %endif
199 199 </ul>
200 200 </li>
201 201 </ul>
202 202 %else:
203 203 ##ROOT MENU
204 204 <ul id="quick">
205 205 <li>
206 206 <a title="${_('Home')}" href="${h.url('hg_home')}">
207 207 <span class="icon">
208 208 <img src="/images/icons/home_16.png" alt="${_('Home')}" />
209 209 </span>
210 210 <span>${_('Home')}</span>
211 211 </a>
212 212 </li>
213 213
214 214 <li>
215 215 <a title="${_('Search')}" href="${h.url('search')}">
216 216 <span class="icon">
217 217 <img src="/images/icons/search_16.png" alt="${_('Search')}" />
218 218 </span>
219 219 <span>${_('Search')}</span>
220 220 </a>
221 221 </li>
222 222
223 223 %if h.HasPermissionAll('hg.admin')('access admin main page'):
224 224 <li ${is_current('admin')}>
225 225 <a title="${_('Admin')}" href="${h.url('admin_home')}">
226 226 <span class="icon">
227 227 <img src="/images/icons/cog_edit.png" alt="${_('Admin')}" />
228 228 </span>
229 229 <span>${_('Admin')}</span>
230 230 </a>
231 231 <ul>
232 232 <li>${h.link_to(_('journal'),h.url('admin_home'),class_='journal')}</li>
233 233 <li>${h.link_to(_('repositories'),h.url('repos'),class_='repos')}</li>
234 234 <li>${h.link_to(_('users'),h.url('users'),class_='users')}</li>
235 235 <li>${h.link_to(_('permissions'),h.url('edit_permission',id='default'),class_='permissions')}</li>
236 236 <li class="last">${h.link_to(_('settings'),h.url('admin_settings'),class_='settings')}</li>
237 237 </ul>
238 238 </li>
239 239 %endif
240 240
241 241 </ul>
242 242 %endif
243 243 </%def>
244 244
245 245
246 246 <%def name="css()">
247 247 <link rel="stylesheet" type="text/css" href="/css/reset.css" />
248 248 <link rel="stylesheet" type="text/css" href="/css/style.css" media="screen" />
249 249 <link id="color" rel="stylesheet" type="text/css" href="/css/colors/blue.css" />
250 250 <link rel="stylesheet" type="text/css" href="/css/pygments.css" />
251 251 <link rel="stylesheet" type="text/css" href="/css/diff.css" />
252 252 </%def>
253 253
254 254 <%def name="js()">
255 255 ##<script type="text/javascript" src="/js/yui/utilities/utilities.js"></script>
256 256 ##<script type="text/javascript" src="/js/yui/container/container.js"></script>
257 257 ##<script type="text/javascript" src="/js/yui/datasource/datasource.js"></script>
258 258 ##<script type="text/javascript" src="/js/yui/autocomplete/autocomplete.js"></script>
259 259
260 260 <script type="text/javascript" src="/js/yui2.js"></script>
261 261 <!--[if IE]><script language="javascript" type="text/javascript" src="/js/excanvas.min.js"></script><![endif]-->
262 262 <script type="text/javascript" src="/js/yui.flot.js"></script>
263 263 </%def>
264 264
265 265 <%def name="breadcrumbs()">
266 266 <div class="breadcrumbs">
267 267 ${self.breadcrumbs_links()}
268 268 </div>
269 269 </%def> No newline at end of file
@@ -1,98 +1,98 b''
1 1 ## -*- coding: utf-8 -*-
2 2 <%inherit file="base/base.html"/>
3 3 <%def name="title()">
4 ${c.hg_app_name}
4 ${c.rhodecode_name}
5 5 </%def>
6 6 <%def name="breadcrumbs()">
7 ${c.hg_app_name}
7 ${c.rhodecode_name}
8 8 </%def>
9 9 <%def name="page_nav()">
10 10 ${self.menu('home')}
11 11 </%def>
12 12 <%def name="main()">
13 13 <%def name="get_sort(name)">
14 14 <%name_slug = name.lower().replace(' ','_') %>
15 15
16 16 %if name_slug == c.sort_slug:
17 17 %if c.sort_by.startswith('-'):
18 18 <a href="?sort=${name_slug}">${name}&uarr;</a>
19 19 %else:
20 20 <a href="?sort=-${name_slug}">${name}&darr;</a>
21 21 %endif:
22 22 %else:
23 23 <a href="?sort=${name_slug}">${name}</a>
24 24 %endif
25 25 </%def>
26 26
27 27 <div class="box">
28 28 <!-- box / title -->
29 29 <div class="title">
30 30 <h5>${_('Dashboard')}</h5>
31 31 %if h.HasPermissionAny('hg.admin','hg.create.repository')():
32 32 <ul class="links">
33 33 <li>
34 34 <span>${h.link_to(_('ADD NEW REPOSITORY'),h.url('admin_settings_create_repository'),class_="add_icon")}</span>
35 35 </li>
36 36 </ul>
37 37 %endif
38 38 </div>
39 39 <!-- end box / title -->
40 40 <div class="table">
41 41 <table>
42 42 <thead>
43 43 <tr>
44 44 <th class="left">${get_sort(_('Name'))}</th>
45 45 <th class="left">${get_sort(_('Description'))}</th>
46 46 <th class="left">${get_sort(_('Last change'))}</th>
47 47 <th class="left">${get_sort(_('Tip'))}</th>
48 48 <th class="left">${get_sort(_('Contact'))}</th>
49 49 <th class="left">${_('RSS')}</th>
50 50 <th class="left">${_('Atom')}</th>
51 51 </tr>
52 52 </thead>
53 53 <tbody>
54 54 %for cnt,repo in enumerate(c.repos_list):
55 55 %if h.HasRepoPermissionAny('repository.write','repository.read','repository.admin')(repo['name'],'main page check'):
56 56 <tr class="parity${cnt%2}">
57 57 <td>
58 58 %if repo['repo'].dbrepo.private:
59 59 <img class="icon" alt="${_('private')}" src="/images/icons/lock.png"/>
60 60 %else:
61 61 <img class="icon" alt="${_('public')}" src="/images/icons/lock_open.png"/>
62 62 %endif
63 63 ${h.link_to(repo['name'],
64 64 h.url('summary_home',repo_name=repo['name']))}
65 65 %if repo['repo'].dbrepo.fork:
66 66 <a href="${h.url('summary_home',repo_name=repo['repo'].dbrepo.fork.repo_name)}">
67 67 <img class="icon" alt="${_('public')}"
68 68 title="${_('Fork of')} ${repo['repo'].dbrepo.fork.repo_name}"
69 69 src="/images/icons/arrow_divide.png"/></a>
70 70 %endif
71 71 </td>
72 72 <td title="${repo['description']}">${h.truncate(repo['description'],60)}</td>
73 73 <td>${h.age(repo['last_change'])}</td>
74 74 <td>
75 75 %if repo['rev']>=0:
76 76 ${h.link_to('r%s:%s' % (repo['rev'],repo['tip']),
77 77 h.url('changeset_home',repo_name=repo['name'],revision=repo['tip']),
78 78 class_="tooltip",
79 79 tooltip_title=h.tooltip(repo['last_msg']))}
80 80 %else:
81 81 ${_('No changesets yet')}
82 82 %endif
83 83 </td>
84 84 <td title="${repo['contact']}">${h.person(repo['contact'])}</td>
85 85 <td>
86 86 <a title="${_('Subscribe to %s rss feed')%repo['name']}" class="rss_icon" href="${h.url('rss_feed_home',repo_name=repo['name'])}"></a>
87 87 </td>
88 88 <td>
89 89 <a title="${_('Subscribe to %s atom feed')%repo['name']}" class="atom_icon" href="${h.url('atom_feed_home',repo_name=repo['name'])}"></a>
90 90 </td>
91 91 </tr>
92 92 %endif
93 93 %endfor
94 94 </tbody>
95 95 </table>
96 96 </div>
97 97 </div>
98 98 </%def>
@@ -1,87 +1,87 b''
1 1 ## -*- coding: utf-8 -*-
2 2 <%inherit file="/base/base.html"/>
3 3 <%def name="title()">
4 4 ${_('Search')}
5 5 %if c.repo_name:
6 6 ${_('in repository: ') + c.repo_name}
7 7 %else:
8 8 ${_('in all repositories')}
9 9 %endif
10 10 :${c.cur_query}
11 11 </%def>
12 12 <%def name="breadcrumbs()">
13 ${c.hg_app_name}
13 ${c.rhodecode_name}
14 14 </%def>
15 15 <%def name="page_nav()">
16 16 ${self.menu('home')}
17 17 </%def>
18 18 <%def name="main()">
19 19
20 20 <div class="box">
21 21 <!-- box / title -->
22 22 <div class="title">
23 23 <h5>${_('Search')}
24 24 %if c.repo_name:
25 25 ${_('in repository: ') + c.repo_name}
26 26 %else:
27 27 ${_('in all repositories')}
28 28 %endif
29 29 </h5>
30 30 </div>
31 31 <!-- end box / title -->
32 32 %if c.repo_name:
33 33 ${h.form(h.url('search_repo',search_repo=c.repo_name),method='get')}
34 34 %else:
35 35 ${h.form(h.url('search'),method='get')}
36 36 %endif
37 37 <div class="form">
38 38 <div class="fields">
39 39
40 40 <div class="field ">
41 41 <div class="label">
42 42 <label for="q">${_('Search')}:</label>
43 43 </div>
44 44 <div class="input">
45 45 ${h.text('q',c.cur_query,class_="small")}
46 46 <div class="button highlight">
47 47 <input type="submit" value="${_('Search')}" class="ui-button ui-widget ui-state-default ui-corner-all"/>
48 48 </div>
49 49 <div style="font-weight: bold;clear:both;padding: 5px">${c.runtime}</div>
50 50 </div>
51 51 </div>
52 52 </div>
53 53 </div>
54 54 ${h.end_form()}
55 55
56 56 %for cnt,sr in enumerate(c.formated_results):
57 57 %if h.HasRepoPermissionAny('repository.write','repository.read','repository.admin')(sr['repository'],'search results check'):
58 58 <div class="table">
59 59 <div id="body${cnt}" class="codeblock">
60 60 <div class="code-header">
61 61 <div class="revision">${h.link_to(h.literal('%s &raquo; %s' % (sr['repository'],sr['f_path'])),
62 62 h.url('files_home',repo_name=sr['repository'],revision='tip',f_path=sr['f_path']))}</div>
63 63 </div>
64 64 <div class="code-body">
65 65 <pre>${h.literal(sr['content_short_hl'])}</pre>
66 66 </div>
67 67 </div>
68 68 </div>
69 69 %else:
70 70 %if cnt == 0:
71 71 <div class="table">
72 72 <div id="body${cnt}" class="codeblock">
73 73 <div class="error">${_('Permission denied')}</div>
74 74 </div>
75 75 </div>
76 76 %endif
77 77
78 78 %endif
79 79 %endfor
80 80 %if c.cur_query:
81 81 <div class="pagination-wh pagination-left">
82 82 ${c.formated_results.pager('$link_previous ~2~ $link_next')}
83 83 </div>
84 84 %endif
85 85 </div>
86 86
87 87 </%def>
@@ -1,58 +1,58 b''
1 1 """Pylons application test package
2 2
3 3 This package assumes the Pylons environment is already loaded, such as
4 4 when this script is imported from the `nosetests --with-pylons=test.ini`
5 5 command.
6 6
7 7 This module initializes the application via ``websetup`` (`paster
8 8 setup-app`) and provides the base testing objects.
9 9 """
10 10 from unittest import TestCase
11 11
12 12 from paste.deploy import loadapp
13 13 from paste.script.appinstall import SetupCommand
14 14 from pylons import config, url
15 15 from routes.util import URLGenerator
16 16 from webtest import TestApp
17 17 import os
18 18 from rhodecode.model import meta
19 19 import logging
20 20
21 21
22 22 log = logging.getLogger(__name__)
23 23
24 24 import pylons.test
25 25
26 26 __all__ = ['environ', 'url', 'TestController']
27 27
28 28 # Invoke websetup with the current config file
29 29 #SetupCommand('setup-app').run([config_file])
30 30
31 31 ##RUNNING DESIRED TESTS
32 32 #nosetests rhodecode.tests.functional.test_admin_settings:TestSettingsController.test_my_account
33 33
34 34 environ = {}
35 35
36 36 class TestController(TestCase):
37 37
38 38 def __init__(self, *args, **kwargs):
39 39 wsgiapp = pylons.test.pylonsapp
40 40 config = wsgiapp.config
41 41 self.app = TestApp(wsgiapp)
42 42 url._push_object(URLGenerator(config['routes.map'], environ))
43 43 self.sa = meta.Session
44 44
45 45 TestCase.__init__(self, *args, **kwargs)
46 46
47 47 def log_user(self, username='test_admin', password='test12'):
48 48 response = self.app.post(url(controller='login', action='index'),
49 49 {'username':username,
50 50 'password':password})
51 51 print response
52 52
53 53 if 'invalid user name' in response.body:
54 54 assert False, 'could not login using %s %s' % (username, password)
55 55
56 56 assert response.status == '302 Found', 'Wrong response code from login got %s' % response.status
57 assert response.session['hg_app_user'].username == username, 'wrong logged in user got %s expected %s' % (response.session['hg_app_user'].username, username)
57 assert response.session['rhodecode_user'].username == username, 'wrong logged in user got %s expected %s' % (response.session['rhodecode_user'].username, username)
58 58 return response.follow()
@@ -1,147 +1,147 b''
1 1 from rhodecode.tests import *
2 2 from rhodecode.model.db import User
3 3 from rhodecode.lib.auth import check_password
4 4
5 5
6 6 class TestLoginController(TestController):
7 7
8 8 def test_index(self):
9 9 response = self.app.get(url(controller='login', action='index'))
10 10 assert response.status == '200 OK', 'Wrong response from login page got %s' % response.status
11 11 # Test response...
12 12
13 13 def test_login_admin_ok(self):
14 14 response = self.app.post(url(controller='login', action='index'),
15 15 {'username':'test_admin',
16 16 'password':'test12'})
17 17 assert response.status == '302 Found', 'Wrong response code from login got %s' % response.status
18 assert response.session['hg_app_user'].username == 'test_admin', 'wrong logged in user'
18 assert response.session['rhodecode_user'].username == 'test_admin', 'wrong logged in user'
19 19 response = response.follow()
20 20 assert 'auto description for vcs_test' in response.body
21 21
22 22 def test_login_regular_ok(self):
23 23 response = self.app.post(url(controller='login', action='index'),
24 24 {'username':'test_regular',
25 25 'password':'test12'})
26 26 print response
27 27 assert response.status == '302 Found', 'Wrong response code from login got %s' % response.status
28 assert response.session['hg_app_user'].username == 'test_regular', 'wrong logged in user'
28 assert response.session['rhodecode_user'].username == 'test_regular', 'wrong logged in user'
29 29 response = response.follow()
30 30 assert 'auto description for vcs_test' in response.body
31 31 assert '<a title="Admin" href="/_admin">' not in response.body
32 32
33 33 def test_login_ok_came_from(self):
34 34 test_came_from = '/_admin/users'
35 35 response = self.app.post(url(controller='login', action='index', came_from=test_came_from),
36 36 {'username':'test_admin',
37 37 'password':'test12'})
38 38 assert response.status == '302 Found', 'Wrong response code from came from redirection'
39 39 response = response.follow()
40 40
41 41 assert response.status == '200 OK', 'Wrong response from login page got %s' % response.status
42 42 assert 'Users administration' in response.body, 'No proper title in response'
43 43
44 44
45 45 def test_login_short_password(self):
46 46 response = self.app.post(url(controller='login', action='index'),
47 47 {'username':'error',
48 48 'password':'test'})
49 49 assert response.status == '200 OK', 'Wrong response from login page'
50 50
51 51 assert 'Enter a value 6 characters long or more' in response.body, 'No error password message in response'
52 52
53 53 def test_login_wrong_username_password(self):
54 54 response = self.app.post(url(controller='login', action='index'),
55 55 {'username':'error',
56 56 'password':'test12'})
57 57 assert response.status == '200 OK', 'Wrong response from login page'
58 58
59 59 assert 'invalid user name' in response.body, 'No error username message in response'
60 60 assert 'invalid password' in response.body, 'No error password message in response'
61 61
62 62
63 63 def test_register(self):
64 64 response = self.app.get(url(controller='login', action='register'))
65 65 assert 'Sign Up to hg-app' in response.body, 'wrong page for user registration'
66 66
67 67 def test_register_err_same_username(self):
68 68 response = self.app.post(url(controller='login', action='register'),
69 69 {'username':'test_admin',
70 70 'password':'test',
71 71 'email':'goodmail@domain.com',
72 72 'name':'test',
73 73 'lastname':'test'})
74 74
75 75 assert response.status == '200 OK', 'Wrong response from register page got %s' % response.status
76 76 assert 'This username already exists' in response.body
77 77
78 78 def test_register_err_wrong_data(self):
79 79 response = self.app.post(url(controller='login', action='register'),
80 80 {'username':'xs',
81 81 'password':'',
82 82 'email':'goodmailm',
83 83 'name':'test',
84 84 'lastname':'test'})
85 85
86 86 assert response.status == '200 OK', 'Wrong response from register page got %s' % response.status
87 87 assert 'An email address must contain a single @' in response.body
88 88 assert 'Please enter a value' in response.body
89 89
90 90
91 91
92 92 def test_register_ok(self):
93 93 username = 'test_regular4'
94 94 password = 'qweqwe'
95 95 email = 'marcin@test.com'
96 96 name = 'testname'
97 97 lastname = 'testlastname'
98 98
99 99 response = self.app.post(url(controller='login', action='register'),
100 100 {'username':username,
101 101 'password':password,
102 102 'email':email,
103 103 'name':name,
104 104 'lastname':lastname})
105 105 print response.body
106 106 assert response.status == '302 Found', 'Wrong response from register page got %s' % response.status
107 107 assert 'You have successfully registered into hg-app' in response.session['flash'][0], 'No flash message about user registration'
108 108
109 109 ret = self.sa.query(User).filter(User.username == 'test_regular4').one()
110 110 assert ret.username == username , 'field mismatch %s %s' % (ret.username, username)
111 111 assert check_password(password, ret.password) == True , 'password mismatch'
112 112 assert ret.email == email , 'field mismatch %s %s' % (ret.email, email)
113 113 assert ret.name == name , 'field mismatch %s %s' % (ret.name, name)
114 114 assert ret.lastname == lastname , 'field mismatch %s %s' % (ret.lastname, lastname)
115 115
116 116
117 117 def test_forgot_password_wrong_mail(self):
118 118 response = self.app.post(url(controller='login', action='password_reset'),
119 119 {'email':'marcin@wrongmail.org', })
120 120
121 121 assert "That e-mail address doesn't exist" in response.body, 'Missing error message about wrong email'
122 122
123 123 def test_forgot_password(self):
124 124 response = self.app.get(url(controller='login', action='password_reset'))
125 125 assert response.status == '200 OK', 'Wrong response from login page got %s' % response.status
126 126
127 127 username = 'test_password_reset_1'
128 128 password = 'qweqwe'
129 129 email = 'marcin@python-works.com'
130 130 name = 'passwd'
131 131 lastname = 'reset'
132 132
133 133 response = self.app.post(url(controller='login', action='register'),
134 134 {'username':username,
135 135 'password':password,
136 136 'email':email,
137 137 'name':name,
138 138 'lastname':lastname})
139 139 #register new user for email test
140 140 response = self.app.post(url(controller='login', action='password_reset'),
141 141 {'email':email, })
142 142 print response.session['flash']
143 143 assert 'You have successfully registered into hg-app' in response.session['flash'][0], 'No flash message about user registration'
144 144 assert 'Your new password was sent' in response.session['flash'][1], 'No flash message about password reset'
145 145
146 146
147 147
General Comments 0
You need to be logged in to leave comments. Login now