You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@allura.apache.org by br...@apache.org on 2019/03/27 21:03:12 UTC

[allura] 01/08: [#8273] upgrade TG 2.2.2 webob 1.1.1

This is an automated email from the ASF dual-hosted git repository.

brondsem pushed a commit to branch db/8273
in repository https://gitbox.apache.org/repos/asf/allura.git

commit 330ab363a61ef66872a2c299c635d54b920b97c3
Author: Dave Brondsema <da...@brondsema.net>
AuthorDate: Fri Mar 2 11:04:05 2018 -0500

    [#8273] upgrade TG 2.2.2 webob 1.1.1
    
    Some failures with "." in URL will persist until TG 2.3.2
---
 Allura/allura/app.py                               |  6 +++---
 Allura/allura/controllers/attachments.py           |  2 +-
 Allura/allura/controllers/auth.py                  |  6 +++---
 Allura/allura/controllers/base.py                  |  4 ++--
 Allura/allura/controllers/discuss.py               | 24 +++++++++++-----------
 Allura/allura/controllers/project.py               |  8 ++++----
 Allura/allura/controllers/repository.py            |  4 ++--
 Allura/allura/controllers/site_admin.py            | 12 +++++------
 Allura/allura/ext/admin/admin_main.py              |  2 +-
 Allura/allura/ext/user_profile/user_main.py        |  8 ++++----
 Allura/allura/lib/base.py                          | 16 ---------------
 Allura/allura/lib/repository.py                    |  4 ++--
 .../allura/tests/scripts/test_delete_projects.py   |  2 ++
 ForgeBlog/forgeblog/main.py                        |  4 ++--
 ForgeChat/forgechat/main.py                        |  2 +-
 .../forgediscussion/controllers/root.py            |  2 +-
 ForgeDiscussion/forgediscussion/forum_main.py      |  2 +-
 ForgeSVN/forgesvn/svn_main.py                      |  2 +-
 ForgeShortUrl/forgeshorturl/main.py                |  8 ++++----
 ForgeTracker/forgetracker/tracker_main.py          | 14 ++++++-------
 ForgeWiki/forgewiki/wiki_main.py                   |  6 +++---
 requirements.txt                                   | 12 ++++++-----
 22 files changed, 69 insertions(+), 81 deletions(-)

diff --git a/Allura/allura/app.py b/Allura/allura/app.py
index b687be0..9481eed 100644
--- a/Allura/allura/app.py
+++ b/Allura/allura/app.py
@@ -917,7 +917,7 @@ class DefaultAdminController(BaseController, AdminControllerMixin):
         require_access(self.app, 'configure')
         self.app.config.options['mount_label'] = mount_label
         g.post_event('project_menu_updated')
-        redirect(request.referer)
+        redirect(request.referer or '/')
 
     @expose('jinja:allura:templates/app_admin_options.html')
     def options(self):
@@ -972,7 +972,7 @@ class DefaultAdminController(BaseController, AdminControllerMixin):
                          + self.app.config.options.mount_point
                          + '/')
             else:
-                redirect(request.referer)
+                redirect(request.referer or '/')
 
     @without_trailing_slash
     @expose()
@@ -1024,7 +1024,7 @@ class DefaultAdminController(BaseController, AdminControllerMixin):
             for ace in old_acl:
                 if (ace.permission == perm) and (ace.access == model.ACE.DENY):
                     self.app.config.acl.append(ace)
-        redirect(request.referer)
+        redirect(request.referer or '/')
 
 
 class WebhooksLookup(BaseController, AdminControllerMixin):
diff --git a/Allura/allura/controllers/attachments.py b/Allura/allura/controllers/attachments.py
index 34ef984..11bf24d 100644
--- a/Allura/allura/controllers/attachments.py
+++ b/Allura/allura/controllers/attachments.py
@@ -115,7 +115,7 @@ class AttachmentController(BaseController):
             self.handle_post(delete, **kw)
             if is_ajax(request):
                 return
-            redirect(request.referer)
+            redirect(request.referer or '/')
         if self.artifact.deleted:
             raise exc.HTTPNotFound
         embed = False
diff --git a/Allura/allura/controllers/auth.py b/Allura/allura/controllers/auth.py
index 514d842..3333b66 100644
--- a/Allura/allura/controllers/auth.py
+++ b/Allura/allura/controllers/auth.py
@@ -265,7 +265,7 @@ class AuthController(BaseController):
             flash('Verification link sent')
         else:
             flash('No such address', 'error')
-        redirect(request.referer)
+        redirect(request.referer or '/')
 
     def _verify_addr(self, addr, do_auth_check=True):
         confirmed_by_other = M.EmailAddress.find(dict(email=addr.email, confirmed=True)).all() if addr else []
@@ -681,7 +681,7 @@ class PreferencesController(BaseController):
     @require_post()
     def user_message(self, allow_user_messages=False):
         c.user.set_pref('disable_user_messages', not allow_user_messages)
-        redirect(request.referer)
+        redirect(request.referer or '/')
 
     @expose('jinja:allura:templates/user_totp.html')
     @without_trailing_slash
@@ -1204,7 +1204,7 @@ class SubscriptionsController(BaseController):
         if email_format:
             c.user.set_pref('email_format', email_format)
 
-        redirect(request.referer)
+        redirect(request.referer or '/')
 
 
 class OAuthController(BaseController):
diff --git a/Allura/allura/controllers/base.py b/Allura/allura/controllers/base.py
index 2cf0c70..6a6ab28 100644
--- a/Allura/allura/controllers/base.py
+++ b/Allura/allura/controllers/base.py
@@ -19,7 +19,7 @@ import logging
 
 from tg import expose
 from webob import exc
-from tg.controllers.dispatcher import ObjectDispatcher
+from crank.objectdispatcher import ObjectDispatcher
 from tg import redirect, flash
 from pylons import tmpl_context as c
 
@@ -40,7 +40,7 @@ class BaseController(object):
             msg = '{} rate limit exceeded. '.format(message)
             log.warn(msg + c.app.config.url())
             flash(msg + 'Please try again later.', 'error')
-            redirect(redir)
+            redirect(redir or '/')
 
 
 class DispatchIndex(object):
diff --git a/Allura/allura/controllers/discuss.py b/Allura/allura/controllers/discuss.py
index fdc89cc..34f085d 100644
--- a/Allura/allura/controllers/discuss.py
+++ b/Allura/allura/controllers/discuss.py
@@ -95,7 +95,7 @@ class DiscussionController(BaseController, FeedController):
             self.moderate = ModerationController(self)
 
     def error_handler(self, *args, **kwargs):
-        redirect(request.referer)
+        redirect(request.referer or '/')
 
     @h.vardec
     @expose()
@@ -110,7 +110,7 @@ class DiscussionController(BaseController, FeedController):
                 thread.unsubscribe()
             session(self.M.Thread)._get().skip_mod_date = True
             session(self.M.Thread)._get().skip_last_updated = True
-        redirect(request.referer)
+        redirect(request.referer or '/')
 
     def get_feed(self, project, app, user):
         """Return a :class:`allura.controllers.feed.FeedArgs` object describing
@@ -212,7 +212,7 @@ class ThreadController(BaseController, FeedController):
                     show_moderate=kw.get('show_moderate'))
 
     def error_handler(self, *args, **kwargs):
-        redirect(request.referer)
+        redirect(request.referer or '/')
 
     @memorable_forget()
     @h.vardec
@@ -233,7 +233,7 @@ class ThreadController(BaseController, FeedController):
         if self.thread.ref:
             require_access(self.thread.ref.artifact, 'post')
         self.thread.labels = labels.split(',')
-        redirect(request.referer)
+        redirect(request.referer or '/')
 
     @expose()
     def flag_as_spam(self, **kw):
@@ -264,14 +264,14 @@ def handle_post_or_reply(thread, edit_widget, rate_limit, kw, parent_post_id=Non
     if not kw['text']:
         flash('Your post was not saved. You must provide content.',
               'error')
-        redirect(request.referer)
+        redirect(request.referer or '/')
     file_info = kw.get('file_info', None)
     p = thread.add_post(parent_id=parent_post_id, **kw)
     p.add_multiple_attachments(file_info)
     if thread.artifact:
         thread.artifact.mod_date = datetime.utcnow()
     flash('Message posted')
-    redirect(request.referer)
+    redirect(request.referer or '/')
 
 
 class PostController(BaseController):
@@ -327,7 +327,7 @@ class PostController(BaseController):
                                        target=self.post.thread.artifact or self.post.thread,
                                        related_nodes=[self.post.app_config.project],
                                        tags=['comment'])
-            redirect(request.referer)
+            redirect(request.referer or '/')
         elif request.method == 'GET':
             if self.post.deleted:
                 raise exc.HTTPNotFound
@@ -357,7 +357,7 @@ class PostController(BaseController):
     @without_trailing_slash
     @expose('json:')
     @require_post()
-    def update_markdown(self, text=None, **kw):  
+    def update_markdown(self, text=None, **kw):
         if has_access(self.post, 'moderate'):
             self.post.text = text
             self.post.edit_count = self.post.edit_count + 1
@@ -393,11 +393,11 @@ class PostController(BaseController):
         if r in utils.get_reaction_emoji_list():
             self.post.post_reaction(r, c.user)
         else:
-            status = 'error' 
+            status = 'error'
         return dict(status=status, counts=self.post.react_counts)
 
     def error_handler(self, *args, **kwargs):
-        redirect(request.referer)
+        redirect(request.referer or '/')
 
     @memorable_forget()
     @h.vardec
@@ -439,7 +439,7 @@ class PostController(BaseController):
     def attach(self, file_info=None):
         require_access(self.post, 'moderate')
         self.post.add_multiple_attachments(file_info)
-        redirect(request.referer)
+        redirect(request.referer or '/')
 
     @expose()
     def _lookup(self, id, *remainder):
@@ -539,7 +539,7 @@ class ModerationController(BaseController):
                         posted.approve()
                         g.spam_checker.submit_ham(posted.text, artifact=posted, user=posted.author())
                         posted.thread.post_to_feed(posted)
-        redirect(request.referer)
+        redirect(request.referer or '/')
 
 
 class PostRestController(PostController):
diff --git a/Allura/allura/controllers/project.py b/Allura/allura/controllers/project.py
index 0a6cbfa..d7c2d3d 100644
--- a/Allura/allura/controllers/project.py
+++ b/Allura/allura/controllers/project.py
@@ -881,7 +881,7 @@ class NeighborhoodAwardsController(object):
                     icon.filename, icon.file, content_type=icon.type,
                     square=True, thumbnail_size=(48, 48),
                     thumbnail_meta=dict(award_id=award._id))
-        redirect(request.referer)
+        redirect(request.referer or '/')
 
     @expose()
     @require_post()
@@ -902,7 +902,7 @@ class NeighborhoodAwardsController(object):
             award.comment = comment
             with h.push_context(recipient_q._id):
                 g.post_event('project_updated')
-        redirect(request.referer)
+        redirect(request.referer or '/')
 
 
 class AwardController(object):
@@ -970,7 +970,7 @@ class AwardController(object):
                     g.post_event('project_updated')
             M.AwardFile.query.remove(dict(award_id=self.award._id))
             self.award.delete()
-        redirect(request.referer)
+        redirect(request.referer or '/')
 
 
 class GrantController(object):
@@ -1012,7 +1012,7 @@ class GrantController(object):
         self.grant.delete()
         with h.push_context(self.project._id):
             g.post_event('project_updated')
-        redirect(request.referer)
+        redirect(request.referer or '/')
 
 
 class ProjectImporterController(object):
diff --git a/Allura/allura/controllers/repository.py b/Allura/allura/controllers/repository.py
index 0989e0c..650065c 100644
--- a/Allura/allura/controllers/repository.py
+++ b/Allura/allura/controllers/repository.py
@@ -111,7 +111,7 @@ class RepoRootController(BaseController, FeedController):
         allura.tasks.repo_tasks.refresh.post()
         if request.referer:
             flash('Repository is being refreshed')
-            redirect(request.referer)
+            redirect(request.referer or '/')
         else:
             return '%r refresh queued.\n' % c.app.repo
 
@@ -152,7 +152,7 @@ class RepoRootController(BaseController, FeedController):
                     raise
                 except Exception, ex:
                     flash(str(ex), 'error')
-                    redirect(request.referer)
+                    redirect(request.referer or '/')
 
     @property
     def mr_widget(self):
diff --git a/Allura/allura/controllers/site_admin.py b/Allura/allura/controllers/site_admin.py
index 830bcb4..d5d91f1 100644
--- a/Allura/allura/controllers/site_admin.py
+++ b/Allura/allura/controllers/site_admin.py
@@ -509,7 +509,7 @@ class SiteNotificationController(object):
     def delete(self):
         self.note.delete()
         ThreadLocalORMSession().flush_all()
-        redirect(request.referer)
+        redirect(request.referer or '/')
 
 
 class TaskManagerController(object):
@@ -694,7 +694,7 @@ class AdminUserDetailsController(object):
             flash('Comment added', 'ok')
         else:
             flash('Can not add comment "%s" for user %s' % (comment, user))
-        redirect(request.referer)
+        redirect(request.referer or '/')
 
     @expose()
     @require_post()
@@ -713,7 +713,7 @@ class AdminUserDetailsController(object):
             AuthenticationProvider.get(request).deactivate_user(user)
             AuthenticationProvider.get(request).enable_user(user, audit=False)
             flash('Set user status to pending')
-        redirect(request.referer)
+        redirect(request.referer or '/')
 
     @expose()
     @require_post()
@@ -725,7 +725,7 @@ class AdminUserDetailsController(object):
         AuthenticationProvider.get(request).set_password(user, None, pwd)
         h.auditlog_user('Set random password', user=user)
         flash('Password is set', 'ok')
-        redirect(request.referer)
+        redirect(request.referer or '/')
 
     @expose()
     @require_post()
@@ -738,7 +738,7 @@ class AdminUserDetailsController(object):
             allura.controllers.auth.AuthController().password_recovery_hash(email)
         except HTTPFound:
             pass  # catch redirect to '/'
-        redirect(request.referer)
+        redirect(request.referer or '/')
 
     @h.vardec
     @expose()
@@ -748,7 +748,7 @@ class AdminUserDetailsController(object):
         if not user or user.is_anonymous():
             raise HTTPNotFound()
         allura.controllers.auth.PreferencesController()._update_emails(user, admin=True, form_params=kw)
-        redirect(request.referer)
+        redirect(request.referer or '/')
 
 
 class StatsSiteAdminExtension(SiteAdminExtension):
diff --git a/Allura/allura/ext/admin/admin_main.py b/Allura/allura/ext/admin/admin_main.py
index e0c5369..4b7ec42 100644
--- a/Allura/allura/ext/admin/admin_main.py
+++ b/Allura/allura/ext/admin/admin_main.py
@@ -707,7 +707,7 @@ class ProjectAdminController(BaseController):
             re.search(c.project.url() + r'(admin\/|)' + tool[0]['mount_point']+ r'\/*', request.referer):
             # Redirect to root when deleting currect module
             redirect('../')
-        redirect(request.referer)
+        redirect(request.referer or '/')
 
     @expose('jinja:allura.ext.admin:templates/export.html')
     def export(self, tools=None, with_attachments=False):
diff --git a/Allura/allura/ext/user_profile/user_main.py b/Allura/allura/ext/user_profile/user_main.py
index 1189174..50bd64a 100644
--- a/Allura/allura/ext/user_profile/user_main.py
+++ b/Allura/allura/ext/user_profile/user_main.py
@@ -124,21 +124,21 @@ class UserProfileController(BaseController, FeedController):
     def _check_can_message(self, from_user, to_user):
         if from_user is User.anonymous():
             flash('You must be logged in to send user messages.', 'info')
-            redirect(request.referer)
+            redirect(request.referer or '/')
 
         if not (from_user and from_user.get_pref('email_address')):
             flash('In order to send messages, you must have an email address '
                   'associated with your account.', 'info')
-            redirect(request.referer)
+            redirect(request.referer or '/')
 
         if not (to_user and to_user.get_pref('email_address')):
             flash('This user can not receive messages because they do not have '
                   'an email address associated with their account.', 'info')
-            redirect(request.referer)
+            redirect(request.referer or '/')
 
         if to_user.get_pref('disable_user_messages'):
             flash('This user has disabled direct email messages', 'info')
-            redirect(request.referer)
+            redirect(request.referer or '/')
 
     @expose('jinja:allura.ext.user_profile:templates/user_index.html')
     def index(self, **kw):
diff --git a/Allura/allura/lib/base.py b/Allura/allura/lib/base.py
index b0c9050..8000e88 100644
--- a/Allura/allura/lib/base.py
+++ b/Allura/allura/lib/base.py
@@ -55,19 +55,3 @@ class WsgiDispatchController(TGController):
         for chunk in response:
             yield chunk
         self._cleanup_request()
-
-    def _get_dispatchable(self, url_path):
-        """Patch ``TGController._get_dispatchable`` by overriding.
-
-        This fixes a bug in TG 2.1.5 that causes ``request.response_type``
-        to not be created if ``disable_request_extensions = True`` (see
-        allura/config/app_cfg.py).
-
-        ``request.response_type`` must be set because the "trailing slash"
-        decorators use it (see allura/lib/patches.py).
-
-        This entire method can be removed if/when we upgrade to TG >= 2.2.1
-
-        """
-        pylons.request.response_type = None
-        return super(WsgiDispatchController, self)._get_dispatchable(url_path)
diff --git a/Allura/allura/lib/repository.py b/Allura/allura/lib/repository.py
index c176a63..82d09c4 100644
--- a/Allura/allura/lib/repository.py
+++ b/Allura/allura/lib/repository.py
@@ -266,7 +266,7 @@ class RepoAdminController(DefaultAdminController):
     def set_default_branch_name(self, branch_name=None, **kw):
         if (request.method == 'POST') and branch_name:
             self.repo.set_default_branch(branch_name)
-            redirect(request.referer)
+            redirect(request.referer or '/')
         else:
             return dict(app=self.app,
                         default_branch_name=self.app.default_branch_name)
@@ -302,7 +302,7 @@ class RepoAdminController(DefaultAdminController):
             flash(message,
                   'error' if 'Invalid' in message else 'ok')
 
-        redirect(request.referer)
+        redirect(request.referer or '/')
 
 
 class RepoAdminRestController(BaseController):
diff --git a/Allura/allura/tests/scripts/test_delete_projects.py b/Allura/allura/tests/scripts/test_delete_projects.py
index 5f222e0..d0f8620 100644
--- a/Allura/allura/tests/scripts/test_delete_projects.py
+++ b/Allura/allura/tests/scripts/test_delete_projects.py
@@ -104,6 +104,7 @@ class TestDeleteProjects(TestController):
     @patch('allura.lib.helpers.request', autospec=True)
     def test_userproject_does_disable(self, req, req2):
         req.remote_addr = None
+        req.user_agent = 'MozFoo'
         req2.url = None
         self.run_script(['u/test-user'])
         assert M.User.by_username('test-user').disabled
@@ -149,6 +150,7 @@ class TestDeleteProjects(TestController):
     @patch('allura.lib.helpers.request', autospec=True)
     def test_disable_users(self, req, req2):
         req.remote_addr = None
+        req.user_agent = 'MozFoo'
         req2.url = None
         self._disable_users(disable=True)
 
diff --git a/ForgeBlog/forgeblog/main.py b/ForgeBlog/forgeblog/main.py
index 7205610..407e435 100644
--- a/ForgeBlog/forgeblog/main.py
+++ b/ForgeBlog/forgeblog/main.py
@@ -519,7 +519,7 @@ class BlogAdminController(DefaultAdminController):
         self.app.config.options[
             'AllowEmailPosting'] = allow_email_posting and True or False
         flash('Blog options updated')
-        redirect(request.referer)
+        redirect(request.referer or '/')
 
     @without_trailing_slash
     @expose('jinja:forgeblog:templates/blog/admin_exfeed.html')
@@ -563,7 +563,7 @@ class BlogAdminController(DefaultAdminController):
             flash('Invalid link(s): %s' %
                   ','.join(link for link in invalid_list), 'error')
 
-        redirect(request.referer)
+        redirect(request.referer or '/')
 
 
 class RootRestController(BaseController, AppRestControllerMixin):
diff --git a/ForgeChat/forgechat/main.py b/ForgeChat/forgechat/main.py
index 64b15e5..8e01426 100644
--- a/ForgeChat/forgechat/main.py
+++ b/ForgeChat/forgechat/main.py
@@ -120,7 +120,7 @@ class AdminController(DefaultAdminController):
 
     @with_trailing_slash
     def index(self, **kw):
-        redirect(request.referer)
+        redirect(request.referer or '/')
 
     @expose()
     @require_post()
diff --git a/ForgeDiscussion/forgediscussion/controllers/root.py b/ForgeDiscussion/forgediscussion/controllers/root.py
index 55c2e1f..7ba008c 100644
--- a/ForgeDiscussion/forgediscussion/controllers/root.py
+++ b/ForgeDiscussion/forgediscussion/controllers/root.py
@@ -136,7 +136,7 @@ class RootController(BaseController, DispatchIndex, FeedController):
             shortname=forum)
         if discussion.deleted and not has_access(c.app, 'configure')():
             flash('This forum has been removed.')
-            redirect(request.referrer)
+            redirect(request.referer or '/')
         require_access(discussion, 'post')
         thd = discussion.get_discussion_thread(dict(
             headers=dict(Subject=subject)))[0]
diff --git a/ForgeDiscussion/forgediscussion/forum_main.py b/ForgeDiscussion/forgediscussion/forum_main.py
index 60b97d3..7e957a3 100644
--- a/ForgeDiscussion/forgediscussion/forum_main.py
+++ b/ForgeDiscussion/forgediscussion/forum_main.py
@@ -329,7 +329,7 @@ class ForumAdminController(DefaultAdminController):
                 else:
                     forum.acl = []
         flash('Forums updated')
-        redirect(request.referrer)
+        redirect(request.referer or '/')
 
     @h.vardec
     @expose()
diff --git a/ForgeSVN/forgesvn/svn_main.py b/ForgeSVN/forgesvn/svn_main.py
index 37febd3..60bb345 100644
--- a/ForgeSVN/forgesvn/svn_main.py
+++ b/ForgeSVN/forgesvn/svn_main.py
@@ -181,7 +181,7 @@ class SVNImportController(BaseController, AdminControllerMixin):
             M.Notification.post_user(
                 c.user, self.app.repo, 'error',
                 text="Can't import into non empty repository.")
-        redirect(request.referer)
+        redirect(request.referer or '/')
 
 
 class SVNCommitBrowserController(BaseController):
diff --git a/ForgeShortUrl/forgeshorturl/main.py b/ForgeShortUrl/forgeshorturl/main.py
index 7316ac4..2be5b7f 100644
--- a/ForgeShortUrl/forgeshorturl/main.py
+++ b/ForgeShortUrl/forgeshorturl/main.py
@@ -200,7 +200,7 @@ class ShortURLAdminController(DefaultAdminController):
 
     @expose()
     def index(self, **kw):
-        redirect(request.referer)
+        redirect(request.referer or '/')
 
     @without_trailing_slash
     @expose('json:')
@@ -228,7 +228,7 @@ class ShortURLAdminController(DefaultAdminController):
                     names = {'short_url': 'Short url', 'full_url': 'Full URL'}
                     error_msg += '%s: %s ' % (names[msg], c.form_errors[msg])
                     flash(error_msg, 'error')
-                redirect(request.referer)
+                redirect(request.referer or '/')
 
             shorturl = ShortUrl.query.find({
                 'app_config_id': self.app.config._id,
@@ -237,7 +237,7 @@ class ShortURLAdminController(DefaultAdminController):
             if shorturl is not None:
                 if not update:
                     flash('Short url %s already exists' % short_url, 'error')
-                    redirect(request.referer)
+                    redirect(request.referer or '/')
                 else:
                     msg = ('update short url %s from %s to %s'
                            % (short_url, shorturl.full_url, full_url))
@@ -258,7 +258,7 @@ class ShortURLAdminController(DefaultAdminController):
             shorturl.last_updated = datetime.utcnow()
 
             M.AuditLog.log(msg)
-            redirect(request.referer)
+            redirect(request.referer or '/')
         return dict(
             app=self.app,
             url_len=len(ShortUrl.build_short_url(c.app, short_name='')))
diff --git a/ForgeTracker/forgetracker/tracker_main.py b/ForgeTracker/forgetracker/tracker_main.py
index ad3b2b7..135df0e 100644
--- a/ForgeTracker/forgetracker/tracker_main.py
+++ b/ForgeTracker/forgetracker/tracker_main.py
@@ -1214,7 +1214,7 @@ class BinController(BaseController, AdminControllerMixin):
     def delbin(self, bin=None):
         require(lambda: bin.app_config_id == self.app.config._id)
         bin.delete()
-        redirect(request.referer)
+        redirect(request.referer or '/')
 
     @without_trailing_slash
     @h.vardec
@@ -1624,16 +1624,16 @@ class TicketController(BaseController, FeedController):
             tracker = M.AppConfig.query.get(_id=t_id)
             if tracker is None:
                 flash('Select valid tracker', 'error')
-                redirect(request.referer)
+                redirect(request.referer or '/')
 
             if tracker == self.ticket.app.config:
                 flash('Ticket already in a selected tracker', 'info')
-                redirect(request.referer)
+                redirect(request.referer or '/')
 
             if not has_access(tracker, 'admin')():
                 flash('You should have admin access to destination tracker',
                       'error')
-                redirect(request.referer)
+                redirect(request.referer or '/')
 
             new_ticket = self.ticket.move(tracker)
             c.app.globals.invalidate_bin_counts()
@@ -1721,7 +1721,7 @@ class TrackerAdminController(DefaultAdminController):
         for k, v in kw.iteritems():
             self.app.config.options[k] = v
         flash('Options updated')
-        redirect(request.referer)
+        redirect(request.referer or '/')
 
     @expose()
     @require_post()
@@ -1731,7 +1731,7 @@ class TrackerAdminController(DefaultAdminController):
                 self.app.globals['show_in_search'][column] = True
             else:
                 self.app.globals['show_in_search'][column] = False
-        redirect(request.referer)
+        redirect(request.referer or '/')
 
     @expose()
     def update_tickets(self, **post_data):
@@ -1824,7 +1824,7 @@ class TrackerAdminController(DefaultAdminController):
 
         self.app.globals.custom_fields = custom_fields
         flash('Fields updated')
-        redirect(request.referer)
+        redirect(request.referer or '/')
 
 
 class RootRestController(BaseController, AppRestControllerMixin):
diff --git a/ForgeWiki/forgewiki/wiki_main.py b/ForgeWiki/forgewiki/wiki_main.py
index c62ee0b..5e7b4f5 100644
--- a/ForgeWiki/forgewiki/wiki_main.py
+++ b/ForgeWiki/forgewiki/wiki_main.py
@@ -519,7 +519,7 @@ class RootController(BaseController, DispatchIndex, FeedController):
             M.Mailbox.subscribe(type='direct')
         elif unsubscribe:
             M.Mailbox.unsubscribe()
-        redirect(request.referer)
+        redirect(request.referer or '/')
 
 
 class PageController(BaseController, FeedController):
@@ -808,7 +808,7 @@ class PageController(BaseController, FeedController):
         self.page.add_multiple_attachments(file_info)
         if is_ajax(request):
             return
-        redirect(request.referer)
+        redirect(request.referer or '/')
 
     @expose('json:')
     @require_post()
@@ -956,4 +956,4 @@ class WikiAdminController(DefaultAdminController):
         self.app.show_right_bar = show_right_bar
         self.app.allow_email_posting = allow_email_posting
         flash('Wiki options updated')
-        redirect(request.referer)
+        redirect(request.referer or '/')
diff --git a/requirements.txt b/requirements.txt
index 7189d95..1a6b730 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -49,10 +49,10 @@ textile==2.1.5
 # dep of colander
 translationstring==0.4
 TimerMiddleware==0.4.4
-TurboGears2==2.1.5
+TurboGears2==2.2.2
 # dep of html5lib
 webencodings==0.5.1
-WebOb==1.0.8
+WebOb==1.1.1
 
 # dependencies for cryptography
 cryptography==2.6.1
@@ -66,12 +66,14 @@ asn1crypto==0.24.0
 
 # tg2 deps (not used directly)
 Babel==1.3
-Mako==0.3.2
+crank==0.8.1
+Mako==0.9.1
 MarkupSafe==1.0
 Pylons==1.0
-simplejson==2.2.1
+repoze.lru==0.7
+simplejson==3.16.0
 Tempita==0.5.1
-Routes==1.12.3
+Routes==2.4.1
 WebFlash==0.1a9
 WebHelpers==1.3