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 + 3000 profile-plone.resource:default profile-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 @@ False False + + + 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.svg Discussion Item plone.Comment @@ -18,7 +19,7 @@ False True - 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 @@ + + + ++plone++bootstrap-icons/chat-left-text.svg + + + + + + + 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