Review Board

beta

Redo our URL scheme to use {% url %} tags

Updated 8 months, 3 weeks ago

Christian Hammond Reviewers
trunk reviewboard
458, 459
None Review Board SVN
We've been using the old get_absolute_url() function in our templates, which works for the most part but has limitations. Using it on a User object doesn't work so well when installed in a subdirectory, requiring that we prepend SITE_ROOT and then slice the first "/". This gets ugly fast.

We've also been hard-coding other paths ("/account/login/", "/dashboard/", etc.), requiring prepending SITE_ROOT again.

Django's {% url %} tag provides a cleaner solution, allowing us to give certain pages names that we can refer to. Now something like {{SITE_ROOT}}{{submitter.get_absolute_url|slice:"1"}} turns into {% url user submitter %}.

This in theory could also give extensions in the future the ability to override certain pages if they wanted.

The result of this change is that we use get_absolute_url() a lot less for some things and never for users. We could move the rest of the get_absolute_url() calls in the future if we choose, though what we have works for now. We've also eliminated most references to SITE_ROOT in the templates.

There's some misc. fixes and cleanups in here too, such as using djblets's new user_displayname filter.
Made sure every page I tested worked properly in subdirectory installs. All the user links I could find now point to the correct location.

Diff revision 1 (Latest)

  1. /trunk/reviewboard/urls.py: 14 changes [ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 ]
  2. /trunk/reviewboard/accounts/decorators.py: 2 changes [ 1 2 ]
  3. /trunk/reviewboard/accounts/views.py: 4 changes [ 1 2 3 4 ]
  4. /trunk/reviewboard/iphone/urls.py: 17 changes [ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 ]
  5. /trunk/reviewboard/reviews/datagrids.py: 2 changes [ 1 2 ]
  6. /trunk/reviewboard/reviews/urls.py: 4 changes [ 1 2 3 4 ]
  7. /trunk/reviewboard/reviews/views.py: 3 changes [ 1 2 3 ]
  8. /trunk/reviewboard/reviews/templatetags/reviewtags.py: 2 changes [ 1 2 ]
  9. /trunk/reviewboard/templates/base.html: 9 changes [ 1 2 3 4 5 6 7 8 9 ]
  10. /trunk/reviewboard/templates/base_feeds.html: 1 change [ 1 ]
  11. /trunk/reviewboard/templates/accounts/login.html: 1 change [ 1 ]
  12. /trunk/reviewboard/templates/diffviewer/view_diff.html: 2 changes [ 1 2 ]
  13. /trunk/reviewboard/templates/iphone/base.html: 3 changes [ 1 2 3 ]
  14. /trunk/reviewboard/templates/iphone/dashboard.html: 4 changes [ 1 2 3 4 ]
  15. /trunk/reviewboard/templates/iphone/diff_files.html: 1 change [ 1 ]
  16. /trunk/reviewboard/templates/iphone/group_list.html: 1 change [ 1 ]
  17. /trunk/reviewboard/templates/iphone/login.html: 1 change [ 1 ]
  18. /trunk/reviewboard/templates/iphone/review_request_detail.html: 5 changes [ 1 2 3 4 5 ]
  19. /trunk/reviewboard/templates/iphone/review_request_list.html: 1 change [ 1 ]
  20. /trunk/reviewboard/templates/iphone/submitter_list.html: 1 change [ 1 ]
  21. /trunk/reviewboard/templates/reviews/dashboard_entry.html: 1 change [ 1 ]
  22. /trunk/reviewboard/templates/reviews/review_detail.html: 3 changes [ 1 2 3 ]
  23. /trunk/reviewboard/templates/reviews/review_reply.html: 2 changes [ 1 2 ]
  24. /trunk/reviewboard/templates/reviews/review_request_box.html: 3 changes [ 1 2 3 ]
  25. /trunk/reviewboard/templates/reviews/search.html: 3 changes [ 1 2 3 ]
/trunk/reviewboard/urls.py
Revision 1295 New Change
1
import os.path
1
import os.path
2
2
3
from django.conf import settings
3
from django.conf import settings
4
from django.conf.urls.defaults import patterns, include, handler404, handler500
4
from django.conf.urls.defaults import patterns, include, url
5
5
6
from reviewboard.admin.checks import check_updates_required
6
from reviewboard.admin.checks import check_updates_required
7
from reviewboard.reviews.feeds import RssReviewsFeed, AtomReviewsFeed, \
7
from reviewboard.reviews.feeds import RssReviewsFeed, AtomReviewsFeed, \
8
                                      RssSubmitterReviewsFeed, \
