[#7329] bump Ming version; change ForeignIdProperty(User) to always set allow_none=True
diff --git a/Allura/allura/model/__init__.py b/Allura/allura/model/__init__.py
index a785773..bc40872 100644
--- a/Allura/allura/model/__init__.py
+++ b/Allura/allura/model/__init__.py
@@ -26,7 +26,7 @@
 from .discuss import Discussion, Thread, PostHistory, Post, DiscussionAttachment
 from .attachments import BaseAttachment
 from .auth import AuthGlobals, User, ProjectRole, EmailAddress, ApiToken, ApiTicket, OldProjectRole
-from .auth import AuditLog, audit_log
+from .auth import AuditLog, audit_log, AlluraUserProperty
 from .filesystem import File
 from .notification import Notification, Mailbox
 from .repository import Repository, RepositoryImplementation
diff --git a/Allura/allura/model/auth.py b/Allura/allura/model/auth.py
index 4e93581..7d56e8c 100644
--- a/Allura/allura/model/auth.py
+++ b/Allura/allura/model/auth.py
@@ -99,6 +99,17 @@
     return urllib.urlencode([i for i in generate_smart_str(params)])
 
 
+class AlluraUserProperty(ForeignIdProperty):
+    '''
+    Specialized ForeignIdProperty for users, specifically to set allow_none=True
+    since Allura uses _id=None to represent *anonymous user, and ming
+    (by default) doesn't allow a None foreign key to reference a real object
+    '''
+
+    def __init__(self, **kwargs):
+        super(AlluraUserProperty, self).__init__('User', allow_none=True, **kwargs)
+
+
 class ApiAuthMixIn(object):
 
     def authenticate_request(self, path, params):
@@ -153,7 +164,7 @@
         unique_indexes = ['user_id']
 
     _id = FieldProperty(S.ObjectId)
-    user_id = ForeignIdProperty('User')
+    user_id = AlluraUserProperty()
     api_key = FieldProperty(str, if_missing=lambda: str(uuid.uuid4()))
     secret_key = FieldProperty(str, if_missing=h.cryptographic_nonce)
 
@@ -172,7 +183,7 @@
     PREFIX = 'tck'
 
     _id = FieldProperty(S.ObjectId)
-    user_id = ForeignIdProperty('User')
+    user_id = AlluraUserProperty()
     api_key = FieldProperty(
         str, if_missing=lambda: ApiTicket.PREFIX + h.nonce(20))
     secret_key = FieldProperty(str, if_missing=h.cryptographic_nonce)
@@ -786,7 +797,7 @@
         ]
 
     _id = FieldProperty(S.ObjectId)
-    user_id = ForeignIdProperty('User', if_missing=None)
+    user_id = AlluraUserProperty(if_missing=None)
     project_id = ForeignIdProperty('Project', if_missing=None)
     name = FieldProperty(str)
     roles = FieldProperty([S.ObjectId])
@@ -950,5 +961,5 @@
 main_orm_session.mapper(AuditLog, audit_log, properties=dict(
     project_id=ForeignIdProperty('Project'),
     project=RelationProperty('Project'),
-    user_id=ForeignIdProperty('User'),
+    user_id=AlluraUserProperty(),
     user=RelationProperty('User')))
diff --git a/Allura/allura/model/discuss.py b/Allura/allura/model/discuss.py
index b9f25d1..1f19208 100644
--- a/Allura/allura/model/discuss.py
+++ b/Allura/allura/model/discuss.py
@@ -37,7 +37,7 @@
 from allura.model.notification import Notification, Mailbox
 from .artifact import Artifact, ArtifactReference, VersionedArtifact, Snapshot, Message, Feed
 from .attachments import BaseAttachment
-from .auth import User, ProjectRole
+from .auth import User, ProjectRole, AlluraUserProperty
 from .timeline import ActivityObject
 from .types import MarkdownCache
 
@@ -463,7 +463,7 @@
     flagged_by = FieldProperty([schema.ObjectId])
     flags = FieldProperty(int, if_missing=0)
     last_edit_date = FieldProperty(datetime, if_missing=None)
-    last_edit_by_id = ForeignIdProperty(User)
+    last_edit_by_id = AlluraUserProperty()
     edit_count = FieldProperty(int, if_missing=0)
     spam_check_id = FieldProperty(str, if_missing='')
     text_cache = FieldProperty(MarkdownCache)
diff --git a/Allura/allura/model/notification.py b/Allura/allura/model/notification.py
index 164dc8b..ad27f5f 100644
--- a/Allura/allura/model/notification.py
+++ b/Allura/allura/model/notification.py
@@ -55,7 +55,7 @@
 import allura.tasks.mail_tasks
 
 from .session import main_orm_session
-from .auth import User
+from .auth import User, AlluraUserProperty
 
 
 log = logging.getLogger(__name__)
@@ -95,7 +95,7 @@
     subject = FieldProperty(str)
     text = FieldProperty(str)
     link = FieldProperty(str)
