Add allow_anonymous namespace setting for email-free commenting #35
8 changed files with 422 additions and 53 deletions
|
|
@ -52,6 +52,7 @@ PROTECTED_ATTRIBUTES = {
|
|||
"google_analytics_id": None,
|
||||
"hide_unverified": False,
|
||||
"hide_unless_approved": False,
|
||||
"allow_anonymous": False,
|
||||
"hide_powered_by": False,
|
||||
"mathjax": False,
|
||||
"link_protection": False,
|
||||
|
|
@ -92,6 +93,8 @@ class Namespace(RBase, Base):
|
|||
hide_unverified = Column(Boolean, default=False)
|
||||
# should a node be hidden until approved by a moderator?
|
||||
hide_unless_approved = Column(Boolean, default=False)
|
||||
# allow anonymous commenting (name only, no email required)
|
||||
allow_anonymous = Column(Boolean, default=False)
|
||||
# should we hide the poweredby Remarkbox logo?
|
||||
hide_powered_by = Column(Boolean, default=False)
|
||||
# should the list of root nodes in this namespace be public or hidden?
|
||||
|
|
|
|||
|
|
@ -0,0 +1,24 @@
|
|||
"""Add allow_anonymous column to namespace
|
||||
|
||||
Revision ID: 108519de76ac
|
||||
Revises: 5188e62d0afb
|
||||
Create Date: 2025-12-20 11:05:36.134829
|
||||
|
||||
"""
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '108519de76ac'
|
||||
down_revision = '5188e62d0afb'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
def upgrade():
|
||||
op.add_column('rb_namespace', sa.Column('allow_anonymous', sa.Boolean(), nullable=True, server_default='0'))
|
||||
|
||||
|
||||
def downgrade():
|
||||
op.drop_column('rb_namespace', 'allow_anonymous')
|
||||
|
|
@ -39,6 +39,11 @@
|
|||
If checked, hide new comments until approved by a moderator.
|
||||
<br>
|
||||
<br>
|
||||
<label>Allow Anonymous Comments:</label>
|
||||
<input type="checkbox" name="allow-anonymous-checkbox" id="allow-anonymous-checkbox" {% if request.namespace.allow_anonymous %}checked{% endif %}></input>
|
||||
If checked, allow comments without email verification. Commenters enter a display name only. They cannot edit comments or receive reply notifications.
|
||||
<br>
|
||||
<br>
|
||||
<label>Enable Link Protection:</label>
|
||||
<input type="checkbox" name="link-protection-checkbox" id="link-protection-checkbox" {% if request.namespace.link_protection %}checked{% endif %}></input>
|
||||
If checked, replace all links in comments with <code>[link removed]</code>.
|
||||
|
|
|
|||
|
|
@ -1,4 +1,16 @@
|
|||
{% if not request.user.authenticated %}
|
||||
{% if request.namespace.allow_anonymous %}
|
||||
{# Anonymous mode: show name field instead of email #}
|
||||
<input
|
||||
name = "anonymous_name"
|
||||
type = "text"
|
||||
id = "anonymous_name_input"
|
||||
class = "common-text-input"
|
||||
value = ""
|
||||
placeholder = "Name (optional)"
|
||||
maxlength = "64" />
|
||||
{% else %}
|
||||
{# Normal mode: require email #}
|
||||
<input
|
||||
name = "email2"
|
||||
type = "email"
|
||||
|
|
@ -16,16 +28,16 @@
|
|||
{% if request.email %}style="display: none;"{% endif %}
|
||||
required />
|
||||
|
||||
{% if show_whats_next %}
|
||||
{% if show_whats_next %}
|
||||
<center>
|
||||
<p class="whats-next"><b>What's next?</b> check your email to log in!</p>
|
||||
</center>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
{% if show_whats_next_notifications %}
|
||||
{% if show_whats_next_notifications %}
|
||||
<center>
|
||||
<p class="whats-next"><b>What's next?</b> verify your email address for reply notifications!</p>
|
||||
</center>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
{% endif %}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,9 @@ from remarkbox.models import (
|
|||
get_tm_session,
|
||||
get_or_create_user_by_email,
|
||||
get_user_by_email,
|
||||
get_or_create_namespace,
|
||||
NodeEventNotification,
|
||||
UserSurrogate,
|
||||
)
|
||||
|
||||
from remarkbox.models.meta import Base
|
||||
|
|
@ -477,3 +479,270 @@ class AuthenticatedFunctionalTests(FunctionalTests):
|
|||
redirect_res = self.testapp.get("/billing/success", status=302)
|
||||
res = redirect_res.follow()
|
||||
self.assertIn(b"Missing session information", res.body)
|
||||
|
||||
|
||||
class AnonymousCommentingFunctionalTests(FunctionalTests):
|
||||
"""Tests for anonymous commenting feature."""
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
try:
|
||||
FunctionalTests.setUpClass.im_func(cls)
|
||||
except AttributeError:
|
||||
FunctionalTests.setUpClass.__func__(cls)
|
||||
|
||||
def setUp(self):
|
||||
# Create a namespace with allow_anonymous enabled
|
||||
anon_ns = get_or_create_namespace(
|
||||
self.dbsession, "anon-test.example.com"
|
||||
)
|
||||
anon_ns.allow_anonymous = True
|
||||
self.dbsession.add(anon_ns)
|
||||
|
||||
# Create a namespace with allow_anonymous disabled (default)
|
||||
regular_ns = get_or_create_namespace(
|
||||
self.dbsession, "regular-test.example.com"
|
||||
)
|
||||
regular_ns.allow_anonymous = False
|
||||
self.dbsession.add(regular_ns)
|
||||
|
||||
# Create a test user for namespace ownership
|
||||
test_user = get_or_create_user_by_email(
|
||||
self.dbsession, "anon-test@remarkbox.com"
|
||||
)
|
||||
self.raw_otp = test_user.new_password()
|
||||
self.dbsession.add(test_user)
|
||||
|
||||
self.dbsession.flush()
|
||||
|
||||
# Store IDs and names before commit
|
||||
self.anon_namespace_id = anon_ns.id
|
||||
self.anon_namespace_name = str(anon_ns.name)
|
||||
self.regular_namespace_id = regular_ns.id
|
||||
self.regular_namespace_name = str(regular_ns.name)
|
||||
|
||||
self.tm.commit()
|
||||
|
||||
self.test_creds = ("anon-test@remarkbox.com", self.raw_otp)
|
||||
|
||||
def tearDown(self):
|
||||
super(AnonymousCommentingFunctionalTests, self).tearDown()
|
||||
# Clean up surrogates created during tests
|
||||
self.dbsession.query(UserSurrogate).filter(
|
||||
UserSurrogate.namespace_id.in_([
|
||||
self.anon_namespace_id,
|
||||
self.regular_namespace_id
|
||||
])
|
||||
).delete(synchronize_session=False)
|
||||
# Requery user before delete
|
||||
user = get_user_by_email(self.dbsession, "anon-test@remarkbox.com")
|
||||
if user:
|
||||
self.dbsession.delete(user)
|
||||
self.dbsession.flush()
|
||||
self.tm.commit()
|
||||
|
||||
def _log_in_test_user(self):
|
||||
res_login = self.testapp.post(
|
||||
"/verification-challenge?email={}&raw-otp={}&submit".format(*self.test_creds)
|
||||
)
|
||||
res_csrf = self.testapp.get("/")
|
||||
self.csrf = res_csrf.form.fields["csrf_token"][0].value
|
||||
return res_login
|
||||
|
||||
def test_anonymous_reply_creates_surrogate(self):
|
||||
"""Test that anonymous reply creates a UserSurrogate."""
|
||||
# First create a thread with an authenticated user
|
||||
self._log_in_test_user()
|
||||
|
||||
# Create a root node in the anonymous namespace
|
||||
from remarkbox.models import create_root_node
|
||||
anon_ns = get_or_create_namespace(self.dbsession, self.anon_namespace_name)
|
||||
user = get_or_create_user_by_email(self.dbsession, "anon-test@remarkbox.com")
|
||||
|
||||
root = create_root_node()
|
||||
root.namespace = anon_ns
|
||||
root.user = user
|
||||
root.verified = True
|
||||
root.title = "Test Thread"
|
||||
root.set_data("Test content")
|
||||
self.dbsession.add(root)
|
||||
self.dbsession.flush()
|
||||
root_id = str(root.id)
|
||||
self.tm.commit()
|
||||
|
||||
# Log out
|
||||
self.testapp.get("/log-out")
|
||||
|
||||
# Post anonymous reply (no email, just name)
|
||||
redirect_res = self.testapp.post(
|
||||
"/{}/reply".format(root_id),
|
||||
{
|
||||
"thread_data": "Anonymous comment here",
|
||||
"anonymous_name": "TestAnon",
|
||||
},
|
||||
status=302,
|
||||
)
|
||||
|
||||
# Should redirect to the thread (not to login)
|
||||
res = redirect_res.follow()
|
||||
self.assertIn(b"Your post was successful!", res.body)
|
||||
|
||||
# Verify a surrogate was created
|
||||
surrogate = self.dbsession.query(UserSurrogate).filter(
|
||||
UserSurrogate.name == "TestAnon",
|
||||
UserSurrogate.namespace_id == self.anon_namespace_id
|
||||
).first()
|
||||
self.assertIsNotNone(surrogate)
|
||||
|
||||
def test_anonymous_reply_default_name(self):
|
||||
"""Test that anonymous reply without name uses 'Anonymous'."""
|
||||
self._log_in_test_user()
|
||||
|
||||
from remarkbox.models import create_root_node
|
||||
anon_ns = get_or_create_namespace(self.dbsession, self.anon_namespace_name)
|
||||
user = get_or_create_user_by_email(self.dbsession, "anon-test@remarkbox.com")
|
||||
|
||||
root = create_root_node()
|
||||
root.namespace = anon_ns
|
||||
root.user = user
|
||||
root.verified = True
|
||||
root.title = "Test Thread 2"
|
||||
root.set_data("Test content 2")
|
||||
self.dbsession.add(root)
|
||||
self.dbsession.flush()
|
||||
root_id = str(root.id)
|
||||
self.tm.commit()
|
||||
|
||||
self.testapp.get("/log-out")
|
||||
|
||||
# Post without anonymous_name
|
||||
redirect_res = self.testapp.post(
|
||||
"/{}/reply".format(root_id),
|
||||
{
|
||||
"thread_data": "Anonymous comment without name",
|
||||
},
|
||||
status=302,
|
||||
)
|
||||
|
||||
res = redirect_res.follow()
|
||||
self.assertIn(b"Your post was successful!", res.body)
|
||||
|
||||
# Verify surrogate with default name
|
||||
surrogate = self.dbsession.query(UserSurrogate).filter(
|
||||
UserSurrogate.name == "Anonymous",
|
||||
UserSurrogate.namespace_id == self.anon_namespace_id
|
||||
).first()
|
||||
self.assertIsNotNone(surrogate)
|
||||
|
||||
def test_regular_namespace_requires_email(self):
|
||||
"""Test that non-anonymous namespace still requires email."""
|
||||
self._log_in_test_user()
|
||||
|
||||
from remarkbox.models import create_root_node
|
||||
regular_ns = get_or_create_namespace(self.dbsession, self.regular_namespace_name)
|
||||
user = get_or_create_user_by_email(self.dbsession, "anon-test@remarkbox.com")
|
||||
|
||||
root = create_root_node()
|
||||
root.namespace = regular_ns
|
||||
root.user = user
|
||||
root.verified = True
|
||||
root.title = "Regular Thread"
|
||||
root.set_data("Regular content")
|
||||
self.dbsession.add(root)
|
||||
self.dbsession.flush()
|
||||
root_id = str(root.id)
|
||||
self.tm.commit()
|
||||
|
||||
self.testapp.get("/log-out")
|
||||
|
||||
# Try to post without email on regular namespace
|
||||
redirect_res = self.testapp.post(
|
||||
"/{}/reply".format(root_id),
|
||||
{
|
||||
"thread_data": "This should fail",
|
||||
"anonymous_name": "ShouldFail",
|
||||
},
|
||||
status=302,
|
||||
)
|
||||
|
||||
res = redirect_res.follow()
|
||||
self.assertIn(b"Press the back button to fix your email address", res.body)
|
||||
|
||||
def test_anonymous_comment_is_verified(self):
|
||||
"""Test that anonymous comments are marked as verified."""
|
||||
self._log_in_test_user()
|
||||
|
||||
from remarkbox.models import create_root_node
|
||||
anon_ns = get_or_create_namespace(self.dbsession, self.anon_namespace_name)
|
||||
user = get_or_create_user_by_email(self.dbsession, "anon-test@remarkbox.com")
|
||||
|
||||
root = create_root_node()
|
||||
root.namespace = anon_ns
|
||||
root.user = user
|
||||
root.verified = True
|
||||
root.title = "Verified Test Thread"
|
||||
root.set_data("Verified test content")
|
||||
self.dbsession.add(root)
|
||||
self.dbsession.flush()
|
||||
root_id = root.id
|
||||
self.tm.commit()
|
||||
|
||||
self.testapp.get("/log-out")
|
||||
|
||||
self.testapp.post(
|
||||
"/{}/reply".format(root_id),
|
||||
{
|
||||
"thread_data": "Anonymous verified comment",
|
||||
"anonymous_name": "VerifiedAnon",
|
||||
},
|
||||
status=302,
|
||||
)
|
||||
|
||||
# Check the node is verified
|
||||
child = self.dbsession.query(Node).filter(
|
||||
Node.parent_id == root_id
|
||||
).first()
|
||||
self.assertIsNotNone(child)
|
||||
self.assertTrue(child.verified)
|
||||
self.assertIsNotNone(child.user_surrogate)
|
||||
self.assertIsNone(child.user)
|
||||
|
||||
def test_namespace_settings_toggle(self):
|
||||
"""Test that namespace owner can toggle allow_anonymous setting."""
|
||||
self._log_in_test_user()
|
||||
|
||||
# Make user owner of namespace
|
||||
from remarkbox.models import Namespace
|
||||
anon_ns = get_or_create_namespace(self.dbsession, self.anon_namespace_name)
|
||||
user = get_or_create_user_by_email(self.dbsession, "anon-test@remarkbox.com")
|
||||
|
||||
anon_ns.set_role_for_user(user, "owner")
|
||||
self.dbsession.add(anon_ns)
|
||||
self.dbsession.flush()
|
||||
self.tm.commit()
|
||||
|
||||
# Toggle off
|
||||
self.testapp.post(
|
||||
"/ns/{}/settings".format(self.anon_namespace_name),
|
||||
{
|
||||
"csrf_token": self.csrf,
|
||||
# Not including allow-anonymous-checkbox means it's unchecked
|
||||
},
|
||||
)
|
||||
|
||||
ns = self.dbsession.query(Namespace).filter(
|
||||
Namespace.id == self.anon_namespace_id
|
||||
).first()
|
||||
self.assertFalse(ns.allow_anonymous)
|
||||
|
||||
# Toggle on
|
||||
self.testapp.post(
|
||||
"/ns/{}/settings".format(self.anon_namespace_name),
|
||||
{
|
||||
"csrf_token": self.csrf,
|
||||
"allow-anonymous-checkbox": "on",
|
||||
},
|
||||
)
|
||||
|
||||
self.dbsession.expire(ns)
|
||||
self.assertTrue(ns.allow_anonymous)
|
||||
|
|
|
|||
|
|
@ -60,6 +60,7 @@ def namespace_settings(request):
|
|||
)
|
||||
|
||||
hide_unless_approved_checkbox = p.get("hide-unless-approved-checkbox", "off")
|
||||
allow_anonymous_checkbox = p.get("allow-anonymous-checkbox", "off")
|
||||
link_protection_checkbox = p.get("link-protection-checkbox", "off")
|
||||
reverse_order_checkbox = p.get("reverse-order-checkbox", "off")
|
||||
group_conversations_checkbox = p.get("group-conversations-checkbox", "off")
|
||||
|
|
@ -68,6 +69,7 @@ def namespace_settings(request):
|
|||
hide_powered_by_checkbox = p.get("hide-powered-by-checkbox", "off")
|
||||
|
||||
hide_unless_approved = checkbox_to_bool(hide_unless_approved_checkbox)
|
||||
allow_anonymous = checkbox_to_bool(allow_anonymous_checkbox)
|
||||
link_protection = checkbox_to_bool(link_protection_checkbox)
|
||||
reverse_order = checkbox_to_bool(reverse_order_checkbox)
|
||||
group_conversations = checkbox_to_bool(group_conversations_checkbox)
|
||||
|
|
@ -146,6 +148,17 @@ def namespace_settings(request):
|
|||
)
|
||||
)
|
||||
|
||||
if allow_anonymous != request.namespace.allow_anonymous:
|
||||
request.namespace.allow_anonymous = allow_anonymous
|
||||
request.session.flash(
|
||||
(
|
||||
"You turned {} allow_anonymous".format(
|
||||
allow_anonymous_checkbox
|
||||
),
|
||||
"success",
|
||||
)
|
||||
)
|
||||
|
||||
if link_protection != request.namespace.link_protection:
|
||||
request.namespace.link_protection = link_protection
|
||||
request.session.flash(
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ from pyramid.csrf import check_csrf_token
|
|||
|
||||
from pyramid.httpexceptions import HTTPFound
|
||||
|
||||
from remarkbox.models import create_root_node
|
||||
from remarkbox.models import create_root_node, get_or_create_user_surrogate_by_name
|
||||
|
||||
from . import get_referer_or_home, get_node_route_uri, set_node_to_pending_in_session
|
||||
|
||||
|
|
@ -21,6 +21,7 @@ def new_thread(request):
|
|||
"""Display new page and handle posting of form."""
|
||||
thread_title = request.params.get("thread_title", "")
|
||||
thread_data = request.params.get("thread_data", "")
|
||||
anonymous_name = request.params.get("anonymous_name", "").strip()
|
||||
|
||||
if request.spam:
|
||||
return request.spam
|
||||
|
|
@ -32,59 +33,76 @@ def new_thread(request):
|
|||
if thread_title and thread_data:
|
||||
# handle the submitted form new/create form.
|
||||
|
||||
if request.user is None:
|
||||
# Handle anonymous mode vs regular mode
|
||||
user_surrogate = None
|
||||
if request.namespace.allow_anonymous and not request.user:
|
||||
# Anonymous mode: create or get a surrogate
|
||||
if not anonymous_name:
|
||||
anonymous_name = "Anonymous"
|
||||
user_surrogate = get_or_create_user_surrogate_by_name(
|
||||
request.dbsession, anonymous_name, request.namespace
|
||||
)
|
||||
elif request.user is None:
|
||||
# Regular mode: require email/user
|
||||
request.session.flash(
|
||||
("Press the back button to fix your email address", "error")
|
||||
)
|
||||
return HTTPFound(get_referer_or_home(request))
|
||||
|
||||
else:
|
||||
# create a new root node.
|
||||
node = create_root_node()
|
||||
# create a new root node.
|
||||
node = create_root_node()
|
||||
node.namespace = request.namespace
|
||||
node.ip_address = unicode(request.client_addr)
|
||||
node.title = thread_title
|
||||
node.set_data(thread_data)
|
||||
|
||||
# Handle anonymous vs authenticated user
|
||||
if user_surrogate:
|
||||
# Anonymous mode: attach surrogate, mark as verified
|
||||
node.user_surrogate = user_surrogate
|
||||
node.verified = True
|
||||
node_event = None # No notifications for anonymous posts
|
||||
request.dbsession.add(user_surrogate)
|
||||
else:
|
||||
# Normal mode: attach user
|
||||
node.user = request.user
|
||||
node.verified = request.user.authenticated
|
||||
node.namespace = request.namespace
|
||||
node.ip_address = unicode(request.client_addr)
|
||||
|
||||
node.title = thread_title
|
||||
node.set_data(thread_data)
|
||||
|
||||
node_event = node.new_event(request.user, "created")
|
||||
|
||||
request.dbsession.add(node)
|
||||
request.dbsession.add(node_event)
|
||||
request.dbsession.add(request.user)
|
||||
request.dbsession.add(node.namespace)
|
||||
request.dbsession.flush()
|
||||
|
||||
request.dbsession.add(node)
|
||||
if node_event:
|
||||
request.dbsession.add(node_event)
|
||||
request.dbsession.add(node.namespace)
|
||||
request.dbsession.flush()
|
||||
|
||||
if node_event:
|
||||
# TODO: schedule_notification expects the request to have a node.
|
||||
request.node = node
|
||||
|
||||
schedule_notifications(request, node_event)
|
||||
|
||||
msg = ("Your post was successful!", "success")
|
||||
request.session.flash(msg)
|
||||
msg = ("Your post was successful!", "success")
|
||||
request.session.flash(msg)
|
||||
|
||||
# set return_to to the node's URI.
|
||||
return_to = get_node_route_uri(request, node)
|
||||
# set return_to to the node's URI.
|
||||
return_to = get_node_route_uri(request, node)
|
||||
|
||||
if node.verified == True:
|
||||
# Redirect to new node if verified.
|
||||
return HTTPFound(return_to)
|
||||
# Anonymous users are always verified, redirect immediately
|
||||
if user_surrogate or node.verified:
|
||||
return HTTPFound(return_to)
|
||||
|
||||
set_node_to_pending_in_session(request, node)
|
||||
set_node_to_pending_in_session(request, node)
|
||||
|
||||
# Redirect to join-or-log-in, posting email and submit.
|
||||
uri = request.route_url(
|
||||
route_name="basic-join-or-log-in",
|
||||
_query={
|
||||
"email": request.user.email,
|
||||
"return-to": return_to,
|
||||
"submit": True,
|
||||
},
|
||||
)
|
||||
return HTTPFound(uri)
|
||||
# Redirect to join-or-log-in, posting email and submit.
|
||||
uri = request.route_url(
|
||||
route_name="basic-join-or-log-in",
|
||||
_query={
|
||||
"email": request.user.email,
|
||||
"return-to": return_to,
|
||||
"submit": True,
|
||||
},
|
||||
)
|
||||
return HTTPFound(uri)
|
||||
|
||||
return {
|
||||
"title": "Create a new thread",
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ from . import (
|
|||
)
|
||||
|
||||
from remarkbox.lib.notify import schedule_notifications
|
||||
from remarkbox.models import get_or_create_user_surrogate_by_name
|
||||
|
||||
try:
|
||||
unicode("")
|
||||
|
|
@ -27,6 +28,7 @@ except:
|
|||
def reply_node(request):
|
||||
"""handle posting of reply form from show-node pages."""
|
||||
thread_data = request.params.get("thread_data", "")
|
||||
anonymous_name = request.params.get("anonymous_name", "").strip()
|
||||
|
||||
# return early if spam attribute is truthy.
|
||||
if request.spam:
|
||||
|
|
@ -43,8 +45,17 @@ def reply_node(request):
|
|||
request.session.flash(("No remarks for the disabled.", "error"))
|
||||
return HTTPFound(get_referer_or_home(request))
|
||||
|
||||
# flash error and return early if user is None.
|
||||
if request.user is None:
|
||||
# Handle anonymous mode vs regular mode
|
||||
user_surrogate = None
|
||||
if request.namespace.allow_anonymous and not request.user:
|
||||
# Anonymous mode: create or get a surrogate
|
||||
if not anonymous_name:
|
||||
anonymous_name = "Anonymous"
|
||||
user_surrogate = get_or_create_user_surrogate_by_name(
|
||||
request.dbsession, anonymous_name, request.namespace
|
||||
)
|
||||
elif request.user is None:
|
||||
# Regular mode: require email/user
|
||||
request.session.flash(
|
||||
("Press the back button to fix your email address", "error")
|
||||
)
|
||||
|
|
@ -72,16 +83,30 @@ def reply_node(request):
|
|||
|
||||
# STEP 2: attach a brand new child node to parent node.
|
||||
child = parent.new_child()
|
||||
child.user = request.user
|
||||
child.ip_address = unicode(request.client_addr)
|
||||
child.verified = request.user.authenticated
|
||||
child.set_data(thread_data, namespace=request.namespace)
|
||||
child_event = child.new_event(request.user, "commented")
|
||||
|
||||
# Handle anonymous vs authenticated user
|
||||
if user_surrogate:
|
||||
# Anonymous mode: attach surrogate, mark as verified (no email to verify)
|
||||
child.user_surrogate = user_surrogate
|
||||
child.verified = True
|
||||
child_event = None # No notifications for anonymous comments
|
||||
request.dbsession.add(user_surrogate)
|
||||
else:
|
||||
# Normal mode: attach user
|
||||
child.user = request.user
|
||||
child.verified = request.user.authenticated
|
||||
child_event = child.new_event(request.user, "commented")
|
||||
|
||||
if request.namespace.hide_unless_approved:
|
||||
# by default comments are approved, unless Namespace hide_unless_approved
|
||||
# is enabled, moderators nodes are always auto approved.
|
||||
child.approved = request.namespace.is_moderator(request.user)
|
||||
if request.user:
|
||||
child.approved = request.namespace.is_moderator(request.user)
|
||||
else:
|
||||
# Anonymous users are never auto-approved when moderation is on
|
||||
child.approved = False
|
||||
|
||||
# STEP 3: update root's changed timestamp.
|
||||
# TODO: maybe we should find a better way to "bump" a thread.
|
||||
|
|
@ -90,29 +115,29 @@ def reply_node(request):
|
|||
parent._invalidate_cache()
|
||||
|
||||
# STEP 4: commit to database.
|
||||
request.dbsession.add(request.user)
|
||||
if request.user:
|
||||
request.dbsession.add(request.user)
|
||||
request.dbsession.add(child)
|
||||
request.dbsession.add(child_event)
|
||||
if child_event:
|
||||
request.dbsession.add(child_event)
|
||||
request.dbsession.add(parent)
|
||||
request.dbsession.add(parent.root)
|
||||
request.dbsession.flush()
|
||||
|
||||
schedule_notifications(request, child_event)
|
||||
if child_event:
|
||||
schedule_notifications(request, child_event)
|
||||
|
||||
msg = ("Your post was successful!", "success")
|
||||
request.session.flash(msg)
|
||||
|
||||
### TODO: everything below this is pretty much crap code...
|
||||
# and likely deserves a flowchart...
|
||||
|
||||
# set return_to URI.
|
||||
if request.mode == "embed":
|
||||
return_to = get_embed_route_uri(request, child.root.uri.data, child.id)
|
||||
else:
|
||||
return_to = get_node_route_uri(request, child.root, child.id)
|
||||
|
||||
if child.verified == True:
|
||||
# Redirect to new node if user and new node is verified.
|
||||
# Anonymous users are always verified, redirect immediately
|
||||
if user_surrogate or child.verified:
|
||||
return HTTPFound(return_to)
|
||||
|
||||
set_node_to_pending_in_session(request, child)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue