##// END OF EJS Templates
autocomplete for repository managment of users
marcink -
r298:15e96b5a default
parent child Browse files
Show More
@@ -1,181 +1,184 b''
1 1 #!/usr/bin/env python
2 2 # encoding: utf-8
3 3 # repos controller for pylons
4 4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 5 # This program is free software; you can redistribute it and/or
6 6 # modify it under the terms of the GNU General Public License
7 7 # as published by the Free Software Foundation; version 2
8 8 # of the License or (at your opinion) any later version of the license.
9 9 #
10 10 # This program is distributed in the hope that it will be useful,
11 11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 13 # GNU General Public License for more details.
14 14 #
15 15 # You should have received a copy of the GNU General Public License
16 16 # along with this program; if not, write to the Free Software
17 17 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18 18 # MA 02110-1301, USA.
19 19 """
20 20 Created on April 7, 2010
21 21 admin controller for pylons
22 22 @author: marcink
23 23 """
24 24 from operator import itemgetter
25 25 from pylons import request, response, session, tmpl_context as c, url, \
26 26 app_globals as g
27 27 from pylons.controllers.util import abort, redirect
28 28 from pylons.i18n.translation import _
29 29 from pylons_app.lib import helpers as h
30 30 from pylons_app.lib.auth import LoginRequired
31 31 from pylons_app.lib.base import BaseController, render
32 32 from pylons_app.lib.utils import invalidate_cache
33 33 from pylons_app.model.repo_model import RepoModel
34 34 from pylons_app.model.hg_model import HgModel
35 35 from pylons_app.model.forms import RepoForm
36 36 from pylons_app.model.meta import Session
37 37 from datetime import datetime
38 38 import formencode
39 39 from formencode import htmlfill
40 40 import logging
41 41 import os
42 42 import shutil
43 43 log = logging.getLogger(__name__)
44 44
45 45 class ReposController(BaseController):
46 46 """REST Controller styled on the Atom Publishing Protocol"""
47 47 # To properly map this controller, ensure your config/routing.py
48 48 # file has a resource setup:
49 49 # map.resource('repo', 'repos')
50 50 @LoginRequired()
51 51 def __before__(self):
52 52 c.admin_user = session.get('admin_user')
53 53 c.admin_username = session.get('admin_username')
54 54 super(ReposController, self).__before__()
55 55
56 56 def index(self, format='html'):
57 57 """GET /repos: All items in the collection"""
58 58 # url('repos')
59 59 cached_repo_list = HgModel().get_repos()
60 60 c.repos_list = sorted(cached_repo_list, key=itemgetter('name_sort'))
61 61 return render('admin/repos/repos.html')
62 62
63 63 def create(self):
64 64 """POST /repos: Create a new item"""
65 65 # url('repos')
66 66 repo_model = RepoModel()
67 67 _form = RepoForm()()
68 68 try:
69 69 form_result = _form.to_python(dict(request.POST))
70 70 repo_model.create(form_result, c.hg_app_user)
71 71 invalidate_cache('cached_repo_list')
72 72 h.flash(_('created repository %s') % form_result['repo_name'],
73 73 category='success')
74 74
75 75 except formencode.Invalid as errors:
76 76 c.form_errors = errors.error_dict
77 77 c.new_repo = errors.value['repo_name']
78 78 return htmlfill.render(
79 79 render('admin/repos/repo_add.html'),
80 80 defaults=errors.value,
81 81 encoding="UTF-8")
82 82
83 83 except Exception:
84 84 h.flash(_('error occured during creation of repository %s') \
85 85 % form_result['repo_name'], category='error')
86 86
87 87 return redirect('repos')
88 88
89 89 def new(self, format='html'):
90 90 """GET /repos/new: Form to create a new item"""
91 91 new_repo = request.GET.get('repo', '')
92 92 c.new_repo = h.repo_name_slug(new_repo)
93 93
94 94 return render('admin/repos/repo_add.html')
95 95
96 96 def update(self, id):
97 97 """PUT /repos/id: Update an existing item"""
98 98 # Forms posted to this method should contain a hidden field:
99 99 # <input type="hidden" name="_method" value="PUT" />
100 100 # Or using helpers:
101 101 # h.form(url('repo', id=ID),
102 102 # method='put')
103 103 # url('repo', id=ID)
104 104 repo_model = RepoModel()
105 105 _form = RepoForm(edit=True)()
106 106 try:
107 107 form_result = _form.to_python(dict(request.POST))
108 108 repo_model.update(id, form_result)
109 109 invalidate_cache('cached_repo_list')
110 110 h.flash(_('Repository %s updated succesfully' % id), category='success')
111 111
112 112 except formencode.Invalid as errors:
113 113 c.repo_info = repo_model.get(id)
114 c.users_array = repo_model.get_users_js()
114 115 errors.value.update({'user':c.repo_info.user.username})
115 116 c.form_errors = errors.error_dict
116 117 return htmlfill.render(
117 118 render('admin/repos/repo_edit.html'),
118 119 defaults=errors.value,
119 120 encoding="UTF-8")
120 121 except Exception:
121 122 h.flash(_('error occured during update of repository %s') \
122 123 % form_result['repo_name'], category='error')
123 124 return redirect(url('repos'))
124 125
125 126 def delete(self, id):
126 127 """DELETE /repos/id: Delete an existing item"""
127 128 # Forms posted to this method should contain a hidden field:
128 129 # <input type="hidden" name="_method" value="DELETE" />
129 130 # Or using helpers:
130 131 # h.form(url('repo', id=ID),
131 132 # method='delete')
132 133 # url('repo', id=ID)
133 134
134 135 repo_model = RepoModel()
135 136 repo = repo_model.get(id)
136 137 if not repo:
137 138 h.flash(_('%s repository is not mapped to db perhaps'
138 139 ' it was moved or renamed from the filesystem'
139 140 ' please run the application again'
140 141 ' in order to rescan repositories') % id, category='error')
141 142
142 143 return redirect(url('repos'))
143 144 try:
144 145 repo_model.delete(repo)
145 146 invalidate_cache('cached_repo_list')
146 147 h.flash(_('deleted repository %s') % id, category='success')
147 148 except Exception:
148 149 h.flash(_('An error occured during deletion of %s') % id,
149 150 category='error')
150 151
151 152 return redirect(url('repos'))
152 153
153 154 def show(self, id, format='html'):
154 155 """GET /repos/id: Show a specific item"""
155 156 # url('repo', id=ID)
156 157
157 158 def edit(self, id, format='html'):
158 159 """GET /repos/id/edit: Form to edit an existing item"""
159 160 # url('edit_repo', id=ID)
160 161 repo_model = RepoModel()
161 162 c.repo_info = repo = repo_model.get(id)
162 163 if not repo:
163 164 h.flash(_('%s repository is not mapped to db perhaps'
164 165 ' it was created or renamed from the filesystem'
165 166 ' please run the application again'
166 167 ' in order to rescan repositories') % id, category='error')
167 168
168 169 return redirect(url('repos'))
169 170 defaults = c.repo_info.__dict__
170 171 defaults.update({'user':c.repo_info.user.username})
171 172
173 c.users_array = repo_model.get_users_js()
174
172 175 for p in c.repo_info.repo2perm:
173 176 defaults.update({'perm_%s' % p.user.username:
174 177 p.permission.permission_name})
175 178
176 179 return htmlfill.render(
177 180 render('admin/repos/repo_edit.html'),
178 181 defaults=defaults,
179 182 encoding="UTF-8",
180 183 force_defaults=False
181 184 )
@@ -1,145 +1,154 b''
1 1 #!/usr/bin/env python
2 2 # encoding: utf-8
3 3 # model for handling repositories actions
4 4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 5 # This program is free software; you can redistribute it and/or
6 6 # modify it under the terms of the GNU General Public License
7 7 # as published by the Free Software Foundation; version 2
8 8 # of the License or (at your opinion) any later version of the license.
9 9 #
10 10 # This program is distributed in the hope that it will be useful,
11 11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 13 # GNU General Public License for more details.
14 14 #
15 15 # You should have received a copy of the GNU General Public License
16 16 # along with this program; if not, write to the Free Software
17 17 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18 18 # MA 02110-1301, USA.
19 19
20 20 """
21 21 Created on Jun 5, 2010
22 22 model for handling repositories actions
23 23 @author: marcink
24 24 """
25 25 from pylons_app.model.meta import Session
26 26 from pylons_app.model.db import Repository, Repo2Perm, User, Permission
27 27 import shutil
28 28 import os
29 29 from datetime import datetime
30 30 from pylons_app.lib.utils import check_repo
31 31 from pylons import app_globals as g
32 32 import traceback
33 33 import logging
34 34 log = logging.getLogger(__name__)
35 35
36 36 class RepoModel(object):
37 37
38 38 def __init__(self):
39 39 self.sa = Session()
40 40
41 41 def get(self, id):
42 42 return self.sa.query(Repository).get(id)
43 43
44 def get_users_js(self):
45
46 users = self.sa.query(User).all()
47 u_tmpl = '''{id:%s, fname:"%s", lname:"%s", nname:"%s"},'''
48 users_array = '[%s];' % '\n'.join([u_tmpl % (u.user_id, u.name,
49 u.lastname, u.username)
50 for u in users])
51 return users_array
52
44 53
45 54 def update(self, repo_id, form_data):
46 55 try:
47 56 if repo_id != form_data['repo_name']:
48 57 self.__rename_repo(repo_id, form_data['repo_name'])
49 58 cur_repo = self.sa.query(Repository).get(repo_id)
50 59 for k, v in form_data.items():
51 60 if k == 'user':
52 61 cur_repo.user_id = v
53 62 else:
54 63 setattr(cur_repo, k, v)
55 64
56 65 #update permissions
57 66 for username, perm in form_data['perms_updates']:
58 67 r2p = self.sa.query(Repo2Perm)\
59 68 .filter(Repo2Perm.user == self.sa.query(User)\
60 69 .filter(User.username == username).one())\
61 70 .filter(Repo2Perm.repository == repo_id).one()
62 71
63 72 r2p.permission_id = self.sa.query(Permission).filter(
64 73 Permission.permission_name ==
65 74 perm).one().permission_id
66 75 self.sa.add(r2p)
67 76
68 77 for username, perm in form_data['perms_new']:
69 78 r2p = Repo2Perm()
70 79 r2p.repository = repo_id
71 80 r2p.user = self.sa.query(User)\
72 81 .filter(User.username == username).one()
73 82
74 83 r2p.permission_id = self.sa.query(Permission).filter(
75 84 Permission.permission_name ==
76 85 perm).one().permission_id
77 86 self.sa.add(r2p)
78 87
79 88 self.sa.add(cur_repo)
80 89 self.sa.commit()
81 90 except:
82 91 log.error(traceback.format_exc())
83 92 self.sa.rollback()
84 93 raise
85 94
86 95 def create(self, form_data, cur_user, just_db=False):
87 96 try:
88 97 repo_name = form_data['repo_name']
89 98 new_repo = Repository()
90 99 for k, v in form_data.items():
91 100 setattr(new_repo, k, v)
92 101
93 102 new_repo.user_id = cur_user.user_id
94 103 self.sa.add(new_repo)
95 104
96 105 #create default permission
97 106 repo2perm = Repo2Perm()
98 107 repo2perm.permission_id = self.sa.query(Permission)\
99 108 .filter(Permission.permission_name == 'repository.read')\
100 109 .one().permission_id
101 110
102 111 repo2perm.repository = repo_name
103 112 repo2perm.user_id = self.sa.query(User)\
104 113 .filter(User.username == 'default').one().user_id
105 114
106 115 self.sa.add(repo2perm)
107 116 self.sa.commit()
108 117 if not just_db:
109 118 self.__create_repo(repo_name)
110 119 except:
111 120 log.error(traceback.format_exc())
112 121 self.sa.rollback()
113 122 raise
114 123
115 124 def delete(self, repo):
116 125 try:
117 126 self.sa.delete(repo)
118 127 self.sa.commit()
119 128 self.__delete_repo(repo.repo_name)
120 129 except:
121 130 log.error(traceback.format_exc())
122 131 self.sa.rollback()
123 132 raise
124 133
125 134 def __create_repo(self, repo_name):
126 135 repo_path = os.path.join(g.base_path, repo_name)
127 136 if check_repo(repo_name, g.base_path):
128 137 log.info('creating repo %s in %s', repo_name, repo_path)
129 138 from vcs.backends.hg import MercurialRepository
130 139 MercurialRepository(repo_path, create=True)
131 140
132 141 def __rename_repo(self, old, new):
133 142 log.info('renaming repoo from %s to %s', old, new)
134 143 old_path = os.path.join(g.base_path, old)
135 144 new_path = os.path.join(g.base_path, new)
136 145 shutil.move(old_path, new_path)
137 146
138 147 def __delete_repo(self, name):
139 148 rm_path = os.path.join(g.base_path, name)
140 149 log.info("Removing %s", rm_path)
141 150 #disable hg
142 151 shutil.move(os.path.join(rm_path, '.hg'), os.path.join(rm_path, 'rm__.hg'))
143 152 #disable repo
144 153 shutil.move(rm_path, os.path.join(g.base_path, 'rm__%s__%s' \
145 154 % (datetime.today(), name)))
@@ -1,838 +1,946 b''
1 1 /*** Initial Settings ***/
2 2 #mainhtml{
3 3 margin: 15px 50px;
4 4 background: #DBD4C6;
5 5 font-family: sans-serif;
6 6 }
7
7 8 #mainhtml .breadcrumbs a:HOVER{
8 9 text-decoration: underline;
9 10 }
11
10 12 a {
11 13 color: #556CB5;
12 14 text-decoration: none;
13 15 }
16
14 17 a:HOVER{
15 18 text-decoration: underline;
16 19 }
17 20
18 21 /*** end of Initial Settings ***/
19 22
20 23 /*** ***/
21 24 .table_disp {
22 25 border-left: 0px solid #666666;
23 26 border-bottom: 1px solid #666666;
24 27 border-right: 1px solid #666666;
25 28 padding: 0px;
26 29 margin: 0px;
27 30 border-spacing: 0px;
28 31 }
29 32
30 33 .table_disp .header {
31 34 border-top: 1px solid #666666;
32 35 background-color: #556CB5;
33 36 font-weight: bold;
34 37 color: white;
35 38 vertical-align: middle;
36 39 padding: 3px 5px;
37 40 text-align: left;
38 41 font-size: 0.9em;
39 42 }
40 43
41 44 .table_disp .header td {
42 45 padding: 4px;
43 46 vertical-align: middle;
44 47 border-top: 1px solid #AAAAAA;
45 48 border-bottom: 2px solid #666666;
46 49 }
50
47 51 .table_disp td {
48 52 border-left: 1px solid #AAAAAA;
49 53 padding-left: 4px;
50 54 padding-right: 4px;
51 55 }
52 56
53 57 table tr.parity0:hover,table tr.parity1:hover {
54 58 background: #D5E1E6;
55 59 }
56 60
57 61 table tr.parity0 {
58 62 background: #EAEAE9;
59 63 }
60 64
61 65 table tr.parity1 {
62 66 background: #FFFFFF;
63 67 }
64 68
65
66 69 /*** ***/
67 70
68 /** common settings **/
71 /** COMMON SETTINGS **/
69 72 .add_icon{
70 73 background: url("/images/icons/add.png") no-repeat scroll 3px;
71 74 height: 16px;
72 75 padding-left: 20px;
73 76 padding-top: 0px;
74 77 text-align: left;
78 }
75 79
76 }
77 80 .edit_icon{
78 81 background: url("/images/icons/folder_edit.png") no-repeat scroll 3px;
79 82 height: 16px;
80 83 padding-left: 20px;
81 84 padding-top: 0px;
82 85 text-align: left;
83 86 }
84 87
85 88 .delete_icon{
86 89 background: url("/images/icons/delete.png") no-repeat scroll 3px;
87 90 height: 16px;
88 91 padding-left: 20px;
89 92 padding-top: 0px;
90 93 text-align: left;
91
92 94 }
93 95
94 96 .action_button{
95 97 border:0px;
96 98 display: block;
97 99 }
100
98 101 .action_button:hover{
99 102 border:0px;
100 103 font-style:italic;
101 104 cursor: pointer;
102 105 }
103 106
104 107 .flash_msg ul{
105 108 margin:0;
106 109 padding:25px 0px 0px 0px;
110 }
107 111
108 }
109 112 .error_msg {
110 113 background-color:#FFCFCF;
111 114 background-image: url("/images/icons/error_msg.png");
112 115 border:1px solid #FF9595;
113 116 color:#CC3300;
114 117 }
118
115 119 .warning_msg {
116 120 background-color:#FFFBCC;
117 121 background-image: url("/images/icons/warning_msg.png");
118 122 border:1px solid #FFF35E;
119 123 color:#C69E00;
120 124 }
125
121 126 .success_msg {
122 127 background-color:#D5FFCF;
123 128 background-image: url("/images/icons/success_msg.png");
124 129 border:1px solid #97FF88;
125 130 color:#009900;
126 131 }
132
127 133 .notice_msg {
128 134 background-color:#DCE3FF;
129 135 background-image: url("/images/icons/notice_msg.png");
130 136 border:1px solid #93A8FF;
131 137 color:#556CB5;
132 138 }
133 139
134 140 .success_msg, .error_msg, .notice_msg, .warning_msg{
135 141 background-position:10px center;
136 142 background-repeat:no-repeat;
137 143 font-size:12px;
138 144 font-weight:bold;
139 145 min-height:14px;
140 146 line-height:14px;
141 147 margin-bottom:0px;
142 148 margin-top:0px;
143 149 padding:3px 10px 3px 40px;
144 150 display:block;
145 151 overflow: auto;
146 152 }
147 153
148 154 #msg_close {
149 background:transparent url("icons/cross_grey_small.png") no-repeat scroll 0 0;
155 background: transparent url("icons/cross_grey_small.png") no-repeat
156 scroll 0 0;
150 157 cursor:pointer;
151 158 height:16px;
152 159 position:absolute;
153 160 right:5px;
154 161 top:5px;
155 162 width:16px;
156 163 }
157 164
158 165 .error-message{
159 166 color:#CC3300;
160 167 }
161 /**** Tooltip ****/
162 .yui-overlay,
163 .yui-panel-container {
168
169 /**** TOOLTIP ****/
170 .yui-overlay,.yui-panel-container {
164 171 visibility:hidden;
165 172 position:absolute;
166 173 z-index: 2;
167 174 }
168 175
169 176 .yui-tt {
170 177 visibility:hidden;
171 178 position:absolute;
172 179 color:#666666;
173 180 background-color:#FFFFFF;
174 181 font-family:arial,helvetica,verdana,sans-serif;
175 182 padding:8px;
176 183 border:2px solid #556CB5;
177 184 font:100% sans-serif;
178 185 width:auto;
179 186 opacity:1.0;
180 187 }
181 188
182 189 .yui-tt-shadow {
183 190 display: none;
184 191 }
185 /** end of Tooltip **/
192
193 /** END TOOLTIP **/
194
195 /** AUTOCOMPLETE **/
196
197 .ac{
198 vertical-align: top;
199
200 }
201 .ac .match {
202 font-weight:bold;
203 }
204
205 .ac .yui-ac {
206 position: relative;
207 font-family: arial;
208 font-size: 100%;
209 }
210
211 .ac #perm_ac{
212 width:15em;
213 }
214 /* styles for input field */
215 .ac .yui-ac-input {
216 position: absolute;
217 width: 100%;
218 }
219
220 /* styles for results container */
221 .ac .yui-ac-container {
222 position: absolute;
223 top: 1.6em;
224 width: 100%;
225 }
186 226
227 /* styles for header/body/footer wrapper within container */
228 .ac .yui-ac-content {
229 position: absolute;
230 width: 100%;
231 border: 1px solid #808080;
232 background: #fff;
233 overflow: hidden;
234 z-index: 9050;
235 }
187 236
237 /* styles for container shadow */
238 .ac .yui-ac-shadow {
239 position: absolute;
240 margin: .3em;
241 width: 100%;
242 background: #000;
243 -moz-opacity: 0.10;
244 opacity: .10;
245 filter: alpha(opacity = 10);
246 z-index: 9049;
247 }
248
249 /* styles for results list */
250 .ac .yui-ac-content ul {
251 margin: 0;
252 padding: 0;
253 width: 100%;
254 }
255
256 /* styles for result item */
257 .ac .yui-ac-content li {
258 margin: 0;
259 padding: 2px 5px;
260 cursor: default;
261 white-space: nowrap;
262 }
263
264 /* styles for prehighlighted result item */
265 .ac .yui-ac-content li.yui-ac-prehighlight {
266 background: #B3D4FF;
267 }
268
269 /* styles for highlighted result item */
270 .ac .yui-ac-content li.yui-ac-highlight {
271 background: #556CB5;
272 color: #FFF;
273 }
274
275 /** END AUTOCOMPLETE **/
188 276 div#main {
189 277 padding: 5px;
190 278 }
191 279
192 280 div#container {
193 281 background: #FFFFFF;
194 282 position: relative;
195 283 color: #666;
196 284 }
197 285
198 286 div.page-header {
199 287 padding: 50px 20px 0;
200 288 background: #556cb5 top left repeat-x;
201 289 position: relative;
202 290 }
203 291
204 292 div.page-header h1 {
205 293 margin: 10px 0 30px;
206 294 font-size: 1.8em;
207 295 font-weight: bold;
208 296 font-family: sans-serif;
209 297 letter-spacing: 1px;
210 298 color: #FFFFFF;
211 299 }
212 300
213 301 div.page-header h1 a {
214 302 font-weight: bold;
215 303 color: #FFFFFF;
216 304 }
217 305
218 306 div.page-header a {
219 307 text-decoration: none;
220 308 }
221 309
222 310 div.page-header form {
223 311 position: absolute;
224 312 margin-bottom: 2px;
225 313 bottom: 0;
226 314 right: 20px;
227 315 }
228 316
229 317 div.page-header form label {
230 318 color: #DDD;
231 319 }
232 320
233 321 div.page-header form input {
234 322 padding: 2px;
235 323 border: solid 1px #DDD;
236 324 }
237 325
238 326 div.page-header form dl {
239 327 overflow: hidden;
240 328 }
241 329
242 330 div.page-header form dl dt {
243 331 font-size: 1.2em;
244 332 }
245 333
246 334 div.page-header form dl dt,div.page-header form dl dd {
247 335 margin: 0 0 0 5px;
248 336 float: left;
249 337 height: 24px;
250 338 line-height: 20px;
251 339 }
252 340
253 341 ul.page-nav {
254 342 margin: 10px 0 0 0;
255 343 list-style-type: none;
256 344 overflow: hidden;
257 345 width: 800px;
258 346 padding: 0;
259 347 }
260 348
261 349 ul.page-nav li {
262 350 margin: 0 4px 0 0;
263 351 float: left;
264 352 height: 24px;
265 353 font-size: 1.1em;
266 354 line-height: 24px;
267 355 text-align: center;
268 356 background: #556CB5;
269 357 }
270 358
271 359 ul.page-nav li.current {
272 360 background: #FFF;
273 361 padding-right: 5px;
274 362 padding-left: 5px;
275 363 }
364
276 365 ul.page-nav li.current a {
277 366 color: #556CB5;
278 367 }
368
279 369 ul.page-nav li a {
280 370 height: 24px;
281 371 color: #FFF;
282 372 padding-right: 5px;
283 373 padding-left: 5px;
284 374 display: block;
285 375 text-decoration: none;
286 376 font-weight: bold;
287 377 }
378
288 379 ul.page-nav li.logout a {
289 380 color: #FDAC9D;
290 381 }
382
291 383 ul.page-nav li a:hover {
292 384 background: #FFF;
293 385 color: #556CB5;
294 386 }
295 387
296 388 ul.submenu {
297 389 margin: 5px 0px -20px 0px;
298 390 list-style-type: none;
299 391 }
300 392
301 393 ul.submenu li {
302 394 margin: 0 10px 0 0;
303 395 font-size: 0.9em;
304 396 font-weight:bold;
305 397 display: inline;
306 398 }
399
307 400 ul.submenu .repos {
308 401 background: url("/images/icons/folder_edit.png") no-repeat scroll 3px;
309 402 height: 16px;
310 403 padding-left: 20px;
311 404 padding-top: 0px;
312 405 text-align: left;
406 }
313 407
314 }
315 408 ul.submenu .users {
316 409 background: url("/images/icons/user_edit.png") no-repeat scroll 3px;
317 410 height: 16px;
318 411 padding-left: 20px;
319 412 padding-top: 0px;
320 413 text-align: left;
321 414 }
415
322 416 ul.submenu .permissions {
323 417 background: url("/images/icons/folder_key.png") no-repeat scroll 3px;
324 418 height: 16px;
325 419 padding-left: 20px;
326 420 padding-top: 0px;
327 421 text-align: left;
328 422 }
329 423
330 424 ul.submenu .current_submenu {
331 425 border-bottom: 2px solid #556CB5;
332 426 }
333 427
334 428 h2 {
335 429 margin: 20px 0 10px;
336 430 height: 30px;
337 431 line-height: 30px;
338 432 text-indent: 20px;
339 433 background: #FFF;
340 434 font-size: 1.2em;
341 435 border-top: dotted 1px #D5E1E6;
342 436 font-weight: bold;
343 437 color:#556CB5;
344 438 }
345 439
346 440 h2.no-link {
347 441 color: #006699;
348 442 }
349 443
350 444 h2.no-border {
351 445 color: #FFF;
352 446 background: #556CB5;
353 447 border: 0;
354 448 }
355 449
356 450 h2 a {
357 451 font-weight: bold;
358 452 color: #006699;
359 453 }
360 454
361 455 div.page-path {
362 456 text-align: right;
363 457 padding: 20px 30px 10px 0;
364 458 border: solid #d9d8d1;
365 459 border-width: 0px 0px 1px;
366 460 font-size: 1.2em;
367 461 }
368 462
369 463 div.page-footer {
370 464 margin: 50px 0 0;
371 465 position: relative;
372 466 text-align: center;
373 467 font-weight: bold;
374 468 font-size: 90%;
375 469 }
376 470
377 471 div.page-footer p {
378 472 position: relative;
379 473 left: 20px;
380 474 bottom: 5px;
381 475 font-size: 1.2em;
382 476 }
383 477
384 478 ul.rss-logo {
385 479 position: absolute;
386 480 top: -10px;
387 481 right: 20px;
388 482 height: 20px;
389 483 list-style-type: none;
390 484 }
391 485
392 486 ul.rss-logo li {
393 487 display: inline;
394 488 }
395 489
396 490 ul.rss-logo li a {
397 491 padding: 3px 6px;
398 492 line-height: 10px;
399 493 border: 1px solid;
400 494 border-color: #fcc7a5 #7d3302 #3e1a01 #ff954e;
401 495 color: #ffffff;
402 496 background-color: #ff6600;
403 497 font-weight: bold;
404 498 font-family: sans-serif;
405 499 font-size: 10px;
406 500 text-align: center;
407 501 text-decoration: none;
408 502 }
409 503
410 504 div.rss-logo li a:hover {
411 505 background-color: #ee5500;
412 506 }
413 507
414 508 p.normal {
415 509 margin: 20px 0 20px 30px;
416 510 font-size: 1.2em;
417 511 }
418 512
419 513 span.logtags span {
420 514 background-repeat: no-repeat;
421 515 height: 16px;
422 516 padding-left: 20px;
423 517 padding-top: 0px;
424 518 text-align: left;
425 519 font-weight: bold;
426 520 }
427 521
428 522 span.logtags span.tagtag {
429 523 background-image: url("/images/icons/tag_green.png");
430 524 }
431 525
432 526 span.logtags span.branchtag {
433 527 background-image: url("/images/icons/arrow_branch.png");
434 528 color: #628F53;
435 529 }
436 530
437 531 span.logtags span.inbranchtag {
438 532 background-image: url("/images/icons/arrow_branch.png");
439 533 }
440 534
441 535 div.diff pre {
442 536 margin: 10px 0 0 0;
443 537 }
444 538
445 539 div.diff pre span {
446 540 font-family: monospace;
447 541 white-space: pre;
448 542 font-size: 1.2em;
449 543 padding: 3px 0;
450 544 }
451 545
452 546 td.source {
453 547 white-space: pre;
454 548 font-family: monospace;
455 549 margin: 10px 30px 0;
456 550 font-size: 1.2em;
457 551 font-family: monospace;
458 552 }
459 553
460 554 div.source div.parity0,div.source div.parity1 {
461 555 padding: 1px;
462 556 font-size: 1.2em;
463 557 }
464 558
465 559 div.source div.parity0 {
466 560 background: #F1F6F7;
467 561 }
468 562
469 563 div.source div.parity1 {
470 564 background: #FFFFFF;
471 565 }
472 566
473 567 div.parity0:hover,div.parity1:hover {
474 568 background: #D5E1E6;
475 569 }
476 570
477 571 .linenr {
478 572 color: #999;
479 573 text-align: right;
480 574 }
481 575
482 576 .lineno {
483 577 text-align: right;
484 578 }
485 579
486 580 .lineno a {
487 581 color: #999;
488 582 }
489 583
490 584 td.linenr {
491 585 width: 60px;
492 586 }
493 587
494 588 div#powered-by {
495 589 position: absolute;
496 590 width: 75px;
497 591 top: 15px;
498 592 right: 20px;
499 593 font-size: 1.2em;
500 594 }
501 595
502 596 div#powered-by a {
503 597 color: #EEE;
504 598 text-decoration: none;
505 599 }
506 600
507 601 div#powered-by a:hover {
508 602 text-decoration: underline;
509 603 }
510 604
511 605 dl.overview {
512 606 margin: 0 0 0 30px;
513 607 font-size: 1.1em;
514 608 overflow: hidden;
515 609 }
516 610
517 611 dl.overview dt,dl.overview dd {
518 612 margin: 5px 0;
519 613 float: left;
520 614 }
521 615
522 616 dl.overview dt {
523 617 clear: left;
524 618 font-weight: bold;
525 619 width: 150px;
526 620 }
527 621
528 622 #clone_url{
529 623 border: 0px;
530 624 }
531 /** end of summary **/
532 625
533 /** chagelog **/
626 /** end of summary **/ /** chagelog **/
534 627 h3.changelog {
535 628 margin: 20px 0 5px 30px;
536 629 padding: 0 0 2px;
537 630 font-size: 1.4em;
538 631 border-bottom: dotted 1px #D5E1E6;
539 632 }
540 633
541 634 ul.changelog-entry {
542 635 margin: 0 0 10px 30px;
543 636 list-style-type: none;
544 637 position: relative;
545 638 }
546 639
547 640 ul.changelog-entry li span.revdate {
548 641 font-size: 1.1em;
549 642 }
550 643
551 644 ul.changelog-entry li.age {
552 645 position: absolute;
553 646 top: -25px;
554 647 right: 10px;
555 648 font-size: 1.4em;
556 649 color: #CCC;
557 650 font-weight: bold;
558 651 font-style: italic;
559 652 }
560 653
561 654 ul.changelog-entry li span.name {
562 655 font-size: 1.2em;
563 656 font-weight: bold;
564 657 }
565 658
566 659 ul.changelog-entry li.description {
567 660 margin: 10px 0 0;
568 661 font-size: 1.1em;
569 662 }
570 663
571 /** end of changelog **/
572
573 /** file **/
664 /** end of changelog **/ /** file **/
574 665 p.files {
575 666 margin: 0 0 0 20px;
576 667 font-size: 2.0em;
577 668 font-weight: bold;
578 669 }
579 670
580 /** end of file **/
581
582 /** changeset **/
671 /** end of file **/ /** changeset **/
583 672 #changeset_content{
584 673 width:60%;
585 674 float:left;
586 675 }
587 676
588 677 #changeset_content .container .wrapper{
589 678 width: 600px;
590 679 }
680
591 681 #changeset_content .container{
592 682 border:1px solid #CCCCCC;
593 683 height:120px;
594 684 }
595 685
596 686 #changeset_content .container .left{
597 687 float:left;
598 688 width: 70%;
599 689 padding-left: 5px;
600 690 }
601 691
602 692 #changeset_content .container .right{
603 693 float:right;
604 694 width: 25%;
605 695 text-align: right;
606 696 }
607 697
608 698 #changeset_content .container .left .date{
609 699 font-weight:bold;
610 700 }
701
611 702 #changeset_content .container .left .author{
612 703
613 704 }
705
614 706 #changeset_content .container .left .message{
615 707 font-style: italic;
616 708 color: #556CB5;
617 709 }
618 710
619 711 .cs_files{
620 712 width: 60%;
621 713 }
622 714
623 715 .cs_files .cs_added{
624 716 background: url("/images/icons/page_white_add.png") no-repeat scroll 3px;
625 717 /*background-color:#BBFFBB;*/
626 718 height: 16px;
627 719 padding-left: 20px;
628 720 margin-top: 7px;
629 721 text-align: left;
630 722 }
723
631 724 .cs_files .cs_changed{
632 background: url("/images/icons/page_white_edit.png") no-repeat scroll 3px;
725 background: url("/images/icons/page_white_edit.png") no-repeat scroll
726 3px;
633 727 /*background-color: #FFDD88;*/
634 728 height: 16px;
635 729 padding-left: 20px;
636 730 margin-top: 7px;
637 731 text-align: left;
638 732 }
733
639 734 .cs_files .cs_removed{
640 background: url("/images/icons/page_white_delete.png") no-repeat scroll 3px;
735 background: url("/images/icons/page_white_delete.png") no-repeat scroll
736 3px;
641 737 /*background-color: #FF8888;*/
642 738 height: 16px;
643 739 padding-left: 20px;
644 740 margin-top: 7px;
645 741 text-align: left;
646 742 }
647 743
648 /** end of changeset **/
649
650 /** canvas **/
744 /** end of changeset **/ /** canvas **/
651 745 #graph_nodes{
652 746 margin-top:8px;
653 747 }
748
654 749 #graph{
655 750 overflow: hidden;
751 }
656 752
657 }
658 753 #graph_nodes{
659 754 width:160px;
660 755 float:left;
661 756 }
662 757
663 758 #graph_content{
664 759 width:800px;
665 760 float:left;
666 761 }
762
667 763 #graph_content .container_header{
668 764 border:1px solid #CCCCCC;
669 765 height:30px;
670 766 background: #EEEEEE;
671 767 }
672 768
673
674 769 #graph_content .container .wrapper{
675 770 width: 600px;
676 771 }
772
677 773 #graph_content .container{
678 774 border-bottom: 1px solid #CCCCCC;
679 775 border-left: 1px solid #CCCCCC;
680 776 border-right: 1px solid #CCCCCC;
681 777 height:120px;
682 778 }
683 779
684 780 #graph_content .container .left{
685 781 float:left;
686 782 width: 70%;
687 783 padding-left: 5px;
688 784 }
689 785
690 786 #graph_content .container .right{
691 787 float:right;
692 788 width: 25%;
693 789 text-align: right;
694 790 }
791
695 792 #graph_content .container .left .date{
696 793 font-weight:bold;
697 794 }
795
698 796 #graph_content .container .left .author{
699 797
700 798 }
799
701 800 #graph_content .container .left .message{
702 801 font-size: 80%;
703 802 }
704 803
705 804 .right div{
706 805 clear: both;
707 806 }
807
708 808 .right .changes .added,.changed,.removed{
709 809 border:1px solid #DDDDDD;
710 810 display:block;
711 811 float:right;
712 812 font-size:0.75em;
713 813 text-align:center;
714 814 min-width:15px;
715 815 }
816
716 817 .right .changes .added{
717 818 background:#BBFFBB;
718 819 }
820
719 821 .right .changes .changed{
720 822 background: #FFDD88;
721 823 }
824
722 825 .right .changes .removed{
723 826 background: #FF8888;
724 827 }
725 828
726 829 .right .merge{
727 830 vertical-align: top;
728 831 font-size: 60%;
729 832 font-weight: bold;
730 833 }
834
731 835 .right .merge img{
732 836 vertical-align: bottom;
733 837 }
734 838
735 839 .right .parent{
736 840 font-size: 90%;
737 841 font-family: monospace;
738 842 }
739 /** end of canvas **/
740 843
741 /* FILE BROWSER */
844 /** end of canvas **/ /* FILE BROWSER */
742 845 div.browserblock {
743 846 overflow: hidden;
744 847 padding: 0px;
745 848 border: 1px solid #ccc;
746 849 background: #f8f8f8;
747 850 font-size: 100%;
748 851 line-height: 100%;
749 852 /* new */
750 853 line-height: 125%;
751 854 }
855
752 856 div.browserblock .browser-header{
753 857 border-bottom: 1px solid #CCCCCC;
754 858 background: #EEEEEE;
755 859 color:blue;
756 860 padding:10px 0 10px 0;
757 861 }
862
758 863 div.browserblock .browser-header span{
759 864 margin-left:25px;
760 865 font-weight: bold;
761 866 }
867
762 868 div.browserblock .browser-body{
763 869 background: #EEEEEE;
764 870 }
765 871
766 872 table.code-browser {
767 873 border-collapse:collapse;
768 874 width: 100%;
769 875 }
876
770 877 table.code-browser tr{
771 878 margin:3px;
772 879 }
773 880
774 881 table.code-browser thead th {
775 882 background-color: #EEEEEE;
776 883 height: 20px;
777 884 font-size: 1.1em;
778 885 font-weight: bold;
779 886 text-align: center;
780 887 text-align: left;
781 888 padding-left: 10px;
782 889 }
890
783 891 table.code-browser tbody tr {
784 892
785 893 }
786 894
787 895 table.code-browser tbody td {
788
789 896 padding-left:10px;
790 897 height: 20px;
791 898 }
792 899
793 900 .info-table {
794 901 background: none repeat scroll 0 0 #FAFAFA;
795 902 border-bottom: 1px solid #DDDDDD;
796 903 width: 100%;
797 904 }
798 905
799 906 .rss_logo {
800 907 background: url("/images/icons/rss_16.png") no-repeat scroll 3px;
801 908 height: 16px;
802 909 padding-left: 20px;
803 910 padding-top: 0px;
804 911 text-align: left;
805 912 }
806 913
807 914 .atom_logo {
808 915 background: url("/images/icons/atom.png") no-repeat scroll 3px;
809 916 height: 16px;
810 917 padding-left: 20px;
811 918 padding-top: 0px;
812 919 text-align: left;
813 920 }
921
814 922 .archive_logo{
815 923 background: url("/images/icons/compress.png") no-repeat scroll 3px;
816 924 height: 16px;
817 925 padding-left: 20px;
818 926 text-align: left;
819 927 }
820 928
821 929 .browser-file {
822 930 background: url("/images/icons/document_16.png") no-repeat scroll 3px;
823 931 height: 16px;
824 932 padding-left: 20px;
825 933 text-align: left;
826 934 }
827 935
828 936 .browser-dir {
829 937 background: url("/images/icons/folder_16.png") no-repeat scroll 3px;
830 938 height: 16px;
831 939 padding-left: 20px;
832 940 text-align: left;
833 941 }
834 942
835 943 #repos_list {
836 944 border: 1px solid #556CB5;
837 945 background: #FFFFFF;
838 946 } No newline at end of file
@@ -1,109 +1,225 b''
1 1 ## -*- coding: utf-8 -*-
2 2 <%inherit file="/base/base.html"/>
3 3
4 4 <%def name="title()">
5 5 ${_('Repositories administration')}
6 6 </%def>
7 7 <%def name="breadcrumbs()">
8 8 ${h.link_to(u'Admin',h.url('admin_home'))}
9 9 /
10 10 ${_('Repos')}
11 11 </%def>
12 12 <%def name="page_nav()">
13 13 ${self.menu('admin')}
14 14 ${self.submenu('repos')}
15 15 </%def>
16 16 <%def name="main()">
17 17 <div>
18 18 <h2>${_('Repositories')} - ${_('edit')}</h2>
19 19 ${h.form(url('repo', id=c.repo_info.repo_name),method='put')}
20 20 <table>
21 21 <tr>
22 22 <td>${_('Name')}</td>
23 23 <td>${h.text('repo_name')}</td>
24 24 <td>${self.get_form_error('repo_name')}</td>
25 25 </tr>
26 26 <tr>
27 27 <td>${_('Description')}</td>
28 28 <td>${h.textarea('description',cols=23,rows=5)}</td>
29 29 <td>${self.get_form_error('description')}</td>
30 30 </tr>
31 31 <tr>
32 32 <td>${_('Private')}</td>
33 33 <td>${h.checkbox('private')}</td>
34 34 <td>${self.get_form_error('private')}</td>
35 35 </tr>
36 36 <tr>
37 37 <td>${_('Owner')}</td>
38 <td>${h.text('user')}</td>
38 <td class='ac'>
39 <div id="perm_ac">
40 ${h.text('user',class_='yui-ac-input')}
41 <div id="owner_container"></div>
42 </div>
43 </td>
39 44 <td>${self.get_form_error('user')}</td>
40 45 </tr>
41 46 <tr>
42 47 <td>${_('Permissions')}</td>
43 48 <td>
44 49 <table>
45 50 <tr>
46 51 <td>${_('none')}</td>
47 52 <td>${_('read')}</td>
48 53 <td>${_('write')}</td>
49 54 <td>${_('admin')}</td>
50 55 <td>${_('user')}</td>
51 56 </tr>
52 57
53 58 %for r2p in c.repo_info.repo2perm:
54 59 <tr>
55 60 <td>${h.radio('perm_%s' % r2p.user.username,'repository.none')}</td>
56 61 <td>${h.radio('perm_%s' % r2p.user.username,'repository.read')}</td>
57 62 <td>${h.radio('perm_%s' % r2p.user.username,'repository.write')}</td>
58 63 <td>${h.radio('perm_%s' % r2p.user.username,'repository.admin')}</td>
59 64 <td>${r2p.user.username}</td>
60 65 </tr>
61 66 %endfor
62
63
64 67 <%
65
66 68 if not hasattr(c,'form_errors'):
67 69 d = 'display:none;'
68 70 else:
69 71 d=''
70 72 %>
71 73
72 74 <tr id="add_perm_input" style="${d}">
73 75 <td>${h.radio('perm_new_user','repository.none')}</td>
74 76 <td>${h.radio('perm_new_user','repository.read')}</td>
75 77 <td>${h.radio('perm_new_user','repository.write')}</td>
76 78 <td>${h.radio('perm_new_user','repository.admin')}</td>
77 <td>${h.text('perm_new_user_name',size=10)}</td>
79 <td class='ac'>
80 <div id="perm_ac">
81 ${h.text('perm_new_user_name',class_='yui-ac-input')}
82 <div id="perm_container"></div>
83 </div>
84 </td>
78 85 <td>${self.get_form_error('perm_new_user_name')}</td>
79 86 </tr>
80 87 <tr>
81 88 <td colspan="4">
82 89 <span id="add_perm" class="add_icon" style="cursor: pointer;">
83 90 ${_('Add another user')}
84 91 </span>
85 92 </td>
86 93 </tr>
87 94 </table>
88 95 </td>
89 96
90 97 </tr>
91 98 <tr>
92 99 <td></td>
93 100 <td>${h.submit('update','update')}</td>
94 101 </tr>
95 102
96 103 </table>
97 104 ${h.end_form()}
98 105 <script type="text/javascript">
99 106 YAHOO.util.Event.onDOMReady(function(){
100 107 var D = YAHOO.util.Dom;
101 108 YAHOO.util.Event.addListener('add_perm','click',function(){
102 109 D.setStyle('add_perm_input','display','');
103 110 D.setStyle('add_perm','opacity','0.6');
104 111 D.setStyle('add_perm','cursor','default');
105 112 });
106 113 });
107 114 </script>
115 <script type="text/javascript">
116 YAHOO.example.FnMultipleFields = function(){
117 var myContacts = ${c.users_array|n}
118
119 // Define a custom search function for the DataSource
120 var matchNames = function(sQuery) {
121 // Case insensitive matching
122 var query = sQuery.toLowerCase(),
123 contact,
124 i=0,
125 l=myContacts.length,
126 matches = [];
127
128 // Match against each name of each contact
129 for(; i<l; i++) {
130 contact = myContacts[i];
131 if((contact.fname.toLowerCase().indexOf(query) > -1) ||
132 (contact.lname.toLowerCase().indexOf(query) > -1) ||
133 (contact.nname && (contact.nname.toLowerCase().indexOf(query) > -1))) {
134 matches[matches.length] = contact;
135 }
136 }
137
138 return matches;
139 };
140
141 // Use a FunctionDataSource
142 var oDS = new YAHOO.util.FunctionDataSource(matchNames);
143 oDS.responseSchema = {
144 fields: ["id", "fname", "lname", "nname"]
145 }
146
147 // Instantiate AutoComplete for perms
148 var oAC_perms = new YAHOO.widget.AutoComplete("perm_new_user_name", "perm_container", oDS);
149 oAC_perms.useShadow = false;
150 oAC_perms.resultTypeList = false;
151
152 // Instantiate AutoComplete for owner
153 var oAC_owner = new YAHOO.widget.AutoComplete("user", "owner_container", oDS);
154 oAC_owner.useShadow = false;
155 oAC_owner.resultTypeList = false;
156
157
158 // Custom formatter to highlight the matching letters
159 var custom_formatter = function(oResultData, sQuery, sResultMatch) {
160 var query = sQuery.toLowerCase(),
161 fname = oResultData.fname,
162 lname = oResultData.lname,
163 nname = oResultData.nname || "", // Guard against null value
164 query = sQuery.toLowerCase(),
165 fnameMatchIndex = fname.toLowerCase().indexOf(query),
166 lnameMatchIndex = lname.toLowerCase().indexOf(query),
167 nnameMatchIndex = nname.toLowerCase().indexOf(query),
168 displayfname, displaylname, displaynname;
169
170 if(fnameMatchIndex > -1) {
171 displayfname = highlightMatch(fname, query, fnameMatchIndex);
172 }
173 else {
174 displayfname = fname;
175 }
176
177 if(lnameMatchIndex > -1) {
178 displaylname = highlightMatch(lname, query, lnameMatchIndex);
179 }
180 else {
181 displaylname = lname;
182 }
183
184 if(nnameMatchIndex > -1) {
185 displaynname = "(" + highlightMatch(nname, query, nnameMatchIndex) + ")";
186 }
187 else {
188 displaynname = nname ? "(" + nname + ")" : "";
189 }
190
191 return displayfname + " " + displaylname + " " + displaynname;
192
193 };
194 oAC_perms.formatResult = custom_formatter;
195 oAC_owner.formatResult = custom_formatter;
196
197 // Helper function for the formatter
198 var highlightMatch = function(full, snippet, matchindex) {
199 return full.substring(0, matchindex) +
200 "<span class='match'>" +
201 full.substr(matchindex, snippet.length) +
202 "</span>" +
203 full.substring(matchindex + snippet.length);
204 };
205
206 var myHandler = function(sType, aArgs) {
207 var myAC = aArgs[0]; // reference back to the AC instance
208 var elLI = aArgs[1]; // reference to the selected LI element
209 var oData = aArgs[2]; // object literal of selected item's result data
210 myAC.getInputEl().value = oData.nname;
211 };
212
213 oAC_perms.itemSelectEvent.subscribe(myHandler);
214 oAC_owner.itemSelectEvent.subscribe(myHandler);
215
216 return {
217 oDS: oDS,
218 oAC_perms: oAC_perms,
219 oAC_owner: oAC_owner,
220 };
221 }();
222
223 </script>
108 224 </div>
109 225 </%def>
@@ -1,156 +1,159 b''
1 1 ## -*- coding: utf-8 -*-
2 2 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
3 3 <html xmlns="http://www.w3.org/1999/xhtml" id="mainhtml">
4 4 <head>
5 5 <link rel="icon" href="/images/hgicon.png" type="image/png" />
6 6 <meta name="robots" content="index, nofollow"/>
7 7 <title>${next.title()}</title>
8 8 ##For future use yui reset for cross browser compatability.
9 9 ##<link rel="stylesheet" href="/js/yui/reset-fonts-grids/reset-fonts-grids.css" type="text/css" />
10 10 ${self.css()}
11 11 ${self.js()}
12 12 </head>
13 13
14 14 <body class="mainbody">
15 15 <div id="container">
16 16 <div class="page-header">
17 17 <h1 class="breadcrumbs">${next.breadcrumbs()}</h1>
18 18 ${self.page_nav()}
19 19 <div class="flash_msg">
20 20 <% messages = h.flash.pop_messages() %>
21 21 % if messages:
22 22 <ul id="flash-messages">
23 23 % for message in messages:
24 24 <li class="${message.category}_msg">${message}</li>
25 25 % endfor
26 26 </ul>
27 27 % endif
28 28 </div>
29 29 <div id="main">
30 30 ${next.main()}
31 31 <script type="text/javascript">${h.tooltip.activate()}</script>
32 32 </div>
33 33 <div class="page-footer">
34 34 Hg App ${c.hg_app_version} &copy; 2010 by Marcin Kuzminski
35 35 </div>
36 36
37 37 <div id="powered-by">
38 38 <p>
39 39 <a href="http://mercurial.selenic.com/" title="Mercurial">
40 40 <img src="/images/hglogo.png" width="75" height="90" alt="mercurial"/></a>
41 41 </p>
42 42 </div>
43 43
44 44 <div id="corner-top-left"></div>
45 45 <div id="corner-top-right"></div>
46 46 <div id="corner-bottom-left"></div>
47 47 <div id="corner-bottom-right"></div>
48 48
49 49 </div>
50 50 </body>
51 51 </html>
52 52
53 53 ### MAKO DEFS ###
54 54
55 55 <%def name="page_nav()">
56 56 ${self.menu()}
57 57 ${self.submenu()}
58 58 </%def>
59 59
60 60 <%def name="menu(current)">
61 61 <%
62 62 def is_current(selected):
63 63 if selected == current:
64 64 return "class='current'"
65 65 %>
66 66 %if current not in ['home','admin']:
67 67 ##regular menu
68 68 <script type="text/javascript">
69 69 YAHOO.util.Event.onDOMReady(function(){
70 70 YAHOO.util.Event.addListener('repo_switcher','click',function(){
71 71 if(YAHOO.util.Dom.hasClass('repo_switcher','selected')){
72 72 YAHOO.util.Dom.setStyle('switch_repos','display','none');
73 73 YAHOO.util.Dom.setStyle('repo_switcher','background','');
74 74 YAHOO.util.Dom.removeClass('repo_switcher','selected');
75 75 YAHOO.util.Dom.get('repo_switcher').removeAttribute('style');
76 76 }
77 77 else{
78 78 YAHOO.util.Dom.setStyle('switch_repos','display','');
79 79 YAHOO.util.Dom.setStyle('repo_switcher','background','#FFFFFF');
80 80 YAHOO.util.Dom.setStyle('repo_switcher','color','#556CB5');
81 81 YAHOO.util.Dom.addClass('repo_switcher','selected');
82 82 }
83 83 });
84 84 YAHOO.util.Event.addListener('repos_list','change',function(e){
85 85 var wa = YAHOO.util.Dom.get('repos_list').value;
86 86
87 87 var url = "${h.url('summary_home',repo_name='__REPLACE__')}".replace('__REPLACE__',wa);
88 88 window.location = url;
89 89 })
90 90 });
91 91 </script>
92 92 <ul class="page-nav">
93 93 <li>
94 94 <a id="repo_switcher" title="${_('Switch repository')}" href="#">&darr;</a>
95 95 <div id="switch_repos" style="display:none;position: absolute;height: 25px">
96 96 <select id="repos_list" size="=10" style="min-width: 150px">
97 97 %for repo in sorted(x.name.lower() for x in c.cached_repo_list.values()):
98 98 <option value="${repo}">${repo}</option>
99 99 %endfor
100 100 </select>
101 101 </div>
102 102 </li>
103 103 <li ${is_current('summary')}>${h.link_to(_('summary'),h.url('summary_home',repo_name=c.repo_name))}</li>
104 104 <li ${is_current('shortlog')}>${h.link_to(_('shortlog'),h.url('shortlog_home',repo_name=c.repo_name))}</li>
105 105 <li ${is_current('changelog')}>${h.link_to(_('changelog'),h.url('changelog_home',repo_name=c.repo_name))}</li>
106 106 <li ${is_current('branches')}>${h.link_to(_('branches'),h.url('branches_home',repo_name=c.repo_name))}</li>
107 107 <li ${is_current('tags')}>${h.link_to(_('tags'),h.url('tags_home',repo_name=c.repo_name))}</li>
108 108 <li ${is_current('files')}>${h.link_to(_('files'),h.url('files_home',repo_name=c.repo_name))}</li>
109 <li>${h.link_to(_('settings'),h.url('edit_repo',id=c.repo_name))}</li>
109 110 </ul>
110 111 %else:
111 112 ##Root menu
112 113 <ul class="page-nav">
113 114 <li ${is_current('home')}>${h.link_to(_('Home'),h.url('/'))}</li>
114 115 <li ${is_current('admin')}>${h.link_to(_('Admin'),h.url('admin_home'))}</li>
115 116 <li class="logout">${h.link_to(u'Logout',h.url('logout_home'))}</li>
116 117 </ul>
117 118 %endif
118 119 </div>
119 120 </%def>
120 121 <%def name="submenu(current=None)">
121 122 <%
122 123 def is_current(selected):
123 124 if selected == current:
124 125 return "class='current_submenu'"
125 126 %>
126 127 %if current != None:
127 128 <div>
128 129 <ul class="submenu">
129 130 <li ${is_current('repos')}>${h.link_to(u'repos',h.url('repos'),class_='repos')}</li>
130 131 <li ${is_current('users')}>${h.link_to(u'users',h.url('users'),class_='users')}</li>
131 132 <li ${is_current('permissions')}>${h.link_to(u'permissions',h.url('permissions'),class_='permissions')}</li>
132 133 </ul>
133 134 </div>
134 135 %endif
135 136 </%def>
136 137
137 138
138 139 <%def name="css()">
139 140 <link rel="stylesheet" href="/css/monoblue_custom.css" type="text/css" />
140 141 </%def>
141 142
142 143 <%def name="js()">
143 144 <script type="text/javascript" src="/js/yui/utilities/utilities.js"></script>
144 145 <script type="text/javascript" src="/js/yui/container/container-min.js"></script>
146 <script type="text/javascript" src="/js/yui/datasource/datasource-min.js"></script>
147 <script type="text/javascript" src="/js/yui/autocomplete/autocomplete-min.js"></script>
145 148 </%def>
146 149
147 150 <!-- DEFINITION OF FORM ERROR FETCHER -->
148 151 <%def name="get_form_error(element)">
149 152 %if hasattr(c,'form_errors') and type(c.form_errors) == dict:
150 153 %if c.form_errors.get(element,False):
151 154 <span class="error-message">
152 155 ${c.form_errors.get(element,'')}
153 156 </span>
154 157 %endif
155 158 %endif
156 159 </%def> No newline at end of file
@@ -1,37 +1,38 b''
1 1 ## -*- coding: utf-8 -*-
2 2 <%inherit file="base/base.html"/>
3 3 <%def name="title()">
4 4 ${c.repos_prefix} Mercurial Repositories
5 5 </%def>
6 6 <%def name="breadcrumbs()">
7 7 ${c.repos_prefix} Mercurial Repositories
8 8 </%def>
9 9 <%def name="page_nav()">
10 ${self.menu('home')}
10 &nbsp;
11 </div>
11 12 </%def>
12 13 <%def name="main()">
13 14 <div>
14 15 <br />
15 <h2>${_('Login')}</h2>
16 <h2>${_('Login to hg app')}</h2>
16 17 ${h.form(h.url.current())}
17 18 <table>
18 19 <tr>
19 20 <td>${_('Username')}</td>
20 21 <td>${h.text('username')}</td>
21 22 <td>${self.get_form_error('username')}</td>
22 23 </tr>
23 24 <tr>
24 25 <td>${_('Password')}</td>
25 26 <td>${h.password('password')}</td>
26 27 <td>${self.get_form_error('password')}</td>
27 28 </tr>
28 29 <tr>
29 30 <td></td>
30 31 <td>${h.submit('login','login')}</td>
31 32 </tr>
32 33 </table>
33 34 ${h.end_form()}
34 35 </div>
35 36 </%def>
36 37
37 38
General Comments 0
You need to be logged in to leave comments. Login now