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