Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions news/88.bugfix
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fixed mishandling of dynamic form label updates - New Comment & Thread @rohnsha0
86 changes: 83 additions & 3 deletions plone/app/discussion/browser/comments.pt
Original file line number Diff line number Diff line change
Expand Up @@ -288,9 +288,18 @@

<fieldset>

<legend i18n:translate="label_add_comment">Add comment</legend>

<p tal:content="view/comment_transform_message">
<legend class="comment-form-legend"
data-new-comment-text="Add a new comment"
data-reply-text="Reply here (this thread)"
i18n:translate="label_add_comment"
>Add a new comment</legend>

<p class="comment-form-description"
tal:content="view/comment_transform_message"
tal:attributes="
data-reply-html view/get_reply_transform_message;
"
>
You can add a comment by filling out the form below. Plain text
formatting.
</p>
Expand All @@ -299,5 +308,76 @@

</fieldset>
</div>

<!-- JavaScript to handle dynamic form label updates -->
<script type="text/javascript">
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should update the code in https://github.com/plone/mockup/blob/master/src/pat/controlpanels/discussion-comments--implementation.js (then release it in plone.staticresources), not add a script to every page that uses comments.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@davisagli I am unable to test out changes made in mockup repo locally using buildout.coredev... I have already implemented some fix but unable to test it out

Is there some other docs to test the changes?!

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rohnsha0 I am not up to date on the current processes for developing javascript for Classic UI, since I'm only working on Volto sites these days.

There is some information in https://github.com/plone/plone.staticresources?tab=readme-ov-file#how-to-upgrade-the-resources-in-this-package (which is where the mockup scripts are built and released to be used in Plone) and in https://github.com/plone/mockup?tab=readme-ov-file#development

If you have questions maybe @thet or @petschki can help, or ask in the Classic UI channel on Discord.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@davisagli I am unable to test out changes made in mockup repo locally using buildout.coredev... I have already implemented some fix but unable to test it out

Is there some other docs to test the changes?!

@rohnsha0 to test local mockup changes please check out the Development section of the readme: https://github.com/plone/mockup?tab=readme-ov-file#development ... feel free to contact me on discord if you need help.

(function() {
'use strict';

// Cache selectors for better performance
var REPLY_FORM_SELECTOR = '.reply';
var LEGEND_SELECTOR = '.comment-form-legend';
var DESCRIPTION_SELECTOR = '.comment-form-description';
var IN_REPLY_TO_SELECTOR = 'input[name*="in_reply_to"]';

// Update form labels for reply forms
function updateFormLabels(form) {
var legend = form.querySelector(LEGEND_SELECTOR);
var description = form.querySelector(DESCRIPTION_SELECTOR);
var inReplyToField = form.querySelector(IN_REPLY_TO_SELECTOR);
var isReply = inReplyToField && inReplyToField.value;

if (legend) {
legend.textContent = isReply && legend.dataset.replyText ?
legend.dataset.replyText :
legend.dataset.newCommentText || legend.textContent;
}

if (description) {
description.innerHTML = isReply && description.dataset.replyHtml ?
description.dataset.replyHtml :
description.textContent.trim();
}
}

// Process all reply forms
function processReplyForms() {
document.querySelectorAll(REPLY_FORM_SELECTOR).forEach(updateFormLabels);
}

// Initialize observer for dynamically added forms
function initObserver() {
var observer = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
mutation.addedNodes.forEach(function(node) {
if (node.nodeType === Node.ELEMENT_NODE) {
// Check if this node is or contains a reply form
var forms = node.matches && node.matches(REPLY_FORM_SELECTOR) ?
[node] :
(node.querySelectorAll ? Array.from(node.querySelectorAll(REPLY_FORM_SELECTOR)) : []);

forms.forEach(updateFormLabels);
}
});
});
});

observer.observe(document.body, { childList: true, subtree: true });
}

// Initialize when DOM is ready
function init() {
processReplyForms();
initObserver();
}

if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
})();
</script>
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This sounds like a perfect task for pat-inject which is shipped with Classic-UI ... see its documentation here: https://patternslib.com/demos/inject


