diff --git a/.meta.toml b/.meta.toml
index abdbeba6..3d682dc3 100644
--- a/.meta.toml
+++ b/.meta.toml
@@ -3,7 +3,10 @@
# See the inline comments on how to expand/tweak this configuration file
[meta]
template = "default"
-commit-id = "a89af8f2"
+commit-id = "7a017355"
[pyproject]
dependencies_ignores = "['Products.LinguaPlone.interfaces.ITranslatable', 'collective.akismet', 'collective.z3cform.norobots', 'plone.formwidget.captcha', 'plone.formwidget.recaptcha', 'plone.formwidget.hcaptcha', 'plone.contentrules', 'plone.app.contentrules', 'plone.stringinterp', 'plone.app.collection']"
+
+[tox]
+constraints_file = "https://dist.plone.org/release/6.1-dev/constraints.txt"
diff --git a/README.rst b/README.rst
index 3753d940..caf71abd 100644
--- a/README.rst
+++ b/README.rst
@@ -1,18 +1,22 @@
Introduction
============
+plone.app.discussion is the commenting add-on for Plone.
+It is part of the maintained Plone core.
-plone.app.discussion is the commenting system used since Plone 4.1.
-It was initially developed as part of the Google Summer of Code 2009 by Timo Stollenwerk (student) and Martin Aspeli (mentor).
+Installation
+============
+If your installation depends on the `Plone `_ package, you can install it via the Plone control panel.
+In case you do only depend on either the `plone.volto`, `plone.classicui` or `Products.CMFPlone` package, you need to add it to your requirements file.
+After adding it and installing the requirement, you can install it via the Plone control panel.
-Add-on Products
+Spam protection
===============
-- `collective.autoresizetextarea
- `_
- (for auto-resizing the comment textarea while typing)
+These days it is essential to protect your site from commenting spam.
+The following add-ons can help to protect your site:
- `plone.formwidget.captcha
`_
@@ -22,33 +26,24 @@ Add-on Products
`_
(for ReCaptcha spam protection)
-- `collective.akismet
- `_
- (for Akismet spam protection)
-
- `collective.z3cform.norobots
- `_
+ `_
(provides a "human" captcha widget based on a list of questions/answers)
- `plone.formwidget.hcaptcha
`_
(for spam protection by `HCaptcha `_ )
-Note: not all of these may be compatible with the current version of ``plone.app.discussion`` and ``Plone`` itself.
-
Documentation
=============
-There is initial `documentation `_ but it is outdated.
-You will still get a feel for how the package is structured though.
-
+For further information, please refer to the `official Plone documentation `_.
Credits
=======
-- Timo Stollenwerk
-- Martin Aspeli
+This package was initially developed as part of the Google Summer of Code 2009 by Timo Stollenwerk (student) and Martin Aspeli (mentor).
Many thanks to:
@@ -59,4 +54,4 @@ Many thanks to:
- Hanno Schlichting (for making p.a.d work with Zope 2.12)
- Alan Hoey (for providing fixes)
- Maik Roeder (for providing and setting up a buildbot)
-
+- Jens Klein (for ripping it out of core and making it a separate core-addon for Plone 6.1)
diff --git a/news/211.breaking b/news/211.breaking
new file mode 100644
index 00000000..f4cad3c5
--- /dev/null
+++ b/news/211.breaking
@@ -0,0 +1,4 @@
+Move this package in the space of Plone Core add-ons.
+It now depends on Products.CMFPlone and is no longer installed by default.
+It is still available in the default Plone distribution, but can be omitted in customizations.
+[jensens]
\ No newline at end of file
diff --git a/news/211.bugfix b/news/211.bugfix
new file mode 100644
index 00000000..43a08fec
--- /dev/null
+++ b/news/211.bugfix
@@ -0,0 +1 @@
+Fix redirection after comment edit to main content, preventing NotFound. [@jensens]
\ No newline at end of file
diff --git a/news/222.bugfix b/news/222.bugfix
new file mode 100644
index 00000000..6f0a228f
--- /dev/null
+++ b/news/222.bugfix
@@ -0,0 +1,3 @@
+Add missing icon on comments' `view` action
+Register contenttype icon for comments.
+[gforcada, maurits]
diff --git a/plone/app/discussion/TODO.txt b/plone/app/discussion/TODO.txt
deleted file mode 100644
index 30a1031e..00000000
--- a/plone/app/discussion/TODO.txt
+++ /dev/null
@@ -1,100 +0,0 @@
-==========================
-plone.app.discussion to-do
-==========================
-
- [ ] Add BBB support for the existing portal_discussion interface
-
- - implement in BBB package
- - mix into tool.CommentingTool
- - emit deprecation warnings
-
-MINOR/FUTURE RELEASES:
-----------------------
-
- [ ] During recursive deletion of child comments, events are fired when the
- conversation data structures may be in an inconsistent state. We need
- some tests for this, and possibly some different handling of those
- events.
-
- [ ] Ajaxify adding and deleting comments in the comments viewlet.
-
- [ ] Rebuild the zebra table after batch deleting/publishing
-
- [ ] Replace the comment_review_workflow
-
- [ ] Thread building in conversation.getThreads()
-
- [ ] Batching in conversation.getComments()
-
-
-DONE:
------
-
- [X] Make sure a catalog Clear & Rebuild doesn't lose all comments
-
- [X] Add UI
-
- - comment forms should use z3c.form subforms and plone.z3cform's
- ExtensibleForm support
-
- [X] Implement plone.indexer indexers for comments, filling standard metadata
-
- - Note discrepancy between Python datetime and indexing expecting a Zope 2
- DateTime field
-
- [X] Implement plone.indexer indexers for commented-upon content
-
- - Unique set of commentators
- - Number of comments
- - Date/time of most recent comment
-
- These have to be reindexed when comment is added/removed
- (IContainerModifiedEvent). They also need to be set up in catalog.xml.
-
- [X] Add jQuery auto-resize to comment text field
- http://www.aclevercookie.com/demos/autogrow_textarea.html
-
- [X] Add event handlers to ensure we don't get stale comments in the catalog
- when parent objects are removed/moved/cloned:
-
- - Create the conversation when an object is created
- - Dispatch object added/removed/moved/cloned events to conversations
- - Dispatch conversation added/removed/moved/cloned events to comments
-
- [X] Add tests for conversation dict API
-
- [X] Add tests for IReplies adapters
-
- [X] Add control panel
-
- - install plone.registry records using registry.xml
- - create control panel using helper class in plone.app.registry
-
- [X] Discussion Control Panel: Add icon
-
- [X] Add id fall back for Creator if no Username (Title) has been added
-
- [X] Replace the reply-to-comment button with a Plone-like reply-button
-
- [X] IE: cancel button in reply-to-comment form does not work
-
- [X] Chrome (Linux): Reply to comment is not working
- (TypeError: long() argument must be a string or a number)
-
- [X] Restrict nesting of comments on a certain level
-
- [X] Fix temporary commenter's image css
-
- [X] Make comments viewlet format_time return localized time
-
- [X] Add i18n translations
-
- [X] Add i18n translations for author_username and author_email
-
- [X] Plone reports "unsuccessfully attempted to uncatalog an object" while
- trying to delete a comment.
-
- [X] Fix that when opening a reply form before the page has been fully loaded,
- the reply layer is closed again.
-
- [X] Form validation is not working in the reply-to-comment form
\ No newline at end of file
diff --git a/plone/app/discussion/architecture.txt b/plone/app/discussion/architecture.txt
index f8026e38..5002ef0e 100644
--- a/plone/app/discussion/architecture.txt
+++ b/plone/app/discussion/architecture.txt
@@ -14,15 +14,15 @@ plone.app.discussion.
content.
**Discussion items are subject to workflow and permission**
- Moderation, anonymous commenting, and auto approve/reject should be
+ Moderation, anonymous commenting, and auto-approve/reject should be
handled using workflow states, automatic and manual transitions, and
permissions.
- **Discussion items are light weight objects**
- Discussion item objects are as light weight as possible. Ideally, a
+ **Discussion items are lightweight objects**
+ Discussion item objects are as lightweight as possible. Ideally, a
discussion item should be as lightweight as a catalog brain. This may mean
that we forego convenience base classes and re-implement certain interfaces.
- Comments should not provide the full set of dublin core metadata, though
+ Comments should not provide the full set of Dublin Core metadata, though
custom indexers can be used to provide values for standard catalog indexes.
**Optimise for retrieval speed**
@@ -49,7 +49,7 @@ plone.app.discussion.
**Discussion items are retrieved in reverse creation date order**
Discussion items do not need to support explicit ordering. They should
always be retrieved in reverse creation date order (most recent for).
- They can be stored with keys so that this is always true.
+ They can be stored with keys so this is always true.
**Discussion items do not need readable ids**
Ids can be based on the creation date.
diff --git a/plone/app/discussion/behavior.py b/plone/app/discussion/behavior.py
new file mode 100644
index 00000000..9691f694
--- /dev/null
+++ b/plone/app/discussion/behavior.py
@@ -0,0 +1,39 @@
+from plone.autoform import directives
+from plone.autoform.interfaces import IFormFieldProvider
+from plone.base import PloneMessageFactory as _
+from plone.supermodel import model
+from z3c.form.interfaces import IAddForm
+from z3c.form.interfaces import IEditForm
+from zope import schema
+from zope.interface import provider
+from zope.schema.vocabulary import SimpleTerm
+from zope.schema.vocabulary import SimpleVocabulary
+
+
+options = SimpleVocabulary(
+ [
+ SimpleTerm(value=True, title=_("Yes")),
+ SimpleTerm(value=False, title=_("No")),
+ ]
+)
+
+
+@provider(IFormFieldProvider)
+class IAllowDiscussion(model.Schema):
+ model.fieldset(
+ "settings",
+ label=_("Settings"),
+ fields=["allow_discussion"],
+ )
+
+ allow_discussion = schema.Choice(
+ title=_("Allow discussion"),
+ description=_("Allow discussion for this content object."),
+ vocabulary=options,
+ required=False,
+ default=None,
+ )
+
+ directives.omitted("allow_discussion")
+ directives.no_omit(IEditForm, "allow_discussion")
+ directives.no_omit(IAddForm, "allow_discussion")
diff --git a/plone/app/discussion/behavior.zcml b/plone/app/discussion/behavior.zcml
new file mode 100644
index 00000000..6a3a26fc
--- /dev/null
+++ b/plone/app/discussion/behavior.zcml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
diff --git a/plone/app/discussion/browser/comment.py b/plone/app/discussion/browser/comment.py
index d11dfeb4..e52f86ae 100644
--- a/plone/app/discussion/browser/comment.py
+++ b/plone/app/discussion/browser/comment.py
@@ -104,7 +104,8 @@ def handle_cancel(self, action):
_("comment_edit_cancel_notification", default="Edit comment cancelled"),
type="info",
)
- return self._redirect(target=self.context.absolute_url())
+ main_content = aq_parent(aq_parent(self.context))
+ return self._redirect(target=main_content.absolute_url())
EditComment = wrap_form(EditCommentForm)
diff --git a/plone/app/discussion/configure.zcml b/plone/app/discussion/configure.zcml
index d8ab08fa..2653af7a 100644
--- a/plone/app/discussion/configure.zcml
+++ b/plone/app/discussion/configure.zcml
@@ -13,6 +13,7 @@
+
@@ -38,11 +39,26 @@
+
+
diff --git a/plone/app/discussion/design.txt b/plone/app/discussion/design.txt
index 11c75bd0..c5af0354 100644
--- a/plone/app/discussion/design.txt
+++ b/plone/app/discussion/design.txt
@@ -8,7 +8,7 @@ Storage and traversal
---------------------
For each content item, there is a Conversation object stored in annotations.
-This can be traversed to via the ++conversation++ namespace, but also fetched
+This can be traversed via the ++conversation++ namespace, but also fetched
via an adapter lookup to IConversation.
The conversation stores all comments related to a content object. Each
@@ -22,7 +22,7 @@ incremental. Ids must be positive integers - 0 or negative numbers are not
allowed.
Threading information is stored in the conversation: we keep track of the
-set of children and the parent if any comment. Top-level comments have a
+set of children and the parent if any comments. Top-level comments have a
parent id of 0. This information is managed by the conversation class when
comments are manipulated using a dict-like API.
@@ -82,20 +82,20 @@ Thus, we want:
* Traversable, to get absolute_url() and friends
- this requires a good acquisition chain at all times
- * Acquisition.Explicit, to support acquisition
+ * Acquisition.Explicit, to support the acquisition
- we do not want implicit acquisition
* Owned, to be able to track ownership
* RoleManager, to support permissions and local roles
We also want to use a number of custom indexers for most of the standard
-metadata such as creator, effective date etc.
+metadata such as creator, effective date, etc.
Finally, we'll need event handlers to perform the actual indexing.
Discussion settings
-------------------
-Discussion can be enabled per-type and per-instance, via values in the FTI
+Discussion can be enabled per type and per instance, via values in the FTI
(allow_discussion) and on the object. These will remain unchanged. The
IConversation object's 'enabled' property should consult these.
@@ -117,7 +117,7 @@ Where possible, we should use existing permissions:
* Modify Portal Content
* Request Review
-In addition, we'll need a 'Moderator' role and a moderation permission,
+In addition, we'll need a 'Moderator' role and moderation permission,
* Moderate comment
* Bypass moderation
@@ -137,11 +137,11 @@ These could work in a workflow like this:
| |
+----- {auto-moderate} ----+
-The 'posted' state is the initial state. 'published' is the state where the
-comment is visible to non-reviewers.
+The 'posted' state is the initial state.
+'published', is the state where the comment is visible to non-reviewers.
The 'publish' transition would be protected by the 'Moderate comment'
-permission. We could have states and transition for 'rejected', etc, but it
+permission. We could have states and transitions for 'rejected', etc, but it
is probably just as good to delete comments that are rejected.
The 'auto-publish' transition would be an automatic transition protected by
diff --git a/plone/app/discussion/profiles/default/controlpanel.xml b/plone/app/discussion/profiles/default/controlpanel.xml
index d85f7a47..d98078ea 100644
--- a/plone/app/discussion/profiles/default/controlpanel.xml
+++ b/plone/app/discussion/profiles/default/controlpanel.xml
@@ -7,7 +7,7 @@
- 2001
+ 3000profile-plone.resource:defaultprofile-plone.app.registry:default
diff --git a/plone/app/discussion/profiles/default/portal_atct.xml b/plone/app/discussion/profiles/default/portal_atct.xml
deleted file mode 100644
index 0c7068c6..00000000
--- a/plone/app/discussion/profiles/default/portal_atct.xml
+++ /dev/null
@@ -1,39 +0,0 @@
-
-
-
-
-
-
- ATCurrentAuthorCriterion
- ATSimpleStringCriterion
- ATListCriterion
-
-
-
-
-
- ATSimpleIntCriterion
-
-
-
-
-
-
-
-
-
-
diff --git a/plone/app/discussion/profiles/default/registry.xml b/plone/app/discussion/profiles/default/registry.xml
index 2a1aaffc..944359d3 100644
--- a/plone/app/discussion/profiles/default/registry.xml
+++ b/plone/app/discussion/profiles/default/registry.xml
@@ -4,4 +4,10 @@
FalseFalse
+
+
+ Plone Image
+
+ ++plone++bootstrap-icons/chat-left-text.svg
+
diff --git a/plone/app/discussion/profiles/default/types/Discussion_Item.xml b/plone/app/discussion/profiles/default/types/Discussion_Item.xml
index c05efcb9..fb1cc595 100644
--- a/plone/app/discussion/profiles/default/types/Discussion_Item.xml
+++ b/plone/app/discussion/profiles/default/types/Discussion_Item.xml
@@ -9,8 +9,9 @@
>Comment
Comments added to a content item.
- discussionitem_icon.png
+ >Discussion Items are documents which reply to other content.
+ They should *not* be addable through the standard 'folder_factories' interface.
+ ++plone++bootstrap-icons/chat-left-text.svgDiscussion Itemplone.Comment
@@ -18,7 +19,7 @@
FalseTrue
- True
+ False
@@ -28,10 +29,22 @@
+
+
+
diff --git a/plone/app/discussion/profiles/to_3000/registry.xml b/plone/app/discussion/profiles/to_3000/registry.xml
new file mode 100644
index 00000000..e7886adb
--- /dev/null
+++ b/plone/app/discussion/profiles/to_3000/registry.xml
@@ -0,0 +1,9 @@
+
+
+
+
+ Plone Image
+
+ ++plone++bootstrap-icons/chat-left-text.svg
+
+
diff --git a/plone/app/discussion/profiles/to_3000/types/Discussion_Item.xml b/plone/app/discussion/profiles/to_3000/types/Discussion_Item.xml
new file mode 100644
index 00000000..92fb9496
--- /dev/null
+++ b/plone/app/discussion/profiles/to_3000/types/Discussion_Item.xml
@@ -0,0 +1,26 @@
+
+
diff --git a/plone/app/discussion/profiles/uninstall/actions.xml b/plone/app/discussion/profiles/uninstall/actions.xml
new file mode 100644
index 00000000..680e5e96
--- /dev/null
+++ b/plone/app/discussion/profiles/uninstall/actions.xml
@@ -0,0 +1,13 @@
+
+
diff --git a/plone/app/discussion/profiles/uninstall/browserlayer.xml b/plone/app/discussion/profiles/uninstall/browserlayer.xml
new file mode 100644
index 00000000..078a4cbb
--- /dev/null
+++ b/plone/app/discussion/profiles/uninstall/browserlayer.xml
@@ -0,0 +1,6 @@
+
+
+
+
diff --git a/plone/app/discussion/profiles/uninstall/catalog.xml b/plone/app/discussion/profiles/uninstall/catalog.xml
new file mode 100644
index 00000000..e4584ca6
--- /dev/null
+++ b/plone/app/discussion/profiles/uninstall/catalog.xml
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/plone/app/discussion/profiles/uninstall/controlpanel.xml b/plone/app/discussion/profiles/uninstall/controlpanel.xml
new file mode 100644
index 00000000..f76f5d75
--- /dev/null
+++ b/plone/app/discussion/profiles/uninstall/controlpanel.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
diff --git a/plone/app/discussion/profiles/uninstall/registry.xml b/plone/app/discussion/profiles/uninstall/registry.xml
new file mode 100644
index 00000000..234fa24d
--- /dev/null
+++ b/plone/app/discussion/profiles/uninstall/registry.xml
@@ -0,0 +1,6 @@
+
+
+
+
diff --git a/plone/app/discussion/profiles/uninstall/types.xml b/plone/app/discussion/profiles/uninstall/types.xml
new file mode 100644
index 00000000..86b5306b
--- /dev/null
+++ b/plone/app/discussion/profiles/uninstall/types.xml
@@ -0,0 +1,9 @@
+
+
+
+
diff --git a/plone/app/discussion/profiles/uninstall/workflows.xml b/plone/app/discussion/profiles/uninstall/workflows.xml
new file mode 100644
index 00000000..b7269a77
--- /dev/null
+++ b/plone/app/discussion/profiles/uninstall/workflows.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
diff --git a/plone/app/discussion/setuphandlers.py b/plone/app/discussion/setuphandlers.py
new file mode 100644
index 00000000..19afede9
--- /dev/null
+++ b/plone/app/discussion/setuphandlers.py
@@ -0,0 +1,59 @@
+from plone.base.interfaces import INonInstallable
+from Products.CMFCore.utils import getToolByName
+from zope.interface import implementer
+
+
+DEFAULT_TYPES = [
+ "Document",
+ "Event",
+ "File",
+ "Folder",
+ "Image",
+ "News Item",
+ "Collection",
+ "Link",
+]
+
+BEHAVIOR = "plone.allowdiscussion"
+
+
+@implementer(INonInstallable)
+class HiddenProfiles:
+ def getNonInstallableProfiles(self):
+ return [
+ "plone.app.discussion:to_3000",
+ ]
+
+
+def add_discussion_behavior_to_default_types(context):
+ """Add the discussion behavior to all default types, if they exist."""
+ types_tool = getToolByName(context, "portal_types")
+ for type_name in DEFAULT_TYPES:
+ if (
+ type_name in types_tool.objectIds()
+ and BEHAVIOR not in types_tool[type_name].behaviors
+ ):
+ types_tool[type_name].behaviors += (BEHAVIOR,)
+
+
+def remove_discussion_behavior_to_default_types(context):
+ """Remove the discussion behavior from all default types, if they exist."""
+ types_tool = getToolByName(context, "portal_types")
+ for type_name in types_tool.objectIds():
+ fti = types_tool[type_name]
+ if getattr(fti, "behaviors", None) is None:
+ continue
+ if BEHAVIOR in fti.behaviors:
+ behaviors = list(fti.behaviors)
+ behaviors.remove(BEHAVIOR)
+ fti.behaviors = tuple(behaviors)
+
+
+def post_install(context):
+ """Post install script"""
+ add_discussion_behavior_to_default_types(context)
+
+
+def post_uninstall(context):
+ """Post uninstall script"""
+ remove_discussion_behavior_to_default_types(context)
diff --git a/plone/app/discussion/testing.py b/plone/app/discussion/testing.py
index fe23d84f..48a74392 100644
--- a/plone/app/discussion/testing.py
+++ b/plone/app/discussion/testing.py
@@ -106,6 +106,7 @@ class PloneAppDiscussionRobot(PloneAppDiscussion):
)
def setUpPloneSite(self, portal):
+ applyProfile(portal, "plone.app.discussion:default")
registry = queryUtility(IRegistry)
settings = registry.forInterface(IDiscussionSettings)
settings.globally_enabled = True
diff --git a/plone/app/discussion/tests/functional_test_behavior_discussion.rst b/plone/app/discussion/tests/functional_test_behavior_discussion.rst
new file mode 100644
index 00000000..f86925b4
--- /dev/null
+++ b/plone/app/discussion/tests/functional_test_behavior_discussion.rst
@@ -0,0 +1,106 @@
+Allow Discussion
+================
+
+Test Setup
+----------
+
+We create a dexterity content type that provides the allow discussion behavior::
+
+ >>> portal = layer['portal']
+ >>> from plone.dexterity.fti import DexterityFTI
+ >>> fti = DexterityFTI('discussiondocument')
+ >>> fti.behaviors = ('plone.allowdiscussion',)
+ >>> portal.portal_types._setObject('discussiondocument', fti)
+ 'discussiondocument'
+
+Set up a test browser::
+
+ >>> from plone.app.testing import TEST_USER_ID, TEST_USER_NAME, TEST_USER_PASSWORD, setRoles
+ >>> setRoles(portal, TEST_USER_ID, ['Manager'])
+ >>> import transaction; transaction.commit()
+ >>> from plone.testing.zope import Browser
+ >>> browser = Browser(layer['app'])
+ >>> browser.addHeader('Authorization', 'Basic %s:%s' % (TEST_USER_NAME, TEST_USER_PASSWORD,))
+
+We have to make sure the request provides IDiscussonLayer because the enabled
+method on the conversation calls conversation_view (which is only registered
+for IDiscussionLayer).
+
+ >>> from plone.app.discussion.interfaces import IDiscussionLayer
+ >>> from zope.interface import alsoProvides
+ >>> alsoProvides(portal.REQUEST, IDiscussionLayer)
+
+Add a document::
+
+ >>> browser.open('http://nohost/plone/++add++discussiondocument')
+
+
+Default Allow Discussion Options
+--------------------------------
+
+There are three options for the allow discussion select field::
+
+ >>> allowDiscussion = browser.getControl('Allow discussion')
+ >>> allowDiscussion.options
+ ['--NOVALUE--', 'True', 'False']
+
+By default, no value is set for allow discussion::
+
+ >>> browser.getControl('Allow discussion').value
+ ['--NOVALUE--']
+ >>> browser.getControl('Save').click()
+ >>> browser.url
+ 'http://nohost/plone/discussiondocument/view'
+
+This means discussion is not enabled:
+
+ >>> from plone.app.discussion.interfaces import IConversation
+ >>> conv = IConversation(portal.discussiondocument)
+ >>> conv.enabled()
+ False
+
+We have to globally enable discussion in order to be able to add comments::
+
+ >>> from plone.registry.interfaces import IRegistry
+ >>> from zope.component import queryUtility
+ >>> from plone.app.discussion.interfaces import IDiscussionSettings
+ >>> registry = queryUtility(IRegistry)
+ >>> settings = registry.forInterface(IDiscussionSettings)
+ >>> settings.globally_enabled = True
+
+Discussion is still disabled for our content object though::
+
+ >>> from plone.app.discussion.interfaces import IConversation
+ >>> conv = IConversation(portal.discussiondocument)
+ >>> conv.enabled()
+ False
+
+This is because discussion is disabled by default for the document content
+type::
+
+ >>> fti.allow_discussion
+ False
+
+If we allow discussion for the 'Document' content type, the conversation
+for our content object is enabled because it just uses the default setting
+(because allow_discussion is set to None)::
+
+ >>> fti.allow_discussion = True
+ >>> from plone.app.discussion.interfaces import IConversation
+ >>> conv = IConversation(portal.discussiondocument)
+ >>> conv.enabled()
+ True
+
+We can now override the default value (True) by explicitly setting allow discussion to False::
+
+ >>> browser.open('http://nohost/plone/discussiondocument/edit')
+ >>> allowDiscussion = browser.getControl('Allow discussion')
+ >>> allowDiscussion.value = ['False']
+ >>> browser.getControl('Save').click()
+
+Discussion on our content object is now not enabled::
+
+ >>> from plone.app.discussion.interfaces import IConversation
+ >>> conv = IConversation(portal.discussiondocument)
+ >>> conv.enabled()
+ False
diff --git a/plone/app/discussion/tests/functional_test_comments.txt b/plone/app/discussion/tests/functional_test_comments.rst
similarity index 99%
rename from plone/app/discussion/tests/functional_test_comments.txt
rename to plone/app/discussion/tests/functional_test_comments.rst
index 61ab4a3e..59d08782 100644
--- a/plone/app/discussion/tests/functional_test_comments.txt
+++ b/plone/app/discussion/tests/functional_test_comments.rst
@@ -163,8 +163,8 @@ Check if the comment has been added properly.
True
-Post a comment as anonymous user
---------------------------------
+Post a comment as an anonymous user
+-----------------------------------
Login and post comment as Anonymous
diff --git a/plone/app/discussion/tests/robot/test_allow_discussion.robot b/plone/app/discussion/tests/robot/test_allow_discussion.robot
new file mode 100644
index 00000000..d2ed6117
--- /dev/null
+++ b/plone/app/discussion/tests/robot/test_allow_discussion.robot
@@ -0,0 +1,66 @@
+*** Settings *****************************************************************
+
+Resource plone/app/robotframework/keywords.robot
+Resource plone/app/robotframework/saucelabs.robot
+Resource plone/app/robotframework/selenium.robot
+
+Library Remote ${PLONE_URL}/RobotRemote
+
+Test Setup Run keywords Plone Test Setup
+Test Teardown Run keywords Plone Test Teardown
+
+
+*** Test Cases ***************************************************************
+
+Scenario: Allow comments for Link Type
+ Given a logged-in manager
+ and Globally enabled comments
+ and the types control panel
+ When I select 'Link' in types dropdown
+ and Allow discussion
+ Then Wait until page contains Content Settings
+ When I add new Link 'my_link'
+ Then Link 'my_link' should have comments enabled
+
+
+*** Keywords *****************************************************************
+
+# --- GIVEN ------------------------------------------------------------------
+
+a logged-in manager
+ Enable autologin as Manager
+
+the types control panel
+ Go to ${PLONE_URL}/@@content-controlpanel
+ Wait until page contains Content Settings
+
+Globally enabled comments
+ Go to ${PLONE_URL}/@@discussion-settings
+ Wait until page contains Discussion settings
+ Select checkbox name=form.widgets.globally_enabled:list
+ Click button Save
+
+
+
+# --- WHEN -------------------------------------------------------------------
+
+I select '${content_type}' in types dropdown
+ Select from list by label name=type_id ${content_type}
+ Wait until page contains Globally addable
+
+Allow discussion
+ Select checkbox name=allow_discussion:boolean
+ Click Button Save
+
+I add new Link '${id}'
+ Go to ${PLONE_URL}
+ Wait until page contains Plone site
+ Create content type=Link id=${id} title=${id} remoteUrl=http://www.starzel.de
+
+
+# --- THEN -------------------------------------------------------------------
+
+Link '${id}' should have comments enabled
+ Go to ${PLONE_URL}/${id}
+ Wait until page contains ${id}
+ Page should contain element xpath=//div[@id="commenting"]
diff --git a/plone/app/discussion/tests/test_functional.py b/plone/app/discussion/tests/test_functional.py
index b4ce3f0e..5a3813d7 100644
--- a/plone/app/discussion/tests/test_functional.py
+++ b/plone/app/discussion/tests/test_functional.py
@@ -1,9 +1,7 @@
"""Functional Doctests for plone.app.discussion.
-
- These test are only triggered when Plone 4 (and plone.testing) is installed.
"""
-from ..testing import PLONE_APP_DISCUSSION_FUNCTIONAL_TESTING # noqa
+from ..testing import PLONE_APP_DISCUSSION_FUNCTIONAL_TESTING
from plone.testing import layered
import doctest
@@ -15,8 +13,9 @@
doctest.ELLIPSIS | doctest.NORMALIZE_WHITESPACE | doctest.REPORT_ONLY_FIRST_FAILURE
)
normal_testfiles = [
- "functional_test_comments.txt",
+ "functional_test_comments.rst",
"functional_test_comment_review_workflow.txt",
+ "functional_test_behavior_discussion.rst",
]
diff --git a/plone/app/discussion/tests/test_permissions.py b/plone/app/discussion/tests/test_permissions.py
new file mode 100644
index 00000000..ce14146b
--- /dev/null
+++ b/plone/app/discussion/tests/test_permissions.py
@@ -0,0 +1,19 @@
+from ..testing import PLONE_APP_DISCUSSION_INTEGRATION_TESTING
+from AccessControl.PermissionRole import rolesForPermissionOn
+
+import unittest
+
+
+class PermissionsTest(unittest.TestCase):
+ """Make sure the permissions are set up properly."""
+
+ layer = PLONE_APP_DISCUSSION_INTEGRATION_TESTING
+
+ def test_permissions_site_administrator_role(self):
+ # This integration test shows that the correct permissions were
+ # assigned to the Site Administrator role (whether inherited from the
+ # Zope application, or specified in the portal rolemap).
+ self.assertTrue(
+ "Site Administrator"
+ not in rolesForPermissionOn("Reply to item", self.layer["portal"])
+ )
diff --git a/plone/app/discussion/upgrades.py b/plone/app/discussion/upgrades.py
index dc78ea76..b63a666b 100644
--- a/plone/app/discussion/upgrades.py
+++ b/plone/app/discussion/upgrades.py
@@ -1,6 +1,7 @@
+from .interfaces import IDiscussionSettings
+from .setuphandlers import add_discussion_behavior_to_default_types
from datetime import timezone
from plone import api
-from plone.app.discussion.interfaces import IDiscussionSettings
from plone.registry.interfaces import IRegistry
from Products.CMFCore.utils import getToolByName
from Products.ZCatalog.ProgressHandler import ZLogHandler
@@ -115,3 +116,8 @@ def set_timezone_on_dates(context):
logger.info(
"Updated %i creation dates and %i modification dates" % (creations, modifieds)
)
+
+
+def set_discussion_behavior(context):
+ """Add the discussion behavior to all default types, if they exist."""
+ add_discussion_behavior_to_default_types(context)
diff --git a/plone/app/discussion/upgrades.zcml b/plone/app/discussion/upgrades.zcml
index d265d428..129b73ee 100644
--- a/plone/app/discussion/upgrades.zcml
+++ b/plone/app/discussion/upgrades.zcml
@@ -108,4 +108,28 @@
/>
+
+
+
+
+
+
+
diff --git a/setup.py b/setup.py
index 21d1e9bd..5489fc9c 100644
--- a/setup.py
+++ b/setup.py
@@ -3,35 +3,14 @@
from setuptools import setup
-version = "4.1.3.dev0"
-
long_description = (
f"{Path('README.rst').read_text()}\n{Path('CHANGES.rst').read_text()}"
)
-install_requires = [
- "Products.GenericSetup",
- "Products.ZCatalog",
- "Products.statusmessages",
- "plone.api",
- "plone.app.event",
- "plone.registry",
- "plone.resource",
- "plone.uuid",
- "setuptools",
- "plone.app.layout",
- "plone.app.registry",
- "plone.app.uuid",
- "plone.base",
- "plone.indexer",
- "plone.z3cform",
- "Zope",
- "z3c.form>=2.3.3",
-]
setup(
name="plone.app.discussion",
- version=version,
+ version="5.0.0.dev0",
description="Enhanced discussion support for Plone",
long_description=long_description,
long_description_content_type="text/x-rst",
@@ -41,16 +20,15 @@
"Development Status :: 5 - Production/Stable",
"Environment :: Web Environment",
"Framework :: Plone",
- "Framework :: Plone :: 6.0",
+ "Framework :: Plone :: 6.1",
"Framework :: Plone :: Core",
"Framework :: Zope :: 5",
"License :: OSI Approved :: GNU General Public License v2 (GPLv2)",
"Operating System :: OS Independent",
"Programming Language :: Python",
- "Programming Language :: Python :: 3.8",
- "Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
+ "Programming Language :: Python :: 3.12",
],
keywords="plone discussion",
author="Timo Stollenwerk - Plone Foundation",
@@ -61,8 +39,29 @@
namespace_packages=["plone", "plone.app"],
include_package_data=True,
zip_safe=False,
- python_requires=">=3.8",
- install_requires=install_requires,
+ python_requires=">=3.10",
+ install_requires=[
+ "Products.GenericSetup",
+ "Products.ZCatalog",
+ "Products.statusmessages",
+ "plone.api",
+ "plone.app.event",
+ "plone.registry",
+ "plone.resource",
+ "plone.autoform",
+ "plone.behavior",
+ "plone.supermodel",
+ "plone.uuid",
+ "setuptools",
+ "plone.app.layout",
+ "plone.app.registry",
+ "plone.app.uuid",
+ "plone.base",
+ "plone.indexer",
+ "plone.z3cform",
+ "z3c.form>=2.3.3",
+ "Zope",
+ ],
extras_require={
"test": [
"plone.app.testing",
@@ -72,6 +71,7 @@
"plone.app.contenttypes[test]",
"plone.app.robotframework",
"plone.app.vocabularies",
+ "plone.dexterity",
"plone.testing",
"plone.protect",
"Products.MailHost",
@@ -80,4 +80,8 @@
"python-dateutil",
],
},
+ entry_points="""
+ [z3c.autoinclude.plugin]
+ target = plone
+ """,
)
diff --git a/tox.ini b/tox.ini
index c6869f58..a44a828d 100644
--- a/tox.ini
+++ b/tox.ini
@@ -108,7 +108,7 @@ set_env =
##
deps =
zope.testrunner
- -c https://dist.plone.org/release/6.0-dev/constraints.txt
+ -c https://dist.plone.org/release/6.1-dev/constraints.txt
##
# Specify additional deps in .meta.toml:
@@ -152,7 +152,7 @@ set_env =
deps =
coverage
zope.testrunner
- -c https://dist.plone.org/release/6.0-dev/constraints.txt
+ -c https://dist.plone.org/release/6.1-dev/constraints.txt
commands =
rfbrowser init
@@ -171,7 +171,7 @@ deps =
twine
build
towncrier
- -c https://dist.plone.org/release/6.0-dev/constraints.txt
+ -c https://dist.plone.org/release/6.1-dev/constraints.txt
commands =
# fake version to not have to install the package
@@ -202,7 +202,7 @@ allowlist_externals =
deps =
pipdeptree
pipforester
- -c https://dist.plone.org/release/6.0-dev/constraints.txt
+ -c https://dist.plone.org/release/6.1-dev/constraints.txt
commands =
# Generate the full dependency tree