##// END OF EJS Templates
white space cleanup
marcink -
r2673:d5e42c00 beta
parent child Browse files
Show More
@@ -1,143 +1,142 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 package.rhodecode.lib.cleanup
4 4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 :created_on: Jul 14, 2012
7 7 :author: marcink
8 8 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
9 9 :license: GPLv3, see COPYING for more details.
10 10 """
11 11 # This program is free software: you can redistribute it and/or modify
12 12 # it under the terms of the GNU General Public License as published by
13 13 # the Free Software Foundation, either version 3 of the License, or
14 14 # (at your option) any later version.
15 15 #
16 16 # This program is distributed in the hope that it will be useful,
17 17 # but WITHOUT ANY WARRANTY; without even the implied warranty of
18 18 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 19 # GNU General Public License for more details.
20 20 #
21 21 # You should have received a copy of the GNU General Public License
22 22 # along with this program. If not, see <http://www.gnu.org/licenses/>.
23 23 from __future__ import with_statement
24 24
25 25 import os
26 26 import sys
27 27 import re
28 28 import shutil
29 29 import logging
30 30 import datetime
31 31
32 32 from os.path import dirname as dn, join as jn
33 33 from rhodecode.model import init_model
34 34 from rhodecode.lib.utils2 import engine_from_config
35 35 from rhodecode.model.db import RhodeCodeUi
36 36
37 37
38 38 #to get the rhodecode import
39 39 sys.path.append(dn(dn(dn(os.path.realpath(__file__)))))
40 40
41 41 from rhodecode.lib.utils import BasePasterCommand, Command, ask_ok,\
42 42 REMOVED_REPO_PAT, add_cache
43 43
44 44 log = logging.getLogger(__name__)
45 45
46 46
47 47 class CleanupCommand(BasePasterCommand):
48 48
49 49 max_args = 1
50 50 min_args = 1
51 51
52 52 usage = "CONFIG_FILE"
53 53 summary = "Cleanup deleted repos"
54 54 group_name = "RhodeCode"
55 55 takes_config_file = -1
56 56 parser = Command.standard_parser(verbose=True)
57 57
58 58 def _parse_older_than(self, val):
59 59 regex = re.compile(r'((?P<days>\d+?)d)?((?P<hours>\d+?)h)?((?P<minutes>\d+?)m)?((?P<seconds>\d+?)s)?')
60 60 parts = regex.match(val)
61 61 if not parts:
62 62 return
63 63 parts = parts.groupdict()
64 64 time_params = {}
65 65 for (name, param) in parts.iteritems():
66 66 if param:
67 67 time_params[name] = int(param)
68 68 return datetime.timedelta(**time_params)
69 69
70 70 def _extract_date(self, name):
71 71 """
72 72 Extract the date part from rm__<date> pattern of removed repos,
73 73 and convert it to datetime object
74 74
75 75 :param name:
76 76 """
77 77 date_part = name[4:19] # 4:19 since we don't parse milisecods
78 78 return datetime.datetime.strptime(date_part, '%Y%m%d_%H%M%S')
79 79
80 80 def command(self):
81 81 logging.config.fileConfig(self.path_to_ini_file)
82 82 from pylons import config
83 83
84 84 #get to remove repos !!
85 85 add_cache(config)
86 86 engine = engine_from_config(config, 'sqlalchemy.db1.')
87 87 init_model(engine)
88 88
89 89 repos_location = RhodeCodeUi.get_repos_location()
90 90 to_remove = []
91 91 for loc in os.listdir(repos_location):
92 92 if REMOVED_REPO_PAT.match(loc):
93 93 to_remove.append([loc, self._extract_date(loc)])
94 94
95 95 #filter older than (if present)!
96 96 now = datetime.datetime.now()
97 97 older_than = self.options.older_than
98 98 if older_than:
99 99 to_remove_filtered = []
100 100 older_than_date = self._parse_older_than(older_than)
101 101 for name, date_ in to_remove:
102 102 repo_age = now - date_
103 103 if repo_age > older_than_date:
104 104 to_remove_filtered.append([name, date_])
105 105
106 106 to_remove = to_remove_filtered
107 107 print >> sys.stdout, 'removing [%s] deleted repos older than %s[%s]' \
108 108 % (len(to_remove), older_than, older_than_date)
109 109 else:
110 110 print >> sys.stdout, 'removing all [%s] deleted repos' \
111 111 % len(to_remove)
112 112 if self.options.dont_ask or not to_remove:
113 113 # don't ask just remove !
114 114 remove = True
115 115 else:
116 116 remove = ask_ok('are you sure to remove listed repos \n%s [y/n]?'
117 117 % ', \n'.join(['%s removed on %s' % (x[0], x[1])
118 118 for x in to_remove]))
119 119
120 120 if remove:
121 121 for name, date_ in to_remove:
122 122 print >> sys.stdout, 'removing repository %s' % name
123 123 shutil.rmtree(os.path.join(repos_location, name))
124 124 else:
125 125 print 'nothing done exiting...'
126 126 sys.exit(0)
127 127
128 128 def update_parser(self):
129 129 self.parser.add_option('--older-than',
130 130 action='store',
131 131 dest='older_than',
132 132 help=(
133 133 "only remove repos that have been removed "
134 134 "at least given time ago "
135 135 "ex. --older-than=30d deletes repositores "
136 136 "removed more than 30days ago. Possible options "
137 137 "d[ays]/h[ours]/m[inutes]/s[seconds]. OPTIONAL"),
138 138 )
139 139 self.parser.add_option('--dont-ask',
140 140 action='store_true',
141 141 dest='dont_ask',
142 142 help=("Don't ask to remove repos"))
143 No newline at end of file
@@ -1,264 +1,264 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.lib.indexers.__init__
4 4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 Whoosh indexing module for RhodeCode
7 7
8 8 :created_on: Aug 17, 2010
9 9 :author: marcink
10 10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 11 :license: GPLv3, see COPYING for more details.
12 12 """
13 13 # This program is free software: you can redistribute it and/or modify
14 14 # it under the terms of the GNU General Public License as published by
15 15 # the Free Software Foundation, either version 3 of the License, or
16 16 # (at your option) any later version.
17 17 #
18 18 # This program is distributed in the hope that it will be useful,
19 19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 21 # GNU General Public License for more details.
22 22 #
23 23 # You should have received a copy of the GNU General Public License
24 24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 25 import os
26 26 import sys
27 27 import traceback
28 28 import logging
29 29 from os.path import dirname as dn, join as jn
30 30
31 31 #to get the rhodecode import
32 32 sys.path.append(dn(dn(dn(os.path.realpath(__file__)))))
33 33
34 34 from string import strip
35 35 from shutil import rmtree
36 36
37 37 from whoosh.analysis import RegexTokenizer, LowercaseFilter, StopFilter
38 38 from whoosh.fields import TEXT, ID, STORED, NUMERIC, BOOLEAN, Schema, FieldType
39 39 from whoosh.index import create_in, open_dir
40 40 from whoosh.formats import Characters
41 41 from whoosh.highlight import highlight, HtmlFormatter, ContextFragmenter
42 42
43 43 from webhelpers.html.builder import escape, literal
44 44 from sqlalchemy import engine_from_config
45 45
46 46 from rhodecode.model import init_model
47 47 from rhodecode.model.scm import ScmModel
48 48 from rhodecode.model.repo import RepoModel
49 49 from rhodecode.config.environment import load_environment
50 50 from rhodecode.lib.utils2 import LazyProperty
51 51 from rhodecode.lib.utils import BasePasterCommand, Command, add_cache,\
52 52 load_rcextensions
53 53
54 54 log = logging.getLogger(__name__)
55 55
56 56 # CUSTOM ANALYZER wordsplit + lowercase filter
57 57 ANALYZER = RegexTokenizer(expression=r"\w+") | LowercaseFilter()
58 58
59 59 #INDEX SCHEMA DEFINITION
60 60 SCHEMA = Schema(
61 61 fileid=ID(unique=True),
62 62 owner=TEXT(),
63 63 repository=TEXT(stored=True),
64 64 path=TEXT(stored=True),
65 65 content=FieldType(format=Characters(), analyzer=ANALYZER,
66 66 scorable=True, stored=True),
67 67 modtime=STORED(),
68 68 extension=TEXT(stored=True)
69 69 )
70 70
71 71 IDX_NAME = 'HG_INDEX'
72 72 FORMATTER = HtmlFormatter('span', between='\n<span class="break">...</span>\n')
73 73 FRAGMENTER = ContextFragmenter(200)
74 74
75 75 CHGSETS_SCHEMA = Schema(
76 76 raw_id=ID(unique=True, stored=True),
77 77 last=BOOLEAN(),
78 78 owner=TEXT(),
79 79 repository=ID(unique=True, stored=True),
80 80 author=TEXT(stored=True),
81 81 message=FieldType(format=Characters(), analyzer=ANALYZER,
82 82 scorable=True, stored=True),
83 83 parents=TEXT(),
84 84 added=TEXT(),
85 85 removed=TEXT(),
86 86 changed=TEXT(),
87 87 )
88 88
89 89 CHGSET_IDX_NAME = 'CHGSET_INDEX'
90 90
91 91 class MakeIndex(BasePasterCommand):
92 92
93 93 max_args = 1
94 94 min_args = 1
95 95
96 96 usage = "CONFIG_FILE"
97 97 summary = "Creates index for full text search given configuration file"
98 98 group_name = "RhodeCode"
99 99 takes_config_file = -1
100 100 parser = Command.standard_parser(verbose=True)
101 101
102 102 def command(self):
103 103 logging.config.fileConfig(self.path_to_ini_file)
104 104 from pylons import config
105 105 add_cache(config)
106 106 engine = engine_from_config(config, 'sqlalchemy.db1.')
107 107 init_model(engine)
108 108 index_location = config['index_dir']
109 109 repo_location = self.options.repo_location \
110 110 if self.options.repo_location else RepoModel().repos_path
111 111 repo_list = map(strip, self.options.repo_list.split(',')) \
112 112 if self.options.repo_list else None
113 113 repo_update_list = map(strip, self.options.repo_update_list.split(',')) \
114 114 if self.options.repo_update_list else None
115 115 load_rcextensions(config['here'])
116 116 #======================================================================
117 117 # WHOOSH DAEMON
118 118 #======================================================================
119 119 from rhodecode.lib.pidlock import LockHeld, DaemonLock
120 120 from rhodecode.lib.indexers.daemon import WhooshIndexingDaemon
121 121 try:
122 122 l = DaemonLock(file_=jn(dn(dn(index_location)), 'make_index.lock'))
123 123 WhooshIndexingDaemon(index_location=index_location,
124 124 repo_location=repo_location,
125 125 repo_list=repo_list,
126 126 repo_update_list=repo_update_list)\
127 127 .run(full_index=self.options.full_index)
128 128 l.release()
129 129 except LockHeld:
130 130 sys.exit(1)
131 131
132 132 def update_parser(self):
133 133 self.parser.add_option('--repo-location',
134 134 action='store',
135 135 dest='repo_location',
136 136 help="Specifies repositories location to index OPTIONAL",
137 137 )
138 138 self.parser.add_option('--index-only',
139 139 action='store',
140 140 dest='repo_list',
141 141 help="Specifies a comma separated list of repositores "
142 142 "to build index on. If not given all repositories "
143 143 "are scanned for indexing. OPTIONAL",
144 144 )
145 145 self.parser.add_option('--update-only',
146 146 action='store',
147 147 dest='repo_update_list',
148 148 help="Specifies a comma separated list of repositores "
149 149 "to re-build index on. OPTIONAL",
150 150 )
151 151 self.parser.add_option('-f',
152 152 action='store_true',
153 153 dest='full_index',
154 154 help="Specifies that index should be made full i.e"
155 155 " destroy old and build from scratch",
156 156 default=False)
157 157
158 158
159 159 class WhooshResultWrapper(object):
160 160 def __init__(self, search_type, searcher, matcher, highlight_items,
161 161 repo_location):
162 162 self.search_type = search_type
163 163 self.searcher = searcher
164 164 self.matcher = matcher
165 165 self.highlight_items = highlight_items
166 166 self.fragment_size = 200
167 167 self.repo_location = repo_location
168 168
169 169 @LazyProperty
170 170 def doc_ids(self):
171 171 docs_id = []
172 172 while self.matcher.is_active():
173 173 docnum = self.matcher.id()
174 174 chunks = [offsets for offsets in self.get_chunks()]
175 175 docs_id.append([docnum, chunks])
176 176 self.matcher.next()
177 177 return docs_id
178 178
179 179 def __str__(self):
180 180 return '<%s at %s>' % (self.__class__.__name__, len(self.doc_ids))
181 181
182 182 def __repr__(self):
183 183 return self.__str__()
184 184
185 185 def __len__(self):
186 186 return len(self.doc_ids)
187 187
188 188 def __iter__(self):
189 189 """
190 190 Allows Iteration over results,and lazy generate content
191 191
192 192 *Requires* implementation of ``__getitem__`` method.
193 193 """
194 194 for docid in self.doc_ids:
195 195 yield self.get_full_content(docid)
196 196
197 197 def __getitem__(self, key):
198 198 """
199 199 Slicing of resultWrapper
200 200 """
201 201 i, j = key.start, key.stop
202 202
203 203 slices = []
204 204 for docid in self.doc_ids[i:j]:
205 205 slices.append(self.get_full_content(docid))
206 206 return slices
207 207
208 208 def get_full_content(self, docid):
209 209 res = self.searcher.stored_fields(docid[0])
210 210 log.debug('result: %s' % res)
211 211 if self.search_type == 'content':
212 212 full_repo_path = jn(self.repo_location, res['repository'])
213 213 f_path = res['path'].split(full_repo_path)[-1]
214 214 f_path = f_path.lstrip(os.sep)
215 215 content_short = self.get_short_content(res, docid[1])
216 216 res.update({'content_short': content_short,
217 217 'content_short_hl': self.highlight(content_short),
218 218 'f_path': f_path
219 219 })
220 220 elif self.search_type == 'message':
221 221 res.update({'message_hl': self.highlight(res['message'])})
222 222
223 223 log.debug('result: %s' % res)
224 224
225 225 return res
226 226
227 227 def get_short_content(self, res, chunks):
228 228
229 229 return ''.join([res['content'][chunk[0]:chunk[1]] for chunk in chunks])
230 230
231 231 def get_chunks(self):
232 232 """
233 233 Smart function that implements chunking the content
234 234 but not overlap chunks so it doesn't highlight the same
235 235 close occurrences twice.
236 236
237 237 :param matcher:
238 238 :param size:
239 239 """
240 240 memory = [(0, 0)]
241 if self.matcher.supports('positions'):
241 if self.matcher.supports('positions'):
242 242 for span in self.matcher.spans():
243 243 start = span.startchar or 0
244 244 end = span.endchar or 0
245 245 start_offseted = max(0, start - self.fragment_size)
246 246 end_offseted = end + self.fragment_size
247 247
248 248 if start_offseted < memory[-1][1]:
249 249 start_offseted = memory[-1][1]
250 250 memory.append((start_offseted, end_offseted,))
251 251 yield (start_offseted, end_offseted,)
252 252
253 253 def highlight(self, content, top=5):
254 254 if self.search_type not in ['content', 'message']:
255 255 return ''
256 256 hl = highlight(
257 257 text=content,
258 258 terms=self.highlight_items,
259 259 analyzer=ANALYZER,
260 260 fragmenter=FRAGMENTER,
261 261 formatter=FORMATTER,
262 262 top=top
263 263 )
264 264 return hl
@@ -1,261 +1,261 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.lib.middleware.simplehg
4 4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 SimpleHG middleware for handling mercurial protocol request
7 7 (push/clone etc.). It's implemented with basic auth function
8 8
9 9 :created_on: Apr 28, 2010
10 10 :author: marcink
11 11 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
12 12 :license: GPLv3, see COPYING for more details.
13 13 """
14 14 # This program is free software: you can redistribute it and/or modify
15 15 # it under the terms of the GNU General Public License as published by
16 16 # the Free Software Foundation, either version 3 of the License, or
17 17 # (at your option) any later version.
18 18 #
19 19 # This program is distributed in the hope that it will be useful,
20 20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
21 21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 22 # GNU General Public License for more details.
23 23 #
24 24 # You should have received a copy of the GNU General Public License
25 25 # along with this program. If not, see <http://www.gnu.org/licenses/>.
26 26
27 27 import os
28 28 import logging
29 29 import traceback
30 30 import urllib
31 31
32 32 from mercurial.error import RepoError
33 33 from mercurial.hgweb import hgweb_mod
34 34
35 35 from paste.httpheaders import REMOTE_USER, AUTH_TYPE
36 36 from webob.exc import HTTPNotFound, HTTPForbidden, HTTPInternalServerError, \
37 37 HTTPBadRequest, HTTPNotAcceptable
38 38
39 39 from rhodecode.lib.utils2 import safe_str
40 40 from rhodecode.lib.base import BaseVCSController
41 41 from rhodecode.lib.auth import get_container_username
42 42 from rhodecode.lib.utils import make_ui, is_valid_repo, ui_sections
43 43 from rhodecode.model.db import User
44 44
45 45
46 46 log = logging.getLogger(__name__)
47 47
48 48
49 49 def is_mercurial(environ):
50 50 """
51 51 Returns True if request's target is mercurial server - header
52 52 ``HTTP_ACCEPT`` of such request would start with ``application/mercurial``.
53 53 """
54 54 http_accept = environ.get('HTTP_ACCEPT')
55 55 path_info = environ['PATH_INFO']
56 56 if http_accept and http_accept.startswith('application/mercurial'):
57 57 ishg_path = True
58 58 else:
59 59 ishg_path = False
60 60
61 61 log.debug('pathinfo: %s detected as HG %s' % (
62 62 path_info, ishg_path)
63 63 )
64 64 return ishg_path
65 65
66 66
67 67 class SimpleHg(BaseVCSController):
68 68
69 69 def _handle_request(self, environ, start_response):
70 70 if not is_mercurial(environ):
71 71 return self.application(environ, start_response)
72 72 if not self._check_ssl(environ, start_response):
73 73 return HTTPNotAcceptable('SSL REQUIRED !')(environ, start_response)
74 74
75 75 ipaddr = self._get_ip_addr(environ)
76 username = None
76 username = None
77 77 # skip passing error to error controller
78 78 environ['pylons.status_code_redirect'] = True
79 79
80 80 #======================================================================
81 81 # EXTRACT REPOSITORY NAME FROM ENV
82 82 #======================================================================
83 83 try:
84 84 repo_name = environ['REPO_NAME'] = self.__get_repository(environ)
85 85 log.debug('Extracted repo name is %s' % repo_name)
86 86 except:
87 87 return HTTPInternalServerError()(environ, start_response)
88 88
89 89 # quick check if that dir exists...
90 90 if is_valid_repo(repo_name, self.basepath) is False:
91 91 return HTTPNotFound()(environ, start_response)
92 92
93 93 #======================================================================
94 94 # GET ACTION PULL or PUSH
95 95 #======================================================================
96 96 action = self.__get_action(environ)
97 97
98 98 #======================================================================
99 99 # CHECK ANONYMOUS PERMISSION
100 100 #======================================================================
101 101 if action in ['pull', 'push']:
102 102 anonymous_user = self.__get_user('default')
103 103 username = anonymous_user.username
104 104 anonymous_perm = self._check_permission(action, anonymous_user,
105 105 repo_name)
106 106
107 107 if anonymous_perm is not True or anonymous_user.active is False:
108 108 if anonymous_perm is not True:
109 109 log.debug('Not enough credentials to access this '
110 110 'repository as anonymous user')
111 111 if anonymous_user.active is False:
112 112 log.debug('Anonymous access is disabled, running '
113 113 'authentication')
114 114 #==============================================================
115 115 # DEFAULT PERM FAILED OR ANONYMOUS ACCESS IS DISABLED SO WE
116 116 # NEED TO AUTHENTICATE AND ASK FOR AUTH USER PERMISSIONS
117 117 #==============================================================
118 118
119 119 # Attempting to retrieve username from the container
120 120 username = get_container_username(environ, self.config)
121 121
122 122 # If not authenticated by the container, running basic auth
123 123 if not username:
124 124 self.authenticate.realm = \
125 125 safe_str(self.config['rhodecode_realm'])
126 126 result = self.authenticate(environ)
127 127 if isinstance(result, str):
128 128 AUTH_TYPE.update(environ, 'basic')
129 129 REMOTE_USER.update(environ, result)
130 130 username = result
131 131 else:
132 132 return result.wsgi_application(environ, start_response)
133 133
134 134 #==============================================================
135 135 # CHECK PERMISSIONS FOR THIS REQUEST USING GIVEN USERNAME
136 136 #==============================================================
137 137 try:
138 138 user = self.__get_user(username)
139 139 if user is None or not user.active:
140 140 return HTTPForbidden()(environ, start_response)
141 141 username = user.username
142 142 except:
143 143 log.error(traceback.format_exc())
144 144 return HTTPInternalServerError()(environ, start_response)
145 145
146 146 #check permissions for this repository
147 147 perm = self._check_permission(action, user, repo_name)
148 148 if perm is not True:
149 149 return HTTPForbidden()(environ, start_response)
150 150
151 151 # extras are injected into mercurial UI object and later available
152 152 # in hg hooks executed by rhodecode
153 153 extras = {
154 154 'ip': ipaddr,
155 155 'username': username,
156 156 'action': action,
157 157 'repository': repo_name,
158 158 'scm': 'hg',
159 159 }
160 160
161 161 #======================================================================
162 162 # MERCURIAL REQUEST HANDLING
163 163 #======================================================================
164 164 repo_path = os.path.join(safe_str(self.basepath), safe_str(repo_name))
165 165 log.debug('Repository path is %s' % repo_path)
166 166
167 167 baseui = make_ui('db')
168 168 self.__inject_extras(repo_path, baseui, extras)
169 169
170 170 try:
171 171 # invalidate cache on push
172 172 if action == 'push':
173 173 self._invalidate_cache(repo_name)
174 174 log.info('%s action on HG repo "%s"' % (action, repo_name))
175 175 app = self.__make_app(repo_path, baseui, extras)
176 176 return app(environ, start_response)
177 177 except RepoError, e:
178 178 if str(e).find('not found') != -1:
179 179 return HTTPNotFound()(environ, start_response)
180 180 except Exception:
181 181 log.error(traceback.format_exc())
182 182 return HTTPInternalServerError()(environ, start_response)
183 183
184 184 def __make_app(self, repo_name, baseui, extras):
185 185 """
186 186 Make an wsgi application using hgweb, and inject generated baseui
187 187 instance, additionally inject some extras into ui object
188 188 """
189 189 return hgweb_mod.hgweb(repo_name, name=repo_name, baseui=baseui)
190 190
191 191 def __get_repository(self, environ):
192 192 """
193 193 Get's repository name out of PATH_INFO header
194 194
195 195 :param environ: environ where PATH_INFO is stored
196 196 """
197 197 try:
198 198 environ['PATH_INFO'] = self._get_by_id(environ['PATH_INFO'])
199 199 repo_name = '/'.join(environ['PATH_INFO'].split('/')[1:])
200 200 if repo_name.endswith('/'):
201 201 repo_name = repo_name.rstrip('/')
202 202 except:
203 203 log.error(traceback.format_exc())
204 204 raise
205 205
206 206 return repo_name
207 207
208 208 def __get_user(self, username):
209 209 return User.get_by_username(username)
210 210
211 211 def __get_action(self, environ):
212 212 """
213 213 Maps mercurial request commands into a clone,pull or push command.
214 214 This should always return a valid command string
215 215
216 216 :param environ:
217 217 """
218 218 mapping = {'changegroup': 'pull',
219 219 'changegroupsubset': 'pull',
220 220 'stream_out': 'pull',
221 221 'listkeys': 'pull',
222 222 'unbundle': 'push',
223 223 'pushkey': 'push', }
224 224 for qry in environ['QUERY_STRING'].split('&'):
225 225 if qry.startswith('cmd'):
226 226 cmd = qry.split('=')[-1]
227 227 if cmd in mapping:
228 228 return mapping[cmd]
229 229
230 230 return 'pull'
231 231
232 232 raise Exception('Unable to detect pull/push action !!'
233 233 'Are you using non standard command or client ?')
234 234
235 235 def __inject_extras(self, repo_path, baseui, extras={}):
236 236 """
237 237 Injects some extra params into baseui instance
238 238
239 239 also overwrites global settings with those takes from local hgrc file
240 240
241 241 :param baseui: baseui instance
242 242 :param extras: dict with extra params to put into baseui
243 243 """
244 244
245 245 hgrc = os.path.join(repo_path, '.hg', 'hgrc')
246 246
247 247 # make our hgweb quiet so it doesn't print output
248 248 baseui.setconfig('ui', 'quiet', 'true')
249 249
250 250 #inject some additional parameters that will be available in ui
251 251 #for hooks
252 252 for k, v in extras.items():
253 253 baseui.setconfig('rhodecode_extras', k, v)
254 254
255 255 repoui = make_ui('file', hgrc, False)
256 256
257 257 if repoui:
258 258 #overwrite our ui instance with the section from hgrc file
259 259 for section in ui_sections:
260 260 for k, v in repoui.configitems(section):
261 261 baseui.setconfig(section, k, v)
@@ -1,19 +1,19 b''
1 1 """
2 2 Email message and email sending related helper functions.
3 3 """
4 4
5 5 import socket
6 6
7 7
8 8 # Cache the hostname, but do it lazily: socket.getfqdn() can take a couple of
9 9 # seconds, which slows down the restart of the server.
10 10 class CachedDnsName(object):
11 11 def __str__(self):
12 12 return self.get_fqdn()
13 13
14 14 def get_fqdn(self):
15 15 if not hasattr(self, '_fqdn'):
16 16 self._fqdn = socket.getfqdn()
17 17 return self._fqdn
18 18
19 DNS_NAME = CachedDnsName() No newline at end of file
19 DNS_NAME = CachedDnsName()
@@ -1,65 +1,65 b''
1 1 ## -*- coding: utf-8 -*-
2 2 <%inherit file="/base/base.html"/>
3 3
4 4 <%def name="title()">
5 5 ${_('My Notifications')} ${c.rhodecode_user.username} - ${c.rhodecode_name}
6 6 </%def>
7 7
8 8 <%def name="breadcrumbs_links()">
9 9 ${_('My Notifications')}
10 10 </%def>
11 11
12 12 <%def name="page_nav()">
13 13 ${self.menu('admin')}
14 14 </%def>
15 15
16 16 <%def name="main()">
17 17 <div class="box">
18 18 <!-- box / title -->
19 19 <div class="title">
20 20 ${self.breadcrumbs()}
21 21 ##<ul class="links">
22 22 ## <li>
23 23 ## <span style="text-transform: uppercase;"><a href="#">${_('Compose message')}</a></span>
24 24 ## </li>
25 25 ##</ul>
26 26 </div>
27
27
28 28 <div style="padding:14px 18px;text-align: right;float:left">
29 29 <span id='all' class="ui-btn"><a href="${h.url.current()}">${_('All')}</a></span>
30 30 <span id='comment' class="ui-btn"><a href="${h.url.current(type=c.comment_type)}">${_('Comments')}</a></span>
31 31 <span id='pull_request' class="ui-btn"><a href="${h.url.current(type=c.pull_request_type)}">${_('Pull requests')}</a></span>
32 32 </div>
33 33 %if c.notifications:
34 34 <div style="padding:14px 18px;text-align: right;float:right">
35 35 <span id='mark_all_read' class="ui-btn">${_('Mark all read')}</span>
36 36 </div>
37 37 %endif
38 38 <div id='notification_data'>
39 39 <%include file='notifications_data.html'/>
40 40 </div>
41 41 </div>
42 42 <script type="text/javascript">
43 43 var url_action = "${url('notification', notification_id='__NOTIFICATION_ID__')}";
44 44 var run = function(){
45 45 YUE.on(YUQ('.delete-notification'),'click',function(e){
46 46 var notification_id = e.currentTarget.id;
47 47 deleteNotification(url_action,notification_id)
48 48 })
49 49 YUE.on(YUQ('.read-notification'),'click',function(e){
50 50 var notification_id = e.currentTarget.id;
51 51 readNotification(url_action,notification_id)
52 52 })
53 53 }
54 54 run()
55 55 YUE.on('mark_all_read','click',function(e){
56 56 var url = "${h.url('notifications_mark_all_read', **request.GET.mixed())}";
57 57 ypjax(url,'notification_data',function(){run()});
58 58 })
59 59
60 60 var current_filter = "${c.current_filter}";
61 61 if (YUD.get(current_filter)){
62 62 YUD.addClass(current_filter, 'active');
63 63 }
64 64 </script>
65 65 </%def>
@@ -1,40 +1,40 b''
1 1
2 2 %if c.notifications:
3 3 <%
4 4 unread = lambda n:{False:'unread'}.get(n)
5 5 %>
6 6
7 7
8 8 <div class="notification-list notification-table">
9 9 %for notification in c.notifications:
10 10 <div id="notification_${notification.notification.notification_id}" class="container ${unread(notification.read)}">
11 11 <div class="notification-header">
12 12 <div class="gravatar">
13 13 <img alt="gravatar" src="${h.gravatar_url(h.email(notification.notification.created_by_user.email),24)}"/>
14 14 </div>
15 15 <div class="desc ${unread(notification.read)}">
16 16 <a href="${url('notification', notification_id=notification.notification.notification_id)}">${notification.notification.description}</a>
17 17 </div>
18 18 <div class="delete-notifications">
19 19 <span id="${notification.notification.notification_id}" class="delete-notification delete_icon action"></span>
20 20 </div>
21 21 %if not notification.read:
22 22 <div class="read-notifications">
23 23 <span id="${notification.notification.notification_id}" class="read-notification accept_icon action"></span>
24 </div>
24 </div>
25 25 %endif
26 26 </div>
27 27 <div class="notification-subject">${h.literal(notification.notification.subject)}</div>
28 28 </div>
29 29 %endfor
30 30 </div>
31 31
32 32 <div class="notification-paginator">
33 33 <div class="pagination-wh pagination-left">
34 34 ${c.notifications.pager('$link_previous ~2~ $link_next',**request.GET.mixed())}
35 35 </div>
36 36 </div>
37 37
38 38 %else:
39 39 <div class="table">${_('No notifications here yet')}</div>
40 40 %endif
@@ -1,135 +1,135 b''
1 1 ## -*- coding: utf-8 -*-
2 2 <%inherit file="/base/base.html"/>
3 3
4 4 <%def name="title()">
5 5 ${_('Repositories administration')} - ${c.rhodecode_name}
6 6 </%def>
7 7
8 8 <%def name="breadcrumbs_links()">
9 9 <input class="q_filter_box" id="q_filter" size="15" type="text" name="filter" value="${_('quick filter...')}"/> ${h.link_to(_('Admin'),h.url('admin_home'))} &raquo; <span id="repo_count">0</span> ${_('repositories')}
10 10 </%def>
11 11 <%def name="page_nav()">
12 12 ${self.menu('admin')}
13 13 </%def>
14 14 <%def name="main()">
15 15 <div class="box">
16 16
17 17 <div class="title">
18 18 ${self.breadcrumbs()}
19 19 <ul class="links">
20 20 <li>
21 21 <span>${h.link_to(_(u'ADD REPOSITORY'),h.url('new_repo'))}</span>
22 22 </li>
23 23 </ul>
24 24 </div>
25 25 <div class="table yui-skin-sam" id="repos_list_wrap"></div>
26 26 <div id="user-paginator" style="padding: 0px 0px 0px 20px"></div>
27
27
28 28
29 29 </div>
30 30 <script>
31 31 var url = "${h.url('formatted_users', format='json')}";
32 32 var data = ${c.data|n};
33 33 var myDataSource = new YAHOO.util.DataSource(data);
34 34 myDataSource.responseType = YAHOO.util.DataSource.TYPE_JSON;
35 35
36 36 myDataSource.responseSchema = {
37 37 resultsList: "records",
38 38 fields: [
39 39 {key:"menu"},
40 40 {key:"raw_name"},
41 41 {key:"name"},
42 42 {key:"desc"},
43 43 {key:"owner"},
44 44 {key:"action"},
45 45 ]
46 46 };
47 47 myDataSource.doBeforeCallback = function(req,raw,res,cb) {
48 48 // This is the filter function
49 49 var data = res.results || [],
50 50 filtered = [],
51 51 i,l;
52
52
53 53 if (req) {
54 54 req = req.toLowerCase();
55 55 for (i = 0; i<data.length; i++) {
56 56 var pos = data[i].raw_name.toLowerCase().indexOf(req)
57 57 if (pos != -1) {
58 58 filtered.push(data[i]);
59 59 }
60 60 }
61 61 res.results = filtered;
62 62 }
63 63 YUD.get('repo_count').innerHTML = res.results.length;
64 64 return res;
65 65 }
66 66
67 67 // main table sorting
68 68 var myColumnDefs = [
69 69 {key:"menu",label:"",sortable:false,className:"quick_repo_menu hidden"},
70 70 {key:"name",label:"${_('Name')}",sortable:true,
71 71 sortOptions: { sortFunction: nameSort }},
72 72 {key:"desc",label:"${_('Description')}",sortable:true},
73 73 {key:"owner",label:"${_('Owner')}",sortable:true},
74 74 {key:"action",label:"${_('Action')}",sortable:false},
75 75 ];
76 76
77 77 var myDataTable = new YAHOO.widget.DataTable("repos_list_wrap", myColumnDefs, myDataSource,{
78 78 sortedBy:{key:"name",dir:"asc"},
79 79 paginator: new YAHOO.widget.Paginator({
80 80 rowsPerPage: 15,
81 81 alwaysVisible: false,
82 82 template : "{PreviousPageLink} {FirstPageLink} {PageLinks} {LastPageLink} {NextPageLink}",
83 83 pageLinks: 5,
84 84 containerClass: 'pagination-wh',
85 85 currentPageClass: 'pager_curpage',
86 86 pageLinkClass: 'pager_link',
87 87 nextPageLinkLabel: '&gt;',
88 88 previousPageLinkLabel: '&lt;',
89 89 firstPageLinkLabel: '&lt;&lt;',
90 90 lastPageLinkLabel: '&gt;&gt;',
91 91 containers:['user-paginator']
92 92 }),
93 93
94 94 MSG_SORTASC:"${_('Click to sort ascending')}",
95 95 MSG_SORTDESC:"${_('Click to sort descending')}",
96 96 MSG_EMPTY:"${_('No records found.')}",
97 97 MSG_ERROR:"${_('Data error.')}",
98 98 MSG_LOADING:"${_('Loading...')}",
99 99 }
100 100 );
101 101 myDataTable.subscribe('postRenderEvent',function(oArgs) {
102 102 tooltip_activate();
103 103 quick_repo_menu();
104 104 });
105
105
106 106 var filterTimeout = null;
107 107
108 108 updateFilter = function () {
109 109 // Reset timeout
110 110 filterTimeout = null;
111 111
112 112 // Reset sort
113 113 var state = myDataTable.getState();
114 114 state.sortedBy = {key:'name', dir:YAHOO.widget.DataTable.CLASS_ASC};
115 115
116 116 // Get filtered data
117 117 myDataSource.sendRequest(YUD.get('q_filter').value,{
118 118 success : myDataTable.onDataReturnInitializeTable,
119 119 failure : myDataTable.onDataReturnInitializeTable,
120 120 scope : myDataTable,
121 121 argument: state
122 122 });
123 123
124 };
124 };
125 125 YUE.on('q_filter','click',function(){
126 126 YUD.get('q_filter').value = '';
127 127 });
128 128
129 129 YUE.on('q_filter','keyup',function (e) {
130 130 clearTimeout(filterTimeout);
131 131 filterTimeout = setTimeout(updateFilter,600);
132 });
132 });
133 133 </script>
134 134
135 135 </%def>
@@ -1,264 +1,264 b''
1 1 ## -*- coding: utf-8 -*-
2 2 <%inherit file="/base/base.html"/>
3 3
4 4 <%def name="title()">
5 5 ${_('Edit user')} ${c.user.username} - ${c.rhodecode_name}
6 6 </%def>
7 7
8 8 <%def name="breadcrumbs_links()">
9 9 ${h.link_to(_('Admin'),h.url('admin_home'))}
10 10 &raquo;
11 11 ${h.link_to(_('Users'),h.url('users'))}
12 12 &raquo;
13 13 ${_('edit')} "${c.user.username}"
14 14 </%def>
15 15
16 16 <%def name="page_nav()">
17 17 ${self.menu('admin')}
18 18 </%def>
19 19
20 20 <%def name="main()">
21 21 <div class="box box-left">
22 22 <!-- box / title -->
23 23 <div class="title">
24 24 ${self.breadcrumbs()}
25 25 </div>
26 26 <!-- end box / title -->
27 27 ${h.form(url('update_user', id=c.user.user_id),method='put')}
28 28 <div class="form">
29 29 <div class="field">
30 30 <div class="gravatar_box">
31 31 <div class="gravatar"><img alt="gravatar" src="${h.gravatar_url(c.user.email)}"/></div>
32 32 <p>
33 33 %if c.use_gravatar:
34 34 <strong>${_('Change your avatar at')} <a href="http://gravatar.com">gravatar.com</a></strong>
35 35 <br/>${_('Using')} ${c.user.email}
36 36 %else:
37 37 <br/>${c.user.email}
38 38 %endif
39 39 </div>
40 40 </div>
41 41 <div class="field">
42 42 <div class="label">
43 43 <label>${_('API key')}</label> ${c.user.api_key}
44 44 </div>
45 45 </div>
46 46
47 47 <div class="fields">
48 48 <div class="field">
49 49 <div class="label">
50 50 <label for="username">${_('Username')}:</label>
51 51 </div>
52 52 <div class="input">
53 53 ${h.text('username',class_='medium')}
54 54 </div>
55 55 </div>
56 56
57 57 <div class="field">
58 58 <div class="label">
59 59 <label for="ldap_dn">${_('LDAP DN')}:</label>
60 60 </div>
61 61 <div class="input">
62 62 ${h.text('ldap_dn',class_='medium disabled',readonly="readonly")}
63 63 </div>
64 64 </div>
65 65
66 66 <div class="field">
67 67 <div class="label">
68 68 <label for="new_password">${_('New password')}:</label>
69 69 </div>
70 70 <div class="input">
71 71 ${h.password('new_password',class_='medium',autocomplete="off")}
72 72 </div>
73 73 </div>
74 74
75 75 <div class="field">
76 76 <div class="label">
77 77 <label for="password_confirmation">${_('New password confirmation')}:</label>
78 78 </div>
79 79 <div class="input">
80 80 ${h.password('password_confirmation',class_="medium",autocomplete="off")}
81 81 </div>
82 82 </div>
83 83
84 84 <div class="field">
85 85 <div class="label">
86 86 <label for="firstname">${_('First Name')}:</label>
87 87 </div>
88 88 <div class="input">
89 89 ${h.text('firstname',class_='medium')}
90 90 </div>
91 91 </div>
92 92
93 93 <div class="field">
94 94 <div class="label">
95 95 <label for="lastname">${_('Last Name')}:</label>
96 96 </div>
97 97 <div class="input">
98 98 ${h.text('lastname',class_='medium')}
99 99 </div>
100 100 </div>
101 101
102 102 <div class="field">
103 103 <div class="label">
104 104 <label for="email">${_('Email')}:</label>
105 105 </div>
106 106 <div class="input">
107 107 ${h.text('email',class_='medium')}
108 108 </div>
109 109 </div>
110 110
111 111 <div class="field">
112 112 <div class="label label-checkbox">
113 113 <label for="active">${_('Active')}:</label>
114 114 </div>
115 115 <div class="checkboxes">
116 116 ${h.checkbox('active',value=True)}
117 117 </div>
118 118 </div>
119 119
120 120 <div class="field">
121 121 <div class="label label-checkbox">
122 122 <label for="admin">${_('Admin')}:</label>
123 123 </div>
124 124 <div class="checkboxes">
125 125 ${h.checkbox('admin',value=True)}
126 126 </div>
127 127 </div>
128 128 <div class="buttons">
129 129 ${h.submit('save',_('Save'),class_="ui-btn large")}
130 130 ${h.reset('reset',_('Reset'),class_="ui-btn large")}
131 131 </div>
132 132 </div>
133 133 </div>
134 134 ${h.end_form()}
135 135 </div>
136 136 <div style="min-height:780px" class="box box-right">
137 137 <!-- box / title -->
138 138 <div class="title">
139 139 <h5>${_('Permissions')}</h5>
140 140 </div>
141 141 ${h.form(url('user_perm', id=c.user.user_id),method='put')}
142 142 <div class="form">
143 143 <!-- fields -->
144 144 <div class="fields">
145 145 <div class="field">
146 146 <div class="label label-checkbox">
147 147 <label for="create_repo_perm">${_('Create repositories')}:</label>
148 148 </div>
149 149 <div class="checkboxes">
150 150 ${h.checkbox('create_repo_perm',value=True)}
151 151 </div>
152 152 </div>
153 153 <div class="buttons">
154 154 ${h.submit('save',_('Save'),class_="ui-btn large")}
155 155 ${h.reset('reset',_('Reset'),class_="ui-btn large")}
156 156 </div>
157 157 </div>
158 158 </div>
159 159 ${h.end_form()}
160 160
161 161 ## permissions overview
162 162 <div id="perms" class="table">
163 163 %for section in sorted(c.perm_user.permissions.keys()):
164 164 <div class="perms_section_head">${section.replace("_"," ").capitalize()}</div>
165 165 %if not c.perm_user.permissions[section]:
166 166 <span style="color:#B9B9B9">${_('Nothing here yet')}</span>
167 167 %else:
168 168 <div id='tbl_list_wrap_${section}' class="yui-skin-sam">
169 169 <table id="tbl_list_${section}">
170 170 <thead>
171 171 <tr>
172 172 <th class="left">${_('Name')}</th>
173 173 <th class="left">${_('Permission')}</th>
174 174 <th class="left">${_('Edit Permission')}</th>
175 175 </thead>
176 176 <tbody>
177 177 %for k in c.perm_user.permissions[section]:
178 178 <%
179 179 if section != 'global':
180 180 section_perm = c.perm_user.permissions[section].get(k)
181 181 _perm = section_perm.split('.')[-1]
182 182 else:
183 183 _perm = section_perm = None
184 184 %>
185 185 <tr>
186 186 <td>
187 187 %if section == 'repositories':
188 188 <a href="${h.url('summary_home',repo_name=k)}">${k}</a>
189 189 %elif section == 'repositories_groups':
190 190 <a href="${h.url('repos_group_home',group_name=k)}">${k}</a>
191 191 %else:
192 192 ${h.get_permission_name(k)}
193 193 %endif
194 194 </td>
195 195 <td>
196 196 %if section == 'global':
197 197 ${h.bool2icon(k.split('.')[-1] != 'none')}
198 198 %else:
199 199 <span class="perm_tag ${_perm}">${section_perm}</span>
200 200 %endif
201 201 </td>
202 202 <td>
203 203 %if section == 'repositories':
204 204 <a href="${h.url('edit_repo',repo_name=k,anchor='permissions_manage')}">${_('edit')}</a>
205 205 %elif section == 'repositories_groups':
206 206 <a href="${h.url('edit_repos_group',id=k,anchor='permissions_manage')}">${_('edit')}</a>
207 207 %else:
208 208 --
209 %endif
209 %endif
210 210 </td>
211 211 </tr>
212 212 %endfor
213 213 </tbody>
214 214 </table>
215 215 </div>
216 216 %endif
217 217 %endfor
218 218 </div>
219 219 </div>
220 220 <div class="box box-left">
221 221 <!-- box / title -->
222 222 <div class="title">
223 223 <h5>${_('Email addresses')}</h5>
224 224 </div>
225 225
226 226 <div class="emails_wrap">
227 227 <table class="noborder">
228 228 %for em in c.user_email_map:
229 229 <tr>
230 230 <td><div class="gravatar"><img alt="gravatar" src="${h.gravatar_url(em.user.email,16)}"/> </div></td>
231 231 <td><div class="email">${em.email}</div></td>
232 232 <td>
233 233 ${h.form(url('user_emails_delete', id=c.user.user_id),method='delete')}
234 234 ${h.hidden('del_email',em.email_id)}
235 235 ${h.submit('remove_',_('delete'),id="remove_email_%s" % em.email_id,
236 236 class_="delete_icon action_button", onclick="return confirm('"+_('Confirm to delete this email: %s') % em.email+"');")}
237 237 ${h.end_form()}
238 238 </td>
239 239 </tr>
240 240 %endfor
241 241 </table>
242 242 </div>
243 243
244 244 ${h.form(url('user_emails', id=c.user.user_id),method='put')}
245 245 <div class="form">
246 246 <!-- fields -->
247 247 <div class="fields">
248 248 <div class="field">
249 249 <div class="label">
250 250 <label for="email">${_('New email address')}:</label>
251 251 </div>
252 252 <div class="input">
253 253 ${h.text('new_email', class_='medium')}
254 254 </div>
255 255 </div>
256 256 <div class="buttons">
257 257 ${h.submit('save',_('Add'),class_="ui-btn large")}
258 258 ${h.reset('reset',_('Reset'),class_="ui-btn large")}
259 259 </div>
260 260 </div>
261 261 </div>
262 262 ${h.end_form()}
263 263 </div>
264 264 </%def>
@@ -1,219 +1,219 b''
1 1 ## -*- coding: utf-8 -*-
2 2 <%inherit file="/base/base.html"/>
3 3
4 4 <%def name="title()">
5 5 ${_('My account')} ${c.rhodecode_user.username} - ${c.rhodecode_name}
6 6 </%def>
7 7
8 8 <%def name="breadcrumbs_links()">
9 9 ${_('My Account')}
10 10 </%def>
11 11
12 12 <%def name="page_nav()">
13 13 ${self.menu('admin')}
14 14 </%def>
15 15
16 16 <%def name="main()">
17 17
18 18 <div class="box box-left">
19 19 <!-- box / title -->
20 20 <div class="title">
21 21 ${self.breadcrumbs()}
22 22 </div>
23 23 <!-- end box / title -->
24 24 ${c.form|n}
25 25 </div>
26 26
27 27 <div class="box box-right">
28 28 <!-- box / title -->
29 29 <div class="title">
30 30 <h5>
31 31 <input class="q_filter_box" id="q_filter" size="15" type="text" name="filter" value="${_('quick filter...')}" style="display: none"/>
32 32 </h5>
33 33 <ul class="links" style="color:#DADADA">
34 34 <li>
35 35 <span><a id="show_perms" class="link-white current" href="#perms">${_('My permissions')}</a> </span>
36 36 </li>
37 37 <li>
38 38 <span><a id="show_my" class="link-white" href="#my">${_('My repos')}</a> </span>
39 39 </li>
40 40 <li>
41 41 <span><a id="show_pullrequests" class="link-white" href="#perms">${_('My pull requests')}</a> </span>
42 42 </li>
43 43 %if h.HasPermissionAny('hg.admin','hg.create.repository')():
44 44 <li>
45 45 <span>${h.link_to(_('Add repo'),h.url('admin_settings_create_repository'))}</span>
46 46 </li>
47 47 %endif
48 48 </ul>
49 49 </div>
50 50 <!-- end box / title -->
51 51 <div id="perms" class="table">
52 52 %for section in sorted(c.rhodecode_user.permissions.keys()):
53 53 <div class="perms_section_head">${section.replace("_"," ").capitalize()}</div>
54 54
55 55 <div id='tbl_list_wrap_${section}' class="yui-skin-sam">
56 56 <table id="tbl_list_${section}">
57 57 <thead>
58 58 <tr>
59 59 <th class="left">${_('Name')}</th>
60 60 <th class="left">${_('Permission')}</th>
61 61 </thead>
62 62 <tbody>
63 63 %for k in c.rhodecode_user.permissions[section]:
64 64 <%
65 65 if section != 'global':
66 66 section_perm = c.rhodecode_user.permissions[section].get(k)
67 67 _perm = section_perm.split('.')[-1]
68 68 else:
69 69 _perm = section_perm = None
70 70 %>
71 71 %if _perm not in ['none']:
72 72 <tr>
73 73 <td>
74 74 %if section == 'repositories':
75 75 <a href="${h.url('summary_home',repo_name=k)}">${k}</a>
76 76 %elif section == 'repositories_groups':
77 77 <a href="${h.url('repos_group_home',group_name=k)}">${k}</a>
78 78 %else:
79 79 ${k}
80 80 %endif
81 81 </td>
82 82 <td>
83 83 %if section == 'global':
84 84 ${h.bool2icon(True)}
85 85 %else:
86 86 <span class="perm_tag ${_perm}">${section_perm}</span>
87 87 %endif
88 88 </td>
89 89 </tr>
90 90 %endif
91 91 %endfor
92 92 </tbody>
93 93 </table>
94 94 </div>
95 95 %endfor
96 96 </div>
97 97 <div id="my" class="table" style="display:none">
98 </div>
98 </div>
99 99 <div id="pullrequests" class="table" style="display:none">
100 100 </div>
101 101 <script type="text/javascript">
102 102 var filter_activate = function(){
103 103 var nodes = YUQ('#my tr td a.repo_name');
104 104 var func = function(node){
105 105 return node.parentNode.parentNode.parentNode.parentNode;
106 106 }
107 107 q_filter('q_filter',YUQ('#my tr td a.repo_name'),func);
108 108 }
109 109 YUE.on('show_perms','click',function(e){
110 110 YUD.addClass('show_perms', 'current');
111 111 YUD.removeClass('show_my','current');
112 112 YUD.removeClass('show_pullrequests','current');
113
113
114 114 YUD.setStyle('my','display','none');
115 115 YUD.setStyle('pullrequests','display','none');
116 116 YUD.setStyle('perms','display','');
117 117 YUD.setStyle('q_filter','display','none');
118 118 YUE.preventDefault(e);
119 119 })
120 120 YUE.on('show_my','click',function(e){
121 121 YUD.addClass('show_my', 'current');
122 122 YUD.removeClass('show_perms','current');
123 123 YUD.removeClass('show_pullrequests','current');
124
124
125 125 YUD.setStyle('perms','display','none');
126 126 YUD.setStyle('pullrequests','display','none');
127 127 YUD.setStyle('my','display','');
128 128 YUD.setStyle('q_filter','display','');
129
129
130 130 YUE.preventDefault(e);
131 131 var url = "${h.url('admin_settings_my_repos')}";
132 132 ypjax(url, 'my', function(){
133 133 table_sort();
134 134 filter_activate();
135 135 });
136 136 })
137 137 YUE.on('show_pullrequests','click',function(e){
138 138 YUD.addClass('show_pullrequests', 'current');
139 139 YUD.removeClass('show_my','current');
140 140 YUD.removeClass('show_perms','current');
141 141
142 142 YUD.setStyle('my','display','none');
143 143 YUD.setStyle('perms','display','none');
144 144 YUD.setStyle('pullrequests','display','');
145 145 YUD.setStyle('q_filter','display','none');
146 146 YUE.preventDefault(e);
147 147 var url = "${h.url('admin_settings_my_pullrequests')}";
148 148 ypjax(url, 'pullrequests');
149 149 })
150 150
151 151 // main table sorting
152 152 var myColumnDefs = [
153 153 {key:"menu",label:"",sortable:false,className:"quick_repo_menu hidden"},
154 154 {key:"name",label:"${_('Name')}",sortable:true,
155 155 sortOptions: { sortFunction: nameSort }},
156 156 {key:"tip",label:"${_('Tip')}",sortable:true,
157 157 sortOptions: { sortFunction: revisionSort }},
158 158 {key:"action1",label:"",sortable:false},
159 159 {key:"action2",label:"",sortable:false},
160 160 ];
161 161
162 162 function table_sort(){
163 163 var myDataSource = new YAHOO.util.DataSource(YUD.get("repos_list"));
164 164 myDataSource.responseType = YAHOO.util.DataSource.TYPE_HTMLTABLE;
165 165 myDataSource.responseSchema = {
166 166 fields: [
167 167 {key:"menu"},
168 168 {key:"name"},
169 169 {key:"tip"},
170 170 {key:"action1"},
171 171 {key:"action2"},
172 172 ]
173 173 };
174 174 var trans_defs = {
175 175 sortedBy:{key:"name",dir:"asc"},
176 176 MSG_SORTASC:"${_('Click to sort ascending')}",
177 177 MSG_SORTDESC:"${_('Click to sort descending')}",
178 178 MSG_EMPTY:"${_('No records found.')}",
179 179 MSG_ERROR:"${_('Data error.')}",
180 180 MSG_LOADING:"${_('Loading...')}",
181 181 }
182 182 var myDataTable = new YAHOO.widget.DataTable("repos_list_wrap", myColumnDefs, myDataSource,trans_defs);
183 183 myDataTable.subscribe('postRenderEvent',function(oArgs) {
184 184 tooltip_activate();
185 185 quick_repo_menu();
186 186 filter_activate();
187 187 });
188 188
189 189 var permsColumnDefs = [
190 190 {key:"name",label:"${_('Name')}",sortable:true, sortOptions: { sortFunction: permNameSort }},
191 191 {key:"perm",label:"${_('Permission')}",sortable:false,},
192 192 ];
193 193
194 194 // perms repos table
195 195 var myDataSource2 = new YAHOO.util.DataSource(YUD.get("tbl_list_repositories"));
196 196 myDataSource2.responseType = YAHOO.util.DataSource.TYPE_HTMLTABLE;
197 197 myDataSource2.responseSchema = {
198 198 fields: [
199 199 {key:"name"},
200 200 {key:"perm"},
201 201 ]
202 202 };
203 203
204 204 new YAHOO.widget.DataTable("tbl_list_wrap_repositories", permsColumnDefs, myDataSource2, trans_defs);
205 205
206 206 //perms groups table
207 207 var myDataSource3 = new YAHOO.util.DataSource(YUD.get("tbl_list_repositories_groups"));
208 208 myDataSource3.responseType = YAHOO.util.DataSource.TYPE_HTMLTABLE;
209 209 myDataSource3.responseSchema = {
210 210 fields: [
211 211 {key:"name"},
212 212 {key:"perm"},
213 213 ]
214 214 };
215 215
216 216 new YAHOO.widget.DataTable("tbl_list_wrap_repositories_groups", permsColumnDefs, myDataSource3, trans_defs);
217 217 }
218 218 </script>
219 219 </%def>
@@ -1,46 +1,46 b''
1 1 <div id='repos_list_wrap' class="yui-skin-sam">
2 2 <table id="repos_list">
3 3 <thead>
4 4 <tr>
5 5 <th></th>
6 6 <th class="left">${_('Name')}</th>
7 7 <th class="left">${_('Revision')}</th>
8 8 <th class="left">${_('Action')}</th>
9 9 <th class="left">${_('Action')}</th>
10 10 </thead>
11 11 <tbody>
12 12 <%namespace name="dt" file="/data_table/_dt_elements.html"/>
13 13 %if c.user_repos:
14 14 %for repo in c.user_repos:
15 15 <tr>
16 16 ##QUICK MENU
17 17 <td class="quick_repo_menu">
18 18 ${dt.quick_menu(repo['name'])}
19 19 </td>
20 20 ##REPO NAME AND ICONS
21 21 <td class="reponame">
22 22 ${dt.repo_name(repo['name'],repo['dbrepo']['repo_type'],repo['dbrepo']['private'],repo['dbrepo_fork'].get('repo_name'))}
23 23 </td>
24 24 ##LAST REVISION
25 25 <td>
26 26 ${dt.revision(repo['name'],repo['rev'],repo['tip'],repo['author'],repo['last_msg'])}
27 27 </td>
28 28 <td><a href="${h.url('repo_settings_home',repo_name=repo['name'])}" title="${_('edit')}"><img class="icon" alt="${_('private')}" src="${h.url('/images/icons/application_form_edit.png')}"/></a></td>
29 29 <td>
30 30 ${h.form(url('repo_settings_delete', repo_name=repo['name']),method='delete')}
31 31 ${h.submit('remove_%s' % repo['name'],'',class_="delete_icon action_button",onclick="return confirm('"+_('Confirm to delete this repository: %s') % repo['name']+"');")}
32 32 ${h.end_form()}
33 33 </td>
34 34 </tr>
35 35 %endfor
36 36 %else:
37 37 <div style="padding:5px 0px 10px 0px;">
38 38 ${_('No repositories yet')}
39 39 %if h.HasPermissionAny('hg.admin','hg.create.repository')():
40 40 ${h.link_to(_('create one now'),h.url('admin_settings_create_repository'),class_="ui-btn")}
41 41 %endif
42 42 </div>
43 43 %endif
44 44 </tbody>
45 45 </table>
46 </div> No newline at end of file
46 </div>
@@ -1,145 +1,145 b''
1 1 ## -*- coding: utf-8 -*-
2 2 <%inherit file="/base/base.html"/>
3 3
4 4 <%def name="title()">
5 5 ${_('Users administration')} - ${c.rhodecode_name}
6 6 </%def>
7 7
8 8 <%def name="breadcrumbs_links()">
9 9 <input class="q_filter_box" id="q_filter" size="15" type="text" name="filter" value="${_('quick filter...')}"/> ${h.link_to(_('Admin'),h.url('admin_home'))} &raquo; <span id="user_count">0</span> ${_('users')}
10 10 </%def>
11 11
12 12 <%def name="page_nav()">
13 13 ${self.menu('admin')}
14 14 </%def>
15 15
16 16 <%def name="main()">
17 17 <div class="box">
18 18 <!-- box / title -->
19 19 <div class="title">
20 ${self.breadcrumbs()}
20 ${self.breadcrumbs()}
21 21 <ul class="links">
22 22 <li>
23 23 <span>${h.link_to(_(u'ADD NEW USER'),h.url('new_user'))}</span>
24 24 </li>
25 25 </ul>
26 26 </div>
27 27 <!-- end box / title -->
28 28 <div class="table yui-skin-sam" id="users_list_wrap"></div>
29 29 <div id="user-paginator" style="padding: 0px 0px 0px 20px"></div>
30 30 </div>
31 31
32 32 <script>
33 33 var url = "${h.url('formatted_users', format='json')}";
34 34 var data = ${c.data|n};
35 35 var myDataSource = new YAHOO.util.DataSource(data);
36 36 myDataSource.responseType = YAHOO.util.DataSource.TYPE_JSON;
37 37
38 38 myDataSource.responseSchema = {
39 39 resultsList: "records",
40 40 fields: [
41 41 {key: "gravatar"},
42 42 {key: "raw_username"},
43 43 {key: "username"},
44 44 {key: "firstname"},
45 45 {key: "lastname"},
46 46 {key: "last_login"},
47 47 {key: "active"},
48 48 {key: "admin"},
49 49 {key: "ldap"},
50 50 {key: "action"},
51 51 ]
52 52 };
53 53 myDataSource.doBeforeCallback = function(req,raw,res,cb) {
54 54 // This is the filter function
55 55 var data = res.results || [],
56 56 filtered = [],
57 57 i,l;
58
58
59 59 if (req) {
60 60 req = req.toLowerCase();
61 61 for (i = 0; i<data.length; i++) {
62 62 var pos = data[i].raw_username.toLowerCase().indexOf(req)
63 63 if (pos != -1) {
64 64 filtered.push(data[i]);
65 65 }
66 66 }
67 67 res.results = filtered;
68 68 }
69 69 YUD.get('user_count').innerHTML = res.results.length;
70 70 return res;
71 71 }
72 72
73 73 // main table sorting
74 74 var myColumnDefs = [
75 {key:"gravatar",label:"",sortable:false,},
75 {key:"gravatar",label:"",sortable:false,},
76 76 {key:"username",label:"${_('username')}",sortable:true,
77 77 sortOptions: { sortFunction: linkSort }
78 78 },
79 79 {key:"firstname",label:"${_('firstname')}",sortable:true,},
80 80 {key:"lastname",label:"${_('lastname')}",sortable:true,},
81 81 {key:"last_login",label:"${_('last login')}",sortable:true,},
82 82 {key:"active",label:"${_('active')}",sortable:true,},
83 83 {key:"admin",label:"${_('admin')}",sortable:true,},
84 84 {key:"ldap",label:"${_('ldap')}",sortable:true,},
85 85 {key:"action",label:"${_('action')}",sortable:false},
86 86 ];
87 87
88 88 var myDataTable = new YAHOO.widget.DataTable("users_list_wrap", myColumnDefs, myDataSource,{
89 89 sortedBy:{key:"username",dir:"asc"},
90 90 paginator: new YAHOO.widget.Paginator({
91 91 rowsPerPage: 15,
92 92 alwaysVisible: false,
93 93 template : "{PreviousPageLink} {FirstPageLink} {PageLinks} {LastPageLink} {NextPageLink}",
94 94 pageLinks: 5,
95 95 containerClass: 'pagination-wh',
96 96 currentPageClass: 'pager_curpage',
97 97 pageLinkClass: 'pager_link',
98 98 nextPageLinkLabel: '&gt;',
99 99 previousPageLinkLabel: '&lt;',
100 100 firstPageLinkLabel: '&lt;&lt;',
101 101 lastPageLinkLabel: '&gt;&gt;',
102 102 containers:['user-paginator']
103 103 }),
104 104
105 105 MSG_SORTASC:"${_('Click to sort ascending')}",
106 106 MSG_SORTDESC:"${_('Click to sort descending')}",
107 107 MSG_EMPTY:"${_('No records found.')}",
108 108 MSG_ERROR:"${_('Data error.')}",
109 109 MSG_LOADING:"${_('Loading...')}",
110 110 }
111 111 );
112 112 myDataTable.subscribe('postRenderEvent',function(oArgs) {
113 113
114 114 });
115
115
116 116 var filterTimeout = null;
117 117
118 118 updateFilter = function () {
119 119 // Reset timeout
120 120 filterTimeout = null;
121 121
122 122 // Reset sort
123 123 var state = myDataTable.getState();
124 124 state.sortedBy = {key:'username', dir:YAHOO.widget.DataTable.CLASS_ASC};
125 125
126 126 // Get filtered data
127 127 myDataSource.sendRequest(YUD.get('q_filter').value,{
128 128 success : myDataTable.onDataReturnInitializeTable,
129 129 failure : myDataTable.onDataReturnInitializeTable,
130 130 scope : myDataTable,
131 131 argument: state
132 132 });
133 133
134 };
134 };
135 135 YUE.on('q_filter','click',function(){
136 136 YUD.get('q_filter').value = '';
137 137 });
138 138
139 139 YUE.on('q_filter','keyup',function (e) {
140 140 clearTimeout(filterTimeout);
141 141 filterTimeout = setTimeout(updateFilter,600);
142 });
142 });
143 143 </script>
144 144
145 145 </%def>
@@ -1,179 +1,179 b''
1 1 ## -*- coding: utf-8 -*-
2 2
3 3 <%inherit file="/base/base.html"/>
4 4
5 5 <%def name="title()">
6 6 ${_('%s Changeset') % c.repo_name} - r${c.changeset.revision}:${h.short_id(c.changeset.raw_id)} - ${c.rhodecode_name}
7 7 </%def>
8 8
9 9 <%def name="breadcrumbs_links()">
10 10 ${h.link_to(_(u'Home'),h.url('/'))}
11 11 &raquo;
12 12 ${h.link_to(c.repo_name,h.url('summary_home',repo_name=c.repo_name))}
13 13 &raquo;
14 14 ${_('Changeset')} - <span class='hash'>r${c.changeset.revision}:${h.short_id(c.changeset.raw_id)}</span>
15 15 </%def>
16 16
17 17 <%def name="page_nav()">
18 18 ${self.menu('changelog')}
19 19 </%def>
20 20
21 21 <%def name="main()">
22 22 <div class="box">
23 23 <!-- box / title -->
24 24 <div class="title">
25 25 ${self.breadcrumbs()}
26 26 </div>
27 27 <div class="table">
28 28 <div class="diffblock">
29 29 <div class="code-header">
30 30 <div class="hash">
31 31 r${c.changeset.revision}:${h.short_id(c.changeset.raw_id)}
32 32 </div>
33 33 <div class="date">
34 34 ${h.fmt_date(c.changeset.date)}
35 35 </div>
36 36 <div class="changeset-status-container">
37 37 %if c.statuses:
38 38 <div title="${_('Changeset status')}" class="changeset-status-lbl">[${h.changeset_status_lbl(c.statuses[0])}]</div>
39 39 <div class="changeset-status-ico"><img src="${h.url('/images/icons/flag_status_%s.png' % c.statuses[0])}" /></div>
40 40 %endif
41 41 </div>
42 42 <div class="diff-actions">
43 43 <a href="${h.url('raw_changeset_home',repo_name=c.repo_name,revision=c.changeset.raw_id,diff='show')}" class="tooltip" title="${h.tooltip(_('raw diff'))}"><img class="icon" src="${h.url('/images/icons/page_white.png')}"/></a>
44 44 <a href="${h.url('raw_changeset_home',repo_name=c.repo_name,revision=c.changeset.raw_id,diff='download')}" class="tooltip" title="${h.tooltip(_('download diff'))}"><img class="icon" src="${h.url('/images/icons/page_white_get.png')}"/></a>
45 45 ${c.ignorews_url(request.GET)}
46 46 ${c.context_url(request.GET)}
47 47 </div>
48 48 <div class="comments-number" style="float:right;padding-right:5px">${ungettext("%d comment", "%d comments", len(c.comments)) % len(c.comments)} ${ungettext("(%d inline)", "(%d inline)", c.inline_cnt) % c.inline_cnt}</div>
49 49 </div>
50 50 </div>
51 51 <div id="changeset_content">
52 52 <div class="container">
53 53 <div class="left">
54 54 <div class="author">
55 55 <div class="gravatar">
56 56 <img alt="gravatar" src="${h.gravatar_url(h.email(c.changeset.author),20)}"/>
57 57 </div>
58 58 <span>${h.person(c.changeset.author)}</span><br/>
59 59 <span><a href="mailto:${h.email_or_none(c.changeset.author)}">${h.email_or_none(c.changeset.author)}</a></span><br/>
60 60 </div>
61 61 <div class="message">${h.urlify_commit(c.changeset.message, c.repo_name)}</div>
62 62 </div>
63 63 <div class="right">
64 64 <div class="changes">
65 65 % if len(c.changeset.affected_files) <= c.affected_files_cut_off:
66 66 <span class="removed" title="${_('removed')}">${len(c.changeset.removed)}</span>
67 67 <span class="changed" title="${_('changed')}">${len(c.changeset.changed)}</span>
68 68 <span class="added" title="${_('added')}">${len(c.changeset.added)}</span>
69 69 % else:
70 70 <span class="removed" title="${_('affected %s files') % len(c.changeset.affected_files)}">!</span>
71 71 <span class="changed" title="${_('affected %s files') % len(c.changeset.affected_files)}">!</span>
72 72 <span class="added" title="${_('affected %s files') % len(c.changeset.affected_files)}">!</span>
73 73 % endif
74 74 </div>
75 75
76 76 %if c.changeset.parents:
77 77 %for p_cs in reversed(c.changeset.parents):
78 78 <div class="parent">${_('Parent')}
79 79 <span class="changeset_id">${p_cs.revision}:<span class="changeset_hash">${h.link_to(h.short_id(p_cs.raw_id),
80 80 h.url('changeset_home',repo_name=c.repo_name,revision=p_cs.raw_id),title=p_cs.message)}</span></span>
81 81 </div>
82 82 %endfor
83 83 %else:
84 84 <div class="parent">${_('No parents')}</div>
85 85 %endif
86 86 <span class="logtags">
87 87 %if len(c.changeset.parents)>1:
88 88 <span class="merge">${_('merge')}</span>
89 89 %endif
90 90 %if c.changeset.branch:
91 91 <span class="branchtag" title="${'%s %s' % (_('branch'),c.changeset.branch)}">
92 92 ${h.link_to(c.changeset.branch,h.url('files_home',repo_name=c.repo_name,revision=c.changeset.raw_id))}
93 93 </span>
94 94 %endif
95 95 %for tag in c.changeset.tags:
96 96 <span class="tagtag" title="${'%s %s' % (_('tag'),tag)}">
97 97 ${h.link_to(tag,h.url('files_home',repo_name=c.repo_name,revision=c.changeset.raw_id))}</span>
98 98 %endfor
99 99 </span>
100 100 </div>
101 101 </div>
102 102 <span>
103 103 ${_('%s files affected with %s insertions and %s deletions:') % (len(c.changeset.affected_files),c.lines_added,c.lines_deleted)}
104 104 </span>
105 105 <div class="cs_files">
106 106 %for change,filenode,diff,cs1,cs2,stat in c.changes:
107 107 <div class="cs_${change}">
108 108 <div class="node">
109 109 %if change != 'removed':
110 110 ${h.link_to(h.safe_unicode(filenode.path),c.anchor_url(filenode.changeset.raw_id,filenode.path,request.GET)+"_target")}
111 111 %else:
112 112 ${h.link_to(h.safe_unicode(filenode.path),h.url.current(anchor=h.FID('',filenode.path)))}
113 113 %endif
114 114 </div>
115 115 <div class="changes">${h.fancy_file_stats(stat)}</div>
116 116 </div>
117 117 %endfor
118 118 % if c.cut_off:
119 119 ${_('Changeset was too big and was cut off...')}
120 120 % endif
121 121 </div>
122 122 </div>
123 123
124 124 </div>
125 125 <script>
126 126 var _USERS_AC_DATA = ${c.users_array|n};
127 127 var _GROUPS_AC_DATA = ${c.users_groups_array|n};
128 128 AJAX_COMMENT_URL = "${url('changeset_comment',repo_name=c.repo_name,revision=c.changeset.raw_id)}";
129 AJAX_COMMENT_DELETE_URL = "${url('changeset_comment_delete',repo_name=c.repo_name,comment_id='__COMMENT_ID__')}";
129 AJAX_COMMENT_DELETE_URL = "${url('changeset_comment_delete',repo_name=c.repo_name,comment_id='__COMMENT_ID__')}";
130 130 </script>
131 131 ## diff block
132 132 <%namespace name="diff_block" file="/changeset/diff_block.html"/>
133 133 ${diff_block.diff_block(c.changes)}
134 134
135 135 ## template for inline comment form
136 136 <%namespace name="comment" file="/changeset/changeset_file_comment.html"/>
137 137 ${comment.comment_inline_form()}
138 138
139 139 ## render comments and inlines
140 140 ${comment.generate_comments()}
141 141
142 142 ## main comment form and it status
143 143 ${comment.comments(h.url('changeset_comment', repo_name=c.repo_name, revision=c.changeset.raw_id),
144 144 h.changeset_status(c.rhodecode_db_repo, c.changeset.raw_id))}
145 145
146 146 ## FORM FOR MAKING JS ACTION AS CHANGESET COMMENTS
147 147 <script type="text/javascript">
148 148 YUE.onDOMReady(function(){
149 149 YUE.on(YUQ('.show-inline-comments'),'change',function(e){
150 150 var show = 'none';
151 151 var target = e.currentTarget;
152 152 if(target.checked){
153 153 var show = ''
154 154 }
155 155 var boxid = YUD.getAttribute(target,'id_for');
156 156 var comments = YUQ('#{0} .inline-comments'.format(boxid));
157 157 for(c in comments){
158 158 YUD.setStyle(comments[c],'display',show);
159 159 }
160 160 var btns = YUQ('#{0} .inline-comments-button'.format(boxid));
161 161 for(c in btns){
162 162 YUD.setStyle(btns[c],'display',show);
163 163 }
164 164 })
165 165
166 166 YUE.on(YUQ('.line'),'click',function(e){
167 167 var tr = e.currentTarget;
168 168 injectInlineForm(tr);
169 169 });
170 170
171 171 // inject comments into they proper positions
172 172 var file_comments = YUQ('.inline-comment-placeholder');
173 173 renderInlineComments(file_comments);
174 174 })
175 175
176 176 </script>
177 177
178 178 </div>
179 179 </%def>
@@ -1,163 +1,163 b''
1 1 ## -*- coding: utf-8 -*-
2 2 ## usage:
3 3 ## <%namespace name="comment" file="/changeset/changeset_file_comment.html"/>
4 4 ## ${comment.comment_block(co)}
5 5 ##
6 6 <%def name="comment_block(co)">
7 7 <div class="comment" id="comment-${co.comment_id}" line="${co.line_no}">
8 8 <div class="comment-wrapp">
9 9 <div class="meta">
10 10 <div style="float:left"> <img src="${h.gravatar_url(co.author.email, 20)}" /> </div>
11 11 <div class="user">
12 12 ${co.author.username}
13 13 </div>
14 14 <div class="date">
15 15 ${h.age(co.modified_at)}
16 16 </div>
17 17 %if co.status_change:
18 18 <div style="float:left" class="changeset-status-container">
19 19 <div style="float:left;padding:0px 2px 0px 2px"><span style="font-size: 18px;">&rsaquo;</span></div>
20 20 <div title="${_('Changeset status')}" class="changeset-status-lbl"> ${co.status_change.status_lbl}</div>
21 21 <div class="changeset-status-ico"><img src="${h.url(str('/images/icons/flag_status_%s.png' % co.status_change.status))}" /></div>
22 22 </div>
23 23 %endif
24 24 %if h.HasPermissionAny('hg.admin', 'repository.admin')() or co.author.user_id == c.rhodecode_user.user_id:
25 25 <div class="buttons">
26 26 <span onClick="deleteComment(${co.comment_id})" class="delete-comment ui-btn">${_('Delete')}</span>
27 27 </div>
28 28 %endif
29 29 </div>
30 30 <div class="text">
31 31 ${h.rst_w_mentions(co.text)|n}
32 32 </div>
33 33 </div>
34 34 </div>
35 35 </%def>
36 36
37 37
38 38 <%def name="comment_inline_form()">
39 39 <div id='comment-inline-form-template' style="display:none">
40 40 <div class="comment-inline-form ac">
41 41 %if c.rhodecode_user.username != 'default':
42 42 <div class="overlay"><div class="overlay-text">${_('Submitting...')}</div></div>
43 43 ${h.form('#', class_='inline-form')}
44 44 <div class="clearfix">
45 45 <div class="comment-help">${_('Commenting on line {1}.')}
46 46 ${(_('Comments parsed using %s syntax with %s support.') % (
47 47 ('<a href="%s">RST</a>' % h.url('rst_help')),
48 48 ('<span style="color:#003367" class="tooltip" title="%s">@mention</span>' % _('Use @username inside this text to send notification to this RhodeCode user'))
49 49 )
50 50 )|n
51 51 }
52 52 </div>
53 53 <div class="mentions-container" id="mentions_container_{1}"></div>
54 54 <textarea id="text_{1}" name="text" class="yui-ac-input"></textarea>
55 55 </div>
56 56 <div class="comment-button">
57 57 <input type="hidden" name="f_path" value="{0}">
58 58 <input type="hidden" name="line" value="{1}">
59 59 ${h.submit('save', _('Comment'), class_='ui-btn save-inline-form')}
60 60 ${h.reset('hide-inline-form', _('Hide'), class_='ui-btn hide-inline-form')}
61 61 </div>
62 62 ${h.end_form()}
63 63 %else:
64 64 ${h.form('')}
65 65 <div class="clearfix">
66 66 <div class="comment-help">
67 67 ${_('You need to be logged in to comment.')} <a href="${h.url('login_home',came_from=h.url.current())}">${_('Login now')}</a>
68 68 </div>
69 69 </div>
70 70 <div class="comment-button">
71 71 ${h.reset('hide-inline-form', _('Hide'), class_='ui-btn hide-inline-form')}
72 72 </div>
73 73 ${h.end_form()}
74 74 %endif
75 75 </div>
76 76 </div>
77 77 </%def>
78 78
79 79
80 80 ## generates inlines taken from c.comments var
81 81 <%def name="inlines()">
82 82 <div class="comments-number">${ungettext("%d comment", "%d comments", len(c.comments)) % len(c.comments)} ${ungettext("(%d inline)", "(%d inline)", c.inline_cnt) % c.inline_cnt}</div>
83 83 %for path, lines in c.inline_comments:
84 84 % for line,comments in lines.iteritems():
85 85 <div style="display:none" class="inline-comment-placeholder" path="${path}" target_id="${h.safeid(h.safe_unicode(path))}">
86 86 %for co in comments:
87 87 ${comment_block(co)}
88 88 %endfor
89 89 </div>
90 90 %endfor
91 91 %endfor
92 92
93 93 </%def>
94 94
95 95 ## generate inline comments and the main ones
96 96 <%def name="generate_comments()">
97 97 <div class="comments">
98 98 <div id="inline-comments-container">
99 99 ## generate inlines for this changeset
100 100 ${inlines()}
101 101 </div>
102 102
103 103 %for co in c.comments:
104 104 <div id="comment-tr-${co.comment_id}">
105 105 ${comment_block(co)}
106 106 </div>
107 107 %endfor
108 </div>
108 </div>
109 109 </%def>
110 110
111 111 ## MAIN COMMENT FORM
112 112 <%def name="comments(post_url, cur_status, close_btn=False)">
113 113
114 114 <div class="comments">
115 115 %if c.rhodecode_user.username != 'default':
116 116 <div class="comment-form ac">
117 117 ${h.form(post_url)}
118 118 <strong>${_('Leave a comment')}</strong>
119 119 <div class="clearfix">
120 120 <div class="comment-help">
121 121 ${(_('Comments parsed using %s syntax with %s support.') % (('<a href="%s">RST</a>' % h.url('rst_help')),
122 122 '<span style="color:#003367" class="tooltip" title="%s">@mention</span>' %
123 123 _('Use @username inside this text to send notification to this RhodeCode user')))|n}
124 124 | <label for="show_changeset_status_box" class="tooltip" title="${_('Check this to change current status of code-review for this changeset')}"> ${_('change status')}</label>
125 125 <input style="vertical-align: bottom;margin-bottom:-2px" id="show_changeset_status_box" type="checkbox" name="change_changeset_status" />
126 126 </div>
127 127 <div id="status_block_container" class="status-block" style="display:none">
128 128 %for status,lbl in c.changeset_statuses:
129 129 <div class="">
130 130 <img src="${h.url('/images/icons/flag_status_%s.png' % status)}" /> <input ${'checked="checked"' if status == cur_status else ''}" type="radio" name="changeset_status" value="${status}"> <label>${lbl}</label>
131 131 </div>
132 132 %endfor
133 133 </div>
134 134 <div class="mentions-container" id="mentions_container"></div>
135 135 ${h.textarea('text')}
136 136 </div>
137 137 <div class="comment-button">
138 138 ${h.submit('save', _('Comment'), class_="ui-btn large")}
139 139 %if close_btn:
140 140 ${h.submit('save_close', _('Comment and close'), class_='ui-btn blue large')}
141 141 %endif
142 142 </div>
143 143 ${h.end_form()}
144 144 </div>
145 145 %endif
146 146 </div>
147 147 <script>
148 148 YUE.onDOMReady(function () {
149 149 MentionsAutoComplete('text', 'mentions_container', _USERS_AC_DATA, _GROUPS_AC_DATA);
150 150
151 151 // changeset status box listener
152 152 YUE.on(YUD.get('show_changeset_status_box'),'change',function(e){
153 153 if(e.currentTarget.checked){
154 154 YUD.setStyle('status_block_container','display','');
155 155 }
156 156 else{
157 157 YUD.setStyle('status_block_container','display','none');
158 158 }
159 159 })
160 160
161 161 });
162 162 </script>
163 163 </%def>
@@ -1,99 +1,99 b''
1 1 ## -*- coding: utf-8 -*-
2 2 <%inherit file="/base/base.html"/>
3 3
4 4 <%def name="title()">
5 5 ${_('%s Changesets') % c.repo_name} - r${c.cs_ranges[0].revision}:${h.short_id(c.cs_ranges[0].raw_id)} -> r${c.cs_ranges[-1].revision}:${h.short_id(c.cs_ranges[-1].raw_id)} - ${c.rhodecode_name}
6 6 </%def>
7 7
8 8 <%def name="breadcrumbs_links()">
9 9 ${h.link_to(_(u'Home'),h.url('/'))}
10 10 &raquo;
11 11 ${h.link_to(c.repo_name,h.url('summary_home',repo_name=c.repo_name))}
12 12 &raquo;
13 13 ${_('Changesets')} - r${c.cs_ranges[0].revision}:${h.short_id(c.cs_ranges[0].raw_id)} -> r${c.cs_ranges[-1].revision}:${h.short_id(c.cs_ranges[-1].raw_id)}
14 14 </%def>
15 15
16 16 <%def name="page_nav()">
17 17 ${self.menu('changelog')}
18 18 </%def>
19 19
20 20 <%def name="main()">
21 21 <div class="box">
22 22 <!-- box / title -->
23 23 <div class="title">
24 24 ${self.breadcrumbs()}
25 25 </div>
26 26 <div class="table">
27 27 <div id="body" class="diffblock">
28 28 <div class="code-header cv">
29 29 <h3 class="code-header-title">${_('Compare View')}</h3>
30 30 <div>
31 31 ${_('Changesets')} - r${c.cs_ranges[0].revision}:${h.short_id(c.cs_ranges[0].raw_id)} -> r${c.cs_ranges[-1].revision}:${h.short_id(c.cs_ranges[-1].raw_id)}
32 32 </div>
33 33 </div>
34 34 </div>
35 35 <div id="changeset_compare_view_content">
36 36 <div class="container">
37 37 <table class="compare_view_commits noborder">
38 38 %for cnt,cs in enumerate(c.cs_ranges):
39 39 <tr>
40 40 <td><div class="gravatar"><img alt="gravatar" src="${h.gravatar_url(h.email(cs.author),14)}"/></div></td>
41 41 <td>${h.link_to('r%s:%s' % (cs.revision,h.short_id(cs.raw_id)),h.url('changeset_home',repo_name=c.repo_name,revision=cs.raw_id))}</td>
42 42 <td><div class="author">${h.person(cs.author)}</div></td>
43 43 <td><span class="tooltip" title="${h.age(cs.date)}">${cs.date}</span></td>
44 44 <td>
45 45 %if c.statuses:
46 46 <div title="${h.tooltip(_('Changeset status'))}" class="changeset-status-ico"><img src="${h.url('/images/icons/flag_status_%s.png' % c.statuses[cnt])}" /></div>
47 47 %endif
48 48 </td>
49 49 <td><div class="message">${h.urlify_commit(h.wrap_paragraphs(cs.message),c.repo_name)}</div></td>
50 50 </tr>
51 51 %endfor
52 52 </table>
53 53 </div>
54 54 <div style="font-size:1.1em;font-weight: bold;clear:both;padding-top:10px">${_('Files affected')}</div>
55 55 <div class="cs_files">
56 56 %for cs in c.cs_ranges:
57 57 <div class="cur_cs">${h.link_to('r%s:%s' % (cs.revision,h.short_id(cs.raw_id)),h.url('changeset_home',repo_name=c.repo_name,revision=cs.raw_id))}</div>
58 58 %for change,filenode,diff,cs1,cs2,st in c.changes[cs.raw_id]:
59 59 <div class="cs_${change}">${h.link_to(h.safe_unicode(filenode.path),h.url.current(anchor=h.FID(cs.raw_id,filenode.path)))}</div>
60 60 %endfor
61 61 %endfor
62 62 </div>
63 63 </div>
64 64
65 65 </div>
66 66 <%namespace name="comment" file="/changeset/changeset_file_comment.html"/>
67 67 <%namespace name="diff_block" file="/changeset/diff_block.html"/>
68 68 %for cs in c.cs_ranges:
69 69 ##${comment.comment_inline_form(cs)}
70 70 ## diff block
71 71 <h3 style="padding-top:8px;">
72 72 <a class="tooltip" title="${h.tooltip(cs.message)}" href="${h.url('changeset_home',repo_name=c.repo_name,revision=cs.raw_id)}">${'r%s:%s' % (cs.revision,h.short_id(cs.raw_id))}</a>
73 73 <div class="gravatar">
74 74 <img alt="gravatar" src="${h.gravatar_url(h.email(cs.author),20)}"/>
75 </div>
75 </div>
76 76 </h3>
77 77 ${diff_block.diff_block(c.changes[cs.raw_id])}
78 78 ##${comment.comments(cs)}
79 79
80 80 %endfor
81 81 <script type="text/javascript">
82 82
83 83 YUE.onDOMReady(function(){
84 84
85 85 YUE.on(YUQ('.diff-menu-activate'),'click',function(e){
86 86 var act = e.currentTarget.nextElementSibling;
87 87
88 88 if(YUD.hasClass(act,'active')){
89 89 YUD.removeClass(act,'active');
90 90 YUD.setStyle(act,'display','none');
91 91 }else{
92 92 YUD.addClass(act,'active');
93 93 YUD.setStyle(act,'display','');
94 94 }
95 95 });
96 96 })
97 97 </script>
98 98 </div>
99 99 </%def>
@@ -1,55 +1,55 b''
1 1 ## -*- coding: utf-8 -*-
2 2 <!DOCTYPE html>
3 3 <html xmlns="http://www.w3.org/1999/xhtml">
4 4 <head>
5 5 <title>Error - ${c.error_message}</title>
6 6 <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
7 7 <meta name="robots" content="index, nofollow"/>
8 8 <link rel="icon" href="${h.url('/images/icons/database_gear.png')}" type="image/png" />
9
9
10 10 <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
11 11 %if c.redirect_time:
12 12 <meta http-equiv="refresh" content="${c.redirect_time}; url=${c.url_redirect}"/>
13 13 %endif
14 14
15 15 <!-- stylesheets -->
16 16 <link rel="stylesheet" type="text/css" href="${h.url('/css/style.css')}" media="screen" />
17 17 <style type="text/css">
18 18 #main_div{
19 19 border: 0px solid #000;
20 20 width: 500px;
21 21 margin: auto;
22 22 text-align: center;
23 23 margin-top: 200px;
24 24 font-size: 1.6em;
25 25 }
26 26 .error_message{
27 27 text-align: center;
28 28 color:#003367;
29 29 font-size: 1.6em;
30 30 margin:10px;
31 31 }
32 32 </style>
33 33
34 34 </head>
35 35 <body>
36 36
37 37 <div id="login">
38 38 <div class="table">
39 39 <div id="main_div">
40 40 <div style="font-size:2.0em;margin: 10px">${c.rhodecode_name}</div>
41 41 <h1 class="error_message">${c.error_message}</h1>
42 42
43 43 <p>${c.error_explanation}</p>
44 44
45 45 %if c.redirect_time:
46 46 <p>${_('You will be redirected to %s in %s seconds') % (c.redirect_module,c.redirect_time)}</p>
47 47 %endif
48 48
49 49 </div>
50 50 </div>
51 51 <!-- end login -->
52 52 </div>
53 53 </body>
54 54
55 55 </html>
@@ -1,100 +1,100 b''
1 1 ## -*- coding: utf-8 -*-
2 2 <%inherit file="/base/base.html"/>
3 3
4 4 <%def name="title()">
5 5 ${_('%s Fork') % c.repo_name} - ${c.rhodecode_name}
6 6 </%def>
7 7
8 8 <%def name="breadcrumbs_links()">
9 9 ${h.link_to(_(u'Home'),h.url('/'))}
10 10 &raquo;
11 11 ${h.link_to(c.repo_info.repo_name,h.url('summary_home',repo_name=c.repo_info.repo_name))}
12 12 &raquo;
13 13 ${_('fork')}
14 14 </%def>
15 15
16 16 <%def name="page_nav()">
17 17 ${self.menu('')}
18 18 </%def>
19 19 <%def name="main()">
20 20 <div class="box">
21 21 <!-- box / title -->
22 22 <div class="title">
23 23 ${self.breadcrumbs()}
24 24 </div>
25 25 ${h.form(url('repo_fork_create_home',repo_name=c.repo_info.repo_name))}
26 26 <div class="form">
27 27 <!-- fields -->
28 28 <div class="fields">
29 29 <div class="field">
30 30 <div class="label">
31 31 <label for="repo_name">${_('Fork name')}:</label>
32 32 </div>
33 33 <div class="input">
34 34 ${h.text('repo_name',class_="small")}
35 35 ${h.hidden('repo_type',c.repo_info.repo_type)}
36 36 ${h.hidden('fork_parent_id',c.repo_info.repo_id)}
37 37 </div>
38 38 </div>
39 39 <div class="field">
40 40 <div class="label">
41 41 <label for="landing_rev">${_('Landing revision')}:</label>
42 42 </div>
43 43 <div class="input">
44 44 ${h.select('landing_rev','',c.landing_revs,class_="medium")}
45 45 <span class="help-block">${_('Default revision for files page, downloads, whoosh and readme')}</span>
46 46 </div>
47 </div>
47 </div>
48 48 <div class="field">
49 49 <div class="label">
50 50 <label for="repo_group">${_('Repository group')}:</label>
51 51 </div>
52 52 <div class="input">
53 53 ${h.select('repo_group','',c.repo_groups,class_="medium")}
54 54 <span class="help-block">${_('Optionaly select a group to put this repository into.')}</span>
55 55 </div>
56 56 </div>
57 57 <div class="field">
58 58 <div class="label label-textarea">
59 59 <label for="description">${_('Description')}:</label>
60 60 </div>
61 61 <div class="textarea text-area editor">
62 62 ${h.textarea('description',cols=23,rows=5)}
63 63 <span class="help-block">${_('Keep it short and to the point. Use a README file for longer descriptions.')}</span>
64 64 </div>
65 65 </div>
66 66 <div class="field">
67 67 <div class="label label-checkbox">
68 68 <label for="private">${_('Private')}:</label>
69 69 </div>
70 70 <div class="checkboxes">
71 71 ${h.checkbox('private',value="True")}
72 72 <span class="help-block">${_('Private repositories are only visible to people explicitly added as collaborators.')}</span>
73 73 </div>
74 74 </div>
75 75 <div class="field">
76 76 <div class="label label-checkbox">
77 77 <label for="private">${_('Copy permissions')}:</label>
78 78 </div>
79 79 <div class="checkboxes">
80 80 ${h.checkbox('copy_permissions',value="True", checked="checked")}
81 81 <span class="help-block">${_('Copy permissions from forked repository')}</span>
82 82 </div>
83 83 </div>
84 84 <div class="field">
85 85 <div class="label label-checkbox">
86 86 <label for="private">${_('Update after clone')}:</label>
87 87 </div>
88 88 <div class="checkboxes">
89 89 ${h.checkbox('update_after_clone',value="True")}
90 90 <span class="help-block">${_('Checkout source after making a clone')}</span>
91 91 </div>
92 92 </div>
93 93 <div class="buttons">
94 94 ${h.submit('',_('fork this repository'),class_="ui-btn large")}
95 95 </div>
96 96 </div>
97 97 </div>
98 98 ${h.end_form()}
99 99 </div>
100 100 </%def>
@@ -1,39 +1,39 b''
1 1 ## -*- coding: utf-8 -*-
2 2
3 3 % if c.forks_pager:
4 4 % for f in c.forks_pager:
5 5 <div>
6 6 <div class="fork_user">
7 7 <div class="gravatar">
8 8 <img alt="gravatar" src="${h.gravatar_url(f.user.email,24)}"/>
9 9 </div>
10 10 <span style="font-size: 20px">
11 11 <b>${f.user.username}</b> (${f.user.name} ${f.user.lastname}) /
12 12 ${h.link_to(f.repo_name,h.url('summary_home',repo_name=f.repo_name))}
13 13 </span>
14 14 <div style="padding:5px 3px 3px 42px;">${f.description}</div>
15 15 </div>
16 16 <div style="clear:both;padding-top: 10px"></div>
17 17 <div class="follower_date">${_('forked')} -
18 18 <span class="tooltip" title="${h.tooltip(h.fmt_date(f.created_on))}"> ${h.age(f.created_on)}</span>
19 <a title="${_('compare fork with %s' % c.repo_name)}"
20 href="${h.url('compare_url',repo_name=f.repo_name,org_ref_type='branch',org_ref='default',other_ref_type='branch',other_ref='default', repo=c.repo_name)}"
19 <a title="${_('compare fork with %s' % c.repo_name)}"
20 href="${h.url('compare_url',repo_name=f.repo_name,org_ref_type='branch',org_ref='default',other_ref_type='branch',other_ref='default', repo=c.repo_name)}"
21 21 class="ui-btn small">${_('Compare fork')}</a>
22 22 </div>
23 23 <div style="border-bottom: 1px solid #DDD;margin:10px 0px 10px 0px"></div>
24 24 </div>
25 25 % endfor
26 26 <div class="pagination-wh pagination-left">
27 27 <script type="text/javascript">
28 28 YUE.onDOMReady(function(){
29 29 YUE.delegate("forks","click",function(e, matchedEl, container){
30 30 ypjax(e.target.href,"forks",function(){show_more_event();tooltip_activate();});
31 31 YUE.preventDefault(e);
32 32 },'.pager_link');
33 33 });
34 34 </script>
35 35 ${c.forks_pager.pager('$link_previous ~2~ $link_next')}
36 36 </div>
37 37 % else:
38 38 ${_('There are no forks yet')}
39 39 % endif
@@ -1,188 +1,188 b''
1 1 <%inherit file="/base/base.html"/>
2 2
3 3 <%def name="title()">
4 4 ${c.repo_name} ${_('New pull request')}
5 5 </%def>
6 6
7 7 <%def name="breadcrumbs_links()">
8 8 ${h.link_to(_(u'Home'),h.url('/'))}
9 9 &raquo;
10 10 ${h.link_to(c.repo_name,h.url('changelog_home',repo_name=c.repo_name))}
11 11 &raquo;
12 12 ${_('New pull request')}
13 13 </%def>
14 14
15 15 <%def name="main()">
16 16
17 17 <div class="box">
18 18 <!-- box / title -->
19 19 <div class="title">
20 20 ${self.breadcrumbs()}
21 21 </div>
22 22 ${h.form(url('pullrequest', repo_name=c.repo_name), method='post', id='pull_request_form')}
23 23 <div style="float:left;padding:0px 30px 30px 30px">
24 24 <div style="padding:0px 5px 5px 5px">
25 25 <span>
26 26 <a id="refresh" href="#">
27 27 <img class="icon" title="${_('Refresh')}" alt="${_('Refresh')}" src="${h.url('/images/icons/arrow_refresh.png')}"/>
28 28 ${_('refresh overview')}
29 29 </a>
30 30 </span>
31 31 </div>
32 32 ##ORG
33 33 <div style="float:left">
34 34 <div class="fork_user">
35 35 <div class="gravatar">
36 36 <img alt="gravatar" src="${h.gravatar_url(c.rhodecode_db_repo.user.email,24)}"/>
37 37 </div>
38 38 <span style="font-size: 20px">
39 39 ${h.select('org_repo','',c.org_repos,class_='refs')}:${h.select('org_ref','',c.org_refs,class_='refs')}
40 40 </span>
41 41 <div style="padding:5px 3px 3px 42px;">${c.rhodecode_db_repo.description}</div>
42 42 </div>
43 43 <div style="clear:both;padding-top: 10px"></div>
44 44 </div>
45 45 <div style="float:left;font-size:24px;padding:0px 20px">
46 46 <img height=32 width=32 src="${h.url('/images/arrow_right_64.png')}"/>
47 47 </div>
48 48
49 49 ##OTHER, most Probably the PARENT OF THIS FORK
50 50 <div style="float:left">
51 51 <div class="fork_user">
52 52 <div class="gravatar">
53 53 <img id="other_repo_gravatar" alt="gravatar" src=""/>
54 54 </div>
55 55 <span style="font-size: 20px">
56 56 ${h.select('other_repo',c.default_pull_request ,c.other_repos,class_='refs')}:${h.select('other_ref','',c.other_refs,class_='refs')}
57 57 </span>
58 58 <div id="other_repo_desc" style="padding:5px 3px 3px 42px;"></div>
59 59 </div>
60 60 <div style="clear:both;padding-top: 10px"></div>
61 61 </div>
62 62 <div style="clear:both;padding-top: 10px"></div>
63 63 ## overview pulled by ajax
64 64 <div style="float:left" id="pull_request_overview"></div>
65 65 <div style="float:left;clear:both;padding:10px 10px 10px 0px;display:none">
66 66 <a id="pull_request_overview_url" href="#">${_('Detailed compare view')}</a>
67 67 </div>
68 68 </div>
69 69 <div style="float:left; border-left:1px dashed #eee">
70 70 <h4>${_('Pull request reviewers')}</h4>
71 71 <div id="reviewers" style="padding:0px 0px 0px 15px">
72 72 ## members goes here !
73 73 <div class="group_members_wrap">
74 74 <ul id="review_members" class="group_members">
75 75 %for member in c.review_members:
76 76 <li id="reviewer_${member.user_id}">
77 77 <div class="reviewers_member">
78 78 <div class="gravatar"><img alt="gravatar" src="${h.gravatar_url(member.email,14)}"/> </div>
79 79 <div style="float:left">${member.full_name} (${_('owner')})</div>
80 80 <input type="hidden" value="${member.user_id}" name="review_members" />
81 81 <span class="delete_icon action_button" onclick="removeReviewer(${member.user_id})"></span>
82 82 </div>
83 83 </li>
84 84 %endfor
85 85 </ul>
86 </div>
87
86 </div>
87
88 88 <div class='ac'>
89 89 <div class="reviewer_ac">
90 90 ${h.text('user', class_='yui-ac-input')}
91 91 <span class="help-block">${_('Add reviewer to this pull request.')}</span>
92 <div id="reviewers_container"></div>
92 <div id="reviewers_container"></div>
93 93 </div>
94 94 </div>
95 95 </div>
96 96 </div>
97 97 <h3>${_('Create new pull request')}</h3>
98 98
99 99 <div class="form">
100 100 <!-- fields -->
101 101
102 102 <div class="fields">
103 103
104 104 <div class="field">
105 105 <div class="label">
106 106 <label for="pullrequest_title">${_('Title')}:</label>
107 107 </div>
108 108 <div class="input">
109 109 ${h.text('pullrequest_title',size=30)}
110 110 </div>
111 111 </div>
112 112
113 113 <div class="field">
114 114 <div class="label label-textarea">
115 115 <label for="pullrequest_desc">${_('description')}:</label>
116 116 </div>
117 117 <div class="textarea text-area editor">
118 118 ${h.textarea('pullrequest_desc',size=30)}
119 119 </div>
120 120 </div>
121 121
122 122 <div class="buttons">
123 123 ${h.submit('save',_('Send pull request'),class_="ui-btn large")}
124 124 ${h.reset('reset',_('Reset'),class_="ui-btn large")}
125 125 </div>
126 126 </div>
127 127 </div>
128 128 ${h.end_form()}
129 129
130 130 </div>
131 131
132 132 <script type="text/javascript">
133 133 var _USERS_AC_DATA = ${c.users_array|n};
134 134 var _GROUPS_AC_DATA = ${c.users_groups_array|n};
135 135 PullRequestAutoComplete('user', 'reviewers_container', _USERS_AC_DATA, _GROUPS_AC_DATA);
136 136
137 137 var other_repos_info = ${c.other_repos_info|n};
138 138 var loadPreview = function(){
139 139 YUD.setStyle(YUD.get('pull_request_overview_url').parentElement,'display','none');
140 140 var url = "${h.url('compare_url',
141 141 repo_name='org_repo',
142 142 org_ref_type='branch', org_ref='org_ref',
143 143 other_ref_type='branch', other_ref='other_ref',
144 144 repo='other_repo',
145 145 as_form=True)}";
146 146
147 147 var select_refs = YUQ('#pull_request_form select.refs')
148 148
149 149 for(var i=0;i<select_refs.length;i++){
150 150 var select_ref = select_refs[i];
151 151 var select_ref_data = select_ref.value.split(':');
152 152 var key = null;
153 153 var val = null;
154 154 if(select_ref_data.length>1){
155 155 key = select_ref.name+"_type";
156 156 val = select_ref_data[0];
157 157 url = url.replace(key,val);
158 158
159 159 key = select_ref.name;
160 160 val = select_ref_data[1];
161 161 url = url.replace(key,val);
162 162
163 163 }else{
164 164 key = select_ref.name;
165 165 val = select_ref.value;
166 166 url = url.replace(key,val);
167 167 }
168 168 }
169 169
170 170 ypjax(url,'pull_request_overview', function(data){
171 171 var sel_box = YUQ('#pull_request_form #other_repo')[0];
172 172 var repo_name = sel_box.options[sel_box.selectedIndex].value;
173 173 YUD.get('pull_request_overview_url').href = url;
174 174 YUD.setStyle(YUD.get('pull_request_overview_url').parentElement,'display','');
175 175 YUD.get('other_repo_gravatar').src = other_repos_info[repo_name]['gravatar'];
176 176 YUD.get('other_repo_desc').innerHTML = other_repos_info[repo_name]['description'];
177 177 })
178 178 }
179 179 YUE.on('refresh','click',function(e){
180 180 loadPreview()
181 181 })
182 182
183 183 //lazy load overview after 0.5s
184 184 setTimeout(loadPreview, 500)
185 185
186 186 </script>
187 187
188 188 </%def>
@@ -1,174 +1,174 b''
1 1 <%inherit file="/base/base.html"/>
2 2
3 3 <%def name="title()">
4 4 ${c.repo_name} ${_('Pull request #%s') % c.pull_request.pull_request_id}
5 5 </%def>
6 6
7 7 <%def name="breadcrumbs_links()">
8 8 ${h.link_to(_(u'Home'),h.url('/'))}
9 9 &raquo;
10 10 ${h.link_to(c.repo_name,h.url('changelog_home',repo_name=c.repo_name))}
11 11 &raquo;
12 12 ${_('Pull request #%s') % c.pull_request.pull_request_id}
13 13 </%def>
14 14
15 15 <%def name="main()">
16 16
17 17 <div class="box">
18 18 <!-- box / title -->
19 19 <div class="title">
20 20 ${self.breadcrumbs()}
21 21 </div>
22 22 %if c.pull_request.is_closed():
23 23 <div style="padding:10px; font-size:22px;width:100%;text-align: center; color:#88D882">${_('Closed %s') % (h.age(c.pull_request.updated_on))}</div>
24 %endif
25 <h3>${_('Title')}: ${c.pull_request.title}
24 %endif
25 <h3>${_('Title')}: ${c.pull_request.title}
26 26 <div class="changeset-status-container" style="float:none">
27 27 %if c.current_changeset_status:
28 28 <div title="${_('Pull request status')}" class="changeset-status-lbl">[${h.changeset_status_lbl(c.current_changeset_status)}]</div>
29 <div class="changeset-status-ico" style="padding:4px"><img src="${h.url('/images/icons/flag_status_%s.png' % c.current_changeset_status)}" /></div>
29 <div class="changeset-status-ico" style="padding:4px"><img src="${h.url('/images/icons/flag_status_%s.png' % c.current_changeset_status)}" /></div>
30 30 %endif
31 31 </div>
32 32 </h3>
33 33 <div style="white-space:pre-wrap;padding:3px 3px 5px 20px">${h.literal(c.pull_request.description)}</div>
34 34 <div style="padding:4px 4px 10px 20px">
35 35 <div>${_('Created on')}: ${h.fmt_date(c.pull_request.created_on)}</div>
36 36 </div>
37 37
38 38 <div style="min-height:160px">
39 39 ##DIFF
40 40 <div class="table" style="float:left;clear:none">
41 41 <div id="body" class="diffblock">
42 42 <div style="white-space:pre-wrap;padding:5px">${_('Compare view')}</div>
43 43 </div>
44 44 <div id="changeset_compare_view_content">
45 45 ##CS
46 46 <div style="font-size:1.1em;font-weight: bold;clear:both;padding-top:10px">${_('Incoming changesets')}</div>
47 47 <%include file="/compare/compare_cs.html" />
48
48
49 49 ## FILES
50 50 <div style="font-size:1.1em;font-weight: bold;clear:both;padding-top:10px">${_('Files affected')}</div>
51 51 <div class="cs_files">
52 52 %for fid, change, f, stat in c.files:
53 53 <div class="cs_${change}">
54 54 <div class="node">${h.link_to(h.safe_unicode(f),h.url.current(anchor=fid))}</div>
55 55 <div class="changes">${h.fancy_file_stats(stat)}</div>
56 56 </div>
57 57 %endfor
58 58 </div>
59 59 </div>
60 60 </div>
61 61 ## REVIEWERS
62 62 <div style="float:left; border-left:1px dashed #eee">
63 63 <h4>${_('Pull request reviewers')}</h4>
64 64 <div id="reviewers" style="padding:0px 0px 0px 15px">
65 65 ## members goes here !
66 66 <div class="group_members_wrap">
67 67 <ul id="review_members" class="group_members">
68 68 %for member,status in c.pull_request_reviewers:
69 69 <li id="reviewer_${member.user_id}">
70 70 <div class="reviewers_member">
71 71 <div style="float:left;padding:0px 3px 0px 0px" class="tooltip" title="${h.tooltip(h.changeset_status_lbl(status[0][1].status if status else 'not_reviewed'))}">
72 72 <img src="${h.url(str('/images/icons/flag_status_%s.png' % (status[0][1].status if status else 'not_reviewed')))}"/>
73 </div>
73 </div>
74 74 <div class="gravatar"><img alt="gravatar" src="${h.gravatar_url(member.email,14)}"/> </div>
75 75 <div style="float:left">${member.full_name} (${_('owner')})</div>
76 76 <input type="hidden" value="${member.user_id}" name="review_members" />
77 77 %if not c.pull_request.is_closed():
78 78 <span class="delete_icon action_button" onclick="removeReviewer(${member.user_id})"></span>
79 79 %endif
80 80 </div>
81 81 </li>
82 82 %endfor
83 83 </ul>
84 </div>
84 </div>
85 85 %if not c.pull_request.is_closed():
86 86 <div class='ac'>
87 87 <div class="reviewer_ac">
88 88 ${h.text('user', class_='yui-ac-input')}
89 89 <span class="help-block">${_('Add reviewer to this pull request.')}</span>
90 <div id="reviewers_container"></div>
90 <div id="reviewers_container"></div>
91 91 </div>
92 92 <div style="padding:0px 10px">
93 93 <span id="update_pull_request" class="ui-btn xsmall">${_('save')}</span>
94 94 </div>
95 95 </div>
96 96 %endif
97 </div>
98 </div>
97 </div>
98 </div>
99 99 </div>
100 100 <script>
101 101 var _USERS_AC_DATA = ${c.users_array|n};
102 102 var _GROUPS_AC_DATA = ${c.users_groups_array|n};
103 103 AJAX_COMMENT_URL = "${url('pullrequest_comment',repo_name=c.repo_name,pull_request_id=c.pull_request.pull_request_id)}";
104 104 AJAX_COMMENT_DELETE_URL = "${url('pullrequest_comment_delete',repo_name=c.repo_name,comment_id='__COMMENT_ID__')}";
105 105 AJAX_UPDATE_PULLREQUEST = "${url('pullrequest_update',repo_name=c.repo_name,pull_request_id=c.pull_request.pull_request_id)}"
106 106 </script>
107 107
108 108 ## diff block
109 109 <%namespace name="diff_block" file="/changeset/diff_block.html"/>
110 110 %for fid, change, f, stat in c.files:
111 111 ${diff_block.diff_block_simple([c.changes[fid]])}
112 112 %endfor
113 113
114 114 ## template for inline comment form
115 115 <%namespace name="comment" file="/changeset/changeset_file_comment.html"/>
116 116 ${comment.comment_inline_form()}
117
117
118 118 ## render comments and inlines
119 119 ${comment.generate_comments()}
120
120
121 121 % if not c.pull_request.is_closed():
122 122 ## main comment form and it status
123 123 ${comment.comments(h.url('pullrequest_comment', repo_name=c.repo_name,
124 124 pull_request_id=c.pull_request.pull_request_id),
125 125 c.current_changeset_status,
126 126 close_btn=True)}
127 127 %endif
128 128
129 129 <script type="text/javascript">
130 130 YUE.onDOMReady(function(){
131 131 PullRequestAutoComplete('user', 'reviewers_container', _USERS_AC_DATA, _GROUPS_AC_DATA);
132 132
133 133 YUE.on(YUQ('.show-inline-comments'),'change',function(e){
134 134 var show = 'none';
135 135 var target = e.currentTarget;
136 136 if(target.checked){
137 137 var show = ''
138 138 }
139 139 var boxid = YUD.getAttribute(target,'id_for');
140 140 var comments = YUQ('#{0} .inline-comments'.format(boxid));
141 141 for(c in comments){
142 142 YUD.setStyle(comments[c],'display',show);
143 143 }
144 144 var btns = YUQ('#{0} .inline-comments-button'.format(boxid));
145 145 for(c in btns){
146 146 YUD.setStyle(btns[c],'display',show);
147 147 }
148 148 })
149 149
150 150 YUE.on(YUQ('.line'),'click',function(e){
151 151 var tr = e.currentTarget;
152 152 injectInlineForm(tr);
153 153 });
154 154
155 155 // inject comments into they proper positions
156 156 var file_comments = YUQ('.inline-comment-placeholder');
157 157 renderInlineComments(file_comments);
158
158
159 159 YUE.on(YUD.get('update_pull_request'),'click',function(e){
160
160
161 161 var reviewers_ids = [];
162 162 var ids = YUQ('#review_members input');
163 163 for(var i=0; i<ids.length;i++){
164 164 var id = ids[i].value
165 165 reviewers_ids.push(id);
166 166 }
167 167 updateReviewers(reviewers_ids);
168 168 })
169 169 })
170 170 </script>
171 171
172 172 </div>
173 173
174 174 </%def>
@@ -1,42 +1,42 b''
1 1 <%inherit file="/base/base.html"/>
2 2
3 3 <%def name="title()">
4 4 ${c.repo_name} ${_('all pull requests')}
5 5 </%def>
6 6
7 7 <%def name="breadcrumbs_links()">
8 8 ${h.link_to(_(u'Home'),h.url('/'))}
9 9 &raquo;
10 10 ${h.link_to(c.repo_name,h.url('changelog_home',repo_name=c.repo_name))}
11 11 &raquo;
12 12 ${_('All pull requests')}
13 13 </%def>
14 14
15 15 <%def name="main()">
16 16
17 17 <div class="box">
18 18 <!-- box / title -->
19 19 <div class="title">
20 20 ${self.breadcrumbs()}
21 21 </div>
22 22
23 23 %for pr in c.pull_requests:
24 24 <div>
25 25 <h4>
26 26 %if pr.is_closed():
27 27 <img src="${h.url('/images/icons/tick.png')}" alt="${_('Closed')}" />
28 %endif
28 %endif
29 29 <a href="${h.url('pullrequest_show',repo_name=c.repo_name,pull_request_id=pr.pull_request_id)}">
30 30 ${_('Pull request #%s opened by %s on %s') % (pr.pull_request_id, pr.author.full_name, h.fmt_date(pr.created_on))}
31 31 </a>
32 32 </h4>
33 33 <h5 style="border:0px;padding-bottom:0px">${_('Title')}: ${pr.title}</h5>
34 34 <div style="padding:0px 24px">${pr.description}</div>
35 35 </div>
36 36 %endfor
37 37
38 38 </div>
39 39
40 40 <script type="text/javascript"></script>
41 41
42 42 </%def>
@@ -1,101 +1,101 b''
1 1 ## -*- coding: utf-8 -*-
2 2 <%inherit file="/base/base.html"/>
3 3
4 4 <%def name="title()">
5 5 ${_('%s Settings') % c.repo_name} - ${c.rhodecode_name}
6 6 </%def>
7 7
8 8 <%def name="breadcrumbs_links()">
9 9 ${h.link_to(_(u'Home'),h.url('/'))}
10 10 &raquo;
11 11 ${h.link_to(c.repo_info.repo_name,h.url('summary_home',repo_name=c.repo_info.repo_name))}
12 12 &raquo;
13 13 ${_('Settings')}
14 14 </%def>
15 15
16 16 <%def name="page_nav()">
17 17 ${self.menu('settings')}
18 18 </%def>
19 19 <%def name="main()">
20 20 <div class="box">
21 21 <!-- box / title -->
22 22 <div class="title">
23 23 ${self.breadcrumbs()}
24 24 </div>
25 25 ${h.form(url('repo_settings_update', repo_name=c.repo_info.repo_name),method='put')}
26 26 <div class="form">
27 27 <!-- fields -->
28 28 <div class="fields">
29 29 <div class="field">
30 30 <div class="label">
31 31 <label for="repo_name">${_('Name')}:</label>
32 32 </div>
33 33 <div class="input input-medium">
34 34 ${h.text('repo_name',class_="small")}
35 35 </div>
36 36 </div>
37 37 <div class="field">
38 38 <div class="label">
39 39 <label for="clone_uri">${_('Clone uri')}:</label>
40 40 </div>
41 41 <div class="input">
42 42 ${h.text('clone_uri',class_="medium")}
43 43 <span class="help-block">${_('Optional http[s] url from which repository should be cloned.')}</span>
44 44 </div>
45 45 </div>
46 46 <div class="field">
47 47 <div class="label">
48 48 <label for="repo_group">${_('Repository group')}:</label>
49 49 </div>
50 50 <div class="input">
51 51 ${h.select('repo_group','',c.repo_groups,class_="medium")}
52 52 <span class="help-block">${_('Optional select a group to put this repository into.')}</span>
53 53 </div>
54 54 </div>
55 55 <div class="field">
56 56 <div class="label">
57 57 <label for="landing_rev">${_('Landing revision')}:</label>
58 58 </div>
59 59 <div class="input">
60 60 ${h.select('landing_rev','',c.landing_revs,class_="medium")}
61 61 <span class="help-block">${_('Default revision for files page, downloads, whoosh and readme')}</span>
62 62 </div>
63 </div>
63 </div>
64 64 <div class="field">
65 65 <div class="label label-textarea">
66 66 <label for="description">${_('Description')}:</label>
67 67 </div>
68 68 <div class="textarea text-area editor">
69 69 ${h.textarea('description')}
70 70 <span class="help-block">${_('Keep it short and to the point. Use a README file for longer descriptions.')}</span>
71 71 </div>
72 72 </div>
73 73
74 74 <div class="field">
75 75 <div class="label label-checkbox">
76 76 <label for="private">${_('Private repository')}:</label>
77 77 </div>
78 78 <div class="checkboxes">
79 79 ${h.checkbox('private',value="True")}
80 80 <span class="help-block">${_('Private repositories are only visible to people explicitly added as collaborators.')}</span>
81 81 </div>
82 82 </div>
83 83
84 84 <div class="field">
85 85 <div class="label">
86 86 <label for="">${_('Permissions')}:</label>
87 87 </div>
88 88 <div class="input">
89 89 <%include file="../admin/repos/repo_edit_perms.html"/>
90 90 </div>
91 91
92 92 <div class="buttons">
93 93 ${h.submit('save',_('Save'),class_="ui-btn large")}
94 94 ${h.reset('reset',_('Reset'),class_="ui-btn large")}
95 95 </div>
96 96 </div>
97 97 </div>
98 98 ${h.end_form()}
99 99 </div>
100 100 </div>
101 101 </%def>
@@ -1,88 +1,88 b''
1 1 ## -*- coding: utf-8 -*-
2 2 %if c.repo_changesets:
3 3 <table class="table_disp">
4 4 <tr>
5 5 <th class="left">${_('revision')}</th>
6 6 <th class="left">${_('commit message')}</th>
7 7 <th class="left">${_('age')}</th>
8 8 <th class="left">${_('author')}</th>
9 9 <th class="left">${_('branch')}</th>
10 10 <th class="left">${_('tags')}</th>
11 11 </tr>
12 12 %for cnt,cs in enumerate(c.repo_changesets):
13 13 <tr class="parity${cnt%2}">
14 14 <td>
15 15 <div><pre><a href="${h.url('files_home',repo_name=c.repo_name,revision=cs.raw_id)}">r${cs.revision}:${h.short_id(cs.raw_id)}</a></pre></div>
16 16 </td>
17 17 <td>
18 18 ${h.link_to(h.truncate(cs.message,50) or _('No commit message'),
19 19 h.url('changeset_home',repo_name=c.repo_name,revision=cs.raw_id),
20 20 title=cs.message)}
21 21 </td>
22 22 <td><span class="tooltip" title="${h.tooltip(h.fmt_date(cs.date))}">
23 23 ${h.age(cs.date)}</span>
24 24 </td>
25 25 <td title="${cs.author}">${h.person(cs.author)}</td>
26 26 <td>
27 27 <span class="logtags">
28 28 %if cs.branch:
29 29 <span class="branchtag">
30 30 ${cs.branch}
31 31 </span>
32 32 %endif
33 33 </span>
34 34 </td>
35 35 <td>
36 36 <span class="logtags">
37 37 %for tag in cs.tags:
38 38 <span class="tagtag">${tag}</span>
39 39 %endfor
40 40 </span>
41 41 </td>
42 42 </tr>
43 43 %endfor
44 44
45 45 </table>
46 46
47 47 <script type="text/javascript">
48 48 YUE.onDOMReady(function(){
49 49 YUE.delegate("shortlog_data","click",function(e, matchedEl, container){
50 50 ypjax(e.target.href,"shortlog_data",function(){tooltip_activate();});
51 51 YUE.preventDefault(e);
52 52 },'.pager_link');
53 53 });
54 54 </script>
55 55
56 56 <div class="pagination-wh pagination-left">
57 57 ${c.repo_changesets.pager('$link_previous ~2~ $link_next')}
58 58 </div>
59 59 %else:
60 60
61 61 %if h.HasRepoPermissionAny('repository.write','repository.admin')(c.repo_name):
62 62 <h4>${_('Add or upload files directly via RhodeCode')}</h4>
63 63 <div style="margin: 20px 30px;">
64 64 <div id="add_node_id" class="add_node">
65 65 <a class="ui-btn" href="${h.url('files_add_home',repo_name=c.repo_name,revision=0,f_path='')}">${_('add new file')}</a>
66 66 </div>
67 67 </div>
68 68 %endif
69 69
70 70
71 71 <h4>${_('Push new repo')}</h4>
72 72 <pre>
73 73 ${c.rhodecode_repo.alias} clone ${c.clone_repo_url}
74 74 ${c.rhodecode_repo.alias} add README # add first file
75 75 ${c.rhodecode_repo.alias} commit -m "Initial" # commit with message
76 76 ${c.rhodecode_repo.alias} push ${'origin master' if h.is_git(c.rhodecode_repo) else ''} # push changes back
77 77 </pre>
78 78
79 79 <h4>${_('Existing repository?')}</h4>
80 80 <pre>
81 81 %if h.is_git(c.rhodecode_repo):
82 82 git remote add origin ${c.clone_repo_url}
83 83 git push -u origin master
84 84 %else:
85 85 hg push ${c.clone_repo_url}
86 %endif
86 %endif
87 87 </pre>
88 88 %endif
@@ -1,991 +1,990 b''
1 1 from __future__ import with_statement
2 2 import random
3 3 import mock
4 4
5 5 from rhodecode.tests import *
6 6 from rhodecode.lib.compat import json
7 7 from rhodecode.lib.auth import AuthUser
8 8 from rhodecode.model.user import UserModel
9 9 from rhodecode.model.users_group import UsersGroupModel
10 10 from rhodecode.model.repo import RepoModel
11 11 from rhodecode.model.meta import Session
12 12
13 13 API_URL = '/_admin/api'
14 14
15 15
16 16 def _build_data(apikey, method, **kw):
17 17 """
18 18 Builds API data with given random ID
19 19
20 20 :param random_id:
21 21 :type random_id:
22 22 """
23 23 random_id = random.randrange(1, 9999)
24 24 return random_id, json.dumps({
25 25 "id": random_id,
26 26 "api_key": apikey,
27 27 "method": method,
28 28 "args": kw
29 29 })
30 30
31 31 jsonify = lambda obj: json.loads(json.dumps(obj))
32 32
33 33
34 34 def crash(*args, **kwargs):
35 35 raise Exception('Total Crash !')
36 36
37 37
38 38 TEST_USERS_GROUP = 'test_users_group'
39 39
40 40
41 41 def make_users_group(name=TEST_USERS_GROUP):
42 42 gr = UsersGroupModel().create(name=name)
43 43 UsersGroupModel().add_user_to_group(users_group=gr,
44 44 user=TEST_USER_ADMIN_LOGIN)
45 45 Session().commit()
46 46 return gr
47 47
48 48
49 49 def destroy_users_group(name=TEST_USERS_GROUP):
50 50 UsersGroupModel().delete(users_group=name, force=True)
51 51 Session().commit()
52 52
53 53
54 54 def create_repo(repo_name, repo_type):
55 55 # create new repo
56 56 form_data = dict(repo_name=repo_name,
57 57 repo_name_full=repo_name,
58 58 fork_name=None,
59 59 description='description %s' % repo_name,
60 60 repo_group=None,
61 61 private=False,
62 62 repo_type=repo_type,
63 63 clone_uri=None,
64 64 landing_rev='tip')
65 65 cur_user = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN)
66 66 r = RepoModel().create(form_data, cur_user)
67 67 Session().commit()
68 68 return r
69 69
70 70
71 71 def create_fork(fork_name, fork_type, fork_of):
72 72 fork = RepoModel(Session())._get_repo(fork_of)
73 73 r = create_repo(fork_name, fork_type)
74 74 r.fork = fork
75 75 Session().add(r)
76 76 Session().commit()
77 77 return r
78 78
79 79
80 80 def destroy_repo(repo_name):
81 81 RepoModel().delete(repo_name)
82 82 Session().commit()
83 83
84 84
85 85 class BaseTestApi(object):
86 86 REPO = None
87 87 REPO_TYPE = None
88 88
89 89 @classmethod
90 90 def setUpClass(self):
91 91 self.usr = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN)
92 92 self.apikey = self.usr.api_key
93 93 self.TEST_USER = UserModel().create_or_update(
94 94 username='test-api',
95 95 password='test',
96 96 email='test@api.rhodecode.org',
97 97 firstname='first',
98 98 lastname='last'
99 99 )
100 100 Session().commit()
101 101 self.TEST_USER_LOGIN = self.TEST_USER.username
102 102
103 103 @classmethod
104 104 def teardownClass(self):
105 105 pass
106 106
107 107 def setUp(self):
108 108 self.maxDiff = None
109 109 make_users_group()
110 110
111 111 def tearDown(self):
112 112 destroy_users_group()
113 113
114 114 def _compare_ok(self, id_, expected, given):
115 115 expected = jsonify({
116 116 'id': id_,
117 117 'error': None,
118 118 'result': expected
119 119 })
120 120 given = json.loads(given)
121 121 self.assertEqual(expected, given)
122 122
123 123 def _compare_error(self, id_, expected, given):
124 124 expected = jsonify({
125 125 'id': id_,
126 126 'error': expected,
127 127 'result': None
128 128 })
129 129 given = json.loads(given)
130 130 self.assertEqual(expected, given)
131 131
132 132 # def test_Optional(self):
133 133 # from rhodecode.controllers.api.api import Optional
134 134 # option1 = Optional(None)
135 135 # self.assertEqual('<Optional:%s>' % None, repr(option1))
136 136 #
137 137 # self.assertEqual(1, Optional.extract(Optional(1)))
138 138 # self.assertEqual('trololo', Optional.extract('trololo'))
139 139
140 140 def test_api_wrong_key(self):
141 141 id_, params = _build_data('trololo', 'get_user')
142 142 response = self.app.post(API_URL, content_type='application/json',
143 143 params=params)
144 144
145 145 expected = 'Invalid API KEY'
146 146 self._compare_error(id_, expected, given=response.body)
147 147
148 148 def test_api_missing_non_optional_param(self):
149 149 id_, params = _build_data(self.apikey, 'get_user')
150 150 response = self.app.post(API_URL, content_type='application/json',
151 151 params=params)
152 152
153 153 expected = 'Missing non optional `userid` arg in JSON DATA'
154 154 self._compare_error(id_, expected, given=response.body)
155 155
156 156 def test_api_get_users(self):
157 157 id_, params = _build_data(self.apikey, 'get_users',)
158 158 response = self.app.post(API_URL, content_type='application/json',
159 159 params=params)
160 160 ret_all = []
161 161 for usr in UserModel().get_all():
162 162 ret = usr.get_api_data()
163 163 ret_all.append(jsonify(ret))
164 164 expected = ret_all
165 165 self._compare_ok(id_, expected, given=response.body)
166 166
167 167 def test_api_get_user(self):
168 168 id_, params = _build_data(self.apikey, 'get_user',
169 169 userid=TEST_USER_ADMIN_LOGIN)
170 170 response = self.app.post(API_URL, content_type='application/json',
171 171 params=params)
172 172
173 173 usr = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN)
174 174 ret = usr.get_api_data()
175 175 ret['permissions'] = AuthUser(usr.user_id).permissions
176 176
177 177 expected = ret
178 178 self._compare_ok(id_, expected, given=response.body)
179 179
180 180 def test_api_get_user_that_does_not_exist(self):
181 181 id_, params = _build_data(self.apikey, 'get_user',
182 182 userid='trololo')
183 183 response = self.app.post(API_URL, content_type='application/json',
184 184 params=params)
185 185
186 186 expected = "user `%s` does not exist" % 'trololo'
187 187 self._compare_error(id_, expected, given=response.body)
188 188
189 189 def test_api_pull(self):
190 190 #TODO: issues with rhodecode_extras here.. not sure why !
191 191 pass
192 192
193 193 # repo_name = 'test_pull'
194 194 # r = create_repo(repo_name, self.REPO_TYPE)
195 195 # r.clone_uri = TEST_self.REPO
196 196 # Session.add(r)
197 197 # Session.commit()
198 198 #
199 199 # id_, params = _build_data(self.apikey, 'pull',
200 200 # repoid=repo_name,)
201 201 # response = self.app.post(API_URL, content_type='application/json',
202 202 # params=params)
203 203 #
204 204 # expected = 'Pulled from `%s`' % repo_name
205 205 # self._compare_ok(id_, expected, given=response.body)
206 206 #
207 207 # destroy_repo(repo_name)
208 208
209 209 def test_api_pull_error(self):
210 210 id_, params = _build_data(self.apikey, 'pull',
211 211 repoid=self.REPO,)
212 212 response = self.app.post(API_URL, content_type='application/json',
213 213 params=params)
214 214
215 215 expected = 'Unable to pull changes from `%s`' % self.REPO
216 216 self._compare_error(id_, expected, given=response.body)
217 217
218 218 def test_api_create_existing_user(self):
219 219 id_, params = _build_data(self.apikey, 'create_user',
220 220 username=TEST_USER_ADMIN_LOGIN,
221 221 email='test@foo.com',
222 222 password='trololo')
223 223 response = self.app.post(API_URL, content_type='application/json',
224 224 params=params)
225 225
226 226 expected = "user `%s` already exist" % TEST_USER_ADMIN_LOGIN
227 227 self._compare_error(id_, expected, given=response.body)
228 228
229 229 def test_api_create_user_with_existing_email(self):
230 230 id_, params = _build_data(self.apikey, 'create_user',
231 231 username=TEST_USER_ADMIN_LOGIN + 'new',
232 232 email=TEST_USER_REGULAR_EMAIL,
233 233 password='trololo')
234 234 response = self.app.post(API_URL, content_type='application/json',
235 235 params=params)
236 236
237 237 expected = "email `%s` already exist" % TEST_USER_REGULAR_EMAIL
238 238 self._compare_error(id_, expected, given=response.body)
239 239
240 240 def test_api_create_user(self):
241 241 username = 'test_new_api_user'
242 242 email = username + "@foo.com"
243 243
244 244 id_, params = _build_data(self.apikey, 'create_user',
245 245 username=username,
246 246 email=email,
247 247 password='trololo')
248 248 response = self.app.post(API_URL, content_type='application/json',
249 249 params=params)
250 250
251 251 usr = UserModel().get_by_username(username)
252 252 ret = dict(
253 253 msg='created new user `%s`' % username,
254 254 user=jsonify(usr.get_api_data())
255 255 )
256 256
257 257 expected = ret
258 258 self._compare_ok(id_, expected, given=response.body)
259 259
260 260 UserModel().delete(usr.user_id)
261 261 self.Session().commit()
262 262
263 263 @mock.patch.object(UserModel, 'create_or_update', crash)
264 264 def test_api_create_user_when_exception_happened(self):
265 265
266 266 username = 'test_new_api_user'
267 267 email = username + "@foo.com"
268 268
269 269 id_, params = _build_data(self.apikey, 'create_user',
270 270 username=username,
271 271 email=email,
272 272 password='trololo')
273 273 response = self.app.post(API_URL, content_type='application/json',
274 274 params=params)
275 275 expected = 'failed to create user `%s`' % username
276 276 self._compare_error(id_, expected, given=response.body)
277 277
278 278 def test_api_delete_user(self):
279 279 usr = UserModel().create_or_update(username=u'test_user',
280 280 password=u'qweqwe',
281 281 email=u'u232@rhodecode.org',
282 282 firstname=u'u1', lastname=u'u1')
283 283 self.Session().commit()
284 284 username = usr.username
285 285 email = usr.email
286 286 usr_id = usr.user_id
287 287 ## DELETE THIS USER NOW
288 288
289 289 id_, params = _build_data(self.apikey, 'delete_user',
290 290 userid=username,)
291 291 response = self.app.post(API_URL, content_type='application/json',
292 292 params=params)
293 293
294 294 ret = {'msg': 'deleted user ID:%s %s' % (usr_id, username),
295 295 'user': None}
296 296 expected = ret
297 297 self._compare_ok(id_, expected, given=response.body)
298 298
299 299 @mock.patch.object(UserModel, 'delete', crash)
300 300 def test_api_delete_user_when_exception_happened(self):
301 301 usr = UserModel().create_or_update(username=u'test_user',
302 302 password=u'qweqwe',
303 303 email=u'u232@rhodecode.org',
304 304 firstname=u'u1', lastname=u'u1')
305 305 self.Session().commit()
306 306 username = usr.username
307 307
308 308 id_, params = _build_data(self.apikey, 'delete_user',
309 309 userid=username,)
310 310 response = self.app.post(API_URL, content_type='application/json',
311 311 params=params)
312 312 ret = 'failed to delete ID:%s %s' % (usr.user_id,
313 313 usr.username)
314 314 expected = ret
315 315 self._compare_error(id_, expected, given=response.body)
316 316
317 317 @parameterized.expand([('firstname', 'new_username'),
318 318 ('lastname', 'new_username'),
319 319 ('email', 'new_username'),
320 320 ('admin', True),
321 321 ('admin', False),
322 322 ('ldap_dn', 'test'),
323 323 ('ldap_dn', None),
324 324 ('active', False),
325 325 ('active', True),
326 326 ('password', 'newpass')
327 327 ])
328 328 def test_api_update_user(self, name, expected):
329 329 usr = UserModel().get_by_username(self.TEST_USER_LOGIN)
330 330 kw = {name: expected,
331 331 'userid': usr.user_id}
332 332 id_, params = _build_data(self.apikey, 'update_user', **kw)
333 333 response = self.app.post(API_URL, content_type='application/json',
334 334 params=params)
335 335
336 336 ret = {
337 337 'msg': 'updated user ID:%s %s' % (usr.user_id, self.TEST_USER_LOGIN),
338 338 'user': jsonify(UserModel()\
339 339 .get_by_username(self.TEST_USER_LOGIN)\
340 340 .get_api_data())
341 341 }
342 342
343 343 expected = ret
344 344 self._compare_ok(id_, expected, given=response.body)
345 345
346 346 def test_api_update_user_no_changed_params(self):
347 347 usr = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN)
348 348 ret = jsonify(usr.get_api_data())
349 349 id_, params = _build_data(self.apikey, 'update_user',
350 350 userid=TEST_USER_ADMIN_LOGIN)
351 351
352 352 response = self.app.post(API_URL, content_type='application/json',
353 353 params=params)
354 354 ret = {
355 355 'msg': 'updated user ID:%s %s' % (usr.user_id, TEST_USER_ADMIN_LOGIN),
356 356 'user': ret
357 357 }
358 358 expected = ret
359 359 self._compare_ok(id_, expected, given=response.body)
360 360
361 361 def test_api_update_user_by_user_id(self):
362 362 usr = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN)
363 363 ret = jsonify(usr.get_api_data())
364 364 id_, params = _build_data(self.apikey, 'update_user',
365 365 userid=usr.user_id)
366 366
367 367 response = self.app.post(API_URL, content_type='application/json',
368 368 params=params)
369 369 ret = {
370 370 'msg': 'updated user ID:%s %s' % (usr.user_id, TEST_USER_ADMIN_LOGIN),
371 371 'user': ret
372 372 }
373 373 expected = ret
374 374 self._compare_ok(id_, expected, given=response.body)
375 375
376 376 @mock.patch.object(UserModel, 'update_user', crash)
377 377 def test_api_update_user_when_exception_happens(self):
378 378 usr = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN)
379 379 ret = jsonify(usr.get_api_data())
380 380 id_, params = _build_data(self.apikey, 'update_user',
381 381 userid=usr.user_id)
382 382
383 383 response = self.app.post(API_URL, content_type='application/json',
384 384 params=params)
385 385 ret = 'failed to update user `%s`' % usr.user_id
386 386
387 387 expected = ret
388 388 self._compare_error(id_, expected, given=response.body)
389 389
390 390 def test_api_get_repo(self):
391 391 new_group = 'some_new_group'
392 392 make_users_group(new_group)
393 393 RepoModel().grant_users_group_permission(repo=self.REPO,
394 394 group_name=new_group,
395 395 perm='repository.read')
396 396 self.Session().commit()
397 397 id_, params = _build_data(self.apikey, 'get_repo',
398 398 repoid=self.REPO)
399 399 response = self.app.post(API_URL, content_type='application/json',
400 400 params=params)
401 401
402 402 repo = RepoModel().get_by_repo_name(self.REPO)
403 403 ret = repo.get_api_data()
404 404
405 405 members = []
406 406 for user in repo.repo_to_perm:
407 407 perm = user.permission.permission_name
408 408 user = user.user
409 409 user_data = user.get_api_data()
410 410 user_data['type'] = "user"
411 411 user_data['permission'] = perm
412 412 members.append(user_data)
413 413
414 414 for users_group in repo.users_group_to_perm:
415 415 perm = users_group.permission.permission_name
416 416 users_group = users_group.users_group
417 417 users_group_data = users_group.get_api_data()
418 418 users_group_data['type'] = "users_group"
419 419 users_group_data['permission'] = perm
420 420 members.append(users_group_data)
421 421
422 422 ret['members'] = members
423 423
424 424 expected = ret
425 425 self._compare_ok(id_, expected, given=response.body)
426 426 destroy_users_group(new_group)
427 427
428 428 def test_api_get_repo_that_doesn_not_exist(self):
429 429 id_, params = _build_data(self.apikey, 'get_repo',
430 430 repoid='no-such-repo')
431 431 response = self.app.post(API_URL, content_type='application/json',
432 432 params=params)
433 433
434 434 ret = 'repository `%s` does not exist' % 'no-such-repo'
435 435 expected = ret
436 436 self._compare_error(id_, expected, given=response.body)
437 437
438 438 def test_api_get_repos(self):
439 439 id_, params = _build_data(self.apikey, 'get_repos')
440 440 response = self.app.post(API_URL, content_type='application/json',
441 441 params=params)
442 442
443 443 result = []
444 444 for repo in RepoModel().get_all():
445 445 result.append(repo.get_api_data())
446 446 ret = jsonify(result)
447 447
448 448 expected = ret
449 449 self._compare_ok(id_, expected, given=response.body)
450 450
451 451 @parameterized.expand([('all', 'all'),
452 452 ('dirs', 'dirs'),
453 453 ('files', 'files'), ])
454 454 def test_api_get_repo_nodes(self, name, ret_type):
455 455 rev = 'tip'
456 456 path = '/'
457 457 id_, params = _build_data(self.apikey, 'get_repo_nodes',
458 458 repoid=self.REPO, revision=rev,
459 459 root_path=path,
460 460 ret_type=ret_type)
461 461 response = self.app.post(API_URL, content_type='application/json',
462 462 params=params)
463 463
464 464 # we don't the actual return types here since it's tested somewhere
465 465 # else
466 466 expected = json.loads(response.body)['result']
467 467 self._compare_ok(id_, expected, given=response.body)
468 468
469 469 def test_api_get_repo_nodes_bad_revisions(self):
470 470 rev = 'i-dont-exist'
471 471 path = '/'
472 472 id_, params = _build_data(self.apikey, 'get_repo_nodes',
473 473 repoid=self.REPO, revision=rev,
474 474 root_path=path,)
475 475 response = self.app.post(API_URL, content_type='application/json',
476 476 params=params)
477 477
478 478 expected = 'failed to get repo: `%s` nodes' % self.REPO
479 479 self._compare_error(id_, expected, given=response.body)
480 480
481 481 def test_api_get_repo_nodes_bad_path(self):
482 482 rev = 'tip'
483 483 path = '/idontexits'
484 484 id_, params = _build_data(self.apikey, 'get_repo_nodes',
485 485 repoid=self.REPO, revision=rev,
486 486 root_path=path,)
487 487 response = self.app.post(API_URL, content_type='application/json',
488 488 params=params)
489 489
490 490 expected = 'failed to get repo: `%s` nodes' % self.REPO
491 491 self._compare_error(id_, expected, given=response.body)
492 492
493 493 def test_api_get_repo_nodes_bad_ret_type(self):
494 494 rev = 'tip'
495 495 path = '/'
496 496 ret_type = 'error'
497 497 id_, params = _build_data(self.apikey, 'get_repo_nodes',
498 498 repoid=self.REPO, revision=rev,
499 499 root_path=path,
500 500 ret_type=ret_type)
501 501 response = self.app.post(API_URL, content_type='application/json',
502 502 params=params)
503 503
504 504 expected = 'ret_type must be one of %s' % (['files', 'dirs', 'all'])
505 505 self._compare_error(id_, expected, given=response.body)
506 506
507 507 def test_api_create_repo(self):
508 508 repo_name = 'api-repo'
509 509 id_, params = _build_data(self.apikey, 'create_repo',
510 510 repo_name=repo_name,
511 511 owner=TEST_USER_ADMIN_LOGIN,
512 512 repo_type='hg',
513 513 )
514 514 response = self.app.post(API_URL, content_type='application/json',
515 515 params=params)
516 516
517 517 repo = RepoModel().get_by_repo_name(repo_name)
518 518 ret = {
519 519 'msg': 'Created new repository `%s`' % repo_name,
520 520 'repo': jsonify(repo.get_api_data())
521 521 }
522 522 expected = ret
523 523 self._compare_ok(id_, expected, given=response.body)
524 524 destroy_repo(repo_name)
525 525
526 526 def test_api_create_repo_unknown_owner(self):
527 527 repo_name = 'api-repo'
528 528 owner = 'i-dont-exist'
529 529 id_, params = _build_data(self.apikey, 'create_repo',
530 530 repo_name=repo_name,
531 531 owner=owner,
532 532 repo_type='hg',
533 533 )
534 534 response = self.app.post(API_URL, content_type='application/json',
535 535 params=params)
536 536 expected = 'user `%s` does not exist' % owner
537 537 self._compare_error(id_, expected, given=response.body)
538 538
539 539 def test_api_create_repo_exists(self):
540 540 repo_name = self.REPO
541 541 id_, params = _build_data(self.apikey, 'create_repo',
542 542 repo_name=repo_name,
543 543 owner=TEST_USER_ADMIN_LOGIN,
544 544 repo_type='hg',
545 545 )
546 546 response = self.app.post(API_URL, content_type='application/json',
547 547 params=params)
548 548 expected = "repo `%s` already exist" % repo_name
549 549 self._compare_error(id_, expected, given=response.body)
550 550
551 551 @mock.patch.object(RepoModel, 'create_repo', crash)
552 552 def test_api_create_repo_exception_occurred(self):
553 553 repo_name = 'api-repo'
554 554 id_, params = _build_data(self.apikey, 'create_repo',
555 555 repo_name=repo_name,
556 556 owner=TEST_USER_ADMIN_LOGIN,
557 557 repo_type='hg',
558 558 )
559 559 response = self.app.post(API_URL, content_type='application/json',
560 560 params=params)
561 561 expected = 'failed to create repository `%s`' % repo_name
562 562 self._compare_error(id_, expected, given=response.body)
563 563
564 564 def test_api_delete_repo(self):
565 565 repo_name = 'api_delete_me'
566 566 create_repo(repo_name, self.REPO_TYPE)
567 567
568 568 id_, params = _build_data(self.apikey, 'delete_repo',
569 569 repoid=repo_name,)
570 570 response = self.app.post(API_URL, content_type='application/json',
571 571 params=params)
572 572
573 573 ret = {
574 574 'msg': 'Deleted repository `%s`' % repo_name,
575 575 'success': True
576 576 }
577 577 expected = ret
578 578 self._compare_ok(id_, expected, given=response.body)
579 579
580 580 def test_api_delete_repo_exception_occurred(self):
581 581 repo_name = 'api_delete_me'
582 582 create_repo(repo_name, self.REPO_TYPE)
583 583 try:
584 584 with mock.patch.object(RepoModel, 'delete', crash):
585 585 id_, params = _build_data(self.apikey, 'delete_repo',
586 586 repoid=repo_name,)
587 587 response = self.app.post(API_URL, content_type='application/json',
588 588 params=params)
589 589
590 590 expected = 'failed to delete repository `%s`' % repo_name
591 591 self._compare_error(id_, expected, given=response.body)
592 592 finally:
593 593 destroy_repo(repo_name)
594 594
595 595 def test_api_fork_repo(self):
596 596 fork_name = 'api-repo-fork'
597 597 id_, params = _build_data(self.apikey, 'fork_repo',
598 598 repoid=self.REPO,
599 599 fork_name=fork_name,
600 600 owner=TEST_USER_ADMIN_LOGIN,
601 601 )
602 602 response = self.app.post(API_URL, content_type='application/json',
603 603 params=params)
604 604
605 605 ret = {
606 606 'msg': 'Created fork of `%s` as `%s`' % (self.REPO,
607 607 fork_name),
608 608 'success': True
609 609 }
610 610 expected = ret
611 611 self._compare_ok(id_, expected, given=response.body)
612 612 destroy_repo(fork_name)
613 613
614 614 def test_api_fork_repo_unknown_owner(self):
615 615 fork_name = 'api-repo-fork'
616 616 owner = 'i-dont-exist'
617 617 id_, params = _build_data(self.apikey, 'fork_repo',
618 618 repoid=self.REPO,
619 619 fork_name=fork_name,
620 620 owner=owner,
621 621 )
622 622 response = self.app.post(API_URL, content_type='application/json',
623 623 params=params)
624 624 expected = 'user `%s` does not exist' % owner
625 625 self._compare_error(id_, expected, given=response.body)
626 626
627 627 def test_api_fork_repo_fork_exists(self):
628 628 fork_name = 'api-repo-fork'
629 629 create_fork(fork_name, self.REPO_TYPE, self.REPO)
630 630
631 631 try:
632 632 fork_name = 'api-repo-fork'
633 633
634 634 id_, params = _build_data(self.apikey, 'fork_repo',
635 635 repoid=self.REPO,
636 636 fork_name=fork_name,
637 637 owner=TEST_USER_ADMIN_LOGIN,
638 638 )
639 639 response = self.app.post(API_URL, content_type='application/json',
640 640 params=params)
641 641
642 642 expected = "fork `%s` already exist" % fork_name
643 643 self._compare_error(id_, expected, given=response.body)
644 644 finally:
645 645 destroy_repo(fork_name)
646 646
647 647 def test_api_fork_repo_repo_exists(self):
648 648 fork_name = self.REPO
649 649
650 650 id_, params = _build_data(self.apikey, 'fork_repo',
651 651 repoid=self.REPO,
652 652 fork_name=fork_name,
653 653 owner=TEST_USER_ADMIN_LOGIN,
654 654 )
655 655 response = self.app.post(API_URL, content_type='application/json',
656 656 params=params)
657 657
658 658 expected = "repo `%s` already exist" % fork_name
659 659 self._compare_error(id_, expected, given=response.body)
660 660
661 661 @mock.patch.object(RepoModel, 'create_fork', crash)
662 662 def test_api_fork_repo_exception_occurred(self):
663 663 fork_name = 'api-repo-fork'
664 664 id_, params = _build_data(self.apikey, 'fork_repo',
665 665 repoid=self.REPO,
666 666 fork_name=fork_name,
667 667 owner=TEST_USER_ADMIN_LOGIN,
668 668 )
669 669 response = self.app.post(API_URL, content_type='application/json',
670 670 params=params)
671 671
672 672 expected = 'failed to fork repository `%s` as `%s`' % (self.REPO,
673 673 fork_name)
674 674 self._compare_error(id_, expected, given=response.body)
675 675
676 676 def test_api_get_users_group(self):
677 677 id_, params = _build_data(self.apikey, 'get_users_group',
678 678 usersgroupid=TEST_USERS_GROUP)
679 679 response = self.app.post(API_URL, content_type='application/json',
680 680 params=params)
681 681
682 682 users_group = UsersGroupModel().get_group(TEST_USERS_GROUP)
683 683 members = []
684 684 for user in users_group.members:
685 685 user = user.user
686 686 members.append(user.get_api_data())
687 687
688 688 ret = users_group.get_api_data()
689 689 ret['members'] = members
690 690 expected = ret
691 691 self._compare_ok(id_, expected, given=response.body)
692 692
693 693 def test_api_get_users_groups(self):
694 694
695 695 make_users_group('test_users_group2')
696 696
697 697 id_, params = _build_data(self.apikey, 'get_users_groups',)
698 698 response = self.app.post(API_URL, content_type='application/json',
699 699 params=params)
700 700
701 701 expected = []
702 702 for gr_name in [TEST_USERS_GROUP, 'test_users_group2']:
703 703 users_group = UsersGroupModel().get_group(gr_name)
704 704 ret = users_group.get_api_data()
705 705 expected.append(ret)
706 706 self._compare_ok(id_, expected, given=response.body)
707 707
708 708 UsersGroupModel().delete(users_group='test_users_group2')
709 709 self.Session().commit()
710 710
711 711 def test_api_create_users_group(self):
712 712 group_name = 'some_new_group'
713 713 id_, params = _build_data(self.apikey, 'create_users_group',
714 714 group_name=group_name)
715 715 response = self.app.post(API_URL, content_type='application/json',
716 716 params=params)
717 717
718 718 ret = {
719 719 'msg': 'created new users group `%s`' % group_name,
720 720 'users_group': jsonify(UsersGroupModel()\
721 721 .get_by_name(group_name)\
722 722 .get_api_data())
723 723 }
724 724 expected = ret
725 725 self._compare_ok(id_, expected, given=response.body)
726 726
727 727 destroy_users_group(group_name)
728 728
729 729 def test_api_get_users_group_that_exist(self):
730 730 id_, params = _build_data(self.apikey, 'create_users_group',
731 731 group_name=TEST_USERS_GROUP)
732 732 response = self.app.post(API_URL, content_type='application/json',
733 733 params=params)
734 734
735 735 expected = "users group `%s` already exist" % TEST_USERS_GROUP
736 736 self._compare_error(id_, expected, given=response.body)
737 737
738 738 @mock.patch.object(UsersGroupModel, 'create', crash)
739 739 def test_api_get_users_group_exception_occurred(self):
740 740 group_name = 'exception_happens'
741 741 id_, params = _build_data(self.apikey, 'create_users_group',
742 742 group_name=group_name)
743 743 response = self.app.post(API_URL, content_type='application/json',
744 744 params=params)
745 745
746 746 expected = 'failed to create group `%s`' % group_name
747 747 self._compare_error(id_, expected, given=response.body)
748 748
749 749 def test_api_add_user_to_users_group(self):
750 750 gr_name = 'test_group'
751 751 UsersGroupModel().create(gr_name)
752 752 self.Session().commit()
753 753 id_, params = _build_data(self.apikey, 'add_user_to_users_group',
754 754 usersgroupid=gr_name,
755 755 userid=TEST_USER_ADMIN_LOGIN)
756 756 response = self.app.post(API_URL, content_type='application/json',
757 757 params=params)
758 758
759 759 expected = {
760 760 'msg': 'added member `%s` to users group `%s`' % (
761 761 TEST_USER_ADMIN_LOGIN, gr_name
762 762 ),
763 763 'success': True}
764 764 self._compare_ok(id_, expected, given=response.body)
765 765
766 766 UsersGroupModel().delete(users_group=gr_name)
767 767 self.Session().commit()
768 768
769 769 def test_api_add_user_to_users_group_that_doesnt_exist(self):
770 770 id_, params = _build_data(self.apikey, 'add_user_to_users_group',
771 771 usersgroupid='false-group',
772 772 userid=TEST_USER_ADMIN_LOGIN)
773 773 response = self.app.post(API_URL, content_type='application/json',
774 774 params=params)
775 775
776 776 expected = 'users group `%s` does not exist' % 'false-group'
777 777 self._compare_error(id_, expected, given=response.body)
778 778
779 779 @mock.patch.object(UsersGroupModel, 'add_user_to_group', crash)
780 780 def test_api_add_user_to_users_group_exception_occurred(self):
781 781 gr_name = 'test_group'
782 782 UsersGroupModel().create(gr_name)
783 783 self.Session().commit()
784 784 id_, params = _build_data(self.apikey, 'add_user_to_users_group',
785 785 usersgroupid=gr_name,
786 786 userid=TEST_USER_ADMIN_LOGIN)
787 787 response = self.app.post(API_URL, content_type='application/json',
788 788 params=params)
789 789
790 790 expected = 'failed to add member to users group `%s`' % gr_name
791 791 self._compare_error(id_, expected, given=response.body)
792 792
793 793 UsersGroupModel().delete(users_group=gr_name)
794 794 self.Session().commit()
795 795
796 796 def test_api_remove_user_from_users_group(self):
797 797 gr_name = 'test_group_3'
798 798 gr = UsersGroupModel().create(gr_name)
799 799 UsersGroupModel().add_user_to_group(gr, user=TEST_USER_ADMIN_LOGIN)
800 800 self.Session().commit()
801 801 id_, params = _build_data(self.apikey, 'remove_user_from_users_group',
802 802 usersgroupid=gr_name,
803 803 userid=TEST_USER_ADMIN_LOGIN)
804 804 response = self.app.post(API_URL, content_type='application/json',
805 805 params=params)
806 806
807 807 expected = {
808 808 'msg': 'removed member `%s` from users group `%s`' % (
809 809 TEST_USER_ADMIN_LOGIN, gr_name
810 810 ),
811 811 'success': True}
812 812 self._compare_ok(id_, expected, given=response.body)
813 813
814 814 UsersGroupModel().delete(users_group=gr_name)
815 815 self.Session().commit()
816 816
817 817 @mock.patch.object(UsersGroupModel, 'remove_user_from_group', crash)
818 818 def test_api_remove_user_from_users_group_exception_occurred(self):
819 819 gr_name = 'test_group_3'
820 820 gr = UsersGroupModel().create(gr_name)
821 821 UsersGroupModel().add_user_to_group(gr, user=TEST_USER_ADMIN_LOGIN)
822 822 self.Session().commit()
823 823 id_, params = _build_data(self.apikey, 'remove_user_from_users_group',
824 824 usersgroupid=gr_name,
825 825 userid=TEST_USER_ADMIN_LOGIN)
826 826 response = self.app.post(API_URL, content_type='application/json',
827 827 params=params)
828 828
829 829 expected = 'failed to remove member from users group `%s`' % gr_name
830 830 self._compare_error(id_, expected, given=response.body)
831 831
832 832 UsersGroupModel().delete(users_group=gr_name)
833 833 self.Session().commit()
834 834
835 835 @parameterized.expand([('none', 'repository.none'),
836 836 ('read', 'repository.read'),
837 837 ('write', 'repository.write'),
838 838 ('admin', 'repository.admin')])
839 839 def test_api_grant_user_permission(self, name, perm):
840 840 id_, params = _build_data(self.apikey, 'grant_user_permission',
841 841 repoid=self.REPO,
842 842 userid=TEST_USER_ADMIN_LOGIN,
843 843 perm=perm)
844 844 response = self.app.post(API_URL, content_type='application/json',
845 845 params=params)
846 846
847 847 ret = {
848 848 'msg': 'Granted perm: `%s` for user: `%s` in repo: `%s`' % (
849 849 perm, TEST_USER_ADMIN_LOGIN, self.REPO
850 850 ),
851 851 'success': True
852 852 }
853 853 expected = ret
854 854 self._compare_ok(id_, expected, given=response.body)
855 855
856 856 def test_api_grant_user_permission_wrong_permission(self):
857 857 perm = 'haha.no.permission'
858 858 id_, params = _build_data(self.apikey, 'grant_user_permission',
859 859 repoid=self.REPO,
860 860 userid=TEST_USER_ADMIN_LOGIN,
861 861 perm=perm)
862 862 response = self.app.post(API_URL, content_type='application/json',
863 863 params=params)
864 864
865 865 expected = 'permission `%s` does not exist' % perm
866 866 self._compare_error(id_, expected, given=response.body)
867 867
868 868 @mock.patch.object(RepoModel, 'grant_user_permission', crash)
869 869 def test_api_grant_user_permission_exception_when_adding(self):
870 870 perm = 'repository.read'
871 871 id_, params = _build_data(self.apikey, 'grant_user_permission',
872 872 repoid=self.REPO,
873 873 userid=TEST_USER_ADMIN_LOGIN,
874 874 perm=perm)
875 875 response = self.app.post(API_URL, content_type='application/json',
876 876 params=params)
877 877
878 878 expected = 'failed to edit permission for user: `%s` in repo: `%s`' % (
879 879 TEST_USER_ADMIN_LOGIN, self.REPO
880 880 )
881 881 self._compare_error(id_, expected, given=response.body)
882 882
883 883 def test_api_revoke_user_permission(self):
884 884 id_, params = _build_data(self.apikey, 'revoke_user_permission',
885 885 repoid=self.REPO,
886 886 userid=TEST_USER_ADMIN_LOGIN,)
887 887 response = self.app.post(API_URL, content_type='application/json',
888 888 params=params)
889 889
890 890 expected = {
891 891 'msg': 'Revoked perm for user: `%s` in repo: `%s`' % (
892 892 TEST_USER_ADMIN_LOGIN, self.REPO
893 893 ),
894 894 'success': True
895 895 }
896 896 self._compare_ok(id_, expected, given=response.body)
897 897
898 898 @mock.patch.object(RepoModel, 'revoke_user_permission', crash)
899 899 def test_api_revoke_user_permission_exception_when_adding(self):
900 900 id_, params = _build_data(self.apikey, 'revoke_user_permission',
901 901 repoid=self.REPO,
902 902 userid=TEST_USER_ADMIN_LOGIN,)
903 903 response = self.app.post(API_URL, content_type='application/json',
904 904 params=params)
905 905
906 906 expected = 'failed to edit permission for user: `%s` in repo: `%s`' % (
907 907 TEST_USER_ADMIN_LOGIN, self.REPO
908 908 )
909 909 self._compare_error(id_, expected, given=response.body)
910 910
911 911 @parameterized.expand([('none', 'repository.none'),
912 912 ('read', 'repository.read'),
913 913 ('write', 'repository.write'),
914 914 ('admin', 'repository.admin')])
915 915 def test_api_grant_users_group_permission(self, name, perm):
916 916 id_, params = _build_data(self.apikey, 'grant_users_group_permission',
917 917 repoid=self.REPO,
918 918 usersgroupid=TEST_USERS_GROUP,
919 919 perm=perm)
920 920 response = self.app.post(API_URL, content_type='application/json',
921 921 params=params)
922 922
923 923 ret = {
924 924 'msg': 'Granted perm: `%s` for users group: `%s` in repo: `%s`' % (
925 925 perm, TEST_USERS_GROUP, self.REPO
926 926 ),
927 927 'success': True
928 928 }
929 929 expected = ret
930 930 self._compare_ok(id_, expected, given=response.body)
931 931
932 932 def test_api_grant_users_group_permission_wrong_permission(self):
933 933 perm = 'haha.no.permission'
934 934 id_, params = _build_data(self.apikey, 'grant_users_group_permission',
935 935 repoid=self.REPO,
936 936 usersgroupid=TEST_USERS_GROUP,
937 937 perm=perm)
938 938 response = self.app.post(API_URL, content_type='application/json',
939 939 params=params)
940 940
941 941 expected = 'permission `%s` does not exist' % perm
942 942 self._compare_error(id_, expected, given=response.body)
943 943
944 944 @mock.patch.object(RepoModel, 'grant_users_group_permission', crash)
945 945 def test_api_grant_users_group_permission_exception_when_adding(self):
946 946 perm = 'repository.read'
947 947 id_, params = _build_data(self.apikey, 'grant_users_group_permission',
948 948 repoid=self.REPO,
949 949 usersgroupid=TEST_USERS_GROUP,
950 950 perm=perm)
951 951 response = self.app.post(API_URL, content_type='application/json',
952 952 params=params)
953 953
954 954 expected = 'failed to edit permission for users group: `%s` in repo: `%s`' % (
955 955 TEST_USERS_GROUP, self.REPO
956 956 )
957 957 self._compare_error(id_, expected, given=response.body)
958 958
959 959 def test_api_revoke_users_group_permission(self):
960 960 RepoModel().grant_users_group_permission(repo=self.REPO,
961 961 group_name=TEST_USERS_GROUP,
962 962 perm='repository.read')
963 963 self.Session().commit()
964 964 id_, params = _build_data(self.apikey, 'revoke_users_group_permission',
965 965 repoid=self.REPO,
966 966 usersgroupid=TEST_USERS_GROUP,)
967 967 response = self.app.post(API_URL, content_type='application/json',
968 968 params=params)
969 969
970 970 expected = {
971 971 'msg': 'Revoked perm for users group: `%s` in repo: `%s`' % (
972 972 TEST_USERS_GROUP, self.REPO
973 973 ),
974 974 'success': True
975 975 }
976 976 self._compare_ok(id_, expected, given=response.body)
977 977
978 978 @mock.patch.object(RepoModel, 'revoke_users_group_permission', crash)
979 979 def test_api_revoke_users_group_permission_exception_when_adding(self):
980 980
981 981 id_, params = _build_data(self.apikey, 'revoke_users_group_permission',
982 982 repoid=self.REPO,
983 983 usersgroupid=TEST_USERS_GROUP,)
984 984 response = self.app.post(API_URL, content_type='application/json',
985 985 params=params)
986 986
987 987 expected = 'failed to edit permission for users group: `%s` in repo: `%s`' % (
988 988 TEST_USERS_GROUP, self.REPO
989 989 )
990 990 self._compare_error(id_, expected, given=response.body)
991
@@ -1,7 +1,7 b''
1 1 from rhodecode.tests import *
2 2 from rhodecode.tests.api.api_base import BaseTestApi
3 3
4 4
5 5 class TestHgApi(BaseTestApi, TestController):
6 6 REPO = HG_REPO
7 REPO_TYPE = 'hg' No newline at end of file
7 REPO_TYPE = 'hg'
@@ -1,266 +1,266 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 from rhodecode.lib.auth import get_crypt_password, check_password
4 4 from rhodecode.model.db import User, RhodeCodeSetting, Repository
5 5 from rhodecode.tests import *
6 6 from rhodecode.lib import helpers as h
7 7 from rhodecode.model.user import UserModel
8 8 from rhodecode.model.scm import ScmModel
9 9
10 10
11 11 class TestAdminSettingsController(TestController):
12 12
13 13 def test_index(self):
14 14 response = self.app.get(url('admin_settings'))
15 15 # Test response...
16 16
17 17 def test_index_as_xml(self):
18 18 response = self.app.get(url('formatted_admin_settings', format='xml'))
19 19
20 20 def test_create(self):
21 21 response = self.app.post(url('admin_settings'))
22 22
23 23 def test_new(self):
24 24 response = self.app.get(url('admin_new_setting'))
25 25
26 26 def test_new_as_xml(self):
27 27 response = self.app.get(url('formatted_admin_new_setting', format='xml'))
28 28
29 29 def test_update(self):
30 30 response = self.app.put(url('admin_setting', setting_id=1))
31 31
32 32 def test_update_browser_fakeout(self):
33 33 response = self.app.post(url('admin_setting', setting_id=1), params=dict(_method='put'))
34 34
35 35 def test_delete(self):
36 36 response = self.app.delete(url('admin_setting', setting_id=1))
37 37
38 38 def test_delete_browser_fakeout(self):
39 39 response = self.app.post(url('admin_setting', setting_id=1), params=dict(_method='delete'))
40 40
41 41 def test_show(self):
42 42 response = self.app.get(url('admin_setting', setting_id=1))
43 43
44 44 def test_show_as_xml(self):
45 45 response = self.app.get(url('formatted_admin_setting', setting_id=1, format='xml'))
46 46
47 47 def test_edit(self):
48 48 response = self.app.get(url('admin_edit_setting', setting_id=1))
49 49
50 50 def test_edit_as_xml(self):
51 51 response = self.app.get(url('formatted_admin_edit_setting',
52 52 setting_id=1, format='xml'))
53 53
54 54 def test_ga_code_active(self):
55 55 self.log_user()
56 56 old_title = 'RhodeCode'
57 57 old_realm = 'RhodeCode authentication'
58 58 new_ga_code = 'ga-test-123456789'
59 59 response = self.app.post(url('admin_setting', setting_id='global'),
60 60 params=dict(
61 61 _method='put',
62 62 rhodecode_title=old_title,
63 63 rhodecode_realm=old_realm,
64 64 rhodecode_ga_code=new_ga_code
65 65 ))
66 66
67 67 self.checkSessionFlash(response, 'Updated application settings')
68 68
69 69 self.assertEqual(RhodeCodeSetting
70 70 .get_app_settings()['rhodecode_ga_code'], new_ga_code)
71 71
72 72 response = response.follow()
73 73 response.mustcontain("""_gaq.push(['_setAccount', '%s']);""" % new_ga_code)
74 74
75 75 def test_ga_code_inactive(self):
76 76 self.log_user()
77 77 old_title = 'RhodeCode'
78 78 old_realm = 'RhodeCode authentication'
79 79 new_ga_code = ''
80 80 response = self.app.post(url('admin_setting', setting_id='global'),
81 81 params=dict(
82 82 _method='put',
83 83 rhodecode_title=old_title,
84 84 rhodecode_realm=old_realm,
85 85 rhodecode_ga_code=new_ga_code
86 86 ))
87 87
88 88 self.assertTrue('Updated application settings' in
89 89 response.session['flash'][0][1])
90 90 self.assertEqual(RhodeCodeSetting
91 91 .get_app_settings()['rhodecode_ga_code'], new_ga_code)
92 92
93 93 response = response.follow()
94 94 self.assertFalse("""_gaq.push(['_setAccount', '%s']);""" % new_ga_code
95 95 in response.body)
96 96
97 97 def test_title_change(self):
98 98 self.log_user()
99 99 old_title = 'RhodeCode'
100 100 new_title = old_title + '_changed'
101 101 old_realm = 'RhodeCode authentication'
102 102
103 103 for new_title in ['Changed', 'Ε»Γ³Ε‚wik', old_title]:
104 104 response = self.app.post(url('admin_setting', setting_id='global'),
105 105 params=dict(
106 106 _method='put',
107 107 rhodecode_title=new_title,
108 108 rhodecode_realm=old_realm,
109 109 rhodecode_ga_code=''
110 110 ))
111 111
112 112 self.checkSessionFlash(response, 'Updated application settings')
113 113 self.assertEqual(RhodeCodeSetting
114 114 .get_app_settings()['rhodecode_title'],
115 115 new_title.decode('utf-8'))
116 116
117 117 response = response.follow()
118 118 response.mustcontain("""<h1><a href="/">%s</a></h1>""" % new_title)
119 119
120 120 def test_my_account(self):
121 121 self.log_user()
122 122 response = self.app.get(url('admin_settings_my_account'))
123 123
124 124 self.assertTrue('value="test_admin' in response.body)
125 125
126 126 @parameterized.expand([('firstname', 'new_username'),
127 127 ('lastname', 'new_username'),
128 128 ('admin', True),
129 129 ('admin', False),
130 130 ('ldap_dn', 'test'),
131 131 ('ldap_dn', None),
132 132 ('active', False),
133 133 ('active', True),
134 134 ('email', 'some@email.com'),
135 135 ])
136 136 def test_my_account_update(self, name, expected):
137 137 uname = 'testme'
138 138 usr = UserModel().create_or_update(username=uname, password='qweqwe',
139 139 email='testme@rhodecod.org')
140 140 self.Session().commit()
141 141 params = usr.get_api_data()
142 142 user_id = usr.user_id
143 143 self.log_user(username=uname, password='qweqwe')
144 144 params.update({name: expected})
145 145 params.update({'password_confirmation': ''})
146 146 params.update({'new_password': ''})
147 147
148 148 try:
149 149 response = self.app.put(url('admin_settings_my_account_update',
150 150 id=user_id), params)
151 151
152 152 self.checkSessionFlash(response,
153 153 'Your account was updated successfully')
154 154
155 155 updated_user = User.get_by_username(uname)
156 156 updated_params = updated_user.get_api_data()
157 157 updated_params.update({'password_confirmation': ''})
158 158 updated_params.update({'new_password': ''})
159 159
160 160 params['last_login'] = updated_params['last_login']
161 161 if name == 'email':
162 162 params['emails'] = [expected]
163 163 if name == 'ldap_dn':
164 164 #cannot update this via form
165 165 params['ldap_dn'] = None
166 166 if name == 'active':
167 167 #my account cannot deactivate account
168 168 params['active'] = True
169 169 if name == 'admin':
170 170 #my account cannot make you an admin !
171 171 params['admin'] = False
172 172
173 173 self.assertEqual(params, updated_params)
174 174
175 175 finally:
176 176 UserModel().delete('testme')
177 177
178 178 def test_my_account_update_err_email_exists(self):
179 179 self.log_user()
180 180
181 181 new_email = 'test_regular@mail.com' # already exisitn email
182 182 response = self.app.put(url('admin_settings_my_account_update'),
183 183 params=dict(
184 184 username='test_admin',
185 185 new_password='test12',
186 186 password_confirmation='test122',
187 187 firstname='NewName',
188 188 lastname='NewLastname',
189 189 email=new_email,)
190 190 )
191 191
192 192 response.mustcontain('This e-mail address is already taken')
193 193
194 194 def test_my_account_update_err(self):
195 195 self.log_user('test_regular2', 'test12')
196 196
197 197 new_email = 'newmail.pl'
198 198 response = self.app.post(url('admin_settings_my_account_update'),
199 199 params=dict(
200 200 _method='put',
201 201 username='test_admin',
202 202 new_password='test12',
203 203 password_confirmation='test122',
204 204 firstname='NewName',
205 205 lastname='NewLastname',
206 206 email=new_email,)
207 207 )
208 208
209 209 response.mustcontain('An email address must contain a single @')
210 210 from rhodecode.model import validators
211 211 msg = validators.ValidUsername(edit=False,
212 212 old_data={})._messages['username_exists']
213 213 msg = h.html_escape(msg % {'username': 'test_admin'})
214 214 response.mustcontain(u"%s" % msg)
215 215
216 216 def test_set_repo_fork_has_no_self_id(self):
217 217 self.log_user()
218 218 repo = Repository.get_by_repo_name(HG_REPO)
219 219 response = self.app.get(url('edit_repo', repo_name=HG_REPO))
220 220 opt = """<option value="%s">vcs_test_git</option>""" % repo.repo_id
221 221 assert opt not in response.body
222 222
223 223 def test_set_fork_of_repo(self):
224 224 self.log_user()
225 225 repo = Repository.get_by_repo_name(HG_REPO)
226 226 repo2 = Repository.get_by_repo_name(GIT_REPO)
227 227 response = self.app.put(url('repo_as_fork', repo_name=HG_REPO),
228 228 params=dict(
229 229 id_fork_of=repo2.repo_id
230 230 ))
231 231 repo = Repository.get_by_repo_name(HG_REPO)
232 232 repo2 = Repository.get_by_repo_name(GIT_REPO)
233 233 self.checkSessionFlash(response,
234 234 'Marked repo %s as fork of %s' % (repo.repo_name, repo2.repo_name))
235 235
236 236 assert repo.fork == repo2
237 237 response = response.follow()
238 238 # check if given repo is selected
239 239
240 240 opt = """<option value="%s" selected="selected">%s</option>""" % (
241 241 repo2.repo_id, repo2.repo_name)
242 242 response.mustcontain(opt)
243 243
244 244 # clean session flash
245 245 #response = self.app.get(url('edit_repo', repo_name=HG_REPO))
246 246
247 247 ## mark it as None
248 248 response = self.app.put(url('repo_as_fork', repo_name=HG_REPO),
249 249 params=dict(
250 250 id_fork_of=None
251 251 ))
252 252 repo = Repository.get_by_repo_name(HG_REPO)
253 253 repo2 = Repository.get_by_repo_name(GIT_REPO)
254 254 self.checkSessionFlash(response,
255 255 'Marked repo %s as fork of %s' % (repo.repo_name, "Nothing"))
256 256 assert repo.fork == None
257 257
258 258 def test_set_fork_of_same_repo(self):
259 259 self.log_user()
260 260 repo = Repository.get_by_repo_name(HG_REPO)
261 261 response = self.app.put(url('repo_as_fork', repo_name=HG_REPO),
262 262 params=dict(
263 263 id_fork_of=repo.repo_id
264 264 ))
265 265 self.checkSessionFlash(response,
266 'An error occurred during this operation') No newline at end of file
266 'An error occurred during this operation')
@@ -1,191 +1,187 b''
1 1 import os
2 2 import unittest
3 3 from rhodecode.tests import *
4 4
5 5 from rhodecode.model.db import User, Notification, UserNotification
6 6 from rhodecode.model.user import UserModel
7 7
8 8 from rhodecode.model.meta import Session
9 9 from rhodecode.model.notification import NotificationModel
10 10
11 11
12 12 class TestNotifications(unittest.TestCase):
13 13
14 14 def __init__(self, methodName='runTest'):
15 15 Session.remove()
16 16 self.u1 = UserModel().create_or_update(username=u'u1',
17 17 password=u'qweqwe',
18 18 email=u'u1@rhodecode.org',
19 19 firstname=u'u1', lastname=u'u1')
20 20 Session().commit()
21 21 self.u1 = self.u1.user_id
22 22
23 23 self.u2 = UserModel().create_or_update(username=u'u2',
24 24 password=u'qweqwe',
25 25 email=u'u2@rhodecode.org',
26 26 firstname=u'u2', lastname=u'u3')
27 27 Session().commit()
28 28 self.u2 = self.u2.user_id
29 29
30 30 self.u3 = UserModel().create_or_update(username=u'u3',
31 31 password=u'qweqwe',
32 32 email=u'u3@rhodecode.org',
33 33 firstname=u'u3', lastname=u'u3')
34 34 Session().commit()
35 35 self.u3 = self.u3.user_id
36 36
37 37 super(TestNotifications, self).__init__(methodName=methodName)
38 38
39 39 def _clean_notifications(self):
40 40 for n in Notification.query().all():
41 41 Session().delete(n)
42 42
43 43 Session().commit()
44 44 self.assertEqual(Notification.query().all(), [])
45 45
46 46 def tearDown(self):
47 47 self._clean_notifications()
48 48
49 49 def test_create_notification(self):
50 50 self.assertEqual([], Notification.query().all())
51 51 self.assertEqual([], UserNotification.query().all())
52 52
53 53 usrs = [self.u1, self.u2]
54 54 notification = NotificationModel().create(created_by=self.u1,
55 55 subject=u'subj', body=u'hi there',
56 56 recipients=usrs)
57 57 Session().commit()
58 58 u1 = User.get(self.u1)
59 59 u2 = User.get(self.u2)
60 60 u3 = User.get(self.u3)
61 61 notifications = Notification.query().all()
62 62 self.assertEqual(len(notifications), 1)
63 63
64 64 self.assertEqual(notifications[0].recipients, [u1, u2])
65 65 self.assertEqual(notification.notification_id,
66 66 notifications[0].notification_id)
67 67
68 68 unotification = UserNotification.query()\
69 69 .filter(UserNotification.notification == notification).all()
70 70
71 71 self.assertEqual(len(unotification), len(usrs))
72 72 self.assertEqual(set([x.user.user_id for x in unotification]),
73 73 set(usrs))
74 74
75 75 def test_user_notifications(self):
76 76 self.assertEqual([], Notification.query().all())
77 77 self.assertEqual([], UserNotification.query().all())
78 78
79 79 notification1 = NotificationModel().create(created_by=self.u1,
80 80 subject=u'subj', body=u'hi there1',
81 81 recipients=[self.u3])
82 82 Session().commit()
83 83 notification2 = NotificationModel().create(created_by=self.u1,
84 84 subject=u'subj', body=u'hi there2',
85 85 recipients=[self.u3])
86 86 Session().commit()
87 87 u3 = Session().query(User).get(self.u3)
88 88
89 89 self.assertEqual(sorted([x.notification for x in u3.notifications]),
90 90 sorted([notification2, notification1]))
91 91
92 92 def test_delete_notifications(self):
93 93 self.assertEqual([], Notification.query().all())
94 94 self.assertEqual([], UserNotification.query().all())
95 95
96 96 notification = NotificationModel().create(created_by=self.u1,
97 97 subject=u'title', body=u'hi there3',
98 98 recipients=[self.u3, self.u1, self.u2])
99 99 Session().commit()
100 100 notifications = Notification.query().all()
101 101 self.assertTrue(notification in notifications)
102 102
103 103 Notification.delete(notification.notification_id)
104 104 Session().commit()
105 105
106 106 notifications = Notification.query().all()
107 107 self.assertFalse(notification in notifications)
108 108
109 109 un = UserNotification.query().filter(UserNotification.notification
110 110 == notification).all()
111 111 self.assertEqual(un, [])
112 112
113 113 def test_delete_association(self):
114 114
115 115 self.assertEqual([], Notification.query().all())
116 116 self.assertEqual([], UserNotification.query().all())
117 117
118 118 notification = NotificationModel().create(created_by=self.u1,
119 119 subject=u'title', body=u'hi there3',
120 120 recipients=[self.u3, self.u1, self.u2])
121 121 Session().commit()
122 122
123 123 unotification = UserNotification.query()\
124 124 .filter(UserNotification.notification ==
125 125 notification)\
126 126 .filter(UserNotification.user_id == self.u3)\
127 127 .scalar()
128 128
129 129 self.assertEqual(unotification.user_id, self.u3)
130 130
131 131 NotificationModel().delete(self.u3,
132 132 notification.notification_id)
133 133 Session().commit()
134 134
135 135 u3notification = UserNotification.query()\
136 136 .filter(UserNotification.notification ==
137 137 notification)\
138 138 .filter(UserNotification.user_id == self.u3)\
139 139 .scalar()
140 140
141 141 self.assertEqual(u3notification, None)
142 142
143 143 # notification object is still there
144 144 self.assertEqual(Notification.query().all(), [notification])
145 145
146 146 #u1 and u2 still have assignments
147 147 u1notification = UserNotification.query()\
148 148 .filter(UserNotification.notification ==
149 149 notification)\
150 150 .filter(UserNotification.user_id == self.u1)\
151 151 .scalar()
152 152 self.assertNotEqual(u1notification, None)
153 153 u2notification = UserNotification.query()\
154 154 .filter(UserNotification.notification ==
155 155 notification)\
156 156 .filter(UserNotification.user_id == self.u2)\
157 157 .scalar()
158 158 self.assertNotEqual(u2notification, None)
159 159
160 160 def test_notification_counter(self):
161 161 self._clean_notifications()
162 162 self.assertEqual([], Notification.query().all())
163 163 self.assertEqual([], UserNotification.query().all())
164 164
165 165 NotificationModel().create(created_by=self.u1,
166 166 subject=u'title', body=u'hi there_delete',
167 167 recipients=[self.u3, self.u1])
168 168 Session().commit()
169 169
170 170 self.assertEqual(NotificationModel()
171 171 .get_unread_cnt_for_user(self.u1), 1)
172 172 self.assertEqual(NotificationModel()
173 173 .get_unread_cnt_for_user(self.u2), 0)
174 174 self.assertEqual(NotificationModel()
175 175 .get_unread_cnt_for_user(self.u3), 1)
176 176
177 177 notification = NotificationModel().create(created_by=self.u1,
178 178 subject=u'title', body=u'hi there3',
179 179 recipients=[self.u3, self.u1, self.u2])
180 180 Session().commit()
181 181
182 182 self.assertEqual(NotificationModel()
183 183 .get_unread_cnt_for_user(self.u1), 2)
184 184 self.assertEqual(NotificationModel()
185 185 .get_unread_cnt_for_user(self.u2), 1)
186 186 self.assertEqual(NotificationModel()
187 187 .get_unread_cnt_for_user(self.u3), 2)
188
189
190
191
@@ -1,170 +1,170 b''
1 1 import os
2 2 import unittest
3 3 from rhodecode.tests import *
4 4
5 5 from rhodecode.model.repos_group import ReposGroupModel
6 6 from rhodecode.model.repo import RepoModel
7 7 from rhodecode.model.db import RepoGroup, User
8 8 from rhodecode.model.meta import Session
9 9 from sqlalchemy.exc import IntegrityError
10 10
11 11
12 12 def _make_group(path, desc='desc', parent_id=None,
13 13 skip_if_exists=False):
14 14
15 15 gr = RepoGroup.get_by_group_name(path)
16 16 if gr and skip_if_exists:
17 17 return gr
18 18
19 19 gr = ReposGroupModel().create(path, desc, parent_id)
20 20 return gr
21 21
22 22
23 23 class TestReposGroups(unittest.TestCase):
24 24
25 25 def setUp(self):
26 26 self.g1 = _make_group('test1', skip_if_exists=True)
27 27 Session().commit()
28 28 self.g2 = _make_group('test2', skip_if_exists=True)
29 29 Session().commit()
30 30 self.g3 = _make_group('test3', skip_if_exists=True)
31 31 Session().commit()
32 32
33 33 def tearDown(self):
34 34 print 'out'
35 35
36 36 def __check_path(self, *path):
37 37 """
38 38 Checks the path for existance !
39 39 """
40 40 path = [TESTS_TMP_PATH] + list(path)
41 41 path = os.path.join(*path)
42 42 return os.path.isdir(path)
43 43
44 44 def _check_folders(self):
45 45 print os.listdir(TESTS_TMP_PATH)
46 46
47 47 def __delete_group(self, id_):
48 48 ReposGroupModel().delete(id_)
49 49
50 50 def __update_group(self, id_, path, desc='desc', parent_id=None):
51 51 form_data = dict(
52 52 group_name=path,
53 53 group_description=desc,
54 54 group_parent_id=parent_id,
55 55 perms_updates=[],
56 56 perms_new=[]
57 57 )
58 58 gr = ReposGroupModel().update(id_, form_data)
59 59 return gr
60 60
61 61 def test_create_group(self):
62 62 g = _make_group('newGroup')
63 63 self.assertEqual(g.full_path, 'newGroup')
64 64
65 65 self.assertTrue(self.__check_path('newGroup'))
66 66
67 67 def test_create_same_name_group(self):
68 68 self.assertRaises(IntegrityError, lambda: _make_group('newGroup'))
69 69 Session().rollback()
70 70
71 71 def test_same_subgroup(self):
72 72 sg1 = _make_group('sub1', parent_id=self.g1.group_id)
73 73 self.assertEqual(sg1.parent_group, self.g1)
74 74 self.assertEqual(sg1.full_path, 'test1/sub1')
75 75 self.assertTrue(self.__check_path('test1', 'sub1'))
76 76
77 77 ssg1 = _make_group('subsub1', parent_id=sg1.group_id)
78 78 self.assertEqual(ssg1.parent_group, sg1)
79 79 self.assertEqual(ssg1.full_path, 'test1/sub1/subsub1')
80 80 self.assertTrue(self.__check_path('test1', 'sub1', 'subsub1'))
81 81
82 82 def test_remove_group(self):
83 83 sg1 = _make_group('deleteme')
84 84 self.__delete_group(sg1.group_id)
85 85
86 86 self.assertEqual(RepoGroup.get(sg1.group_id), None)
87 87 self.assertFalse(self.__check_path('deteteme'))
88 88
89 89 sg1 = _make_group('deleteme', parent_id=self.g1.group_id)
90 90 self.__delete_group(sg1.group_id)
91 91
92 92 self.assertEqual(RepoGroup.get(sg1.group_id), None)
93 93 self.assertFalse(self.__check_path('test1', 'deteteme'))
94 94
95 95 def test_rename_single_group(self):
96 96 sg1 = _make_group('initial')
97 97
98 98 new_sg1 = self.__update_group(sg1.group_id, 'after')
99 99 self.assertTrue(self.__check_path('after'))
100 100 self.assertEqual(RepoGroup.get_by_group_name('initial'), None)
101 101
102 102 def test_update_group_parent(self):
103 103
104 104 sg1 = _make_group('initial', parent_id=self.g1.group_id)
105 105
106 106 new_sg1 = self.__update_group(sg1.group_id, 'after', parent_id=self.g1.group_id)
107 107 self.assertTrue(self.__check_path('test1', 'after'))
108 108 self.assertEqual(RepoGroup.get_by_group_name('test1/initial'), None)
109 109
110 110 new_sg1 = self.__update_group(sg1.group_id, 'after', parent_id=self.g3.group_id)
111 111 self.assertTrue(self.__check_path('test3', 'after'))
112 112 self.assertEqual(RepoGroup.get_by_group_name('test3/initial'), None)
113 113
114 114 new_sg1 = self.__update_group(sg1.group_id, 'hello')
115 115 self.assertTrue(self.__check_path('hello'))
116 116
117 117 self.assertEqual(RepoGroup.get_by_group_name('hello'), new_sg1)
118 118
119 119 def test_subgrouping_with_repo(self):
120 120
121 121 g1 = _make_group('g1')
122 122 g2 = _make_group('g2')
123 123
124 124 # create new repo
125 125 form_data = dict(repo_name='john',
126 126 repo_name_full='john',
127 127 fork_name=None,
128 128 description=None,
129 129 repo_group=None,
130 130 private=False,
131 131 repo_type='hg',
132 132 clone_uri=None,
133 133 landing_rev='tip')
134 134 cur_user = User.get_by_username(TEST_USER_ADMIN_LOGIN)
135 135 r = RepoModel().create(form_data, cur_user)
136 136
137 137 self.assertEqual(r.repo_name, 'john')
138 138
139 139 # put repo into group
140 140 form_data = form_data
141 141 form_data['repo_group'] = g1.group_id
142 142 form_data['perms_new'] = []
143 143 form_data['perms_updates'] = []
144 144 RepoModel().update(r.repo_name, form_data)
145 145 self.assertEqual(r.repo_name, 'g1/john')
146 146
147 147 self.__update_group(g1.group_id, 'g1', parent_id=g2.group_id)
148 148 self.assertTrue(self.__check_path('g2', 'g1'))
149 149
150 150 # test repo
151 151 self.assertEqual(r.repo_name, RepoGroup.url_sep().join(['g2', 'g1',
152 152 r.just_name]))
153 153
154 154 def test_move_to_root(self):
155 155 g1 = _make_group('t11')
156 156 Session().commit()
157 157 g2 = _make_group('t22', parent_id=g1.group_id)
158 158 Session().commit()
159 159
160 160 self.assertEqual(g2.full_path, 't11/t22')
161 161 self.assertTrue(self.__check_path('t11', 't22'))
162 162
163 163 g2 = self.__update_group(g2.group_id, 'g22', parent_id=None)
164 164 Session().commit()
165 165
166 166 self.assertEqual(g2.group_name, 'g22')
167 167 # we moved out group from t1 to '' so it's full path should be 'g2'
168 168 self.assertEqual(g2.full_path, 'g22')
169 169 self.assertFalse(self.__check_path('t11', 't22'))
170 self.assertTrue(self.__check_path('g22')) No newline at end of file
170 self.assertTrue(self.__check_path('g22'))
General Comments 0
You need to be logged in to leave comments. Login now