blob: 346e278e18ee318b827551a406288245145eca74 [file] [log] [blame]
# Copyright 2008 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from django.utils import safestring
from django.utils import html
from django.template import defaultfilters
import logging
import operator
import library
class FieldRendererException(Exception):
def __init__(self, msg):
self.msg = msg
def __str__(self):
return "FieldRendererException('%s')" % self.msg
def _render_star(change, current_user):
id = change.key().id()
list = []
list.append("""<span id="change-star-%d">""" % id)
if change.is_starred:
list.append("""<a href="javascript:M_removeChangeStar(%d)"><img src="/static/star-lite.gif" width="15" height="15" border="0"></a>"""
% id)
else:
if current_user:
list.append("""<a href="javascript:M_addChangeStar(%d)"><img src="/static/star-dark.gif" width="15" height="15" border="0"></a>"""
% id)
list.append("</span>")
return "".join(list)
class BaseFieldRenderer(object):
"""Base class for renderers for particular fields.
Implement the render_title() and render_contents() methods.
"""
def column_count(self):
raise FieldRendererException, "column_count not implemented"
def render_title(self):
"""Render the title for a column in the table.
Returns:
The string of the rendered title.
"""
raise FieldRendererException, "render_title not implemented"
def render_contents(self, change, current_user):
raise FieldRendererException, "render_contents not implemented"
class IdFieldRenderer(BaseFieldRenderer):
def column_count(self):
return 3
def render_title(self):
return """<th class="header-columns" colspan="3" style="text-align: right;">Id</th>
"""
def render_contents(self, change, current_user):
return """<td class="selection"><img src="/static/closedtriangle.gif" style="visibility: hidden;" width="12" height="9" /></td>
<td class="star" width="17" align="center">%(star)s</td>
<td class="id" align="right"><a href="/%(id)d">%(id)d</a></td>""" % {
'id': change.key().id(),
'star': _render_star(change, current_user)
}
class FieldRenderer(BaseFieldRenderer):
def __init__(self, cell_class, title, func):
self.cell_class = cell_class
self.title = title
self.func = func
def column_count(self):
return 1
def render_title(self):
return """<th class="header-columns %(cell_class)s">%(title)s</th>""" % {
'title': self.title,
'cell_class': self.cell_class,
}
def render_contents(self, change, current_user):
return """<td class="%(cell_class)s">%(cell_contents)s</td>""" % {
'cell_contents': self.func(change),
'cell_class': self.cell_class,
}
class SubjectFieldRenderer(FieldRenderer):
def __init__(self):
FieldRenderer.__init__(self, 'subject', 'Subject',
lambda c: """<a href="/%(id)d">%(subject)s</a> %(closed)s""" % {
'id': c.key().id(),
'subject': html.escape(defaultfilters.truncatewords(c.subject, 11)),
'closed': library.closed_label(c),
})
def render_title(self):
return """<th class="header-columns %(class)s">%(title)s</th>""" % {
'title': self.title,
'class': self.cell_class,
}
class UserListFieldRenderer(FieldRenderer):
def __init__(self, cell_class, title, func):
FieldRenderer.__init__(self, cell_class, title,
lambda c: ", ".join(map(library.show_user, func(c))))
FIELD_ID = IdFieldRenderer()
FIELD_SUBJECT = SubjectFieldRenderer()
FIELD_OWNER = UserListFieldRenderer('owner', 'Owner', lambda c: [c.owner])
FIELD_REVIEWERS = UserListFieldRenderer('reviewers', 'Reviewers',
lambda c: c.reviewers)
FIELD_PROJECT = FieldRenderer('project', 'Project', lambda c: html.escape(c.dest_project.name))
FIELD_MODIFIED = FieldRenderer('modified', 'Last Update',
lambda c: library.abbrevtimesince(c.modified))
# this one should be change so that it's what they see in repo
FIELD_BRANCH = FieldRenderer('branch', 'Branch', lambda c: html.escape(c.dest_branch.short_name))
class ChangeTable(object):
"""An object to help render a list of changes as a table.
Generally, changes are grouped into sections. If a section's title is
None, then the section header won't be rendered, and it will appear to
be merged with the last section.
"""
def __init__(self, current_user, fields):
"""Construct a ChangeTable object.
Args:
fields - A list of BaseFieldRenderer objects. See the FIELD_ constants
in this module for the common ones.
"""
self.current_user = current_user
self.fields = fields
self.sections = []
def add_section(self, title, changes):
self.sections.append({'title': title, 'changes': changes})
def render(self):
column_count = reduce(operator.add,
map(lambda x: x.column_count(), self.fields))
def _render_none():
return ("""<tr><td colspan="%d" class="disabled">(None)</td></tr>"""
% column_count)
lines = []
lines.append("""<table class="change-list" id="user-queues">""")
lines.append("""<tr>""")
for field in self.fields:
lines.append(field.render_title())
lines.append("""</tr>""")
if len(self.sections) == 0:
lines.append(_render_none())
else:
for section in self.sections:
title = section['title']
if not title is None:
lines.append("""<tr><th class="header-title" colspan="%d">%s</th></tr>"""
% (column_count, html.escape(title)))
changes = section['changes']
if not changes:
lines.append(_render_none())
else:
for change in section['changes']:
lines.append("""<tr name="change">""")
for field in self.fields:
lines.append(field.render_contents(change, self.current_user))
lines.append("</tr>")
lines.append("""</table>""")
return safestring.mark_safe("\n".join(lines))