1"""Provide the WikiPage class."""
2from ...const import API_PATH
3from ..listing.generator import ListingGenerator
4from .base import RedditBase
5from .redditor import Redditor
6
7
8class WikiPage(RedditBase):
9    """An individual WikiPage object.
10
11    **Typical Attributes**
12
13    This table describes attributes that typically belong to objects of this
14    class. Since attributes are dynamically provided (see
15    :ref:`determine-available-attributes-of-an-object`), there is not a
16    guarantee that these attributes will always be present, nor is this list
17    necessarily comprehensive.
18
19    ======================= ===================================================
20    Attribute               Description
21    ======================= ===================================================
22    ``content_html``        The contents of the wiki page, as HTML.
23    ``content_md``          The contents of the wiki page, as Markdown.
24    ``may_revise``          A ``bool`` representing whether or not the
25                            authenticated user may edit the wiki page.
26    ``name``                The name of the wiki page.
27    ``revision_by``         The :class:`.Redditor` who authored this
28                            revision of the wiki page.
29    ``revision_date``       The time of this revision, in `Unix Time`_.
30    ``subreddit``           The :class:`.Subreddit` this wiki page belongs to.
31    ======================= ===================================================
32
33    .. _Unix Time: https://en.wikipedia.org/wiki/Unix_time
34    """
35
36    __hash__ = RedditBase.__hash__
37
38    @staticmethod
39    def _revision_generator(subreddit, url, generator_kwargs):
40        for revision in ListingGenerator(
41            subreddit._reddit, url, **generator_kwargs
42        ):
43            if revision["author"] is not None:
44                revision["author"] = Redditor(
45                    subreddit._reddit, _data=revision["author"]["data"]
46                )
47            revision["page"] = WikiPage(
48                subreddit._reddit, subreddit, revision["page"], revision["id"]
49            )
50            yield revision
51
52    @property
53    def mod(self):
54        """Provide an instance of :class:`.WikiPageModeration`."""
55        if self._mod is None:
56            self._mod = WikiPageModeration(self)
57        return self._mod
58
59    def __eq__(self, other):
60        """Return whether the other instance equals the current."""
61        return (
62            isinstance(other, self.__class__)
63            and str(self).lower() == str(other).lower()
64        )
65
66    def __init__(self, reddit, subreddit, name, revision=None, _data=None):
67        """Construct an instance of the WikiPage object.
68
69        :param revision: A specific revision ID to fetch. By default, fetches
70            the most recent revision.
71
72        """
73        self.name = name
74        self._revision = revision
75        self.subreddit = subreddit
76        super(WikiPage, self).__init__(reddit, _data=_data)
77        self._mod = None
78
79    def __repr__(self):
80        """Return an object initialization representation of the instance."""
81        return "{}(subreddit={!r}, name={!r})".format(
82            self.__class__.__name__, self.subreddit, self.name
83        )
84
85    def __str__(self):
86        """Return a string representation of the instance."""
87        return "{}/{}".format(self.subreddit, self.name)
88
89    def _fetch(self):
90        params = {"v": self._revision} if self._revision else None
91        data = self._reddit.get(self._info_path(), params=params)["data"]
92        if data["revision_by"] is not None:
93            data["revision_by"] = Redditor(
94                self._reddit, _data=data["revision_by"]["data"]
95            )
96        self.__dict__.update(data)
97        self._fetched = True
98
99    def _info_path(self):
100        return API_PATH["wiki_page"].format(
101            subreddit=self.subreddit, page=self.name
102        )
103
104    def edit(self, content, reason=None, **other_settings):
105        """Edit this WikiPage's contents.
106
107        :param content: The updated markdown content of the page.
108        :param reason: (Optional) The reason for the revision.
109        :param other_settings: Additional keyword arguments to pass.
110
111        """
112        other_settings.update(
113            {"content": content, "page": self.name, "reason": reason}
114        )
115        self._reddit.post(
116            API_PATH["wiki_edit"].format(subreddit=self.subreddit),
117            data=other_settings,
118        )
119
120    def revision(self, revision):
121        """Return a specific version of this page by revision ID.
122
123        To view revision ``[ID]`` of ``'praw_test'`` in ``'/r/test'``:
124
125        .. code:: python
126
127           page = reddit.subreddit('test').wiki['praw_test'].revision('[ID]')
128
129        """
130        return WikiPage(
131            self.subreddit._reddit, self.subreddit, self.name, revision
132        )
133
134    def revisions(self, **generator_kwargs):
135        """Return a generator for page revisions.
136
137        Additional keyword arguments are passed in the initialization of
138        :class:`.ListingGenerator`.
139
140        To view the wiki revisions for ``'praw_test'`` in ``'/r/test'`` try:
141
142        .. code:: python
143
144           for item in reddit.subreddit('test').wiki['praw_test'].revisions():
145               print(item)
146
147        To get :class:`.WikiPage` objects for each revision:
148
149        .. code:: python
150
151           for item in reddit.subreddit('test').wiki['praw_test'].revisions():
152               print(item['page'])
153
154        """
155        url = API_PATH["wiki_page_revisions"].format(
156            subreddit=self.subreddit, page=self.name
157        )
158        return self._revision_generator(self.subreddit, url, generator_kwargs)
159
160
161class WikiPageModeration(object):
162    """Provides a set of moderation functions for a WikiPage."""
163
164    def __init__(self, wikipage):
165        """Create a WikiPageModeration instance.
166
167        :param wikipage: The wikipage to moderate.
168
169        """
170        self.wikipage = wikipage
171
172    def add(self, redditor):
173        """Add an editor to this WikiPage.
174
175        :param redditor: A redditor name (e.g., ``'spez'``) or
176            :class:`~.Redditor` instance.
177
178        To add ``'spez'`` as an editor on the wikipage ``'praw_test'`` try:
179
180        .. code:: python
181
182           reddit.subreddit('test').wiki['praw_test'].mod.add('spez')
183
184        """
185        data = {"page": self.wikipage.name, "username": str(redditor)}
186        url = API_PATH["wiki_page_editor"].format(
187            subreddit=self.wikipage.subreddit, method="add"
188        )
189        self.wikipage._reddit.post(url, data=data)
190
191    def remove(self, redditor):
192        """Remove an editor from this WikiPage.
193
194        :param redditor: A redditor name (e.g., ``'spez'``) or
195            :class:`~.Redditor` instance.
196
197        To remove ``'spez'`` as an editor on the wikipage ``'praw_test'`` try:
198
199        .. code:: python
200
201           reddit.subreddit('test').wiki['praw_test'].mod.remove('spez')
202
203        """
204        data = {"page": self.wikipage.name, "username": str(redditor)}
205        url = API_PATH["wiki_page_editor"].format(
206            subreddit=self.wikipage.subreddit, method="del"
207        )
208        self.wikipage._reddit.post(url, data=data)
209
210    def settings(self):
211        """Return the settings for this WikiPage."""
212        url = API_PATH["wiki_page_settings"].format(
213            subreddit=self.wikipage.subreddit, page=self.wikipage.name
214        )
215        return self.wikipage._reddit.get(url)["data"]
216
217    def update(self, listed, permlevel, **other_settings):
218        """Update the settings for this WikiPage.
219
220        :param listed: (boolean) Show this page on page list.
221        :param permlevel: (int) Who can edit this page? (0) use subreddit wiki
222            permissions, (1) only approved wiki contributors for this page may
223            edit (see :meth:`.WikiPageModeration.add`), (2) only mods may edit
224            and view
225        :param other_settings: Additional keyword arguments to pass.
226        :returns: The updated WikiPage settings.
227
228        To set the wikipage ``'praw_test'`` in ``'/r/test'`` to mod only and
229          disable it from showing in the page list, try:
230
231        .. code:: python
232
233           reddit.subreddit('test').wiki['praw_test'].mod.update(listed=False,
234                                                                 permlevel=2)
235
236        """
237        other_settings.update({"listed": listed, "permlevel": permlevel})
238        url = API_PATH["wiki_page_settings"].format(
239            subreddit=self.wikipage.subreddit, page=self.wikipage.name
240        )
241        return self.wikipage._reddit.post(url, data=other_settings)["data"]
242