8
                                      RssSubmitterReviewsFeed, \
9
                                      AtomSubmitterReviewsFeed, \
9
                                      AtomSubmitterReviewsFeed, \
60
60
61
61
62
    # reviewboard.reviews.views
62
    # reviewboard.reviews.views
63
    urlpatterns += patterns('reviewboard.reviews.views',
63
    urlpatterns += patterns('reviewboard.reviews.views',
64
        # Review request browsing
64
        # Review request browsing
65
        (r'^dashboard/$', 'dashboard'),
65
        url(r'^dashboard/$', 'dashboard', name="dashboard"),
66
66
67
        # Users
67
        # Users
68
        (r'^users/$', 'submitter_list'),
68
        url(r'^users/$', 'submitter_list', name="all-users"),
69
        (r'^users/(?P<username>[A-Za-z0-9_\-\.]+)/$', 'submitter'),
69
        url(r'^users/(?P<username>[A-Za-z0-9_\-\.]+)/$', 'submitter',
70
            name="user"),
70
71
71
        # Groups
72
        # Groups
72
        (r'^groups/$', 'group_list'),
73
        url(r'^groups/$', 'group_list', name="all-groups"),
73
        (r'^groups/(?P<name>[A-Za-z0-9_-]+)/$', 'group'),
74
        url(r'^groups/(?P<name>[A-Za-z0-9_-]+)/$', 'group', name="group"),
74
    )
75
    )
75
76
76
77
77
    # django.contrib
78
    # django.contrib
78
    urlpatterns += patterns('django.contrib',
79
    urlpatterns += patterns('django.contrib',
79
       # Feeds
80
       # Feeds
80
        (r'^feeds/rss/(?P<url>.*)/$', 'syndication.views.feed',
81
        url(r'^feeds/rss/(?P<url>.*)/$', 'syndication.views.feed',
81
         {'feed_dict': rss_feeds}),
82
            {'feed_dict': rss_feeds},
82
        (r'^feeds/atom/(?P<url>.*)/$', 'syndication.views.feed',
83
            name="rss-feed"),
83
         {'feed_dict': atom_feeds}),
84
        url(r'^feeds/atom/(?P<url>.*)/$', 'syndication.views.feed',
84
        (r'^account/logout/$', 'auth.views.logout',
85
           {'feed_dict': atom_feeds},
85
         {'next_page': settings.LOGIN_URL}),
86
           name="atom-feed"),
87
        url(r'^account/logout/$', 'auth.views.logout',
88
            {'next_page': settings.LOGIN_URL},
89
            name="logout")
86
    )
90
    )
87
91
88
92
89
    # And the rest ...
93
    # And the rest ...
90
    urlpatterns += patterns('',
94
    urlpatterns += patterns('',
91
        (r'^$', 'django.views.generic.simple.redirect_to',
95
        url(r'^$', 'django.views.generic.simple.redirect_to',
92
         {'url': 'dashboard/'}),
96
            {'url': 'dashboard/'},
97
            name="root"),
93
98
94
        # Authentication and accounts
99
        # Authentication and accounts
95
        (r'^account/login/$', 'djblets.auth.views.login',
100
        url(r'^account/login/$', 'djblets.auth.views.login',
96
         {'next_page': settings.SITE_ROOT + 'dashboard/',
101
            {'next_page': settings.SITE_ROOT + 'dashboard/',
97
          'extra_context': {'BUILTIN_AUTH': settings.BUILTIN_AUTH}}),
102
             'extra_context': {'BUILTIN_AUTH': settings.BUILTIN_AUTH}},
98
        (r'^account/preferences/$', 'reviewboard.accounts.views.user_preferences',),
103
            name="login"),
104
        url(r'^account/preferences/$',
105
            'reviewboard.accounts.views.user_preferences',
106
            name="user-preferences"),
99
107
100
        # This must be last.
108
        # This must be last.
101
        (r'^iphone/', include('reviewboard.iphone.urls')),
109
        (r'^iphone/', include('reviewboard.iphone.urls')),
102
    )
110
    )
