##// END OF EJS Templates
more renames for rhode code !!
marcink -
r549:f9907517 default
parent child Browse files
Show More
@@ -1,81 +1,82 b''
1 -------------------------------------
2 Pylons based replacement for hgwebdir
3 -------------------------------------
1 --------------------------------------------------------------
2 Pylons based repository management for mercurial (and soon git)
3 --------------------------------------------------------------
4 4
5 5 Fully customizable, with authentication, permissions. Based on vcs library.
6 6
7 7 **Overview**
8 8
9 9 - has it's own middleware to handle mercurial protocol request each request can
10 10 be logged and authenticated + threaded performance unlikely to hgweb
11 11 - full permissions per project read/write/admin access even on mercurial request
12 12 - mako templates let's you customize look and feel of application.
13 13 - diffs annotations and source code all colored by pygments.
14 14 - mercurial branch graph and yui-flot powered graphs with zooming and statistics
15 15 - admin interface for performing user/permission managements as well as repository
16 management.
16 management.
17 - server side forks, it's possible to fork a project and hack it free without
18 breaking the main.
17 19 - full text search of source codes with indexing daemons using whoosh
18 20 (no external search servers required all in one application)
19 21 - async tasks for speed and performance using celery (works without them too)
20 22 - Additional settings for mercurial web, (hooks editable from admin
21 23 panel !) also manage paths, archive, remote messages
22 24 - backup scripts can do backup of whole app and send it over scp to desired location
23 25 - setup project descriptions and info inside built in db for easy, non
24 26 file-system operations
25 27 - added cache with invalidation on push/repo management for high performance and
26 28 always up to date data.
27 29 - rss / atom feeds, gravatar support
28 30 - based on pylons 1.0 / sqlalchemy 0.6
29 31
30 32 **Incoming**
31 33
32 34 - code review based on hg-review (when it's stable)
33 35 - git support (when vcs can handle it - almost there !)
34 36 - commit based wikis
35 - in server forks
36 - clonning from remote repositories into hg-app
37 - clonning from remote repositories into rhodecode (git/mercurial)
37 38 - other cools stuff that i can figure out (or You can help me figure out)
38 39
39 40 .. note::
40 41 This software is still in beta mode.
41 42 I don't guarantee that it'll work correctly.
42 43
43 44
44 45 -------------
45 46 Installation
46 47 -------------
47 48
48 - I highly recommend to install new virtualenv for hg-app see
49 - I highly recommend to install new virtualenv for rhodecode see
49 50 http://pypi.python.org/pypi/virtualenv
50 - Create new virtualenv using `virtualenv --no-site-packages /var/www/hgapp-venv`
51 this will install new virtual env into /var/www/hgapp-venv.
51 - Create new virtualenv using `virtualenv --no-site-packages /var/www/rhodecode-venv`
52 this will install new virtual env into /var/www/rhodecode-venv.
52 53 Activate the virtualenv by running
53 `source activate /var/www/hgapp-venv/bin/activate`
54 - Make a folder for hg-app somewhere on the filesystem for example /var/www/hgapp
55 - Download and extract http://bitbucket.org/marcinkuzminski/hg-app/get/tip.zip
54 `source activate /var/www/rhodecode-venv/bin/activate`
55 - Make a folder for rhodecode somewhere on the filesystem for example /var/www/rhodecode
56 - Download and extract http://bitbucket.org/marcinkuzminski/rhodecode/get/tip.zip
56 57 into created directory.
57 58 - Run `python setup.py install` in order to install the application and all
58 59 needed dependencies. Make sure that You're using activated virutalenv
59 60 - Run `paster setup-app production.ini` it should create all needed tables
60 61 and an admin account make sure You specify correct path to repositories.
61 62 - Remember that the given path for mercurial repositories must be write
62 63 accessible for the application
63 64 - Run paster serve development.ini - or you can use sample init.d scripts.
64 65 the app should be available at the 127.0.0.1:5000
65 66 - Use admin account you created to login.
66 67 - Default permissions on each repository is read, and owner is admin. So remember
67 68 to update these.
68 69 - In order to use full power of async tasks, You must install message broker
69 preferably rabbitmq and start celeryd daemon together with hg-app.
70 preferably rabbitmq and start celeryd daemon together with rhodecode.
70 71 The app should gain a lot of speed and become much more responsible.
71 72 For installation instructions You can visit:
72 73 http://ask.github.com/celery/getting-started/index.html.
73 - All needed configs are inside hg-app ie. celeryconfig.py , production.ini
74 - All needed configs are inside rhodecode ie. celeryconfig.py , production.ini
74 75 You can configure the email, ports, loggers, workers from there.
75 76 - For full text search You can either put crontab entry for
76 `python /var/www/hgapp/rhodecode/lib/indexers/daemon.py incremental <path_to_repos>`
77 `python /var/www/rhodecode/rhodecode/lib/indexers/daemon.py incremental <path_to_repos>`
77 78 or run indexer from admin panel. This will scann the repos given in the
78 79 application setup or given path for daemon.py and each scann in incremental
79 80 mode will scann only changed files,
80 81 Hg Update hook must be activated to index the content it's enabled by default
81 82 after setup No newline at end of file
@@ -1,160 +1,160 b''
1 1 ################################################################################
2 2 ################################################################################
3 # hg-app - Pylons environment configuration #
3 # rhodecode - 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 ## Additionally those settings will be used by hg-app mailing system ##
13 ## Additionally those settings will be used by rhodecode mailing system ##
14 14 ################################################################################
15 15 #email_to = admin@localhost
16 16 #error_email_from = paste_error@localhost
17 #app_email_from = hg-app-noreply@localhost
17 #app_email_from = rhodecode-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 beaker.session.key = hg-app
69 beaker.session.key = rhodecode
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 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 NO CONTENT: file renamed from init.d/hg_app_daemon to init.d/rhodecode_daemon
@@ -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 # Provides: hg-app
7 # Provides: rhodecode
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 # Short-Description: starts instance of hg-app
13 # Description: starts instance of hg-app using start-stop-daemon
12 # Short-Description: starts instance of rhodecode
13 # Description: starts instance of rhodecode using start-stop-daemon
14 14 ### END INIT INFO
15 15
16 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 # hg-app - Pylons environment configuration #
3 # rhodecode - 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 ## Additionally those settings will be used by hg-app mailing system ##
13 ## Additionally those settings will be used by rhodecode mailing system ##
14 14 ################################################################################
15 15 #email_to = admin@localhost
16 16 #error_email_from = paste_error@localhost
17 #app_email_from = hg-app-noreply@localhost
17 #app_email_from = rhodecode-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 beaker.session.key = hg-app
69 beaker.session.key = rhodecode
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 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 # hg-app - Pylons environment configuration #
3 # rhodecode - 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 ## Additionally those settings will be used by hg-app mailing system ##
13 ## Additionally those settings will be used by rhodecode mailing system ##
14 14 ################################################################################
15 15 #email_to = admin@localhost
16 16 #error_email_from = paste_error@localhost
17 #app_email_from = hg-app-noreply@localhost
17 #app_email_from = rhodecode-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 beaker.session.key = hg-app
70 beaker.session.key = rhodecode
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 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,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 35 set_rhodecode_config, get_hg_settings, get_hg_ui_settings, make_ui
36 from rhodecode.model.db import User, UserLog, HgAppSettings, HgAppUi
36 from rhodecode.model.db import User, UserLog, RhodeCodeSettings, RhodeCodeUi
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 hgsettings1 = self.sa.query(HgAppSettings)\
120 .filter(HgAppSettings.app_settings_name == 'title').one()
119 hgsettings1 = self.sa.query(RhodeCodeSettings)\
120 .filter(RhodeCodeSettings.app_settings_name == 'title').one()
121 121 hgsettings1.app_settings_value = form_result['rhodecode_title']
122 122
123 hgsettings2 = self.sa.query(HgAppSettings)\
124 .filter(HgAppSettings.app_settings_name == 'realm').one()
123 hgsettings2 = self.sa.query(RhodeCodeSettings)\
124 .filter(RhodeCodeSettings.app_settings_name == 'realm').one()
125 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 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 hgsettings1 = self.sa.query(HgAppUi)\
159 .filter(HgAppUi.ui_key == 'push_ssl').one()
158 hgsettings1 = self.sa.query(RhodeCodeUi)\
159 .filter(RhodeCodeUi.ui_key == 'push_ssl').one()
160 160 hgsettings1.ui_value = form_result['web_push_ssl']
161 161
162 hgsettings2 = self.sa.query(HgAppUi)\
163 .filter(HgAppUi.ui_key == '/').one()
162 hgsettings2 = self.sa.query(RhodeCodeUi)\
163 .filter(RhodeCodeUi.ui_key == '/').one()
164 164 hgsettings2.ui_value = form_result['paths_root_path']
165 165
166 166
167 167 #HOOKS
168 hgsettings3 = self.sa.query(HgAppUi)\
169 .filter(HgAppUi.ui_key == 'changegroup.update').one()
168 hgsettings3 = self.sa.query(RhodeCodeUi)\
169 .filter(RhodeCodeUi.ui_key == 'changegroup.update').one()
170 170 hgsettings3.ui_active = bool(form_result['hooks_changegroup_update'])
171 171
172 hgsettings4 = self.sa.query(HgAppUi)\
173 .filter(HgAppUi.ui_key == 'changegroup.repo_size').one()
172 hgsettings4 = self.sa.query(RhodeCodeUi)\
173 .filter(RhodeCodeUi.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 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 262 uid = c.rhodecode_user.user_id
263 263 _form = UserForm(edit=True, old_data={'user_id':uid,
264 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 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,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 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 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 h.flash(_('You have successfully registered into hg-app'),
104 h.flash(_('You have successfully registered into rhodecode'),
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 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,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 from rhodecode.model.db import HgAppSettings
26 from rhodecode.model.db import RhodeCodeSettings
27 27 try:
28 28 sa = get_session()
29 ret = sa.query(HgAppSettings).all()
29 ret = sa.query(RhodeCodeSettings).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 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 from rhodecode.model.db import HgAppUi
42 from rhodecode.model.db import RhodeCodeUi
43 43 try:
44 44 sa = get_session()
45 ret = sa.query(HgAppUi).all()
45 ret = sa.query(RhodeCodeUi).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 "Your new hg-app password",
243 'Your new hg-app password:%s' % (new_passwd))
242 "Your new rhodecode password",
243 'Your new rhodecode 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,265 +1,265 b''
1 1 #!/usr/bin/env python
2 2 # encoding: utf-8
3 3 # database managment for hg app
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 10, 2010
23 23 database managment and creation for hg app
24 24 @author: marcink
25 25 """
26 26
27 27 from os.path import dirname as dn, join as jn
28 28 import os
29 29 import sys
30 30 import uuid
31 31 ROOT = dn(dn(dn(os.path.realpath(__file__))))
32 32 sys.path.append(ROOT)
33 33
34 34 from rhodecode.lib.auth import get_crypt_password
35 35 from rhodecode.lib.utils import ask_ok
36 36 from rhodecode.model import init_model
37 from rhodecode.model.db import User, Permission, HgAppUi, HgAppSettings, \
37 from rhodecode.model.db import User, Permission, RhodeCodeUi, RhodeCodeSettings, \
38 38 UserToPerm
39 39 from rhodecode.model import meta
40 40 from sqlalchemy.engine import create_engine
41 41 import logging
42 42
43 43 log = logging.getLogger(__name__)
44 44
45 45 class DbManage(object):
46 46 def __init__(self, log_sql, dbname, tests=False):
47 47 self.dbname = dbname
48 48 self.tests = tests
49 49 dburi = 'sqlite:////%s' % jn(ROOT, self.dbname)
50 50 engine = create_engine(dburi, echo=log_sql)
51 51 init_model(engine)
52 52 self.sa = meta.Session
53 53 self.db_exists = False
54 54
55 55 def check_for_db(self, override):
56 56 log.info('checking for exisiting db')
57 57 if os.path.isfile(jn(ROOT, self.dbname)):
58 58 self.db_exists = True
59 59 log.info('database exisist')
60 60 if not override:
61 61 raise Exception('database already exists')
62 62
63 63 def create_tables(self, override=False):
64 64 """
65 65 Create a auth database
66 66 """
67 67 self.check_for_db(override)
68 68 if override:
69 69 log.info("database exisist and it's going to be destroyed")
70 70 if self.tests:
71 71 destroy = True
72 72 else:
73 73 destroy = ask_ok('Are you sure to destroy old database ? [y/n]')
74 74 if not destroy:
75 75 sys.exit()
76 76 if self.db_exists and destroy:
77 77 os.remove(jn(ROOT, self.dbname))
78 78 checkfirst = not override
79 79 meta.Base.metadata.create_all(checkfirst=checkfirst)
80 80 log.info('Created tables for %s', self.dbname)
81 81
82 82 def admin_prompt(self):
83 83 if not self.tests:
84 84 import getpass
85 85 username = raw_input('Specify admin username:')
86 86 password = getpass.getpass('Specify admin password:')
87 87 email = raw_input('Specify admin email:')
88 88 self.create_user(username, password, email, True)
89 89 else:
90 90 log.info('creating admin and regular test users')
91 91 self.create_user('test_admin', 'test12', 'test_admin@mail.com', True)
92 92 self.create_user('test_regular', 'test12', 'test_regular@mail.com', False)
93 93 self.create_user('test_regular2', 'test12', 'test_regular2@mail.com', False)
94 94
95 95
96 96
97 97 def config_prompt(self, test_repo_path=''):
98 98 log.info('Setting up repositories config')
99 99
100 100 if not self.tests and not test_repo_path:
101 101 path = raw_input('Specify valid full path to your repositories'
102 102 ' you can change this later in application settings:')
103 103 else:
104 104 path = test_repo_path
105 105
106 106 if not os.path.isdir(path):
107 107 log.error('You entered wrong path: %s', path)
108 108 sys.exit()
109 109
110 hooks1 = HgAppUi()
110 hooks1 = RhodeCodeUi()
111 111 hooks1.ui_section = 'hooks'
112 112 hooks1.ui_key = 'changegroup.update'
113 113 hooks1.ui_value = 'hg update >&2'
114 114
115 hooks2 = HgAppUi()
115 hooks2 = RhodeCodeUi()
116 116 hooks2.ui_section = 'hooks'
117 117 hooks2.ui_key = 'changegroup.repo_size'
118 118 hooks2.ui_value = 'python:rhodecode.lib.hooks.repo_size'
119 119
120 web1 = HgAppUi()
120 web1 = RhodeCodeUi()
121 121 web1.ui_section = 'web'
122 122 web1.ui_key = 'push_ssl'
123 123 web1.ui_value = 'false'
124 124
125 web2 = HgAppUi()
125 web2 = RhodeCodeUi()
126 126 web2.ui_section = 'web'
127 127 web2.ui_key = 'allow_archive'
128 128 web2.ui_value = 'gz zip bz2'
129 129
130 web3 = HgAppUi()
130 web3 = RhodeCodeUi()
131 131 web3.ui_section = 'web'
132 132 web3.ui_key = 'allow_push'
133 133 web3.ui_value = '*'
134 134
135 web4 = HgAppUi()
135 web4 = RhodeCodeUi()
136 136 web4.ui_section = 'web'
137 137 web4.ui_key = 'baseurl'
138 138 web4.ui_value = '/'
139 139
140 paths = HgAppUi()
140 paths = RhodeCodeUi()
141 141 paths.ui_section = 'paths'
142 142 paths.ui_key = '/'
143 143 paths.ui_value = os.path.join(path, '*')
144 144
145 145
146 hgsettings1 = HgAppSettings()
146 hgsettings1 = RhodeCodeSettings()
147 147
148 148 hgsettings1.app_settings_name = 'realm'
149 hgsettings1.app_settings_value = 'hg-app authentication'
149 hgsettings1.app_settings_value = 'rhodecode authentication'
150 150
151 hgsettings2 = HgAppSettings()
151 hgsettings2 = RhodeCodeSettings()
152 152 hgsettings2.app_settings_name = 'title'
153 hgsettings2.app_settings_value = 'hg-app'
153 hgsettings2.app_settings_value = 'rhodecode'
154 154
155 155 try:
156 156 self.sa.add(hooks1)
157 157 self.sa.add(hooks2)
158 158 self.sa.add(web1)
159 159 self.sa.add(web2)
160 160 self.sa.add(web3)
161 161 self.sa.add(web4)
162 162 self.sa.add(paths)
163 163 self.sa.add(hgsettings1)
164 164 self.sa.add(hgsettings2)
165 165 self.sa.commit()
166 166 except:
167 167 self.sa.rollback()
168 168 raise
169 169 log.info('created ui config')
170 170
171 171 def create_user(self, username, password, email='', admin=False):
172 172 log.info('creating administrator user %s', username)
173 173 new_user = User()
174 174 new_user.username = username
175 175 new_user.password = get_crypt_password(password)
176 176 new_user.name = 'Hg'
177 177 new_user.lastname = 'Admin'
178 178 new_user.email = email
179 179 new_user.admin = admin
180 180 new_user.active = True
181 181
182 182 try:
183 183 self.sa.add(new_user)
184 184 self.sa.commit()
185 185 except:
186 186 self.sa.rollback()
187 187 raise
188 188
189 189 def create_default_user(self):
190 190 log.info('creating default user')
191 191 #create default user for handling default permissions.
192 192 def_user = User()
193 193 def_user.username = 'default'
194 194 def_user.password = get_crypt_password(str(uuid.uuid1())[:8])
195 195 def_user.name = 'default'
196 196 def_user.lastname = 'default'
197 197 def_user.email = 'default@default.com'
198 198 def_user.admin = False
199 199 def_user.active = False
200 200 try:
201 201 self.sa.add(def_user)
202 202 self.sa.commit()
203 203 except:
204 204 self.sa.rollback()
205 205 raise
206 206
207 207 def create_permissions(self):
208 208 #module.(access|create|change|delete)_[name]
209 209 #module.(read|write|owner)
210 210 perms = [('repository.none', 'Repository no access'),
211 211 ('repository.read', 'Repository read access'),
212 212 ('repository.write', 'Repository write access'),
213 213 ('repository.admin', 'Repository admin access'),
214 214 ('hg.admin', 'Hg Administrator'),
215 215 ('hg.create.repository', 'Repository create'),
216 216 ('hg.create.none', 'Repository creation disabled'),
217 217 ('hg.register.none', 'Register disabled'),
218 ('hg.register.manual_activate', 'Register new user with hg-app without manual activation'),
219 ('hg.register.auto_activate', 'Register new user with hg-app without auto activation'),
218 ('hg.register.manual_activate', 'Register new user with rhodecode without manual activation'),
219 ('hg.register.auto_activate', 'Register new user with rhodecode without auto activation'),
220 220 ]
221 221
222 222 for p in perms:
223 223 new_perm = Permission()
224 224 new_perm.permission_name = p[0]
225 225 new_perm.permission_longname = p[1]
226 226 try:
227 227 self.sa.add(new_perm)
228 228 self.sa.commit()
229 229 except:
230 230 self.sa.rollback()
231 231 raise
232 232
233 233 def populate_default_permissions(self):
234 234 log.info('creating default user permissions')
235 235
236 236 default_user = self.sa.query(User)\
237 237 .filter(User.username == 'default').scalar()
238 238
239 239 reg_perm = UserToPerm()
240 240 reg_perm.user = default_user
241 241 reg_perm.permission = self.sa.query(Permission)\
242 242 .filter(Permission.permission_name == 'hg.register.manual_activate')\
243 243 .scalar()
244 244
245 245 create_repo_perm = UserToPerm()
246 246 create_repo_perm.user = default_user
247 247 create_repo_perm.permission = self.sa.query(Permission)\
248 248 .filter(Permission.permission_name == 'hg.create.repository')\
249 249 .scalar()
250 250
251 251 default_repo_perm = UserToPerm()
252 252 default_repo_perm.user = default_user
253 253 default_repo_perm.permission = self.sa.query(Permission)\
254 254 .filter(Permission.permission_name == 'repository.read')\
255 255 .scalar()
256 256
257 257 try:
258 258 self.sa.add(reg_perm)
259 259 self.sa.add(create_repo_perm)
260 260 self.sa.add(default_repo_perm)
261 261 self.sa.commit()
262 262 except:
263 263 self.sa.rollback()
264 264 raise
265 265
@@ -1,238 +1,238 b''
1 1 #!/usr/bin/env python
2 2 # encoding: utf-8
3 # whoosh indexer daemon for hg-app
3 # whoosh indexer daemon for rhodecode
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 Jan 26, 2010
22 22
23 23 @author: marcink
24 24 A deamon will read from task table and run tasks
25 25 """
26 26 import sys
27 27 import os
28 28 from os.path import dirname as dn
29 29 from os.path import join as jn
30 30
31 31 #to get the rhodecode import
32 32 project_path = dn(dn(dn(dn(os.path.realpath(__file__)))))
33 33 sys.path.append(project_path)
34 34
35 35 from rhodecode.lib.pidlock import LockHeld, DaemonLock
36 36 from rhodecode.model.hg_model import HgModel
37 37 from rhodecode.lib.helpers import safe_unicode
38 38 from whoosh.index import create_in, open_dir
39 39 from shutil import rmtree
40 40 from rhodecode.lib.indexers import INDEX_EXTENSIONS, IDX_LOCATION, SCHEMA, IDX_NAME
41 41
42 42 import logging
43 43
44 44 log = logging.getLogger('whooshIndexer')
45 45 # create logger
46 46 log.setLevel(logging.DEBUG)
47 47 log.propagate = False
48 48 # create console handler and set level to debug
49 49 ch = logging.StreamHandler()
50 50 ch.setLevel(logging.DEBUG)
51 51
52 52 # create formatter
53 53 formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
54 54
55 55 # add formatter to ch
56 56 ch.setFormatter(formatter)
57 57
58 58 # add ch to logger
59 59 log.addHandler(ch)
60 60
61 61 def scan_paths(root_location):
62 62 return HgModel.repo_scan('/', root_location, None, True)
63 63
64 64 class WhooshIndexingDaemon(object):
65 65 """Deamon for atomic jobs"""
66 66
67 67 def __init__(self, indexname='HG_INDEX', repo_location=None):
68 68 self.indexname = indexname
69 69 self.repo_location = repo_location
70 70 self.initial = False
71 71 if not os.path.isdir(IDX_LOCATION):
72 72 os.mkdir(IDX_LOCATION)
73 73 log.info('Cannot run incremental index since it does not'
74 74 ' yet exist running full build')
75 75 self.initial = True
76 76
77 77 def get_paths(self, root_dir):
78 78 """recursive walk in root dir and return a set of all path in that dir
79 79 excluding files in .hg dir"""
80 80 index_paths_ = set()
81 81 for path, dirs, files in os.walk(root_dir):
82 82 if path.find('.hg') == -1:
83 83 for f in files:
84 84 index_paths_.add(jn(path, f))
85 85
86 86 return index_paths_
87 87
88 88 def add_doc(self, writer, path, repo):
89 89 """Adding doc to writer"""
90 90
91 91 ext = unicode(path.split('/')[-1].split('.')[-1].lower())
92 92 #we just index the content of choosen files
93 93 if ext in INDEX_EXTENSIONS:
94 94 log.debug(' >> %s [WITH CONTENT]' % path)
95 95 fobj = open(path, 'rb')
96 96 content = fobj.read()
97 97 fobj.close()
98 98 u_content = safe_unicode(content)
99 99 else:
100 100 log.debug(' >> %s' % path)
101 101 #just index file name without it's content
102 102 u_content = u''
103 103
104 104
105 105
106 106 try:
107 107 os.stat(path)
108 108 writer.add_document(owner=unicode(repo.contact),
109 109 repository=u"%s" % repo.name,
110 110 path=u"%s" % path,
111 111 content=u_content,
112 112 modtime=os.path.getmtime(path),
113 113 extension=ext)
114 114 except OSError, e:
115 115 import errno
116 116 if e.errno == errno.ENOENT:
117 117 log.debug('path %s does not exist or is a broken symlink' % path)
118 118 else:
119 119 raise e
120 120
121 121
122 122 def build_index(self):
123 123 if os.path.exists(IDX_LOCATION):
124 124 log.debug('removing previos index')
125 125 rmtree(IDX_LOCATION)
126 126
127 127 if not os.path.exists(IDX_LOCATION):
128 128 os.mkdir(IDX_LOCATION)
129 129
130 130 idx = create_in(IDX_LOCATION, SCHEMA, indexname=IDX_NAME)
131 131 writer = idx.writer()
132 132
133 133 for cnt, repo in enumerate(scan_paths(self.repo_location).values()):
134 134 log.debug('building index @ %s' % repo.path)
135 135
136 136 for idx_path in self.get_paths(repo.path):
137 137 self.add_doc(writer, idx_path, repo)
138 138 writer.commit(merge=True)
139 139
140 140 log.debug('>>> FINISHED BUILDING INDEX <<<')
141 141
142 142
143 143 def update_index(self):
144 144 log.debug('STARTING INCREMENTAL INDEXING UPDATE')
145 145
146 146 idx = open_dir(IDX_LOCATION, indexname=self.indexname)
147 147 # The set of all paths in the index
148 148 indexed_paths = set()
149 149 # The set of all paths we need to re-index
150 150 to_index = set()
151 151
152 152 reader = idx.reader()
153 153 writer = idx.writer()
154 154
155 155 # Loop over the stored fields in the index
156 156 for fields in reader.all_stored_fields():
157 157 indexed_path = fields['path']
158 158 indexed_paths.add(indexed_path)
159 159
160 160 if not os.path.exists(indexed_path):
161 161 # This file was deleted since it was indexed
162 162 log.debug('removing from index %s' % indexed_path)
163 163 writer.delete_by_term('path', indexed_path)
164 164
165 165 else:
166 166 # Check if this file was changed since it
167 167 # was indexed
168 168 indexed_time = fields['modtime']
169 169
170 170 mtime = os.path.getmtime(indexed_path)
171 171
172 172 if mtime > indexed_time:
173 173
174 174 # The file has changed, delete it and add it to the list of
175 175 # files to reindex
176 176 log.debug('adding to reindex list %s' % indexed_path)
177 177 writer.delete_by_term('path', indexed_path)
178 178 to_index.add(indexed_path)
179 179 #writer.commit()
180 180
181 181 # Loop over the files in the filesystem
182 182 # Assume we have a function that gathers the filenames of the
183 183 # documents to be indexed
184 184 for repo in scan_paths(self.repo_location).values():
185 185 for path in self.get_paths(repo.path):
186 186 if path in to_index or path not in indexed_paths:
187 187 # This is either a file that's changed, or a new file
188 188 # that wasn't indexed before. So index it!
189 189 self.add_doc(writer, path, repo)
190 190 log.debug('reindexing %s' % path)
191 191
192 192 writer.commit(merge=True)
193 193 #idx.optimize()
194 194 log.debug('>>> FINISHED <<<')
195 195
196 196 def run(self, full_index=False):
197 197 """Run daemon"""
198 198 if full_index or self.initial:
199 199 self.build_index()
200 200 else:
201 201 self.update_index()
202 202
203 203 if __name__ == "__main__":
204 204 arg = sys.argv[1:]
205 205 if len(arg) != 2:
206 206 sys.stderr.write('Please specify indexing type [full|incremental]'
207 207 'and path to repositories as script args \n')
208 208 sys.exit()
209 209
210 210
211 211 if arg[0] == 'full':
212 212 full_index = True
213 213 elif arg[0] == 'incremental':
214 214 # False means looking just for changes
215 215 full_index = False
216 216 else:
217 217 sys.stdout.write('Please use [full|incremental]'
218 218 ' as script first arg \n')
219 219 sys.exit()
220 220
221 221 if not os.path.isdir(arg[1]):
222 222 sys.stderr.write('%s is not a valid path \n' % arg[1])
223 223 sys.exit()
224 224 else:
225 225 if arg[1].endswith('/'):
226 226 repo_location = arg[1] + '*'
227 227 else:
228 228 repo_location = arg[1] + '/*'
229 229
230 230 try:
231 231 l = DaemonLock()
232 232 WhooshIndexingDaemon(repo_location=repo_location)\
233 233 .run(full_index=full_index)
234 234 l.release()
235 235 reload(logging)
236 236 except LockHeld:
237 237 sys.exit(1)
238 238
@@ -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 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 messages.append('thank you for using hg-app')
132 messages.append('thank you for using rhodecode')
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 from rhodecode.model.db import Repository, User, HgAppUi, HgAppSettings, UserLog
29 from rhodecode.model.db import Repository, User, RhodeCodeUi, RhodeCodeSettings, 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 ret = sa.query(HgAppUi).all()
130 ret = sa.query(RhodeCodeUi).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 ret = sa.query(HgAppSettings).all()
139 ret = sa.query(RhodeCodeSettings).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 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 ret = sa.query(HgAppUi).all()
154 ret = sa.query(RhodeCodeUi).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 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 class HgAppSettings(Base):
10 class RhodeCodeSettings(Base):
11 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 class HgAppUi(Base):
17 class RhodeCodeUi(Base):
18 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,78 +1,78 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 <title>${_('Sign In to hg-app')}</title>
5 <title>${_('Sign In to rhodecode')}</title>
6 6 <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
7 7 <link rel="icon" href="/images/hgicon.png" type="image/png" />
8 8 <meta name="robots" content="index, nofollow"/>
9 9
10 10 <!-- stylesheets -->
11 11 <link rel="stylesheet" type="text/css" href="/css/reset.css" />
12 12 <link rel="stylesheet" type="text/css" href="/css/style.css" media="screen" />
13 13 <link id="color" rel="stylesheet" type="text/css" href="/css/colors/blue.css" />
14 14
15 15 <!-- scripts -->
16 16
17 17 </head>
18 18 <body>
19 19 <div id="login">
20 20 <!-- login -->
21 21 <div class="title">
22 <h5>${_('Sign In to hg-app')}</h5>
22 <h5>${_('Sign In to rhodecode')}</h5>
23 23 <div class="corner tl"></div>
24 24 <div class="corner tr"></div>
25 25 </div>
26 26 <div class="inner">
27 27 ${h.form(h.url.current(came_from=c.came_from))}
28 28 <div class="form">
29 29 <!-- fields -->
30 30
31 31 <div class="fields">
32 32 <div class="field">
33 33 <div class="label">
34 34 <label for="username">${_('Username')}:</label>
35 35 </div>
36 36 <div class="input">
37 37 ${h.text('username',class_='focus',size=40)}
38 38 </div>
39 39
40 40 </div>
41 41 <div class="field">
42 42 <div class="label">
43 43 <label for="password">${_('Password')}:</label>
44 44 </div>
45 45 <div class="input">
46 46 ${h.password('password',class_='focus',size=40)}
47 47 </div>
48 48
49 49 </div>
50 50 ##<div class="field">
51 51 ## <div class="checkbox">
52 52 ## <input type="checkbox" id="remember" name="remember" />
53 53 ## <label for="remember">Remember me</label>
54 54 ## </div>
55 55 ##</div>
56 56 <div class="buttons">
57 57 ${h.submit('sign_in','Sign In',class_="ui-button ui-widget ui-state-default ui-corner-all")}
58 58 </div>
59 59 </div>
60 60 <!-- end fields -->
61 61 <!-- links -->
62 62 <div class="links">
63 63 ${h.link_to(_('Forgot your password ?'),h.url('reset_password'))}
64 64 %if h.HasPermissionAny('hg.admin', 'hg.register.auto_activate', 'hg.register.manual_activate')():
65 65 /
66 66 ${h.link_to(_("Don't have an account ?"),h.url('register'))}
67 67 %endif
68 68 </div>
69 69
70 70 <!-- end links -->
71 71 </div>
72 72 ${h.end_form()}
73 73 </div>
74 74 <!-- end login -->
75 75 </div>
76 76 </body>
77 77 </html>
78 78
@@ -1,54 +1,54 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 <title>${_('Reset You password to hg-app')}</title>
5 <title>${_('Reset You password to rhodecode')}</title>
6 6 <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
7 7 <link rel="icon" href="/images/hgicon.png" type="image/png" />
8 8 <meta name="robots" content="index, nofollow"/>
9 9
10 10 <!-- stylesheets -->
11 11 <link rel="stylesheet" type="text/css" href="/css/reset.css" />
12 12 <link rel="stylesheet" type="text/css" href="/css/style.css" media="screen" />
13 13 <link id="color" rel="stylesheet" type="text/css" href="/css/colors/blue.css" />
14 14
15 15 <!-- scripts -->
16 16
17 17 </head>
18 18 <body>
19 19 <div id="register">
20 20
21 21 <div class="title">
22 <h5>${_('Reset You password to hg-app')}</h5>
22 <h5>${_('Reset You password to rhodecode')}</h5>
23 23 <div class="corner tl"></div>
24 24 <div class="corner tr"></div>
25 25 </div>
26 26 <div class="inner">
27 27 ${h.form(url('password_reset'))}
28 28 <div class="form">
29 29 <!-- fields -->
30 30 <div class="fields">
31 31
32 32 <div class="field">
33 33 <div class="label">
34 34 <label for="email">${_('Email address')}:</label>
35 35 </div>
36 36 <div class="input">
37 37 ${h.text('email')}
38 38 </div>
39 39 </div>
40 40
41 41 <div class="buttons">
42 42 <div class="nohighlight">
43 43 ${h.submit('send','Reset my password',class_="ui-button ui-widget ui-state-default ui-corner-all")}
44 44 <div class="activation_msg">${_('Your new password will be send to matching email address')}</div>
45 45 </div>
46 46 </div>
47 47 </div>
48 48 </div>
49 49 ${h.end_form()}
50 50 </div>
51 51 </div>
52 52 </body>
53 53 </html>
54 54
@@ -1,93 +1,93 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 <title>${_('Sign Up to hg-app')}</title>
5 <title>${_('Sign Up to rhodecode')}</title>
6 6 <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
7 7 <link rel="icon" href="/images/hgicon.png" type="image/png" />
8 8 <meta name="robots" content="index, nofollow"/>
9 9
10 10 <!-- stylesheets -->
11 11 <link rel="stylesheet" type="text/css" href="/css/reset.css" />
12 12 <link rel="stylesheet" type="text/css" href="/css/style.css" media="screen" />
13 13 <link id="color" rel="stylesheet" type="text/css" href="/css/colors/blue.css" />
14 14
15 15 <!-- scripts -->
16 16
17 17 </head>
18 18 <body>
19 19 <div id="register">
20 20
21 21 <div class="title">
22 <h5>${_('Sign Up to hg-app')}</h5>
22 <h5>${_('Sign Up to rhodecode')}</h5>
23 23 <div class="corner tl"></div>
24 24 <div class="corner tr"></div>
25 25 </div>
26 26 <div class="inner">
27 27 ${h.form(url('register'))}
28 28 <div class="form">
29 29 <!-- fields -->
30 30 <div class="fields">
31 31 <div class="field">
32 32 <div class="label">
33 33 <label for="username">${_('Username')}:</label>
34 34 </div>
35 35 <div class="input">
36 36 ${h.text('username')}
37 37 </div>
38 38 </div>
39 39
40 40 <div class="field">
41 41 <div class="label">
42 42 <label for="password">${_('New Password')}:</label>
43 43 </div>
44 44 <div class="input">
45 45 ${h.password('password')}
46 46 </div>
47 47 </div>
48 48
49 49 <div class="field">
50 50 <div class="label">
51 51 <label for="name">${_('First Name')}:</label>
52 52 </div>
53 53 <div class="input">
54 54 ${h.text('name')}
55 55 </div>
56 56 </div>
57 57
58 58 <div class="field">
59 59 <div class="label">
60 60 <label for="lastname">${_('Last Name')}:</label>
61 61 </div>
62 62 <div class="input">
63 63 ${h.text('lastname')}
64 64 </div>
65 65 </div>
66 66
67 67 <div class="field">
68 68 <div class="label">
69 69 <label for="email">${_('Email')}:</label>
70 70 </div>
71 71 <div class="input">
72 72 ${h.text('email')}
73 73 </div>
74 74 </div>
75 75
76 76 <div class="buttons">
77 77 <div class="nohighlight">
78 78 ${h.submit('sign_up','Sign Up',class_="ui-button ui-widget ui-state-default ui-corner-all")}
79 79 %if c.auto_active:
80 80 <div class="activation_msg">${_('Your account will be activated right after registration')}</div>
81 81 %else:
82 82 <div class="activation_msg">${_('Your account must wait for activation by administrator')}</div>
83 83 %endif
84 84 </div>
85 85 </div>
86 86 </div>
87 87 </div>
88 88 ${h.end_form()}
89 89 </div>
90 90 </div>
91 91 </body>
92 92 </html>
93 93
@@ -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 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 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 assert 'Sign Up to hg-app' in response.body, 'wrong page for user registration'
65 assert 'Sign Up to rhodecode' 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 assert 'You have successfully registered into hg-app' in response.session['flash'][0], 'No flash message about user registration'
107 assert 'You have successfully registered into rhodecode' 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 assert 'You have successfully registered into hg-app' in response.session['flash'][0], 'No flash message about user registration'
143 assert 'You have successfully registered into rhodecode' 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
@@ -1,49 +1,49 b''
1 1 from rhodecode import get_version
2 2 try:
3 3 from setuptools import setup, find_packages
4 4 except ImportError:
5 5 from ez_setup import use_setuptools
6 6 use_setuptools()
7 7 from setuptools import setup, find_packages
8 8
9 9 setup(
10 name='HgApp-%s' % get_version(),
10 name='RhodeCode-%s' % get_version(),
11 11 version=get_version(),
12 12 description='Mercurial repository serving and browsing app',
13 keywords='mercurial web hgwebdir replacement serving hgweb',
13 keywords='mercurial web hgwebdir replacement serving hgweb rhodecode',
14 14 license='BSD',
15 15 author='marcin kuzminski',
16 16 author_email='marcin@python-works.com',
17 17 url='http://hg.python-works.com',
18 18 install_requires=[
19 19 "Pylons>=1.0.0",
20 20 "SQLAlchemy>=0.6",
21 21 "babel",
22 22 "Mako>=0.3.2",
23 23 "vcs>=0.1.7",
24 24 "pygments>=1.3.0",
25 25 "mercurial>=1.6",
26 26 "pysqlite",
27 27 "whoosh==1.0.0b20",
28 28 "py-bcrypt",
29 29 "celery",
30 30 ],
31 31 setup_requires=["PasteScript>=1.6.3"],
32 32 packages=find_packages(exclude=['ez_setup']),
33 33 include_package_data=True,
34 34 test_suite='nose.collector',
35 35 package_data={'rhodecode': ['i18n/*/LC_MESSAGES/*.mo']},
36 36 message_extractors={'rhodecode': [
37 37 ('**.py', 'python', None),
38 38 ('templates/**.mako', 'mako', {'input_encoding': 'utf-8'}),
39 39 ('public/**', 'ignore', None)]},
40 40 zip_safe=False,
41 41 paster_plugins=['PasteScript', 'Pylons'],
42 42 entry_points="""
43 43 [paste.app_factory]
44 44 main = rhodecode.config.middleware:make_app
45 45
46 46 [paste.app_install]
47 47 main = pylons.util:PylonsInstaller
48 48 """,
49 49 )
@@ -1,160 +1,160 b''
1 1 ################################################################################
2 2 ################################################################################
3 # hg-app - Pylons environment configuration #
3 # rhodecode - 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 ## Additionally those settings will be used by hg-app mailing system ##
13 ## Additionally those settings will be used by rhodecode mailing system ##
14 14 ################################################################################
15 15 #email_to = admin@localhost
16 16 #error_email_from = paste_error@localhost
17 #app_email_from = hg-app-noreply@localhost
17 #app_email_from = rhodecode-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 = 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 beaker.session.key = hg-app
69 beaker.session.key = rhodecode
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 97 sqlalchemy.db1.url = sqlite:///%(here)s/test.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 = ERROR
119 119 handlers = console
120 120
121 121 [logger_routes]
122 122 level = ERROR
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 = ERROR
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
General Comments 0
You need to be logged in to leave comments. Login now