jenkins-bot has submitted this change and it was merged.
Change subject: Moderate Flow topics and posts
......................................................................
Moderate Flow topics and posts
This change allows Pywikibot bots to moderate topics and posts using
Flow's moderation system.
Bug: T109308
Change-Id: I92f7c664d13f8397219a4045d782f4b1123610f5
---
M pywikibot/flow.py
M pywikibot/site.py
M tests/flow_edit_tests.py
3 files changed, 375 insertions(+), 5 deletions(-)
Approvals:
John Vandenberg: Looks good to me, approved
jenkins-bot: Verified
diff --git a/pywikibot/flow.py b/pywikibot/flow.py
index accc5d4..9ccb5f5 100644
--- a/pywikibot/flow.py
+++ b/pywikibot/flow.py
@@ -164,6 +164,10 @@
self._data = self.site.load_topic(self, format)
return self._data
+ def _reload(self):
+ """Forcibly reload the topic's root post."""
+ self.root._load(load_from_topic=True)
+
@classmethod
def create_topic(cls, board, title, content, format='wikitext'):
"""Create and return a Topic object for a new topic on a Board.
@@ -219,6 +223,11 @@
"""Whether this topic is locked."""
return self.root._current_revision['isLocked']
+ @property
+ def is_moderated(self):
+ """Whether this topic is moderated."""
+ return self.root._current_revision['isModerated']
+
def replies(self, format='wikitext', force=False):
"""A list of replies to this topic's root post.
@@ -243,23 +252,60 @@
"""
return self.root.reply(content, format)
- def lock(self, reason='Closed'):
+ # Moderation
+ def lock(self, reason):
"""Lock this topic.
@param reason: The reason for locking this topic
@type reason: unicode
"""
self.site.lock_topic(self, True, reason)
- self.root._load(load_from_topic=True)
+ self._reload()
- def unlock(self, reason='Reopened'):
+ def unlock(self, reason):
"""Unlock this topic.
@param reason: The reason for unlocking this topic
@type reason: unicode
"""
self.site.lock_topic(self, False, reason)
- self.root._load(load_from_topic=True)
+ self._reload()
+
+ def delete_mod(self, reason):
+ """Delete this topic through the Flow moderation system.
+
+ @param reason: The reason for deleting this topic.
+ @type reason: unicode
+ """
+ self.site.delete_topic(self, reason)
+ self._reload()
+
+ def hide(self, reason):
+ """Hide this topic.
+
+ @param reason: The reason for hiding this topic.
+ @type reason: unicode
+ """
+ self.site.hide_topic(self, reason)
+ self._reload()
+
+ def suppress(self, reason):
+ """Suppress this topic.
+
+ @param reason: The reason for suppressing this topic.
+ @type reason: unicode
+ """
+ self.site.suppress_topic(self, reason)
+ self._reload()
+
+ def restore(self, reason):
+ """Restore this topic.
+
+ @param reason: The reason for restoring this topic.
+ @type reason: unicode
+ """
+ self.site.restore_topic(self, reason)
+ self._reload()
# Flow non-page-like objects
@@ -375,6 +421,13 @@
"""
return self._page
+ @property
+ def is_moderated(self):
+ """Whether this post is moderated."""
+ if not hasattr(self, '_current_revision'):
+ self._load()
+ return self._current_revision['isModerated']
+
def get(self, format='wikitext', force=False, sysop=False):
"""Return the contents of the post in the given format.
@@ -445,3 +498,40 @@
data = self.site.reply_to_post(self.page, reply_to, content, format)
post = Post(self.page, data['post-id'])
return post
+
+ # Moderation
+ def delete(self, reason):
+ """Delete this post through the Flow moderation system.
+
+ @param reason: The reason for deleting this post.
+ @type reason: unicode
+ """
+ self.site.delete_post(self, reason)
+ self._load()
+
+ def hide(self, reason):
+ """Hide this post.
+
+ @param reason: The reason for hiding this post.
+ @type reason: unicode
+ """
+ self.site.hide_post(self, reason)
+ self._load()
+
+ def suppress(self, reason):
+ """Suppress this post.
+
+ @param reason: The reason for suppressing this post.
+ @type reason: unicode
+ """
+ self.site.suppress_post(self, reason)
+ self._load()
+
+ def restore(self, reason):
+ """Restore this post.
+
+ @param reason: The reason for restoring this post.
+ @type reason: unicode
+ """
+ self.site.restore_post(self, reason)
+ self._load()
diff --git a/pywikibot/site.py b/pywikibot/site.py
index 25dedf7..bd319d5 100644
--- a/pywikibot/site.py
+++ b/pywikibot/site.py
@@ -6325,7 +6325,7 @@
data = req.submit()
return
data['flow']['reply']['committed']['topic']
- @must_be('user')
+ @must_be('user', 'flow-lock')
@need_extension('Flow')
def lock_topic(self, page, lock, reason):
"""Lock or unlock a Flow topic.
@@ -6348,6 +6348,164 @@
data = req.submit()
return
data['flow']['lock-topic']['committed']['topic']
+ @must_be('user')
+ @need_extension('Flow')
+ def moderate_topic(self, page, state, reason):
+ """Moderate a Flow topic.
+
+ @param page: A Flow topic
+ @type page: Topic
+ @param state: The new moderation state
+ @type state: str
+ @param reason: The reason to moderate the topic
+ @type reason: unicode
+ @return: Metadata returned by the API
+ @rtype: dict
+ """
+ token = self.tokens['csrf']
+ params = {'action': 'flow', 'page': page,
'token': token,
+ 'submodule': 'moderate-topic', 'mtreason':
reason,
+ 'mtmoderationState': state}
+ req = self._request(parameters=params, use_get=False)
+ data = req.submit()
+ return
data['flow']['moderate-topic']['committed']['topic']
+
+ @must_be('user', 'flow-delete')
+ @need_extension('Flow')
+ def delete_topic(self, page, reason):
+ """Delete a Flow topic.
+
+ @param page: A Flow topic
+ @type page: Topic
+ @param reason: The reason to delete the topic
+ @type reason: unicode
+ @return: Metadata returned by the API
+ @rtype: dict
+ """
+ return self.moderate_topic(page, 'delete', reason)
+
+ @must_be('user', 'flow-hide')
+ @need_extension('Flow')
+ def hide_topic(self, page, reason):
+ """Hide a Flow topic.
+
+ @param page: A Flow topic
+ @type page: Topic
+ @param reason: The reason to hide the topic
+ @type reason: unicode
+ @return: Metadata returned by the API
+ @rtype: dict
+ """
+ return self.moderate_topic(page, 'hide', reason)
+
+ @must_be('user', 'flow-suppress')
+ @need_extension('Flow')
+ def suppress_topic(self, page, reason):
+ """Suppress a Flow topic.
+
+ @param page: A Flow topic
+ @type page: Topic
+ @param reason: The reason to suppress the topic
+ @type reason: unicode
+ @return: Metadata returned by the API
+ @rtype: dict
+ """
+ return self.moderate_topic(page, 'suppress', reason)
+
+ @must_be('user')
+ @need_extension('Flow')
+ def restore_topic(self, page, reason):
+ """Restore a Flow topic.
+
+ @param page: A Flow topic
+ @type page: Topic
+ @param reason: The reason to restore the topic
+ @type reason: unicode
+ @return: Metadata returned by the API
+ @rtype: dict
+ """
+ return self.moderate_topic(page, 'restore', reason)
+
+ @must_be('user')
+ @need_extension('Flow')
+ def moderate_post(self, post, state, reason):
+ """Moderate a Flow post.
+
+ @param post: A Flow post
+ @type post: Post
+ @param state: The new moderation state
+ @type state: str
+ @param reason: The reason to moderate the topic
+ @type reason: unicode
+ @return: Metadata returned by the API
+ @rtype: dict
+ """
+ page = post.page
+ uuid = post.uuid
+ token = self.tokens['csrf']
+ params = {'action': 'flow', 'page': page,
'token': token,
+ 'submodule': 'moderate-post', 'mpreason':
reason,
+ 'mpmoderationState': state, 'mppostId': uuid}
+ req = self._request(parameters=params, use_get=False)
+ data = req.submit()
+ return
data['flow']['moderate-post']['committed']['topic']
+
+ @must_be('user', 'flow-delete')
+ @need_extension('Flow')
+ def delete_post(self, post, reason):
+ """Delete a Flow post.
+
+ @param post: A Flow post
+ @type post: Post
+ @param reason: The reason to delete the post
+ @type reason: unicode
+ @return: Metadata returned by the API
+ @rtype: dict
+ """
+ return self.moderate_post(post, 'delete', reason)
+
+ @must_be('user', 'flow-hide')
+ @need_extension('Flow')
+ def hide_post(self, post, reason):
+ """Hide a Flow post.
+
+ @param post: A Flow post
+ @type post: Post
+ @param reason: The reason to hide the post
+ @type reason: unicode
+ @return: Metadata returned by the API
+ @rtype: dict
+ """
+ return self.moderate_post(post, 'hide', reason)
+
+ @must_be('user', 'flow-suppress')
+ @need_extension('Flow')
+ def suppress_post(self, post, reason):
+ """Suppress a Flow post.
+
+ @param post: A Flow post
+ @type post: Post
+ @param reason: The reason to suppress the post
+ @type reason: unicode
+ @return: Metadata returned by the API
+ @rtype: dict
+ """
+ return self.moderate_post(post, 'suppress', reason)
+
+ @must_be('user')
+ @need_extension('Flow')
+ def restore_post(self, post, reason):
+ """Restore a Flow post.
+
+ @param post: A Flow post
+ @type post: Post
+ @param reason: The reason to restore the post
+ @type reason: unicode
+ @return: Metadata returned by the API
+ @rtype: dict
+ """
+ return self.moderate_post(post, 'restore', reason)
+
def watched_pages(self, sysop=False, force=False, step=None, total=None):
"""
Return watchlist.
diff --git a/tests/flow_edit_tests.py b/tests/flow_edit_tests.py
index 899274c..5ae7a54 100644
--- a/tests/flow_edit_tests.py
+++ b/tests/flow_edit_tests.py
@@ -173,6 +173,128 @@
self.assertFalse(topic.is_locked)
+class TestFlowHide(TestCase):
+
+ """Hiding topics and posts."""
+
+ family = 'test'
+ code = 'test'
+
+ user = True
+ write = True
+
+ def test_hide_topic(self):
+ """Hide and restore a test topic."""
+ # Setup
+ topic = Topic(self.site, 'Topic:Sl4svodmrhzmpjjh')
+ if topic.is_moderated:
+ topic.restore('Pywikibot test')
+ self.assertFalse(topic.is_moderated)
+ # Hide
+ topic.hide('Pywikibot test')
+ self.assertTrue(topic.is_moderated)
+ # Restore
+ topic.restore('Pywikibot test')
+ self.assertFalse(topic.is_moderated)
+
+ def test_hide_post(self):
+ """Hide and restore a test post."""
+ # Setup
+ topic = Topic(self.site, 'Topic:Sl4svodmrhzmpjjh')
+ post = Post(topic, 'sq1qvoig1az8w7cd')
+ if post.is_moderated:
+ post.restore('Pywikibot test')
+ self.assertFalse(post.is_moderated)
+ # Hide
+ post.hide('Pywikibot test')
+ self.assertTrue(post.is_moderated)
+ # Restore
+ post.restore('Pywikibot test')
+ self.assertFalse(post.is_moderated)
+
+
+class TestFlowDelete(TestCase):
+
+ """Deleting topics and posts."""
+
+ family = 'test'
+ code = 'test'
+
+ user = True
+ write = True
+ sysop = True
+
+ def test_delete_topic(self):
+ """Delete and restore a test topic."""
+ # Setup
+ topic = Topic(self.site, 'Topic:Sl4svodmrhzmpjjh')
+ if topic.is_moderated:
+ topic.restore('Pywikibot test')
+ self.assertFalse(topic.is_moderated)
+ # Delete
+ topic.delete_mod('Pywikibot test')
+ self.assertTrue(topic.is_moderated)
+ # Restore
+ topic.restore('Pywikibot test')
+ self.assertFalse(topic.is_moderated)
+
+ def test_delete_post(self):
+ """Delete and restore a test post."""
+ # Setup
+ topic = Topic(self.site, 'Topic:Sl4svodmrhzmpjjh')
+ post = Post(topic, 'sq1qvoig1az8w7cd')
+ if post.is_moderated:
+ post.restore('Pywikibot test')
+ self.assertFalse(post.is_moderated)
+ # Delete
+ post.delete('Pywikibot test')
+ self.assertTrue(post.is_moderated)
+ # Restore
+ post.restore('Pywikibot test')
+ self.assertFalse(post.is_moderated)
+
+
+class TestFlowSuppress(TestCase):
+
+ """Suppressing topics and posts."""
+
+ family = 'test'
+ code = 'test'
+
+ user = True
+ write = True
+ sysop = True
+
+ def test_suppress_post(self):
+ """Suppress and restore a test post."""
+ # Setup
+ topic = Topic(self.site, 'Topic:Sl4svodmrhzmpjjh')
+ post = Post(topic, 'sq1qvoig1az8w7cd')
+ if post.is_moderated:
+ post.restore('Pywikibot test')
+ self.assertFalse(post.is_moderated)
+ # Suppress
+ post.suppress('Pywikibot test')
+ self.assertTrue(post.is_moderated)
+ # Restore
+ post.restore('Pywikibot test')
+ self.assertFalse(post.is_moderated)
+
+ def test_suppress_topic(self):
+ """Suppress and restore a test topic."""
+ # Setup
+ topic = Topic(self.site, 'Topic:Sl4svodmrhzmpjjh')
+ if topic.is_moderated:
+ topic.restore('Pywikibot test')
+ self.assertFalse(topic.is_moderated)
+ # Suppress
+ topic.suppress('Pywikibot test')
+ self.assertTrue(topic.is_moderated)
+ # Restore
+ topic.restore('Pywikibot test')
+ self.assertFalse(topic.is_moderated)
+
+
class TestFlowEditFailure(TestCase):
"""Flow-related edit failure tests."""
--
To view, visit
https://gerrit.wikimedia.org/r/233482
To unsubscribe, visit
https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: merged
Gerrit-Change-Id: I92f7c664d13f8397219a4045d782f4b1123610f5
Gerrit-PatchSet: 6
Gerrit-Project: pywikibot/core
Gerrit-Branch: master
Gerrit-Owner: Happy5214 <happy5214(a)gmail.com>
Gerrit-Reviewer: Happy5214 <happy5214(a)gmail.com>
Gerrit-Reviewer: John Vandenberg <jayvdb(a)gmail.com>
Gerrit-Reviewer: Ladsgroup <ladsgroup(a)gmail.com>
Gerrit-Reviewer: Mattflaschen <mflaschen(a)wikimedia.org>
Gerrit-Reviewer: XZise <CommodoreFabianus(a)gmx.de>
Gerrit-Reviewer: jenkins-bot <>