103
111
104
    if settings.BUILTIN_AUTH:
112
    if settings.BUILTIN_AUTH:
105
        urlpatterns += patterns('',
113
        urlpatterns += patterns('',
106
            (r'^account/register/$', 'djblets.auth.views.register',
114
            url(r'^account/register/$', 'djblets.auth.views.register',
107
             {'next_page': settings.SITE_ROOT + 'dashboard/'}),
115
                {'next_page': settings.SITE_ROOT + 'dashboard/'},
116
                name="register"),
108
        )
117
        )
109
    else:
118
    else:
110
        urlpatterns += patterns('',
119
        urlpatterns += patterns('',
111
            (r'^account/register/$',
120
            (r'^account/register/$',
112
             'django.views.generic.simple.redirect_to',
121
             'django.views.generic.simple.redirect_to',
113
             {'url': settings.SITE_ROOT + 'account/login/'}))
122
             {'url': settings.SITE_ROOT + 'account/login/'}))
/trunk/reviewboard/accounts/decorators.py
Revision 1295 New Change
1
from urllib import quote
1
from urllib import quote
2
2
3
from django.conf import settings
3
from django.conf import settings
4
from django.contrib.auth import REDIRECT_FIELD_NAME
4
from django.contrib.auth import REDIRECT_FIELD_NAME
5
from django.core.urlresolvers import reverse
5
from django.http import HttpResponseRedirect
6
from django.http import HttpResponseRedirect
6
7
7
from djblets.auth.util import login_required
8
from djblets.auth.util import login_required
8
from djblets.util.decorators import simple_decorator
9
from djblets.util.decorators import simple_decorator
9
10
39
            if profile.first_time_setup_done:
40
            if profile.first_time_setup_done:
40
                return view_func(request, *args, **kwargs)
41
                return view_func(request, *args, **kwargs)
41
        except Profile.DoesNotExist:
42
        except Profile.DoesNotExist:
42
            pass
43
            pass
43
44
44
        return HttpResponseRedirect("%saccount/preferences/?%s=%s" %
45
        return HttpResponseRedirect("%s?%s=%s" %
45
                                    (settings.SITE_ROOT,
46
                                    (reverse("user-preferences"),
46
                                     REDIRECT_FIELD_NAME,
47
                                     REDIRECT_FIELD_NAME,
47
                                     quote(request.get_full_path())))
48
                                     quote(request.get_full_path())))
48
49
49
    return _check_valid_prefs
50
    return _check_valid_prefs
/trunk/reviewboard/accounts/views.py
Revision 1295 New Change
1
import time
1
import time
2
from sha import sha
2
from sha import sha
3
from django.conf import settings
3
from django.conf import settings
4
from django.contrib.auth import REDIRECT_FIELD_NAME
4
from django.contrib.auth import REDIRECT_FIELD_NAME
5
from django.contrib.auth.models import get_hexdigest
5
from django.contrib.auth.models import get_hexdigest
6
from django.core.urlresolvers import reverse
6
from django.http import HttpResponseRedirect
7
from django.http import HttpResponseRedirect
7
from django.shortcuts import render_to_response
8
from django.shortcuts import render_to_response
8
from django.template.context import RequestContext
9
from django.template.context import RequestContext
9
from djblets.auth.util import login_required
10
from djblets.auth.util import login_required
10
11
11
from reviewboard.accounts.forms import PreferencesForm
12
from reviewboard.accounts.forms import PreferencesForm
12
from reviewboard.accounts.models import Profile
13
from reviewboard.accounts.models import Profile
13
14
14
15
15
@login_required
16
@login_required
16
def user_preferences(request, template_name='accounts/prefs.html'):
17
def user_preferences(request, template_name='accounts/prefs.html'):
17
    redirect_to = request.REQUEST.get(REDIRECT_FIELD_NAME, settings.SITE_ROOT)
18
    redirect_to = request.REQUEST.get(REDIRECT_FIELD_NAME,
19
                                      reverse("dashboard"))
18
20
19
    profile, profile_is_new = \
21
    profile, profile_is_new = \
20
        Profile.objects.get_or_create(user=request.user)
22
        Profile.objects.get_or_create(user=request.user)
21
    must_configure = not profile.first_time_setup_done
23
    must_configure = not profile.first_time_setup_done
