diff --git a/CHANGES.rst b/CHANGES.rst index 48666f31a1..3b50257184 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -28,6 +28,10 @@ Unreleased :meth:`~.SubredditRedditorFlairTemplates.add`, :meth:`~.SubredditLinkFlairTemplates.add`, and :meth:`~.SubredditFlairTemplates.update`, as well as its child classes. +* Add :class:`.SubmissionModerationFlair` which contains the + :meth:`~.SubmissionModerationFlair.choices` and + :meth:`~.SubmissionModerationFlair.select` methods and is back-compatible + with the previous flair method. **Deprecated** @@ -374,8 +378,8 @@ Unreleased * Return values from :meth:`.Comment.block`, :meth:`.Message.block`, :meth:`.SubredditMessage.block`, :meth:`.SubredditFlair.delete`, :meth:`.friend`, :meth:`.Redditor.message`, :meth:`.Subreddit.message`, - :meth:`.select`, and :meth:`.unfriend` are removed as they do not provide - any useful information. + :meth:`~.SubmissionFlair.select`, and :meth:`.unfriend` are removed as they + do not provide any useful information. * ``praw.ini`` no longer reads in ``http_proxy`` and ``https_proxy`` settings. * ``is_link`` parameter of :meth:`.SubredditRedditorFlairTemplates.add` and :meth:`.SubredditRedditorFlairTemplates.clear`. Use @@ -560,7 +564,7 @@ parameter as described below: * Return values from :meth:`.Comment.block`, :meth:`.Message.block`, :meth:`.SubredditMessage.block`, :meth:`.SubredditFlair.delete`, :meth:`.friend`, :meth:`.Redditor.message`, :meth:`.Subreddit.message`, - :meth:`.select`, and :meth:`.unfriend` will be removed in PRAW 5 as they do + :meth:`~.SubmissionFlair.select`, and :meth:`.unfriend` will be removed in PRAW 5 as they do not provide any useful information. **Fixed** @@ -600,7 +604,7 @@ parameter as described below: accidentally missed previously. * Add ``sticky`` parameter to :meth:`.CommentModeration.distinguish` to sticky comments. -* :meth:`.flair` to add a submission's flair from an instance of +* ``~.Submission.flair`` to add a submission's flair from an instance of :class:`.Submission`. * :meth:`.Comment.parent` to obtain the parent of a :class:`.Comment`. * :meth:`.opt_in` and :meth:`.opt_out` to :class:`.Subreddit` to permit working @@ -632,7 +636,7 @@ parameter as described below: ``Subreddit.mod.ignore_reports``, ``Subreddit.mod.remove``, ``Subreddit.mod.undistinguish``, ``Subreddit.mod.unignore_reports``. * Support for passing a :class:`.Submission` to :meth:`.SubredditFlair.set` - will be removed in PRAW 5. Use :meth:`.flair` instead. + will be removed in PRAW 5. Use ``Submission.flair`` instead. * The ``thing`` argument to :meth:`.SubredditFlair.set` is replaced with ``redditor`` and will be removed in PRAW 5. diff --git a/docs/code_overview/other.rst b/docs/code_overview/other.rst index 34314d3ff9..753b8a40fe 100644 --- a/docs/code_overview/other.rst +++ b/docs/code_overview/other.rst @@ -19,6 +19,7 @@ instances of them bound to an attribute of one of the PRAW models. :caption: Flair Helpers other/submissionflair + other/submissionmoderationflair other/subredditflair other/subredditflairtemplates other/subredditlinkflairtemplates diff --git a/docs/code_overview/other/submissionmoderationflair.rst b/docs/code_overview/other/submissionmoderationflair.rst new file mode 100644 index 0000000000..13d6a41103 --- /dev/null +++ b/docs/code_overview/other/submissionmoderationflair.rst @@ -0,0 +1,5 @@ +SubmissionModerationFlair +========================= + +.. autoclass:: praw.models.reddit.submission.SubmissionModerationFlair + :inherited-members: diff --git a/praw/models/reddit/submission.py b/praw/models/reddit/submission.py index 759c234bf0..34e1294261 100644 --- a/praw/models/reddit/submission.py +++ b/praw/models/reddit/submission.py @@ -140,7 +140,7 @@ def flair(self): This attribute is used to work with flair as a regular user of the subreddit the submission belongs to. Moderators can directly use - :meth:`.flair`. + :class:`.SubmissionModerationFlair`. For example, to select an arbitrary editable flair text (assuming there is one) and set a custom value try: @@ -441,6 +441,27 @@ class SubmissionModeration(ThingModerationMixin): REMOVAL_MESSAGE_API = "removal_link_message" + @cachedproperty + def flair(self): + """Provide an instance of :class:`.SubmissionModerationFlair`. + + Allows the moderator to work with setting flairs on a submission + that use the template system introduced in the redesign. + + If a flair template is not being used, then a moderator should directly + call the instance. + + Example usage: + + .. code:: python + + choices = submission.mod.flair.choices() + template_id = next(choices)["flair_template_id"] + submission.mod.flair.select(template_id, text="Custom flair") + + """ + return SubmissionModerationFlair(self.thing) + def __init__(self, submission): """Create a SubmissionModeration instance. @@ -476,33 +497,6 @@ def contest_mode(self, state=True): data={"id": self.thing.fullname, "state": state}, ) - def flair(self, text="", css_class=""): - """Set flair for the submission. - - :param text: The flair text to associate with the Submission (default: - ''). - :param css_class: The css class to associate with the flair html - (default: ''). - - This method can only be used by an authenticated user who is a - moderator of the Submission's Subreddit. - - Example usage: - - .. code:: python - - submission = reddit.submission(id='5or86n') - submission.mod.flair(text='PRAW', css_class='bot') - - """ - data = { - "css_class": css_class, - "link": self.thing.fullname, - "text": text, - } - url = API_PATH["flair"].format(subreddit=self.thing.subreddit) - self.thing._reddit.post(url, data=data) - def nsfw(self): """Mark as not safe for work. @@ -681,4 +675,80 @@ def unspoiler(self): ) +class SubmissionModerationFlair(SubmissionFlair): + """Provides a set of functions allowing moderators to set post flairs.""" + + def __call__(self, text="", css_class=""): + """Set flair for the submission. + + :param text: The flair text to associate with the Submission (default: + ''). + :param css_class: The css class to associate with the flair html + (default: ''). + + This method can only be used by an authenticated user who is a + moderator of the Submission's Subreddit. + + Example usage: + + .. code:: python + + submission = reddit.submission(id='5or86n') + submission.mod.flair(text='PRAW', css_class='bot') + + """ + data = { + "css_class": css_class, + "link": self.submission.fullname, + "text": text, + } + url = API_PATH["flair"].format(subreddit=self.submission.subreddit) + self.submission._reddit.post(url, data=data) + + def choices(self): + """Return list of available flair choices. + + Choices are required in order to use :meth:`.select`. + + For example: + + .. code:: python + + choices = submission.mod.flair.choices() + + """ + return iter(self.submission.subreddit.link_templates) + + def select(self, flair_template_id, text=None, css_class=None): + """Select flair for submission. + + :param flair_template_id: The flair template to select. The possible + ``flair_template_id`` values can be discovered through + :meth:`.choices`. + :param text: The custom text value for the flair (Optional). + :param css_class: The custom css class for the flair (Optional). + + For example, to select the first possible link flair: + + .. code:: python + + choices = submission.mod.flair.choices() + template_id = next(choices)["flair_template_id"] + submission.mod.flair.select(template_id, + text='my custom value', + css_class='my custom css class') + + """ + data = { + "flair_template_id": flair_template_id, + "link": self.submission.fullname, + "text": text, + "css_class": css_class, + } + url = API_PATH["select_flair"].format( + subreddit=self.submission.subreddit + ) + self.submission._reddit.post(url, data=data) + + Subreddit._submission_class = Submission diff --git a/tests/integration/models/reddit/test_submission.py b/tests/integration/models/reddit/test_submission.py index 1402cc39ba..1e3808d6ff 100644 --- a/tests/integration/models/reddit/test_submission.py +++ b/tests/integration/models/reddit/test_submission.py @@ -522,3 +522,41 @@ def test_unspoiler(self): "TestSubmissionModeration.test_unspoiler" ): Submission(self.reddit, "5ouli3").mod.unspoiler() + + def test_modflair_choices(self): + self.reddit.read_only = False + with self.recorder.use_cassette( + "TestSubmissionModeration.test_flair_choices" + ): + choices = Submission(self.reddit, "eh9cxx").mod.flair.choices() + choice_list = list(choices) + assert len(choice_list) > 0 + + def test_modflair_picked(self): + self.reddit.read_only = False + with self.recorder.use_cassette( + "TestSubmissionModeration.test_flair_selection" + ): + sub = Submission(self.reddit, "eh9cxx") + choices = sub.mod.flair.choices() + choice_list = list(choices) + sub.mod.flair.select(choice_list[0]["flair_template_id"]) + sub.mod.flair.select( + choice_list[0]["flair_template_id"], text="something" + ) + sub.mod.flair.select( + choice_list[0]["flair_template_id"], + text="something", + css_class="Something else", + ) + sub.mod.flair.select( + choice_list[0]["flair_template_id"], css_class="Something else" + ) + with pytest.raises(TypeError): + sub.mod.flair.select(text="something") + with pytest.raises(TypeError): + sub.mod.flair.select( + text="something", css_class="Something else" + ) + with pytest.raises(TypeError): + sub.mod.flair.select(css_class="Something else")