##// END OF EJS Templates
implemented yui tooltip, and added it into annotation and main page.
marcink -
r281:cd2ee462 default
parent child Browse files
Show More
@@ -1,124 +1,205 b''
1 1 """Helper functions
2 2
3 3 Consists of functions to typically be used within templates, but also
4 4 available to Controllers. This module is available to both as 'h'.
5 5 """
6 6 from pygments.formatters import HtmlFormatter
7 7 from pygments import highlight as code_highlight
8 8 from pylons import url, app_globals as g
9 9 from pylons.i18n.translation import _, ungettext
10 10 from vcs.utils.annotate import annotate_highlight
11 11 from webhelpers.html import literal, HTML, escape
12 from webhelpers.html.tools import *
12 13 from webhelpers.html.builder import make_tag
13 14 from webhelpers.html.tags import auto_discovery_link, checkbox, css_classes, \
14 15 end_form, file, form, hidden, image, javascript_link, link_to, link_to_if, \
15 16 link_to_unless, ol, required_legend, select, stylesheet_link, submit, text, \
16 password, textarea, title, ul, xml_declaration
17 password, textarea, title, ul, xml_declaration, radio
17 18 from webhelpers.html.tools import auto_link, button_to, highlight, js_obfuscate, \
18 19 mail_to, strip_links, strip_tags, tag_re
19 20 from webhelpers.number import format_byte_size, format_bit_size
20 21 from webhelpers.pylonslib import Flash as _Flash
21 22 from webhelpers.pylonslib.secure_form import secure_form
22 23 from webhelpers.text import chop_at, collapse, convert_accented_entities, \
23 24 convert_misc_entities, lchop, plural, rchop, remove_formatting, \
24 replace_whitespace, urlify, truncate
25 replace_whitespace, urlify, truncate, wrap_paragraphs
25 26
26 27
27 #Custom helper here :)
28 #Custom helpers here :)
28 29 class _Link(object):
29 30 '''
30 31 Make a url based on label and url with help of url_for
31 32 @param label:name of link if not defined url is used
32 33 @param url: the url for link
33 34 '''
34 35
35 36 def __call__(self, label='', *url_, **urlargs):
36 37 if label is None or '':
37 38 label = url
38 39 link_fn = link_to(label, url(*url_, **urlargs))
39 40 return link_fn
40 41
42 link = _Link()
41 43
42 44 class _GetError(object):
43 45
44 46 def __call__(self, field_name, form_errors):
45 47 tmpl = """<span class="error_msg">%s</span>"""
46 48 if form_errors and form_errors.has_key(field_name):
47 49 return literal(tmpl % form_errors.get(field_name))
48 50
51 get_error = _GetError()
52
53 def recursive_replace(str, replace=' '):
54 """
55 Recursive replace of given sign to just one instance
56 @param str: given string
57 @param replace:char to find and replace multiple instances
58
59 Examples::
60 >>> recursive_replace("Mighty---Mighty-Bo--sstones",'-')
61 'Mighty-Mighty-Bo-sstones'
62 """
63
64 if str.find(replace * 2) == -1:
65 return str
66 else:
67 str = str.replace(replace * 2, replace)
68 return recursive_replace(str, replace)
69
70 class _ToolTip(object):
71
72 def __call__(self, tooltip_title, trim_at=50):
73 """
74 Special function just to wrap our text into nice formatted autowrapped
75 text
76 @param tooltip_title:
77 """
78
79 return literal(wrap_paragraphs(tooltip_title, trim_at)\
80 .replace('\n', '<br/>'))
81
82 def activate(self):
83 """
84 Adds tooltip mechanism to the given Html all tooltips have to have
85 set class tooltip and set attribute tooltip_title.
86 Then a tooltip will be generated based on that
87 All with yui js tooltip
88 """
89
90 js = '''
91 YAHOO.util.Event.onDOMReady(function(){
92 function toolTipsId(){
93 var ids = [];
94 var tts = YAHOO.util.Dom.getElementsByClassName('tooltip');
95
96 for (var i = 0; i < tts.length; i++) {
97 //if element doesn not have and id autgenerate one for tooltip
98
99 if (!tts[i].id){
100 tts[i].id='tt'+i*100;
101 }
102 ids.push(tts[i].id);
103 }
104 return ids
105 };
106 var myToolTips = new YAHOO.widget.Tooltip("tooltip", {
107 context: toolTipsId(),
108 monitorresize:false,
109 xyoffset :[0,0],
110 autodismissdelay:300000,
111 hidedelay:5,
112 showdelay:20,
113 });
114
115 //Mouse subscribe optional arguments
116 myToolTips.contextMouseOverEvent.subscribe(
117 function(type, args) {
118 var context = args[0];
119 return true;
120 });
121
122 // Set the text for the tooltip just before we display it. Lazy method
123 myToolTips.contextTriggerEvent.subscribe(
124 function(type, args) {
125 var context = args[0];
126 var txt = context.getAttribute('tooltip_title');
127 this.cfg.setProperty("text", txt);
128 });
129 });
130 '''
131 return literal(js)
132
133 tooltip = _ToolTip()
134
49 135 class _FilesBreadCrumbs(object):
50 136
51 137 def __call__(self, repo_name, rev, paths):
52 138 url_l = [link_to(repo_name, url('files_home', repo_name=repo_name, revision=rev, f_path=''))]
53 paths_l = paths.split('/')
139 paths_l = paths.split(' / ')
54 140
55 141 for cnt, p in enumerate(paths_l, 1):
56 142 if p != '':
57 url_l.append(link_to(p, url('files_home', repo_name=repo_name, revision=rev, f_path='/'.join(paths_l[:cnt]))))
143 url_l.append(link_to(p, url('files_home', repo_name=repo_name, revision=rev, f_path=' / '.join(paths_l[:cnt]))))
58 144
59 145 return literal(' / '.join(url_l))
60 146
147 files_breadcrumbs = _FilesBreadCrumbs()
148
61 149 def pygmentize(filenode, **kwargs):
62 150 """
63 151 pygmentize function using pygments
64 152 @param filenode:
65 153 """
66 154 return literal(code_highlight(filenode.content, filenode.lexer, HtmlFormatter(**kwargs)))
67 155
68 156 def pygmentize_annotation(filenode, **kwargs):
69 157 """
70 158 pygmentize function for annotation
71 159 @param filenode:
72 160 """
73 161
74 162 color_dict = g.changeset_annotation_colors
75 163 def gen_color():
76 164 import random
77 165 return [str(random.randrange(10, 235)) for _ in xrange(3)]
78 166 def get_color_string(cs):
79 167 if color_dict.has_key(cs):
80 168 col = color_dict[cs]
81 169 else:
82 170 color_dict[cs] = gen_color()
83 171 col = color_dict[cs]
84 return "color: rgb(%s) ! important;" % (','.join(col))
172 return "color: rgb(%s) ! important;" % (', '.join(col))
85 173
86 174 def url_func(changeset):
87 return '%s\n' % (link_to('r%s:%s' % (changeset.revision, changeset.raw_id),
88 url('changeset_home', repo_name='test', revision=changeset.raw_id),
89 title=_('author') + ':%s - %s' % (changeset.author, changeset.message,),
90 style=get_color_string(changeset.raw_id)))
91
175 tooltip_html = "<div style='font-size:0.8em'><b>Author:</b> %s<br/><b>Date:</b> %s</b><br/><b>Message:</b> %s<br/></div>"
176
177 tooltip_html = tooltip_html % (changeset.author,
178 changeset.date,
179 tooltip(changeset.message))
180 lnk_format = 'r%s:%s' % (changeset.revision,
181 changeset.raw_id)
182 uri = link_to(
183 lnk_format,
184 url('changeset_home', repo_name='test',
185 revision=changeset.raw_id),
186 style=get_color_string(changeset.raw_id),
187 class_='tooltip',
188 tooltip_title=tooltip_html
189 )
190
191 uri += '\n'
192 return uri
92 193 return literal(annotate_highlight(filenode, url_func, **kwargs))
93
94 def recursive_replace(str, replace=' '):
95 """
96 Recursive replace of given sign to just one instance
97 @param str: given string
98 @param replace:char to find and replace multiple instances
99
100 Examples::
101 >>> recursive_replace("Mighty---Mighty-Bo--sstones",'-')
102 'Mighty-Mighty-Bo-sstones'
103 """
104
105 if str.find(replace * 2) == -1:
106 return str
107 else:
108 str = str.replace(replace * 2, replace)
109 return recursive_replace(str, replace)
110 194
111 195 def repo_name_slug(value):
112 196 """
113 197 Return slug of name of repository
114 198 """
115 199 slug = urlify(value)
116 200 for c in """=[]\;'"<>,/~!@#$%^&*()+{}|:""":
117 201 slug = slug.replace(c, '-')
118 202 slug = recursive_replace(slug, '-')
119 203 return slug
120
121 files_breadcrumbs = _FilesBreadCrumbs()
122 link = _Link()
204
123 205 flash = _Flash()
124 get_error = _GetError()
@@ -1,180 +1,181 b''
1 1 #!/usr/bin/env python
2 2 # encoding: utf-8
3 3 # Utilities for hg app
4 4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 5
6 6 # This program is free software; you can redistribute it and/or
7 7 # modify it under the terms of the GNU General Public License
8 8 # as published by the Free Software Foundation; version 2
9 9 # of the License or (at your opinion) any later version of the license.
10 10 #
11 11 # This program is distributed in the hope that it will be useful,
12 12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 14 # GNU General Public License for more details.
15 15 #
16 16 # You should have received a copy of the GNU General Public License
17 17 # along with this program; if not, write to the Free Software
18 18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 19 # MA 02110-1301, USA.
20 20
21 21 """
22 22 Created on April 18, 2010
23 23 Utilities for hg app
24 24 @author: marcink
25 25 """
26 26
27 27 import os
28 28 import logging
29 29 from mercurial import ui, config, hg
30 30 from mercurial.error import RepoError
31 31 from pylons_app.model.db import Repository, User
32 32 log = logging.getLogger(__name__)
33 33
34 34
35 35 def get_repo_slug(request):
36 36 return request.environ['pylons.routes_dict'].get('repo_name')
37 37
38 38 def is_mercurial(environ):
39 39 """
40 40 Returns True if request's target is mercurial server - header
41 41 ``HTTP_ACCEPT`` of such request would start with ``application/mercurial``.
42 42 """
43 43 http_accept = environ.get('HTTP_ACCEPT')
44 44 if http_accept and http_accept.startswith('application/mercurial'):
45 45 return True
46 46 return False
47 47
48 48 def check_repo_dir(paths):
49 49 repos_path = paths[0][1].split('/')
50 50 if repos_path[-1] in ['*', '**']:
51 51 repos_path = repos_path[:-1]
52 52 if repos_path[0] != '/':
53 53 repos_path[0] = '/'
54 54 if not os.path.isdir(os.path.join(*repos_path)):
55 55 raise Exception('Not a valid repository in %s' % paths[0][1])
56 56
57 57 def check_repo(repo_name, base_path):
58 58
59 59 repo_path = os.path.join(base_path, repo_name)
60 60
61 61 try:
62 62 r = hg.repository(ui.ui(), repo_path)
63 63 hg.verify(r)
64 64 #here we hnow that repo exists it was verified
65 65 log.info('%s repo is already created', repo_name)
66 66 return False
67 67 #raise Exception('Repo exists')
68 68 except RepoError:
69 69 log.info('%s repo is free for creation', repo_name)
70 70 #it means that there is no valid repo there...
71 71 return True
72 72
73 73 def make_ui(path=None, checkpaths=True):
74 74 """
75 75 A funcion that will read python rc files and make an ui from read options
76 76
77 77 @param path: path to mercurial config file
78 78 """
79 79 if not path:
80 80 log.error('repos config path is empty !')
81 81
82 82 if not os.path.isfile(path):
83 83 log.warning('Unable to read config file %s' % path)
84 84 return False
85 85 #propagated from mercurial documentation
86 86 sections = [
87 87 'alias',
88 88 'auth',
89 89 'decode/encode',
90 90 'defaults',
91 91 'diff',
92 92 'email',
93 93 'extensions',
94 94 'format',
95 95 'merge-patterns',
96 96 'merge-tools',
97 97 'hooks',
98 98 'http_proxy',
99 99 'smtp',
100 100 'patch',
101 101 'paths',
102 102 'profiling',
103 103 'server',
104 104 'trusted',
105 105 'ui',
106 106 'web',
107 107 ]
108 108
109 109 baseui = ui.ui()
110 110 cfg = config.config()
111 111 cfg.read(path)
112 112 if checkpaths:check_repo_dir(cfg.items('paths'))
113 113
114 114 for section in sections:
115 115 for k, v in cfg.items(section):
116 116 baseui.setconfig(section, k, v)
117 117
118 118 return baseui
119 119
120 120 def invalidate_cache(name, *args):
121 121 """Invalidates given name cache"""
122 122
123 123 from beaker.cache import region_invalidate
124 124 log.info('INVALIDATING CACHE FOR %s', name)
125 125
126 126 """propagate our arguments to make sure invalidation works. First
127 127 argument has to be the name of cached func name give to cache decorator
128 128 without that the invalidation would not work"""
129 129 tmp = [name]
130 130 tmp.extend(args)
131 131 args = tuple(tmp)
132 132
133 133 if name == 'cached_repo_list':
134 134 from pylons_app.model.hg_model import _get_repos_cached
135 135 region_invalidate(_get_repos_cached, None, *args)
136 136
137 137 if name == 'full_changelog':
138 138 from pylons_app.model.hg_model import _full_changelog_cached
139 139 region_invalidate(_full_changelog_cached, None, *args)
140 140
141 141 from vcs.backends.base import BaseChangeset
142 142 from vcs.utils.lazy import LazyProperty
143 143 class EmptyChangeset(BaseChangeset):
144 144
145 145 revision = -1
146
146 message = ''
147
147 148 @LazyProperty
148 149 def raw_id(self):
149 150 """
150 151 Returns raw string identifing this changeset, useful for web
151 152 representation.
152 153 """
153 154 return '0' * 12
154 155
155 156
156 157 def repo2db_mapper(initial_repo_list):
157 158 """
158 159 maps all found repositories into db
159 160 """
160 161 from pylons_app.model.meta import Session
161 162 sa = Session()
162 163 user = sa.query(User).filter(User.admin == True).first()
163 164 for name, repo in initial_repo_list.items():
164 165 if not sa.query(Repository).get(name):
165 166 log.info('%s not found creating default', name)
166 167 try:
167 168
168 169 new_repo = Repository()
169 170 new_repo.repo_name = name
170 171 desc = repo.description if repo.description != 'unknown' else \
171 172 'auto description for %s' % name
172 173 new_repo.description = desc
173 174 new_repo.user_id = user.user_id
174 175 new_repo.private = False
175 176 sa.add(new_repo)
176 177 sa.commit()
177 178 except:
178 179 sa.rollback()
179 180 raise
180 181
@@ -1,155 +1,156 b''
1 1 #!/usr/bin/env python
2 2 # encoding: utf-8
3 3 # Model for hg app
4 4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 5
6 6 # This program is free software; you can redistribute it and/or
7 7 # modify it under the terms of the GNU General Public License
8 8 # as published by the Free Software Foundation; version 2
9 9 # of the License or (at your opinion) any later version of the license.
10 10 #
11 11 # This program is distributed in the hope that it will be useful,
12 12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 14 # GNU General Public License for more details.
15 15 #
16 16 # You should have received a copy of the GNU General Public License
17 17 # along with this program; if not, write to the Free Software
18 18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 19 # MA 02110-1301, USA.
20 20
21 21 """
22 22 Created on April 9, 2010
23 23 Model for hg app
24 24 @author: marcink
25 25 """
26 26
27 27 from beaker.cache import cache_region
28 28 from mercurial import ui
29 29 from mercurial.hgweb.hgwebdir_mod import findrepos
30 30 from vcs.exceptions import RepositoryError, VCSError
31 31 from pylons_app.model.meta import Session
32 32 from pylons_app.model.db import Repository
33 33 import logging
34 34 import os
35 35 import sys
36 36 log = logging.getLogger(__name__)
37 37
38 38 try:
39 39 from vcs.backends.hg import MercurialRepository
40 40 except ImportError:
41 41 sys.stderr.write('You have to import vcs module')
42 42 raise Exception('Unable to import vcs')
43 43
44 44 def _get_repos_cached_initial(app_globals):
45 45 """
46 46 return cached dict with repos
47 47 """
48 48 g = app_globals
49 49 return HgModel.repo_scan(g.paths[0][0], g.paths[0][1], g.baseui)
50 50
51 51 @cache_region('long_term', 'cached_repo_list')
52 52 def _get_repos_cached():
53 53 """
54 54 return cached dict with repos
55 55 """
56 56 log.info('getting all repositories list')
57 57 from pylons import app_globals as g
58 58 return HgModel.repo_scan(g.paths[0][0], g.paths[0][1], g.baseui)
59 59
60 60 @cache_region('long_term', 'full_changelog')
61 61 def _full_changelog_cached(repo_name):
62 62 log.info('getting full changelog for %s', repo_name)
63 63 return list(reversed(list(HgModel().get_repo(repo_name))))
64 64
65 65 class HgModel(object):
66 66 """
67 67 Mercurial Model
68 68 """
69 69
70 70 def __init__(self):
71 71 """
72 72 Constructor
73 73 """
74 74
75 75 @staticmethod
76 76 def repo_scan(repos_prefix, repos_path, baseui):
77 77 """
78 78 Listing of repositories in given path. This path should not be a
79 79 repository itself. Return a dictionary of repository objects
80 80 :param repos_path: path to directory it could take syntax with
81 81 * or ** for deep recursive displaying repositories
82 82 """
83 83 sa = Session()
84 84 def check_repo_dir(path):
85 85 """
86 86 Checks the repository
87 87 :param path:
88 88 """
89 89 repos_path = path.split('/')
90 90 if repos_path[-1] in ['*', '**']:
91 91 repos_path = repos_path[:-1]
92 92 if repos_path[0] != '/':
93 93 repos_path[0] = '/'
94 94 if not os.path.isdir(os.path.join(*repos_path)):
95 95 raise RepositoryError('Not a valid repository in %s' % path[0][1])
96 96 if not repos_path.endswith('*'):
97 97 raise VCSError('You need to specify * or ** at the end of path '
98 98 'for recursive scanning')
99 99
100 100 check_repo_dir(repos_path)
101 101 log.info('scanning for repositories in %s', repos_path)
102 102 repos = findrepos([(repos_prefix, repos_path)])
103 103 if not isinstance(baseui, ui.ui):
104 104 baseui = ui.ui()
105 105
106 106 repos_list = {}
107 107 for name, path in repos:
108 108 try:
109 109 #name = name.split('/')[-1]
110 110 if repos_list.has_key(name):
111 111 raise RepositoryError('Duplicate repository name %s found in'
112 112 ' %s' % (name, path))
113 113 else:
114 114
115 115 repos_list[name] = MercurialRepository(path, baseui=baseui)
116 116 repos_list[name].name = name
117 117 dbrepo = sa.query(Repository).get(name)
118 118 if dbrepo:
119 119 repos_list[name].description = dbrepo.description
120 120 repos_list[name].contact = dbrepo.user.full_contact
121 121 except OSError:
122 122 continue
123 123 return repos_list
124 124
125 125 def get_repos(self):
126 126 for name, repo in _get_repos_cached().items():
127 127 if repo._get_hidden():
128 128 #skip hidden web repository
129 129 continue
130 130
131 131 last_change = repo.last_change
132 132 try:
133 133 tip = repo.get_changeset('tip')
134 134 except RepositoryError:
135 135 from pylons_app.lib.utils import EmptyChangeset
136 136 tip = EmptyChangeset()
137 137
138 138 tmp_d = {}
139 139 tmp_d['name'] = repo.name
140 140 tmp_d['name_sort'] = tmp_d['name'].lower()
141 141 tmp_d['description'] = repo.description
142 142 tmp_d['description_sort'] = tmp_d['description']
143 143 tmp_d['last_change'] = last_change
144 144 tmp_d['last_change_sort'] = last_change[1] - last_change[0]
145 145 tmp_d['tip'] = tip.raw_id
146 146 tmp_d['tip_sort'] = tip.revision
147 147 tmp_d['rev'] = tip.revision
148 148 tmp_d['contact'] = repo.contact
149 149 tmp_d['contact_sort'] = tmp_d['contact']
150 150 tmp_d['repo_archives'] = list(repo._get_archives())
151 tmp_d['last_msg'] = tip.message
151 152
152 153 yield tmp_d
153 154
154 155 def get_repo(self, repo_name):
155 156 return _get_repos_cached()[repo_name]
@@ -1,812 +1,837 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 8 a {
9 9 color: #556CB5;
10 10 text-decoration: none;
11 11 }
12 12 a:HOVER{
13 13 text-decoration: underline;
14 14 }
15 15
16 16 /*** end of Initial Settings ***/
17 17
18 18 /*** ***/
19 19 .table_disp {
20 20 border-left: 0px solid #666666;
21 21 border-bottom: 1px solid #666666;
22 22 border-right: 1px solid #666666;
23 23 padding: 0px;
24 24 margin: 0px;
25 25 border-spacing: 0px;
26 26 }
27 27
28 28 .table_disp .header {
29 29 border-top: 1px solid #666666;
30 30 background-color: #556CB5;
31 31 font-weight: bold;
32 32 color: white;
33 33 vertical-align: middle;
34 34 padding: 3px 5px;
35 35 text-align: left;
36 36 font-size: 0.9em;
37 37 }
38 38
39 39 .table_disp .header td {
40 40 padding: 4px;
41 41 vertical-align: middle;
42 42 border-top: 1px solid #AAAAAA;
43 43 border-bottom: 2px solid #666666;
44 44 }
45 45 .table_disp td {
46 46 border-left: 1px solid #AAAAAA;
47 47 padding-left: 4px;
48 48 padding-right: 4px;
49 49 }
50 50
51 51 table tr.parity0:hover,table tr.parity1:hover {
52 52 background: #D5E1E6;
53 53 }
54 54
55 55 table tr.parity0 {
56 56 background: #EAEAE9;
57 57 }
58 58
59 59 table tr.parity1 {
60 60 background: #FFFFFF;
61 61 }
62 62
63 63
64 64 /*** ***/
65 65
66 66 /** common settings **/
67 67 .add_icon{
68 68 background: url("/images/icons/add.png") no-repeat scroll 3px;
69 69 height: 16px;
70 70 padding-left: 20px;
71 71 padding-top: 0px;
72 72 text-align: left;
73 73
74 74 }
75 75 .edit_icon{
76 76 background: url("/images/icons/folder_edit.png") no-repeat scroll 3px;
77 77 height: 16px;
78 78 padding-left: 20px;
79 79 padding-top: 0px;
80 80 text-align: left;
81 81 }
82 82
83 83 .delete_icon{
84 84 background: url("/images/icons/delete.png") no-repeat scroll 3px;
85 85 height: 16px;
86 86 padding-left: 20px;
87 87 padding-top: 0px;
88 88 text-align: left;
89 89
90 90 }
91 91
92 92 .action_button{
93 93 border:0px;
94 94 display: block;
95 95 }
96 96 .action_button:hover{
97 97 border:0px;
98 98 font-style:italic;
99 99 cursor: pointer;
100 100 }
101 101
102 102 .flash_msg ul{
103 103 margin:0;
104 104 padding:25px 0px 0px 0px;
105 105
106 106 }
107 107 .error_msg {
108 108 background-color:#FFCFCF;
109 109 background-image: url("/images/icons/error_msg.png");
110 110 border:1px solid #FF9595;
111 111 color:#CC3300;
112 112 }
113 113 .warning_msg {
114 114 background-color:#FFFBCC;
115 115 background-image: url("/images/icons/warning_msg.png");
116 116 border:1px solid #FFF35E;
117 117 color:#C69E00;
118 118 }
119 119 .success_msg {
120 120 background-color:#D5FFCF;
121 121 background-image: url("/images/icons/success_msg.png");
122 122 border:1px solid #97FF88;
123 123 color:#009900;
124 124 }
125 125 .notice_msg {
126 126 background-color:#DCE3FF;
127 127 background-image: url("/images/icons/notice_msg.png");
128 128 border:1px solid #93A8FF;
129 129 color:#556CB5;
130 130 }
131 131
132 132 .success_msg, .error_msg, .notice_msg, .warning_msg{
133 133 background-position:10px center;
134 134 background-repeat:no-repeat;
135 135 font-size:12px;
136 136 font-weight:bold;
137 137 min-height:14px;
138 138 line-height:14px;
139 139 margin-bottom:0px;
140 140 margin-top:0px;
141 141 padding:3px 10px 3px 40px;
142 142 display:block;
143 143 overflow: auto;
144 144 }
145 145
146 146 #msg_close {
147 147 background:transparent url("icons/cross_grey_small.png") no-repeat scroll 0 0;
148 148 cursor:pointer;
149 149 height:16px;
150 150 position:absolute;
151 151 right:5px;
152 152 top:5px;
153 153 width:16px;
154 154 }
155 155
156 156 .error-message{
157 157 color:#CC3300;
158 158 }
159 /**** Tooltip ****/
160 .yui-overlay,
161 .yui-panel-container {
162 visibility:hidden;
163 position:absolute;
164 z-index: 2;
165 }
166
167 .yui-tt {
168 visibility:hidden;
169 position:absolute;
170 color:#666666;
171 background-color:#FFFFFF;
172 font-family:arial,helvetica,verdana,sans-serif;
173 padding:8px;
174 border:2px solid #556CB5;
175 font:100% sans-serif;
176 width:auto;
177 }
178
179 .yui-tt-shadow {
180 display: none;
181 }
182 /** end of Tooltip **/
183
159 184
160 185 div#main {
161 186 padding: 5px;
162 187 }
163 188
164 189 div#container {
165 190 background: #FFFFFF;
166 191 position: relative;
167 192 color: #666;
168 193 }
169 194
170 195 div.page-header {
171 196 padding: 50px 20px 0;
172 197 background: #556cb5 top left repeat-x;
173 198 position: relative;
174 199 }
175 200
176 201 div.page-header h1 {
177 202 margin: 10px 0 30px;
178 203 font-size: 1.8em;
179 204 font-weight: bold;
180 205 font-family: sans-serif;
181 206 letter-spacing: 1px;
182 207 color: #FFFFFF;
183 208 }
184 209
185 210 div.page-header h1 a {
186 211 font-weight: bold;
187 212 color: #FFFFFF;
188 213 }
189 214
190 215 div.page-header a {
191 216 text-decoration: none;
192 217 }
193 218
194 219 div.page-header form {
195 220 position: absolute;
196 221 margin-bottom: 2px;
197 222 bottom: 0;
198 223 right: 20px;
199 224 }
200 225
201 226 div.page-header form label {
202 227 color: #DDD;
203 228 }
204 229
205 230 div.page-header form input {
206 231 padding: 2px;
207 232 border: solid 1px #DDD;
208 233 }
209 234
210 235 div.page-header form dl {
211 236 overflow: hidden;
212 237 }
213 238
214 239 div.page-header form dl dt {
215 240 font-size: 1.2em;
216 241 }
217 242
218 243 div.page-header form dl dt,div.page-header form dl dd {
219 244 margin: 0 0 0 5px;
220 245 float: left;
221 246 height: 24px;
222 247 line-height: 20px;
223 248 }
224 249
225 250 ul.page-nav {
226 251 margin: 10px 0 0 0;
227 252 list-style-type: none;
228 253 overflow: hidden;
229 254 width: 800px;
230 255 padding: 0;
231 256 }
232 257
233 258 ul.page-nav li {
234 259 margin: 0 4px 0 0;
235 260 float: left;
236 261 height: 24px;
237 262 font-size: 1.1em;
238 263 line-height: 24px;
239 264 text-align: center;
240 265 background: #556CB5;
241 266 }
242 267
243 268 ul.page-nav li.current {
244 269 background: #FFF;
245 270 padding-right: 5px;
246 271 padding-left: 5px;
247 272 }
248 273 ul.page-nav li.current a {
249 274 color: #556CB5;
250 275 }
251 276 ul.page-nav li a {
252 277 height: 24px;
253 278 color: #FFF;
254 279 padding-right: 5px;
255 280 padding-left: 5px;
256 281 display: block;
257 282 text-decoration: none;
258 283 font-weight: bold;
259 284 }
260 285 ul.page-nav li.logout a {
261 286 color: #FDAC9D;
262 287 }
263 288 ul.page-nav li a:hover {
264 289 background: #FFF;
265 290 color: #556CB5;
266 291 }
267 292
268 293 ul.submenu {
269 294 margin: 5px 0px -20px 0px;
270 295 list-style-type: none;
271 296 }
272 297
273 298 ul.submenu li {
274 299 margin: 0 10px 0 0;
275 300 font-size: 0.9em;
276 301 font-weight:bold;
277 302 display: inline;
278 303 }
279 304 ul.submenu .repos {
280 305 background: url("/images/icons/folder_edit.png") no-repeat scroll 3px;
281 306 height: 16px;
282 307 padding-left: 20px;
283 308 padding-top: 0px;
284 309 text-align: left;
285 310
286 311 }
287 312 ul.submenu .users {
288 313 background: url("/images/icons/user_edit.png") no-repeat scroll 3px;
289 314 height: 16px;
290 315 padding-left: 20px;
291 316 padding-top: 0px;
292 317 text-align: left;
293 318 }
294 319 ul.submenu .permissions {
295 320 background: url("/images/icons/folder_key.png") no-repeat scroll 3px;
296 321 height: 16px;
297 322 padding-left: 20px;
298 323 padding-top: 0px;
299 324 text-align: left;
300 325 }
301 326
302 327 ul.submenu .current_submenu {
303 328 border-bottom: 2px solid #556CB5;
304 329 }
305 330
306 331 h2 {
307 332 margin: 20px 0 10px;
308 333 height: 30px;
309 334 line-height: 30px;
310 335 text-indent: 20px;
311 336 background: #FFF;
312 337 font-size: 1.2em;
313 338 border-top: dotted 1px #D5E1E6;
314 339 font-weight: bold;
315 340 color:#556CB5;
316 341 }
317 342
318 343 h2.no-link {
319 344 color: #006699;
320 345 }
321 346
322 347 h2.no-border {
323 348 color: #FFF;
324 349 background: #556CB5;
325 350 border: 0;
326 351 }
327 352
328 353 h2 a {
329 354 font-weight: bold;
330 355 color: #006699;
331 356 }
332 357
333 358 div.page-path {
334 359 text-align: right;
335 360 padding: 20px 30px 10px 0;
336 361 border: solid #d9d8d1;
337 362 border-width: 0px 0px 1px;
338 363 font-size: 1.2em;
339 364 }
340 365
341 366 div.page-footer {
342 367 margin: 50px 0 0;
343 368 position: relative;
344 369 text-align: center;
345 370 font-weight: bold;
346 371 font-size: 90%;
347 372 }
348 373
349 374 div.page-footer p {
350 375 position: relative;
351 376 left: 20px;
352 377 bottom: 5px;
353 378 font-size: 1.2em;
354 379 }
355 380
356 381 ul.rss-logo {
357 382 position: absolute;
358 383 top: -10px;
359 384 right: 20px;
360 385 height: 20px;
361 386 list-style-type: none;
362 387 }
363 388
364 389 ul.rss-logo li {
365 390 display: inline;
366 391 }
367 392
368 393 ul.rss-logo li a {
369 394 padding: 3px 6px;
370 395 line-height: 10px;
371 396 border: 1px solid;
372 397 border-color: #fcc7a5 #7d3302 #3e1a01 #ff954e;
373 398 color: #ffffff;
374 399 background-color: #ff6600;
375 400 font-weight: bold;
376 401 font-family: sans-serif;
377 402 font-size: 10px;
378 403 text-align: center;
379 404 text-decoration: none;
380 405 }
381 406
382 407 div.rss-logo li a:hover {
383 408 background-color: #ee5500;
384 409 }
385 410
386 411 p.normal {
387 412 margin: 20px 0 20px 30px;
388 413 font-size: 1.2em;
389 414 }
390 415
391 416 span.logtags span {
392 417 background-repeat: no-repeat;
393 418 height: 16px;
394 419 padding-left: 20px;
395 420 padding-top: 0px;
396 421 text-align: left;
397 422 font-weight: bold;
398 423 }
399 424
400 425 span.logtags span.tagtag {
401 426 background-image: url("/images/icons/tag_green.png");
402 427 }
403 428
404 429 span.logtags span.branchtag {
405 430 background-image: url("/images/icons/arrow_branch.png");
406 431 color: #628F53;
407 432 }
408 433
409 434 span.logtags span.inbranchtag {
410 435 background-image: url("/images/icons/arrow_branch.png");
411 436 }
412 437
413 438 div.diff pre {
414 439 margin: 10px 0 0 0;
415 440 }
416 441
417 442 div.diff pre span {
418 443 font-family: monospace;
419 444 white-space: pre;
420 445 font-size: 1.2em;
421 446 padding: 3px 0;
422 447 }
423 448
424 449 td.source {
425 450 white-space: pre;
426 451 font-family: monospace;
427 452 margin: 10px 30px 0;
428 453 font-size: 1.2em;
429 454 font-family: monospace;
430 455 }
431 456
432 457 div.source div.parity0,div.source div.parity1 {
433 458 padding: 1px;
434 459 font-size: 1.2em;
435 460 }
436 461
437 462 div.source div.parity0 {
438 463 background: #F1F6F7;
439 464 }
440 465
441 466 div.source div.parity1 {
442 467 background: #FFFFFF;
443 468 }
444 469
445 470 div.parity0:hover,div.parity1:hover {
446 471 background: #D5E1E6;
447 472 }
448 473
449 474 .linenr {
450 475 color: #999;
451 476 text-align: right;
452 477 }
453 478
454 479 .lineno {
455 480 text-align: right;
456 481 }
457 482
458 483 .lineno a {
459 484 color: #999;
460 485 }
461 486
462 487 td.linenr {
463 488 width: 60px;
464 489 }
465 490
466 491 div#powered-by {
467 492 position: absolute;
468 493 width: 75px;
469 494 top: 15px;
470 495 right: 20px;
471 496 font-size: 1.2em;
472 497 }
473 498
474 499 div#powered-by a {
475 500 color: #EEE;
476 501 text-decoration: none;
477 502 }
478 503
479 504 div#powered-by a:hover {
480 505 text-decoration: underline;
481 506 }
482 507
483 508 dl.overview {
484 509 margin: 0 0 0 30px;
485 510 font-size: 1.1em;
486 511 overflow: hidden;
487 512 }
488 513
489 514 dl.overview dt,dl.overview dd {
490 515 margin: 5px 0;
491 516 float: left;
492 517 }
493 518
494 519 dl.overview dt {
495 520 clear: left;
496 521 font-weight: bold;
497 522 width: 150px;
498 523 }
499 524
500 525 #clone_url{
501 526 border: 0px;
502 527 }
503 528 /** end of summary **/
504 529
505 530 /** chagelog **/
506 531 h3.changelog {
507 532 margin: 20px 0 5px 30px;
508 533 padding: 0 0 2px;
509 534 font-size: 1.4em;
510 535 border-bottom: dotted 1px #D5E1E6;
511 536 }
512 537
513 538 ul.changelog-entry {
514 539 margin: 0 0 10px 30px;
515 540 list-style-type: none;
516 541 position: relative;
517 542 }
518 543
519 544 ul.changelog-entry li span.revdate {
520 545 font-size: 1.1em;
521 546 }
522 547
523 548 ul.changelog-entry li.age {
524 549 position: absolute;
525 550 top: -25px;
526 551 right: 10px;
527 552 font-size: 1.4em;
528 553 color: #CCC;
529 554 font-weight: bold;
530 555 font-style: italic;
531 556 }
532 557
533 558 ul.changelog-entry li span.name {
534 559 font-size: 1.2em;
535 560 font-weight: bold;
536 561 }
537 562
538 563 ul.changelog-entry li.description {
539 564 margin: 10px 0 0;
540 565 font-size: 1.1em;
541 566 }
542 567
543 568 /** end of changelog **/
544 569
545 570 /** file **/
546 571 p.files {
547 572 margin: 0 0 0 20px;
548 573 font-size: 2.0em;
549 574 font-weight: bold;
550 575 }
551 576
552 577 /** end of file **/
553 578
554 579 /** changeset **/
555 580 #changeset_content{
556 581 width:60%;
557 582 float:left;
558 583 }
559 584
560 585 #changeset_content .container .wrapper{
561 586 width: 600px;
562 587 }
563 588 #changeset_content .container{
564 589 border:1px solid #CCCCCC;
565 590 height:120px;
566 591 }
567 592
568 593 #changeset_content .container .left{
569 594 float:left;
570 595 width: 70%;
571 596 padding-left: 5px;
572 597 }
573 598
574 599 #changeset_content .container .right{
575 600 float:right;
576 601 width: 25%;
577 602 text-align: right;
578 603 }
579 604
580 605 #changeset_content .container .left .date{
581 606 font-weight:bold;
582 607 }
583 608 #changeset_content .container .left .author{
584 609
585 610 }
586 611 #changeset_content .container .left .message{
587 612 font-style: italic;
588 613 color: #556CB5;
589 614 }
590 615
591 616 .cs_files{
592 617 width: 60%;
593 618 }
594 619
595 620 .cs_files .cs_added{
596 621 background: url("/images/icons/page_white_add.png") no-repeat scroll 3px;
597 622 /*background-color:#BBFFBB;*/
598 623 height: 16px;
599 624 padding-left: 20px;
600 625 margin-top: 7px;
601 626 text-align: left;
602 627 }
603 628 .cs_files .cs_changed{
604 629 background: url("/images/icons/page_white_edit.png") no-repeat scroll 3px;
605 630 /*background-color: #FFDD88;*/
606 631 height: 16px;
607 632 padding-left: 20px;
608 633 margin-top: 7px;
609 634 text-align: left;
610 635 }
611 636 .cs_files .cs_removed{
612 637 background: url("/images/icons/page_white_delete.png") no-repeat scroll 3px;
613 638 /*background-color: #FF8888;*/
614 639 height: 16px;
615 640 padding-left: 20px;
616 641 margin-top: 7px;
617 642 text-align: left;
618 643 }
619 644
620 645 /** end of changeset **/
621 646
622 647 /** canvas **/
623 648 canvas {
624 649 position: absolute;
625 650 z-index: 5;
626 651 top: -0.7em;
627 652 }
628 653 #graph{
629 654 overflow: hidden;
630 655
631 656 }
632 657 #graph_nodes{
633 658 width:160px;
634 659 float:left;
635 660 }
636 661
637 662 #graph_content{
638 663 width:800px;
639 664 float:left;
640 665 }
641 666 #graph_content .container_header{
642 667 border:1px solid #CCCCCC;
643 668 height:30px;
644 669 background: #EEEEEE;
645 670 }
646 671
647 672
648 673 #graph_content .container .wrapper{
649 674 width: 600px;
650 675 }
651 676 #graph_content .container{
652 677 border-bottom: 1px solid #CCCCCC;
653 678 border-left: 1px solid #CCCCCC;
654 679 border-right: 1px solid #CCCCCC;
655 680 height:120px;
656 681 }
657 682
658 683 #graph_content .container .left{
659 684 float:left;
660 685 width: 70%;
661 686 padding-left: 5px;
662 687 }
663 688
664 689 #graph_content .container .right{
665 690 float:right;
666 691 width: 25%;
667 692 text-align: right;
668 693 }
669 694 #graph_content .container .left .date{
670 695 font-weight:bold;
671 696 }
672 697 #graph_content .container .left .author{
673 698
674 699 }
675 700 #graph_content .container .left .message{
676 701 font-size: 80%;
677 702 }
678 703
679 704 .right div{
680 705 clear: both;
681 706 }
682 707 .right .changes .added,.changed,.removed{
683 708 border:1px solid #DDDDDD;
684 709 display:block;
685 710 float:right;
686 711 font-size:0.75em;
687 712 text-align:center;
688 713 min-width:15px;
689 714 }
690 715 .right .changes .added{
691 716 background:#BBFFBB;
692 717 }
693 718 .right .changes .changed{
694 719 background: #FFDD88;
695 720 }
696 721 .right .changes .removed{
697 722 background: #FF8888;
698 723 }
699 724
700 725 .right .merge{
701 726 vertical-align: top;
702 727 font-size: 60%;
703 728 font-weight: bold;
704 729 }
705 730 .right .merge img{
706 731 vertical-align: bottom;
707 732 }
708 733
709 734 .right .parent{
710 735 font-size: 90%;
711 736 font-family: monospace;
712 737 }
713 738 /** end of canvas **/
714 739
715 740 /* FILE BROWSER */
716 741 div.browserblock {
717 742 overflow: hidden;
718 743 padding: 0px;
719 744 border: 1px solid #ccc;
720 745 background: #f8f8f8;
721 746 font-size: 100%;
722 747 line-height: 100%;
723 748 /* new */
724 749 line-height: 125%;
725 750 }
726 751 div.browserblock .browser-header{
727 752 border-bottom: 1px solid #CCCCCC;
728 753 background: #EEEEEE;
729 754 color:blue;
730 755 padding:10px 0 10px 0;
731 756 }
732 757 div.browserblock .browser-header span{
733 758 margin-left:25px;
734 759 font-weight: bold;
735 760 }
736 761 div.browserblock .browser-body{
737 762 background: #EEEEEE;
738 763 }
739 764
740 765 table.code-browser {
741 766 border-collapse:collapse;
742 767 width: 100%;
743 768 }
744 769 table.code-browser tr{
745 770 margin:3px;
746 771 }
747 772
748 773 table.code-browser thead th {
749 774 background-color: #EEEEEE;
750 775 height: 20px;
751 776 font-size: 1.1em;
752 777 font-weight: bold;
753 778 text-align: center;
754 779 text-align: left;
755 780 padding-left: 10px;
756 781 }
757 782 table.code-browser tbody tr {
758 783
759 784 }
760 785
761 786 table.code-browser tbody td {
762 787
763 788 padding-left:10px;
764 789 height: 20px;
765 790 }
766 791
767 792 .info-table {
768 793 background: none repeat scroll 0 0 #FAFAFA;
769 794 border-bottom: 1px solid #DDDDDD;
770 795 width: 100%;
771 796 }
772 797
773 798 .rss_logo {
774 799 background: url("/images/icons/rss_16.png") no-repeat scroll 3px;
775 800 height: 16px;
776 801 padding-left: 20px;
777 802 padding-top: 0px;
778 803 text-align: left;
779 804 }
780 805
781 806 .atom_logo {
782 807 background: url("/images/icons/atom.png") no-repeat scroll 3px;
783 808 height: 16px;
784 809 padding-left: 20px;
785 810 padding-top: 0px;
786 811 text-align: left;
787 812 }
788 813 .archive_logo{
789 814 background: url("/images/icons/compress.png") no-repeat scroll 3px;
790 815 height: 16px;
791 816 padding-left: 20px;
792 817 text-align: left;
793 818 }
794 819
795 820 .browser-file {
796 821 background: url("/images/icons/document_16.png") no-repeat scroll 3px;
797 822 height: 16px;
798 823 padding-left: 20px;
799 824 text-align: left;
800 825 }
801 826
802 827 .browser-dir {
803 828 background: url("/images/icons/folder_16.png") no-repeat scroll 3px;
804 829 height: 16px;
805 830 padding-left: 20px;
806 831 text-align: left;
807 832 }
808 833
809 834 #repos_list {
810 835 border: 1px solid #556CB5;
811 836 background: #FFFFFF;
812 837 } No newline at end of file
@@ -1,154 +1,156 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>${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 <script>${h.tooltip.activate()}</script>
31 32 </div>
32 33 <div class="page-footer">
33 34 Hg App ${c.hg_app_version} &copy; 2010 by Marcin Kuzminski
34 35 </div>
35 36
36 37 <div id="powered-by">
37 38 <p>
38 39 <a href="http://mercurial.selenic.com/" title="Mercurial">
39 40 <img src="/images/hglogo.png" width="75" height="90" alt="mercurial"/></a>
40 41 </p>
41 42 </div>
42 43
43 44 <div id="corner-top-left"></div>
44 45 <div id="corner-top-right"></div>
45 46 <div id="corner-bottom-left"></div>
46 47 <div id="corner-bottom-right"></div>
47 48
48 49 </div>
49 50 </body>
50 51 </html>
51 52
52 53 ### MAKO DEFS ###
53 54
54 55 <%def name="page_nav()">
55 56 ${self.menu()}
56 57 ${self.submenu()}
57 58 </%def>
58 59
59 60 <%def name="menu(current)">
60 61 <%
61 62 def is_current(selected):
62 63 if selected == current:
63 64 return "class='current'"
64 65 %>
65 66 %if current not in ['home','admin']:
66 67 ##regular menu
67 68 <script type="text/javascript">
68 69 YAHOO.util.Event.onDOMReady(function(){
69 70 YAHOO.util.Event.addListener('repo_switcher','click',function(){
70 71 if(YAHOO.util.Dom.hasClass('repo_switcher','selected')){
71 72 YAHOO.util.Dom.setStyle('switch_repos','display','none');
72 73 YAHOO.util.Dom.setStyle('repo_switcher','background','');
73 74 YAHOO.util.Dom.removeClass('repo_switcher','selected');
74 75 YAHOO.util.Dom.get('repo_switcher').removeAttribute('style');
75 76 }
76 77 else{
77 78 YAHOO.util.Dom.setStyle('switch_repos','display','');
78 79 YAHOO.util.Dom.setStyle('repo_switcher','background','#FFFFFF');
79 80 YAHOO.util.Dom.setStyle('repo_switcher','color','#556CB5');
80 81 YAHOO.util.Dom.addClass('repo_switcher','selected');
81 82 }
82 83 });
83 84 YAHOO.util.Event.addListener('repos_list','change',function(e){
84 85 var wa = YAHOO.util.Dom.get('repos_list').value;
85 86
86 87 var url = "${h.url('summary_home',repo_name='__REPLACE__')}".replace('__REPLACE__',wa);
87 88 window.location = url;
88 89 })
89 90 });
90 91 </script>
91 92 <ul class="page-nav">
92 93 <li>
93 94 <a id="repo_switcher" title="${_('Switch repository')}" href="#">&darr;</a>
94 95 <div id="switch_repos" style="display:none;position: absolute;height: 25px">
95 96 <select id="repos_list" size="=10" style="min-width: 150px">
96 97 %for repo in sorted(x.name.lower() for x in c.cached_repo_list.values()):
97 98 <option value="${repo}">${repo}</option>
98 99 %endfor
99 100 </select>
100 101 </div>
101 102 </li>
102 103 <li ${is_current('summary')}>${h.link_to(_('summary'),h.url('summary_home',repo_name=c.repo_name))}</li>
103 104 <li ${is_current('shortlog')}>${h.link_to(_('shortlog'),h.url('shortlog_home',repo_name=c.repo_name))}</li>
104 105 <li ${is_current('changelog')}>${h.link_to(_('changelog'),h.url('changelog_home',repo_name=c.repo_name))}</li>
105 106 <li ${is_current('branches')}>${h.link_to(_('branches'),h.url('branches_home',repo_name=c.repo_name))}</li>
106 107 <li ${is_current('tags')}>${h.link_to(_('tags'),h.url('tags_home',repo_name=c.repo_name))}</li>
107 108 <li ${is_current('files')}>${h.link_to(_('files'),h.url('files_home',repo_name=c.repo_name))}</li>
108 109 </ul>
109 110 %else:
110 111 ##Root menu
111 112 <ul class="page-nav">
112 113 <li ${is_current('home')}>${h.link_to(_('Home'),h.url('/'))}</li>
113 114 <li ${is_current('admin')}>${h.link_to(_('Admin'),h.url('admin_home'))}</li>
114 115 <li class="logout">${h.link_to(u'Logout',h.url('logout_home'))}</li>
115 116 </ul>
116 117 %endif
117 118 </div>
118 119 </%def>
119 120 <%def name="submenu(current=None)">
120 121 <%
121 122 def is_current(selected):
122 123 if selected == current:
123 124 return "class='current_submenu'"
124 125 %>
125 126 %if current != None:
126 127 <div>
127 128 <ul class="submenu">
128 129 <li ${is_current('repos')}>${h.link_to(u'repos',h.url('repos'),class_='repos')}</li>
129 130 <li ${is_current('users')}>${h.link_to(u'users',h.url('users'),class_='users')}</li>
130 131 <li ${is_current('permissions')}>${h.link_to(u'permissions',h.url('permissions'),class_='permissions')}</li>
131 132 </ul>
132 133 </div>
133 134 %endif
134 135 </%def>
135 136
136 137
137 138 <%def name="css()">
138 139 <link rel="stylesheet" href="/css/monoblue_custom.css" type="text/css" />
139 140 </%def>
140 141
141 142 <%def name="js()">
142 143 <script type="text/javascript" src="/js/yui/utilities/utilities.js"></script>
144 <script type="text/javascript" src="/js/yui/container/container-min.js"></script>
143 145 </%def>
144 146
145 147 <!-- DEFINITION OF FORM ERROR FETCHER -->
146 148 <%def name="get_form_error(element)">
147 149 %if hasattr(c,'form_errors') and type(c.form_errors) == dict:
148 150 %if c.form_errors.get(element,False):
149 151 <span class="error-message">
150 152 ${c.form_errors.get(element,'')}
151 153 </span>
152 154 %endif
153 155 %endif
154 156 </%def> No newline at end of file
@@ -1,52 +1,56 b''
1 1 ## -*- coding: utf-8 -*-
2 2 <%!
3 3 from pylons_app.lib import filters
4 4 %>
5 5 <%inherit file="base/base.html"/>
6 6 <%def name="title()">
7 7 ${c.repos_prefix} Mercurial Repositories
8 8 </%def>
9 9 <%def name="breadcrumbs()">
10 10 ${c.repos_prefix} Mercurial Repositories
11 11 </%def>
12 12 <%def name="page_nav()">
13 13 ${self.menu('home')}
14 14 </%def>
15 15 <%def name="main()">
16 16 <%def name="get_sort(name)">
17 17 <%name_slug = name.lower().replace(' ','_') %>
18 18 %if name_slug == c.cs_slug:
19 19 <span style="font-weight: bold;text-decoration: underline;">${name}</span>
20 20 %else:
21 21 <span style="font-weight: bold">${name}</span>
22 22 %endif
23 23 <a style="color:#FFF" href="?sort=${name_slug}">&darr;</a>
24 24 <a style="color:#FFF" href="?sort=-${name_slug}">&uarr;</a>
25 25 </%def>
26 26 <table class="table_disp">
27 27 <tr class="header">
28 28 <td>${get_sort(_('Name'))}</td>
29 29 <td>${get_sort(_('Description'))}</td>
30 30 <td>${get_sort(_('Last change'))}</td>
31 31 <td>${get_sort(_('Tip'))}</td>
32 32 <td>${get_sort(_('Contact'))}</td>
33 33 <td>${_('RSS')}</td>
34 34 <td>${_('Atom')}</td>
35 35 </tr>
36 36 %for cnt,repo in enumerate(c.repos_list):
37 37 <tr class="parity${cnt%2}">
38 <td>${h.link(repo['name'],h.url('summary_home',repo_name=repo['name']))}</td>
38 <td>${h.link_to(repo['name'],
39 h.url('summary_home',repo_name=repo['name']))}</td>
39 40 <td title="${repo['description']}">${h.truncate(repo['description'],60)}</td>
40 41 <td>${repo['last_change']|n,filters.age}</td>
41 <td>${h.link_to('r%s:%s' % (repo['rev'],repo['tip']),h.url('changeset_home',repo_name=repo['name'],revision=repo['tip']))}</td>
42 <td>${h.link_to('r%s:%s' % (repo['rev'],repo['tip']),
43 h.url('changeset_home',repo_name=repo['name'],revision=repo['tip']),
44 class_="tooltip",
45 tooltip_title=h.tooltip(repo['last_msg']))}</td>
42 46 <td title="${repo['contact']}">${repo['contact']|n,filters.person}</td>
43 47 <td>
44 48 <a title="${_('Subscribe to %s rss feed')%repo['name']}" class="rss_logo" href="${h.url('rss_feed_home',repo_name=repo['name'])}"></a>
45 49 </td>
46 50 <td>
47 51 <a title="${_('Subscribe to %s atom feed')%repo['name']}" class="atom_logo" href="${h.url('atom_feed_home',repo_name=repo['name'])}"></a>
48 52 </td>
49 53 </tr>
50 54 %endfor
51 55 </table>
52 56 </%def>
General Comments 0
You need to be logged in to leave comments. Login now