</div>
</tal:block>
82 changes: 80 additions & 2 deletions plone/app/discussion/browser/comments.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,27 @@
"transformed into clickable links.",
)

# New reply descriptions
COMMENT_DESCRIPTION_PLAIN_TEXT_REPLY = _(
"comment_description_plain_text_reply",
default="You can reply to this comment by filling out the form below. "
"Plain text formatting.",
)

COMMENT_DESCRIPTION_MARKDOWN_REPLY = _(
"comment_description_markdown_reply",
default="You can reply to this comment by filling out the form below. "
"Plain text formatting. You can use the Markdown syntax for "
"links and images.",
)

COMMENT_DESCRIPTION_INTELLIGENT_TEXT_REPLY = _(
"comment_description_intelligent_text_reply",
default="You can reply to this comment by filling out the form below. "
"Plain text formatting. Web and email addresses are "
"transformed into clickable links.",
)

COMMENT_DESCRIPTION_MODERATION_ENABLED = _(
"comment_description_moderation_enabled",
default="Comments are moderated.",
Expand All @@ -62,7 +83,15 @@
class CommentForm(extensible.ExtensibleForm, form.Form):
ignoreContext = True # don't use context to get widget data
id = None
label = _("Add a comment")

@property
def label(self):
"""Dynamic label based on whether this is a reply or new comment."""
if self.is_reply():
return _("Reply here (this thread)")
else:
return _("Add a new comment")

fields = field.Fields(IComment).omit(
"portal_type",
"__parent__",
Expand All @@ -79,6 +108,11 @@ class CommentForm(extensible.ExtensibleForm, form.Form):
# See https://github.com/plone/Products.CMFPlone/issues/3623
enable_autofocus = False

def is_reply(self):
"""Check if this form is being used to reply to an existing comment."""
in_reply_to = self.request.get("form.widgets.in_reply_to", None)
return bool(in_reply_to)

def updateFields(self):
super().updateFields()
self.fields["user_notification"].widgetFactory = SingleCheckBoxFieldWidget
Expand Down Expand Up @@ -383,7 +417,7 @@ def comment_transform_message(self):
registry = queryUtility(IRegistry)
settings = registry.forInterface(IDiscussionSettings, check=False)

# text transform setting
# text transform setting - always use new comment descriptions for main form
if settings.text_transform == "text/x-web-intelligent":
message = translate(
Message(COMMENT_DESCRIPTION_INTELLIGENT_TEXT), context=self.request
Expand Down Expand Up @@ -417,6 +451,50 @@ def comment_transform_message(self):

return message

def get_reply_transform_message(self):
"""Returns the description for reply forms,
dependent on the text_transform setting and the comment moderation
workflow in the discussion control panel.
"""
context = aq_inner(self.context)
registry = queryUtility(IRegistry)
settings = registry.forInterface(IDiscussionSettings, check=False)

# text transform setting - use reply descriptions
if settings.text_transform == "text/x-web-intelligent":
message = translate(
Message(COMMENT_DESCRIPTION_INTELLIGENT_TEXT_REPLY),
context=self.request,
)
elif settings.text_transform == "text/x-web-markdown":
message = translate(
Message(COMMENT_DESCRIPTION_MARKDOWN_REPLY), context=self.request
)
else:
message = translate(
Message(COMMENT_DESCRIPTION_PLAIN_TEXT_REPLY), context=self.request
)

# comment workflow
wftool = getToolByName(context, "portal_workflow", None)
workflow_chain = wftool.getChainForPortalType("Discussion Item")
if workflow_chain:
comment_workflow = workflow_chain[0]
comment_workflow = wftool[comment_workflow]
# check if the current workflow implements a pending state. If this
# is true comments are moderated
if "pending" in comment_workflow.states:
message = (
message
+ " "
+ translate(
Message(COMMENT_DESCRIPTION_MODERATION_ENABLED),
context=self.request,
)
)

return message

def has_replies(self, workflow_actions=False):
"""Returns true if there are replies."""
if self.get_replies(workflow_actions) is not None:
Expand Down
Loading