22
    profile.save()
24
    profile.save()
42
            profile.syntax_highlighting = \
44
            profile.syntax_highlighting = \
43
                form.cleaned_data['syntax_highlighting']
45
                form.cleaned_data['syntax_highlighting']
44
            profile.save()
46
            profile.save()
45
47
46
            return HttpResponseRedirect(redirect_to)
48
            return HttpResponseRedirect(redirect_to)
47
48
    else:
49
    else:
49
        form = PreferencesForm({
50
        form = PreferencesForm({
50
            'settings': settings,
51
            'settings': settings,
51
            'redirect_to': redirect_to,
52
            'redirect_to': redirect_to,
52
            'first_name': request.user.first_name,
53
            'first_name': request.user.first_name,
/trunk/reviewboard/iphone/urls.py
Revision 1295 New Change
1
from django.conf import settings
1
from django.conf import settings
2
from django.conf.urls.defaults import patterns
2
from django.conf.urls.defaults import patterns, url
3
3
4
urlpatterns = patterns('',
4
urlpatterns = patterns('',
5
    (r'^$', 'django.views.generic.simple.direct_to_template',
5
    (r'^$', 'django.views.generic.simple.direct_to_template',
6
     {'template': 'iphone/base.html'}),
6
     {'template': 'iphone/base.html'}),
7
7
8
    # Accounts
8
    # Accounts
9
    (r'^account/login/$', 'djblets.auth.views.login',
9
    url(r'^account/login/$', 'djblets.auth.views.login',
10
     {'next_page': '/iphone/',
10
        {'next_page': settings.SITE_ROOT + '/iphone/',
11
      'template_name': 'iphone/login.html',
11
         'template_name': 'iphone/login.html',
12
      'extra_context': {'BUILTIN_AUTH': settings.BUILTIN_AUTH}}),
12
         'extra_context': {'BUILTIN_AUTH': settings.BUILTIN_AUTH}},
13
    (r'^account/logout/$', 'django.contrib.auth.views.logout',
13
        name="iphone-login"),
14
     {'next_page': '/iphone/'}),
14
    url(r'^account/logout/$', 'django.contrib.auth.views.logout',
15
    (r'^account/preferences/$', 'reviewboard.accounts.views.user_preferences',
15
        {'next_page': settings.SITE_ROOT + 'iphone/'},
16
     {'template_name': 'iphone/user_prefs.html'}),
16
        name="iphone-logout"),
17
    url(r'^account/preferences/$',
18
        'reviewboard.accounts.views.user_preferences',
19
        {'template_name': 'iphone/user_prefs.html'},
20
        name="iphone-user-preferences"),
17
)
21
)
18
22
19
urlpatterns += patterns('reviewboard.reviews.views',
23
urlpatterns += patterns('reviewboard.reviews.views',
20
    # Dashboard
24
    # Dashboard
21
    (r'^dashboard/$', 'dashboard',
25
    url(r'^dashboard/$', 'dashboard',
22
     {'template_name': 'iphone/dashboard.html'}),
26
        {'template_name': 'iphone/dashboard.html'},
23
    (r'^dashboard/list/$', 'dashboard',
27
        name="iphone-dashboard"),
24
     {'template_name': 'iphone/dashboard_list.html'}),
28
    url(r'^dashboard/list/$', 'dashboard',
29
        {'template_name': 'iphone/dashboard_list.html'},
30
        name="iphone-dashboard-list"),
25
31
26
    # Users
32
    # Users
27
    (r'^users/$', 'submitter_list',
33
    url(r'^users/$', 'submitter_list',
28
     {'template_name': 'iphone/submitter_list.html'}),
34
        {'template_name': 'iphone/submitter_list.html'},
29
    (r'^users/(?P<username>[A-Za-z0-9_-]+)/$', 'submitter',
35
        name="iphone-users"),
30
     {'template_name': 'iphone/review_request_list.html'}),
36
    url(r'^users/(?P<username>[A-Za-z0-9_-]+)/$', 'submitter',
37
        {'template_name': 'iphone/review_request_list.html'},
38
        name="iphone-user"),
31
39
32
    # Groups
40
    # Groups
33
    (r'^groups/$', 'group_list',
41
    url(r'^groups/$', 'group_list',
34
     {'template_name': 'iphone/group_list.html'}),
42
        {'template_name': 'iphone/group_list.html'},
35
    (r'^groups/(?P<name>[A-Za-z0-9_-]+)/$', 'group',
43
        name="iphone-groups"),
36
     {'template_name': 'iphone/review_request_list.html'}),
44
    url(r'^groups/(?P<name>[A-Za-z0-9_-]+)/$', 'group',
45
        {'template_name': 'iphone/review_request_list.html'},
46
        name="iphone-group"),
37
47
38
    # Review Requests
48
    # Review Requests
39
    (r'^r/$', 'all_review_requests',
49
    url(r'^r/$', 'all_review_requests',
40
     {'template_name': 'iphone/review_request_list.html'}),
50
        {'template_name': 'iphone/review_request_list.html'},
41
51
        name="iphone-all-review-requests"),
42
    (r'^r/(?P<review_request_id>[0-9]+)/$', 'review_detail',
52
43
     {'template_name': 'iphone/review_request_detail.html'}),
53
    url(r'^r/(?P<review_request_id>[0-9]+)/$', 'review_detail',
54
        {'template_name': 'iphone/review_request_detail.html'},
55
        name="iphone-review-request"),
44
56
45
    # Diffs
57
    # Diffs
46
    (r'^r/(?P<review_request_id>[0-9]+)/diff/$', 'diff',
58
    url(r'^r/(?P<review_request_id>[0-9]+)/diff/$', 'diff',
47
     {'template_name': 'iphone/diff_files.html'}),
59
        {'template_name': 'iphone/diff_files.html'},
60
        name="iphone-diff"),
48
61
49
    (r'^r/(?P<review_request_id>[0-9]+)/diff/(?P<revision>[0-9]+)/(?P<filediff_id>[0-9]+)/$',
62
    url(r'^r/(?P<review_request_id>[0-9]+)/diff/(?P<revision>[0-9]+)/(?P<filediff_id>[0-9]+)/$',
50
     'diff_fragment',
63
        'diff_fragment',
51
     {'collapseall': True,
64
        {'collapseall': True,
52
      'template_name': 'iphone/diff.html'}),
65
         'template_name': 'iphone/diff.html'},
66
        name="iphone-diff-fragment"),
53
)
67
)
/trunk/reviewboard/reviews/datagrids.py
Revision 1295 New Change
1
from django.conf import settings
1
from django.conf import settings
2
from django.contrib.auth.models import User
2
from django.contrib.auth.models import User
3
from django.core.urlresolvers import reverse
3
from django.db.models import Q
4
from django.db.models import Q
4
from django.template import Template
5
from django.template import Template
5
from django.template.context import RequestContext
6
from django.template.context import RequestContext
6
from django.template.defaultfilters import date, timesince
7
from django.template.defaultfilters import date, timesince
7
from django.template.loader import render_to_string
8
from django.template.loader import render_to_string
321
            "username", "fullname", "pending_reviews"
322
            "username", "fullname", "pending_reviews"
322
        ]
323
        ]
323
324
324
    @staticmethod
325
    @staticmethod
325
    def link_to_object(obj, value):
326
    def link_to_object(obj, value):
326
        return settings.SITE_ROOT + obj.get_absolute_url()[1:]
327
        return reverse("user", obj)
327
328
328
329
329
class GroupDataGrid(DataGrid):
330
class GroupDataGrid(DataGrid):
330
    """
331
    """
331
    A datagrid showing a list of review groups.
332
    A datagrid showing a list of review groups.
/trunk/reviewboard/reviews/urls.py
Revision 1295 New Change
1
from django.conf.urls.defaults import patterns
1
from django.conf.urls.defaults import patterns, url
2
2
3
urlpatterns = patterns('reviewboard.reviews.views',
3
urlpatterns = patterns('reviewboard.reviews.views',
4
    (r'^$', 'all_review_requests'),
4
    url(r'^$', 'all_review_requests', name="all-review-requests"),
5
5
6
    # Review request creation
6
    # Review request creation
7
    (r'^new/$', 'new_review_request'),
7
    url(r'^new/$', 'new_review_request', name="new-review-request"),
8
8
9
    # Review request detail
9
    # Review request detail
10
    (r'^(?P<review_request_id>[0-9]+)/$', 'review_detail',
10
    (r'^(?P<review_request_id>[0-9]+)/$', 'review_detail',
11
     {'template_name': 'reviews/review_detail.html'}),
11
     {'template_name': 'reviews/review_detail.html'}),
12
12
49
     'preview_review_email'),
49
     'preview_review_email'),
50
    (r'^(?P<review_request_id>[0-9]+)/reviews/(?P<review_id>[0-9]+)/replies/(?P<reply_id>[0-9]+)/preview-email/$',
50
    (r'^(?P<review_request_id>[0-9]+)/reviews/(?P<review_id>[0-9]+)/replies/(?P<reply_id>[0-9]+)/preview-email/$',
51
     'preview_reply_email'),
51
     'preview_reply_email'),
52
52
53
    # Search
53
    # Search
54
    (r'^search/$', 'search'),
54
    url(r'^search/$', 'search', name="search"),
55
)
55
)
/trunk/reviewboard/reviews/views.py
Revision 1295 New Change
1
from datetime import datetime
1
from datetime import datetime
2
2
3
from django.conf import settings
3
from django.conf import settings
4
from django.contrib.auth.models import User
4
from django.contrib.auth.models import User
5
from django.contrib.sites.models import Site
5
from django.contrib.sites.models import Site
6
from django.core.urlresolvers import reverse
6
from django.db.models import Q
7
from django.db.models import Q
7
from django.http import HttpResponse, HttpResponseRedirect, Http404, \
8
from django.http import HttpResponse, HttpResponseRedirect, Http404, \
8
                        HttpResponseForbidden
9
                        HttpResponseForbidden
9
from django.shortcuts import get_object_or_404, render_to_response
10
from django.shortcuts import get_object_or_404, render_to_response
10
from django.template.context import RequestContext
11
from django.template.context import RequestContext
427
        # This should never happen under normal circumstances
428
        # This should never happen under normal circumstances
428
        raise Exception('Error when setting review status: unknown status code')
429
        raise Exception('Error when setting review status: unknown status code')
429
430
430
    review_request.save()
431
    review_request.save()
431
    if action == 'discard':
432
    if action == 'discard':
432
        return HttpResponseRedirect(settings.SITE_ROOT + 'dashboard/')
433
        return HttpResponseRedirect(reverse("dashboard"))
433
    else:
434
    else:
434
        return HttpResponseRedirect(review_request.get_absolute_url())
435
        return HttpResponseRedirect(review_request.get_absolute_url())
435
436
436
437
437
@check_login_required
438
@check_login_required
566
        # FIXME: show something useful
567
        # FIXME: show something useful
567
        raise Http404
568
        raise Http404
568
569
569
    if not query:
570
    if not query:
570
        # FIXME: I'm not super thrilled with this
571
        # FIXME: I'm not super thrilled with this
571
        return HttpResponseRedirect('/')
572
        return HttpResponseRedirect(reverse("root"))
