diff options
Diffstat (limited to 'pysite/views')
| -rw-r--r-- | pysite/views/api/bot/bigbrother.py | 118 | ||||
| -rw-r--r-- | pysite/views/api/bot/off_topic_names.py | 2 | ||||
| -rw-r--r-- | pysite/views/api/robots_txt.py | 15 | ||||
| -rw-r--r-- | pysite/views/api/sitemap_xml.py | 11 | ||||
| -rw-r--r-- | pysite/views/main/robots_txt.py | 15 | ||||
| -rw-r--r-- | pysite/views/main/sitemap_xml.py | 69 | ||||
| -rw-r--r-- | pysite/views/staff/robots_txt.py | 15 | ||||
| -rw-r--r-- | pysite/views/staff/sitemap_xml.py | 11 | ||||
| -rw-r--r-- | pysite/views/wiki/edit.py | 24 | ||||
| -rw-r--r-- | pysite/views/wiki/robots_txt.py | 15 | ||||
| -rw-r--r-- | pysite/views/wiki/sitemap_xml.py | 22 | 
11 files changed, 306 insertions, 11 deletions
| diff --git a/pysite/views/api/bot/bigbrother.py b/pysite/views/api/bot/bigbrother.py new file mode 100644 index 00000000..89697811 --- /dev/null +++ b/pysite/views/api/bot/bigbrother.py @@ -0,0 +1,118 @@ +import json + +from flask import jsonify +from schema import And, Optional, Schema + +from pysite.base_route import APIView +from pysite.constants import ValidationTypes +from pysite.decorators import api_key, api_params +from pysite.mixins import DBMixin + + +GET_SCHEMA = Schema({ +    # This is passed as a GET parameter, so it has to be a string +    Optional('user_id'): And(str, str.isnumeric, error="`user_id` must be a numeric string") +}) + +POST_SCHEMA = Schema({ +    'user_id': And(str, str.isnumeric, error="`user_id` must be a numeric string"), +    'channel_id': And(str, str.isnumeric, error="`channel_id` must be a numeric string") +}) + +DELETE_SCHEMA = Schema({ +    'user_id': And(str, str.isnumeric, error="`user_id` must be a numeric string") +}) + + +NOT_A_NUMBER_JSON = json.dumps({ +    'error_message': "The given `user_id` parameter is not a valid number" +}) +NOT_FOUND_JSON = json.dumps({ +    'error_message': "No entry for the requested user ID could be found." +}) + + +class BigBrotherView(APIView, DBMixin): +    path = '/bot/bigbrother' +    name = 'bot.bigbrother' +    table_name = 'watched_users' + +    @api_key +    @api_params(schema=GET_SCHEMA, validation_type=ValidationTypes.params) +    def get(self, params): +        """ +        Without query parameters, returns a list of all monitored users. +        A parameter `user_id` can be specified to return a single entry, +        or a dictionary with the string field 'error_message' that tells why it failed. + +        If the returned status is 200, has got either a list of entries +        or a single object (see above). + +        If the returned status is 400, the `user_id` parameter was incorrectly specified. +        If the returned status is 404, the given `user_id` could not be found. +        See the 'error_message' field in the JSON response for more information. + +        The user ID must be provided as query parameter. +        API key must be provided as header. +        """ + +        user_id = params.get('user_id') +        if user_id is not None: +            data = self.db.get(self.table_name, user_id) +            if data is None: +                return NOT_FOUND_JSON, 404 +            return jsonify(data) + +        else: +            data = self.db.pluck(self.table_name, ('user_id', 'channel_id')) or [] +            return jsonify(data) + +    @api_key +    @api_params(schema=POST_SCHEMA, validation_type=ValidationTypes.json) +    def post(self, data): +        """ +        Adds a new entry to the database. +        Entries take the following form: +        { +            "user_id": ...,  # The user ID of the user being monitored, as a string. +            "channel_id": ...  # The channel ID that the user's messages will be relayed to, as a string. +        } + +        If an entry for the given `user_id` already exists, it will be updated with the new channel ID. + +        Returns 204 (ok, empty response) on success. + +        Data must be provided as JSON. +        API key must be provided as header. +        """ + +        self.db.insert( +            self.table_name, +            { +                'user_id': data['user_id'], +                'channel_id': data['channel_id'] +            }, +            conflict='update' +        ) + +        return '', 204 + +    @api_key +    @api_params(schema=DELETE_SCHEMA, validation_type=ValidationTypes.params) +    def delete(self, params): +        """ +        Removes an entry for the given `user_id`. + +        Returns 204 (ok, empty response) on success. +        Returns 400 if the given `user_id` is invalid. + +        The user ID must be provided as query parameter. +        API key must be provided as header. +        """ + +        self.db.delete( +            self.table_name, +            params['user_id'] +        ) + +        return '', 204 diff --git a/pysite/views/api/bot/off_topic_names.py b/pysite/views/api/bot/off_topic_names.py index e0871067..f353ab02 100644 --- a/pysite/views/api/bot/off_topic_names.py +++ b/pysite/views/api/bot/off_topic_names.py @@ -13,7 +13,7 @@ POST_SCHEMA = Schema({      'name': And(          str,          len, -        lambda name: all(c.isalpha() or c == '-' for c in name), +        lambda name: all(c.isalnum() or c == '-' for c in name),          str.islower,          lambda name: len(name) <= 96,          error=( diff --git a/pysite/views/api/robots_txt.py b/pysite/views/api/robots_txt.py new file mode 100644 index 00000000..d4406d54 --- /dev/null +++ b/pysite/views/api/robots_txt.py @@ -0,0 +1,15 @@ +from flask import Response, url_for + +from pysite.base_route import RouteView + + +class RobotsTXT(RouteView): +    path = "/robots.txt" +    name = "robots_txt" + +    def get(self): +        return Response( +            self.render( +                "robots.txt", sitemap_url=url_for("api.sitemap_xml", _external=True), rules={"*": ["/"]} +            ), content_type="text/plain" +        ) diff --git a/pysite/views/api/sitemap_xml.py b/pysite/views/api/sitemap_xml.py new file mode 100644 index 00000000..26a786b0 --- /dev/null +++ b/pysite/views/api/sitemap_xml.py @@ -0,0 +1,11 @@ +from flask import Response + +from pysite.base_route import RouteView + + +class SitemapXML(RouteView): +    path = "/sitemap.xml" +    name = "sitemap_xml" + +    def get(self): +        return Response(self.render("sitemap.xml", urls=[]), content_type="application/xml") diff --git a/pysite/views/main/robots_txt.py b/pysite/views/main/robots_txt.py new file mode 100644 index 00000000..308fe2a2 --- /dev/null +++ b/pysite/views/main/robots_txt.py @@ -0,0 +1,15 @@ +from flask import Response, url_for + +from pysite.base_route import RouteView + + +class RobotsTXT(RouteView): +    path = "/robots.txt" +    name = "robots_txt" + +    def get(self): +        return Response( +            self.render( +                "robots.txt", sitemap_url=url_for("api.sitemap_xml", _external=True) +            ), content_type="text/plain" +        ) diff --git a/pysite/views/main/sitemap_xml.py b/pysite/views/main/sitemap_xml.py new file mode 100644 index 00000000..98893c21 --- /dev/null +++ b/pysite/views/main/sitemap_xml.py @@ -0,0 +1,69 @@ +from flask import Response, url_for + +from pysite.base_route import RouteView + + +class SitemapXML(RouteView): +    path = "/sitemap.xml" +    name = "sitemap_xml" + +    def get(self): +        urls = [ +            { +                "type": "url", +                "url": url_for("main.index", _external=True), +                "priority": 1.0,  # Max priority + +                "images": [ +                    { +                        "caption": "Python Discord Logo", +                        "url": url_for("static", filename="logos/logo_discord.png", _external=True) +                    }, +                    { +                        "caption": "Python Discord Banner", +                        "url": url_for("static", filename="logos/logo_banner.png", _external=True) +                    } +                ] +            }, + +            { +                "type": "url", +                "url": url_for("main.jams.index", _external=True), +                "priority": 0.9  # Above normal priority +            }, + +            { +                "type": "url", +                "url": url_for("main.about.privacy", _external=True), +                "priority": 0.8  # Above normal priority +            }, +            { +                "type": "url", +                "url": url_for("main.about.rules", _external=True), +                "priority": 0.8  # Above normal priority +            }, + +            { +                "type": "url", +                "url": url_for("main.info.help", _external=True), +                "priority": 0.7  # Above normal priority +            }, +            { +                "type": "url", +                "url": url_for("main.info.faq", _external=True), +                "priority": 0.7  # Above normal priority +            }, +            { +                "type": "url", +                "url": url_for("main.info.resources", _external=True), +                "priority": 0.7  # Above normal priority +            }, + +            { +                "type": "url", +                "url": url_for("main.about.partners", _external=True), +                "priority": 0.6  # Normal priority +            }, +        ] + +        return Response(self.render("sitemap.xml", urls=urls), content_type="application/xml") diff --git a/pysite/views/staff/robots_txt.py b/pysite/views/staff/robots_txt.py new file mode 100644 index 00000000..308fe2a2 --- /dev/null +++ b/pysite/views/staff/robots_txt.py @@ -0,0 +1,15 @@ +from flask import Response, url_for + +from pysite.base_route import RouteView + + +class RobotsTXT(RouteView): +    path = "/robots.txt" +    name = "robots_txt" + +    def get(self): +        return Response( +            self.render( +                "robots.txt", sitemap_url=url_for("api.sitemap_xml", _external=True) +            ), content_type="text/plain" +        ) diff --git a/pysite/views/staff/sitemap_xml.py b/pysite/views/staff/sitemap_xml.py new file mode 100644 index 00000000..26a786b0 --- /dev/null +++ b/pysite/views/staff/sitemap_xml.py @@ -0,0 +1,11 @@ +from flask import Response + +from pysite.base_route import RouteView + + +class SitemapXML(RouteView): +    path = "/sitemap.xml" +    name = "sitemap_xml" + +    def get(self): +        return Response(self.render("sitemap.xml", urls=[]), content_type="application/xml") diff --git a/pysite/views/wiki/edit.py b/pysite/views/wiki/edit.py index 65378807..949c9942 100644 --- a/pysite/views/wiki/edit.py +++ b/pysite/views/wiki/edit.py @@ -40,16 +40,20 @@ class EditView(RouteView, DBMixin, RMQMixin):          lock_expiry = datetime.datetime.utcnow() + datetime.timedelta(minutes=5) -        if not DEBUG_MODE:  # If we are in debug mode we have no user logged in, therefore we can skip locking -            self.db.insert( -                self.table_name, -                { -                    "slug": page, -                    "lock_expiry": lock_expiry.timestamp(), -                    "lock_user": self.user_data.get("user_id") -                }, -                conflict="update" -            ) +        # There are a couple of cases where we will not need to lock a page. One of these is if the application is +        # current set to debug mode. The other of these cases is if the page is empty, because if the page is empty +        # we will only have a partially filled out page if the user quits before saving. +        if obj: +            if not DEBUG_MODE and obj.get("rst"): +                self.db.insert( +                    self.table_name, +                    { +                        "slug": page, +                        "lock_expiry": lock_expiry.timestamp(), +                        "lock_user": self.user_data.get("user_id") +                    }, +                    conflict="update" +                )          return self.render("wiki/page_edit.html", page=page, rst=rst, title=title, preview=preview, can_edit=True) diff --git a/pysite/views/wiki/robots_txt.py b/pysite/views/wiki/robots_txt.py new file mode 100644 index 00000000..308fe2a2 --- /dev/null +++ b/pysite/views/wiki/robots_txt.py @@ -0,0 +1,15 @@ +from flask import Response, url_for + +from pysite.base_route import RouteView + + +class RobotsTXT(RouteView): +    path = "/robots.txt" +    name = "robots_txt" + +    def get(self): +        return Response( +            self.render( +                "robots.txt", sitemap_url=url_for("api.sitemap_xml", _external=True) +            ), content_type="text/plain" +        ) diff --git a/pysite/views/wiki/sitemap_xml.py b/pysite/views/wiki/sitemap_xml.py new file mode 100644 index 00000000..9b7f0980 --- /dev/null +++ b/pysite/views/wiki/sitemap_xml.py @@ -0,0 +1,22 @@ +from flask import Response, url_for + +from pysite.base_route import RouteView +from pysite.mixins import DBMixin + + +class SitemapXML(RouteView, DBMixin): +    path = "/sitemap.xml" +    name = "sitemap_xml" +    table_name = "wiki" + +    def get(self): +        urls = [] + +        for page in self.db.get_all(self.table_name): +            urls.append({ +                "change_frequency": "weekly", +                "type": "url", +                "url": url_for("wiki.page", page=page["slug"], _external=True) +            }) + +        return Response(self.render("sitemap.xml", urls=urls), content_type="application/xml") | 
