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