572
573
573
    import lucene
574
    import lucene
574
575
575
    # We may have already initialized lucene
576
    # We may have already initialized lucene
576
    try:
577
    try:
/trunk/reviewboard/reviews/templatetags/reviewtags.py
Revision 1295 New Change
318
        else:
318
        else:
319
            count = len(review_requests)
319
            count = len(review_requests)
320
320
321
    return {
321
    return {
322
        'MEDIA_URL': settings.MEDIA_URL,
322
        'MEDIA_URL': settings.MEDIA_URL,
323
        'SITE_ROOT': settings.SITE_ROOT,
324
        'level': level,
323
        'level': level,
325
        'text': text,
324
        'text': text,
326
        'view': view,
325
        'view': view,
327
        'group': group,
326
        'group': group,
328
        'count': count,
327
        'count': count,
484
    return render_to_string('reviews/star.html', {
483
    return render_to_string('reviews/star.html', {
485
        'object': obj_info,
484
        'object': obj_info,
486
        'starred': int(starred),
485
        'starred': int(starred),
487
        'user': user,
486
        'user': user,
488
        'MEDIA_URL': settings.MEDIA_URL,
487
        'MEDIA_URL': settings.MEDIA_URL,
489
        'SITE_ROOT': settings.SITE_ROOT,
490
    })
488
    })
/trunk/reviewboard/templates/base.html
Revision 1295 New Change
31
{% block bodytag %}
31
{% block bodytag %}
32
 <body>
32
 <body>