-    author_id = ForeignIdProperty('User')
+    author_id = AlluraUserProperty()
     feed_meta = FieldProperty(S.Deprecated)
     artifact_reference = FieldProperty(S.Deprecated)
     pubdate = FieldProperty(datetime, if_missing=datetime.utcnow)
@@ -397,7 +397,7 @@
         ]
 
     _id = FieldProperty(S.ObjectId)
-    user_id = ForeignIdProperty('User', if_missing=lambda: c.user._id)
+    user_id = AlluraUserProperty(if_missing=lambda: c.user._id)
     project_id = ForeignIdProperty('Project', if_missing=lambda: c.project._id)
     app_config_id = ForeignIdProperty(
         'AppConfig', if_missing=lambda: c.app.config._id)
diff --git a/Allura/allura/model/oauth.py b/Allura/allura/model/oauth.py
index fe21cc1..14b30d1 100644
--- a/Allura/allura/model/oauth.py
+++ b/Allura/allura/model/oauth.py
@@ -27,6 +27,7 @@
 from allura.lib import helpers as h
 from .session import main_orm_session
 from .types import MarkdownCache
+from .auth import AlluraUserProperty
 
 log = logging.getLogger(__name__)
 
@@ -60,7 +61,7 @@
         unique_indexes = ['name']
 
     type = FieldProperty(str, if_missing='consumer')
-    user_id = ForeignIdProperty('User', if_missing=lambda: c.user._id)
+    user_id = AlluraUserProperty(if_missing=lambda: c.user._id)
     name = FieldProperty(str)
     description = FieldProperty(str)
     description_cache = FieldProperty(MarkdownCache)
@@ -90,7 +91,7 @@
 
     type = FieldProperty(str, if_missing='request')
     consumer_token_id = ForeignIdProperty('OAuthConsumerToken')
-    user_id = ForeignIdProperty('User', if_missing=lambda: c.user._id)
+    user_id = AlluraUserProperty(if_missing=lambda: c.user._id)
     callback = FieldProperty(str)
     validation_pin = FieldProperty(str)
 
@@ -105,7 +106,7 @@
     type = FieldProperty(str, if_missing='access')
     consumer_token_id = ForeignIdProperty('OAuthConsumerToken')
     request_token_id = ForeignIdProperty('OAuthToken')
-    user_id = ForeignIdProperty('User', if_missing=lambda: c.user._id)
+    user_id = AlluraUserProperty(if_missing=lambda: c.user._id)
     is_bearer = FieldProperty(bool, if_missing=False)
 
     user = RelationProperty('User')
diff --git a/ForgeShortUrl/forgeshorturl/model/shorturl.py b/ForgeShortUrl/forgeshorturl/model/shorturl.py
index b42d30d..6682632 100644
--- a/ForgeShortUrl/forgeshorturl/model/shorturl.py
+++ b/ForgeShortUrl/forgeshorturl/model/shorturl.py
@@ -35,7 +35,7 @@
     short_name = FieldProperty(str)
     description = FieldProperty(str)
     private = FieldProperty(bool)
-    create_user = ForeignIdProperty(User)
+    create_user = M.AlluraUserProperty()
     created = FieldProperty(datetime, if_missing=datetime.utcnow)
     last_updated = FieldProperty(datetime, if_missing=datetime.utcnow)
 
diff --git a/ForgeTracker/forgetracker/model/ticket.py b/ForgeTracker/forgetracker/model/ticket.py
index b308416..738a5d8 100644
--- a/ForgeTracker/forgetracker/model/ticket.py
+++ b/ForgeTracker/forgetracker/model/ticket.py
@@ -58,6 +58,7 @@
 
     artifact_orm_session,
     project_orm_session,
+    AlluraUserProperty,
 )
 from allura.model.timeline import ActivityObject
 from allura.model.notification import MailFooter
@@ -613,8 +614,8 @@
     summary = FieldProperty(str)
     description = FieldProperty(str, if_missing='')
     description_cache = FieldProperty(MarkdownCache)
-    reported_by_id = ForeignIdProperty(User, if_missing=lambda: c.user._id)
-    assigned_to_id = ForeignIdProperty(User, if_missing=None)
+    reported_by_id = AlluraUserProperty(if_missing=lambda: c.user._id)
+    assigned_to_id = AlluraUserProperty(if_missing=None)
     milestone = FieldProperty(str, if_missing='')
     status = FieldProperty(str, if_missing='')
     custom_fields = FieldProperty({str: None})
diff --git a/requirements-common.txt b/requirements-common.txt
index b1163cf..2721662 100644
--- a/requirements-common.txt
+++ b/requirements-common.txt
@@ -21,7 +21,7 @@
 iso8601==0.1.4
 Jinja2==2.6
 Markdown==2.2.0
-Ming==0.4.6
+Ming==0.4.7
 oauth2==1.5.170
 # tg2 dep PasteDeploy must specified before TurboGears2, to avoid a version/allow-hosts problem
 Paste==1.7.5.1