We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
API: parents endpoint
Endpoint to get parents of a page up to the root. This must be compatible to Integreat API v3.

https://github.com/Integreat/cms/wiki/REST-APIv3-Documentation#parents-of-a-post

@maxammann @Simple-codinger is this used by any app? Otherwise we could move this to another milestone.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `integreat_cms/api/v3/pages.py`
Content:
```
1 """
2 pages API endpoint
3 """
4 from django.conf import settings
5 from django.http import JsonResponse, Http404
6 from django.shortcuts import get_object_or_404
7 
8 from ...cms.models import Region, Page
9 from ..decorators import json_response
10 
11 
12 def transform_page(page_translation):
13     """
14     Function to create a dict from a single page_translation Object.
15 
16     :param page_translation: single page translation object
17     :type page_translation: ~integreat_cms.cms.models.pages.page_translation.PageTranslation
18 
19     :return: data necessary for API
20     :rtype: dict
21     """
22     if page_translation.page.icon:
23         thumbnail = settings.BASE_URL + page_translation.page.icon.url
24     else:
25         thumbnail = None
26     if page_translation.page.parent:
27         parent = {
28             "id": page_translation.page.parent.id,
29             "url": page_translation.page.parent.get_translation(
30                 page_translation.language.slug
31             ).backend_base_link,
32             "path": "/"
33             + page_translation.page.parent.get_translation(
34                 page_translation.language.slug
35             ).permalink
36             + "/",
37         }
38     else:
39         parent = {
40             "id": 0,
41             "url": None,
42             "path": None,
43         }
44     return {
45         "id": page_translation.id,
46         "url": page_translation.backend_base_link,
47         "path": "/" + page_translation.permalink + "/",
48         "title": page_translation.title,
49         "modified_gmt": page_translation.combined_last_updated,
50         "excerpt": page_translation.text,
51         "content": page_translation.combined_text,
52         "parent": parent,
53         "order": page_translation.page.lft,  # use left edge indicator of mptt model for order
54         "available_languages": page_translation.available_languages,
55         "thumbnail": thumbnail,
56         "hash": None,
57     }
58 
59 
60 @json_response
61 # pylint: disable=unused-argument
62 def pages(request, region_slug, language_slug):
63     """
64     Function to iterate through all non-archived pages of a region and return them as JSON.
65 
66     :param request: Django request
67     :type request: ~django.http.HttpRequest
68     :param region_slug: slug of a region
69     :type region_slug: str
70     :param language_slug: language slug
71     :type language_slug: str
72 
73     :return: JSON object according to APIv3 pages endpoint definition
74     :rtype: ~django.http.JsonResponse
75     """
76     region = Region.get_current_region(request)
77     result = []
78     for page in region.get_pages():
79         page_translation = page.get_public_translation(language_slug)
80         if page_translation:
81             result.append(transform_page(page_translation))
82     return JsonResponse(
83         result, safe=False
84     )  # Turn off Safe-Mode to allow serializing arrays
85 
86 
87 def get_single_page(request, language_slug):
88     """
89     Helper function returning the desired page or a 404 if the
90     requested page does not exist.
91 
92     :param request: The request that has been sent to the Django server
93     :type request: ~django.http.HttpRequest
94 
95     :param language_slug: Code to identify the desired language
96     :type language_slug: str
97 
98     :raises ~django.http.Http404: HTTP status 404 if the request is malformed or no page with the given id or url exists.
99 
100     :raises RuntimeError: If neither the id nor the url parameter is given
101 
102     :return: the requested page
103     :rtype: ~integreat_cms.cms.models.pages.page.Page
104     """
105     region = Region.get_current_region(request)
106 
107     if request.GET.get("id"):
108         page = get_object_or_404(region.pages, id=request.GET.get("id"))
109 
110     elif request.GET.get("url"):
111         # Strip leading and trailing slashes to avoid ambiguous urls
112         url = request.GET.get("url").strip("/")
113         # The last path component of the url is the page translation slug
114         page_translation_slug = url.split("/")[-1]
115         # Get page by filtering for translation slug and translation language slug
116         filtered_pages = region.pages.filter(
117             translations__slug=page_translation_slug,
118             translations__language__slug=language_slug,
119         ).distinct()
120 
121         if len(filtered_pages) != 1:
122             raise Http404("No matching page translation found for url.")
123         page = filtered_pages[0]
124 
125     else:
126         raise RuntimeError("Either the id or the url parameter is required.")
127 
128     return page
129 
130 
131 @json_response
132 # pylint: disable=unused-argument
133 def single_page(request, region_slug, language_slug):
134     """
135     View function returning the desired page as a JSON or a 404 if the
136     requested page does not exist.
137 
138     :param request: The request that has been sent to the Django server
139     :type request: ~django.http.HttpRequest
140 
141     :param region_slug: Slug defining the region
142     :type region_slug: str
143 
144     :param language_slug: Code to identify the desired language
145     :type language_slug: str
146 
147     :raises ~django.http.Http404: HTTP status 404 if the request is malformed or no page with the given id or url exists.
148 
149     :return: JSON with the requested page and a HTTP status 200.
150     :rtype: ~django.http.JsonResponse
151     """
152     try:
153         page = get_single_page(request, language_slug)
154     except RuntimeError as e:
155         return JsonResponse({"error": str(e)}, status=400)
156     # Get most recent public revision of the page
157     page_translation = page.get_public_translation(language_slug)
158     if page_translation:
159         return JsonResponse(transform_page(page_translation), safe=False)
160 
161     raise Http404("No Page matches the given url or id.")
162 
163 
164 @json_response
165 # pylint: disable=unused-argument
166 def children(request, region_slug, language_slug):
167     """
168     Retrieves all children for a single page
169 
170     :param request: The request that has been sent to the Django server
171     :type request: ~django.http.HttpRequest
172 
173     :param region_slug: Slug defining the region
174     :type region_slug: str
175 
176     :param language_slug: Code to identify the desired language
177     :type language_slug: str
178 
179     :raises ~django.http.Http404: HTTP status 404 if the request is malformed or no page with the given id or url exists.
180 
181     :return: JSON with the requested page descendants
182     :rtype: ~django.http.JsonResponse
183     """
184     depth = int(request.GET.get("depth", 1))
185     try:
186         # try to get a single ancestor page based on the requests query string
187         root_pages = [get_single_page(request, language_slug)]
188     except RuntimeError:
189         # if neither id nor url is set then get all root pages
190         root_pages = Page.get_root_pages(region_slug)
191         # simulate a virtual root node for WP compatibility
192         # so that depth = 1 returns only those pages without parents (immediate children of this virtual root page)
193         # like in wordpress depth = 0 will return no results in this case
194         depth = depth - 1
195     result = []
196     for root in root_pages:
197         descendants = root.get_descendants_max_depth(True, depth)
198         for descendant in descendants:
199             public_translation = descendant.get_public_translation(language_slug)
200             if public_translation:
201                 result.append(transform_page(public_translation))
202     return JsonResponse(result, safe=False)
203
```

Path: `integreat_cms/api/urls.py`
Content:
```
1 """
2 Expansion of API-Endpoints for the CMS
3 """
4 from django.conf.urls import include, url
5 
6 from .v3.events import events
7 from .v3.feedback import (
8     page_feedback,
9     search_result_feedback,
10     region_feedback,
11     offer_feedback,
12     offer_list_feedback,
13     event_list_feedback,
14     event_feedback,
15     poi_feedback,
16     map_feedback,
17     imprint_page_feedback,
18     legacy_feedback_endpoint,
19 )
20 from .v3.imprint import imprint
21 from .v3.languages import languages
22 from .v3.locations import locations
23 from .v3.pages import pages, children, single_page
24 from .v3.pdf_export import pdf_export
25 from .v3.push_notifications import sent_push_notifications
26 from .v3.regions import regions, liveregions, hiddenregions, pushnew
27 from .v3.offers import offers
28 
29 content_api_urlpatterns = [
30     url(r"^pages/?$", pages, name="api_pages"),
31     url(r"^locations/?$", locations, name="api_locations"),
32     url(r"^events/?$", events, name="api_events"),
33     url(
34         r"^(?:page|post)/?$",
35         single_page,
36         name="api_single_page",
37     ),
38     url(r"^children/?$", children, name="api_children"),
39     url(
40         r"^pdf/?$",
41         pdf_export,
42         name="api_pdf_export",
43     ),
44     url(
45         r"^sent_push_notifications/?$",
46         sent_push_notifications,
47         name="api_sent_push_notifications",
48     ),
49     url(
50         r"^(?:imprint|disclaimer)/?$",
51         imprint,
52         name="api_imprint",
53     ),
54     url(r"^(?:offers|extras)/?$", offers, name="api_offers"),
55     url(
56         r"^feedback/",
57         include(
58             [
59                 url(
60                     r"^$",
61                     legacy_feedback_endpoint.legacy_feedback_endpoint,
62                     name="api_legacy_feedback_endpoint",
63                 ),
64                 url(
65                     r"^categories/?$",
66                     region_feedback.region_feedback,
67                     name="api_region_feedback",
68                 ),
69                 url(
70                     r"^page/?$",
71                     page_feedback.page_feedback,
72                     name="api_page_feedback",
73                 ),
74                 url(
75                     r"^poi/?$",
76                     poi_feedback.poi_feedback,
77                     name="api_poi_feedback",
78                 ),
79                 url(
80                     r"^event/?$",
81                     event_feedback.event_feedback,
82                     name="api_event_feedback",
83                 ),
84                 url(
85                     r"^events/?$",
86                     event_list_feedback.event_list_feedback,
87                     name="api_event_list_feedback",
88                 ),
89                 url(
90                     r"^imprint-page/?$",
91                     imprint_page_feedback.imprint_page_feedback,
92                     name="api_imprint_page_feedbacks",
93                 ),
94                 url(
95                     r"^map/?$",
96                     map_feedback.map_feedback,
97                     name="api_map_feedback",
98                 ),
99                 url(
100                     r"^search/?$",
101                     search_result_feedback.search_result_feedback,
102                     name="api_search_result_feedback",
103                 ),
104                 url(
105                     r"^(?:extras|offers)/?$",
106                     offer_list_feedback.offer_list_feedback,
107                     name="api_offer_list_feedback",
108                 ),
109                 url(
110                     r"^(?:extra|offer)/?$",
111                     offer_feedback.offer_feedback,
112                     name="api_offer_feedback",
113                 ),
114             ]
115         ),
116     ),
117 ]
118 
119 region_api_urlpatterns = [
120     url(r"^$", regions, name="api_regions"),
121     url(r"^live/?$", liveregions, name="api_regions_live"),
122     url(r"^hidden/?$", hiddenregions, name="api_regions_hidden"),
123     url(r"^pushnew/?$", pushnew, name="api_pushnew"),
124 ]
125 
126 
127 urlpatterns = [
128     url(r"^api/regions/", include(region_api_urlpatterns)),
129     url(r"^wp-json/extensions/v3/sites/", include(region_api_urlpatterns)),
130     url(
131         r"^api/(?P<region_slug>[-\w]+)/",
132         include(
133             [
134                 url(r"^languages/?$", languages, name="api_languages"),
135                 url(r"^(?:offers|extras)/?$", offers, name="api_offers"),
136                 url(r"^(?P<language_slug>[-\w]+)/", include(content_api_urlpatterns)),
137             ]
138         ),
139     ),
140     url(
141         r"^(?P<region_slug>[-\w]+)/",
142         include(
143             [
144                 url(
145                     r"^de/wp-json/extensions/v3/languages/?$",
146                     languages,
147                     name="api_languages",
148                 ),
149                 url(
150                     r"^(?P<language_slug>[-\w]+)/wp-json/extensions/v3/",
151                     include(content_api_urlpatterns),
152                 ),
153             ]
154         ),
155     ),
156 ]
157
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/integreat_cms/api/urls.py b/integreat_cms/api/urls.py
--- a/integreat_cms/api/urls.py
+++ b/integreat_cms/api/urls.py
@@ -20,7 +20,7 @@
 from .v3.imprint import imprint
 from .v3.languages import languages
 from .v3.locations import locations
-from .v3.pages import pages, children, single_page
+from .v3.pages import pages, children, parents, single_page
 from .v3.pdf_export import pdf_export
 from .v3.push_notifications import sent_push_notifications
 from .v3.regions import regions, liveregions, hiddenregions, pushnew
@@ -36,6 +36,7 @@
         name="api_single_page",
     ),
     url(r"^children/?$", children, name="api_children"),
+    url(r"^parents/?$", parents, name="api_parents"),
     url(
         r"^pdf/?$",
         pdf_export,
diff --git a/integreat_cms/api/v3/pages.py b/integreat_cms/api/v3/pages.py
--- a/integreat_cms/api/v3/pages.py
+++ b/integreat_cms/api/v3/pages.py
@@ -200,3 +200,33 @@
             if public_translation:
                 result.append(transform_page(public_translation))
     return JsonResponse(result, safe=False)
+
+
+@json_response
+# pylint: disable=unused-argument
+def parents(request, region_slug, language_slug):
+    """
+    Retrieves all ancestors (parent and all nodes up to the root node) of a page
+
+    :param request: The request that has been sent to the Django server
+    :type request: ~django.http.HttpRequest
+
+    :param region_slug: Slug defining the region
+    :type region_slug: str
+
+    :param language_slug: Code to identify the desired language
+    :type language_slug: str
+
+    :raises ~django.http.Http404: HTTP status 404 if the request is malformed or no page with the given id or url exists.
+
+    :return: JSON with the requested page ancestors
+    :rtype: ~django.http.JsonResponse
+    """
+    current_page = get_single_page(request, language_slug)
+    result = []
+    for ancestor in current_page.get_ancestors(include_self=False):
+        public_translation = ancestor.get_public_translation(language_slug)
+        if not public_translation:
+            raise Http404("No Page matches the given url or id.")
+        result.append(transform_page(public_translation))
+    return JsonResponse(result, safe=False)

{"golden_diff": "diff --git a/integreat_cms/api/urls.py b/integreat_cms/api/urls.py\n--- a/integreat_cms/api/urls.py\n+++ b/integreat_cms/api/urls.py\n@@ -20,7 +20,7 @@\n from .v3.imprint import imprint\n from .v3.languages import languages\n from .v3.locations import locations\n-from .v3.pages import pages, children, single_page\n+from .v3.pages import pages, children, parents, single_page\n from .v3.pdf_export import pdf_export\n from .v3.push_notifications import sent_push_notifications\n from .v3.regions import regions, liveregions, hiddenregions, pushnew\n@@ -36,6 +36,7 @@\n         name=\"api_single_page\",\n     ),\n     url(r\"^children/?$\", children, name=\"api_children\"),\n+    url(r\"^parents/?$\", parents, name=\"api_parents\"),\n     url(\n         r\"^pdf/?$\",\n         pdf_export,\ndiff --git a/integreat_cms/api/v3/pages.py b/integreat_cms/api/v3/pages.py\n--- a/integreat_cms/api/v3/pages.py\n+++ b/integreat_cms/api/v3/pages.py\n@@ -200,3 +200,33 @@\n             if public_translation:\n                 result.append(transform_page(public_translation))\n     return JsonResponse(result, safe=False)\n+\n+\n+@json_response\n+# pylint: disable=unused-argument\n+def parents(request, region_slug, language_slug):\n+    \"\"\"\n+    Retrieves all ancestors (parent and all nodes up to the root node) of a page\n+\n+    :param request: The request that has been sent to the Django server\n+    :type request: ~django.http.HttpRequest\n+\n+    :param region_slug: Slug defining the region\n+    :type region_slug: str\n+\n+    :param language_slug: Code to identify the desired language\n+    :type language_slug: str\n+\n+    :raises ~django.http.Http404: HTTP status 404 if the request is malformed or no page with the given id or url exists.\n+\n+    :return: JSON with the requested page ancestors\n+    :rtype: ~django.http.JsonResponse\n+    \"\"\"\n+    current_page = get_single_page(request, language_slug)\n+    result = []\n+    for ancestor in current_page.get_ancestors(include_self=False):\n+        public_translation = ancestor.get_public_translation(language_slug)\n+        if not public_translation:\n+            raise Http404(\"No Page matches the given url or id.\")\n+        result.append(transform_page(public_translation))\n+    return JsonResponse(result, safe=False)\n", "issue": "API: parents endpoint\nEndpoint to get parents of a page up to the root. This must be compatible to Integreat API v3.\r\n\r\nhttps://github.com/Integreat/cms/wiki/REST-APIv3-Documentation#parents-of-a-post\r\n\r\n@maxammann @Simple-codinger is this used by any app? Otherwise we could move this to another milestone.\n", "before_files": [{"content": "\"\"\"\npages API endpoint\n\"\"\"\nfrom django.conf import settings\nfrom django.http import JsonResponse, Http404\nfrom django.shortcuts import get_object_or_404\n\nfrom ...cms.models import Region, Page\nfrom ..decorators import json_response\n\n\ndef transform_page(page_translation):\n    \"\"\"\n    Function to create a dict from a single page_translation Object.\n\n    :param page_translation: single page translation object\n    :type page_translation: ~integreat_cms.cms.models.pages.page_translation.PageTranslation\n\n    :return: data necessary for API\n    :rtype: dict\n    \"\"\"\n    if page_translation.page.icon:\n        thumbnail = settings.BASE_URL + page_translation.page.icon.url\n    else:\n        thumbnail = None\n    if page_translation.page.parent:\n        parent = {\n            \"id\": page_translation.page.parent.id,\n            \"url\": page_translation.page.parent.get_translation(\n                page_translation.language.slug\n            ).backend_base_link,\n            \"path\": \"/\"\n            + page_translation.page.parent.get_translation(\n                page_translation.language.slug\n            ).permalink\n            + \"/\",\n        }\n    else:\n        parent = {\n            \"id\": 0,\n            \"url\": None,\n            \"path\": None,\n        }\n    return {\n        \"id\": page_translation.id,\n        \"url\": page_translation.backend_base_link,\n        \"path\": \"/\" + page_translation.permalink + \"/\",\n        \"title\": page_translation.title,\n        \"modified_gmt\": page_translation.combined_last_updated,\n        \"excerpt\": page_translation.text,\n        \"content\": page_translation.combined_text,\n        \"parent\": parent,\n        \"order\": page_translation.page.lft,  # use left edge indicator of mptt model for order\n        \"available_languages\": page_translation.available_languages,\n        \"thumbnail\": thumbnail,\n        \"hash\": None,\n    }\n\n\n@json_response\n# pylint: disable=unused-argument\ndef pages(request, region_slug, language_slug):\n    \"\"\"\n    Function to iterate through all non-archived pages of a region and return them as JSON.\n\n    :param request: Django request\n    :type request: ~django.http.HttpRequest\n    :param region_slug: slug of a region\n    :type region_slug: str\n    :param language_slug: language slug\n    :type language_slug: str\n\n    :return: JSON object according to APIv3 pages endpoint definition\n    :rtype: ~django.http.JsonResponse\n    \"\"\"\n    region = Region.get_current_region(request)\n    result = []\n    for page in region.get_pages():\n        page_translation = page.get_public_translation(language_slug)\n        if page_translation:\n            result.append(transform_page(page_translation))\n    return JsonResponse(\n        result, safe=False\n    )  # Turn off Safe-Mode to allow serializing arrays\n\n\ndef get_single_page(request, language_slug):\n    \"\"\"\n    Helper function returning the desired page or a 404 if the\n    requested page does not exist.\n\n    :param request: The request that has been sent to the Django server\n    :type request: ~django.http.HttpRequest\n\n    :param language_slug: Code to identify the desired language\n    :type language_slug: str\n\n    :raises ~django.http.Http404: HTTP status 404 if the request is malformed or no page with the given id or url exists.\n\n    :raises RuntimeError: If neither the id nor the url parameter is given\n\n    :return: the requested page\n    :rtype: ~integreat_cms.cms.models.pages.page.Page\n    \"\"\"\n    region = Region.get_current_region(request)\n\n    if request.GET.get(\"id\"):\n        page = get_object_or_404(region.pages, id=request.GET.get(\"id\"))\n\n    elif request.GET.get(\"url\"):\n        # Strip leading and trailing slashes to avoid ambiguous urls\n        url = request.GET.get(\"url\").strip(\"/\")\n        # The last path component of the url is the page translation slug\n        page_translation_slug = url.split(\"/\")[-1]\n        # Get page by filtering for translation slug and translation language slug\n        filtered_pages = region.pages.filter(\n            translations__slug=page_translation_slug,\n            translations__language__slug=language_slug,\n        ).distinct()\n\n        if len(filtered_pages) != 1:\n            raise Http404(\"No matching page translation found for url.\")\n        page = filtered_pages[0]\n\n    else:\n        raise RuntimeError(\"Either the id or the url parameter is required.\")\n\n    return page\n\n\n@json_response\n# pylint: disable=unused-argument\ndef single_page(request, region_slug, language_slug):\n    \"\"\"\n    View function returning the desired page as a JSON or a 404 if the\n    requested page does not exist.\n\n    :param request: The request that has been sent to the Django server\n    :type request: ~django.http.HttpRequest\n\n    :param region_slug: Slug defining the region\n    :type region_slug: str\n\n    :param language_slug: Code to identify the desired language\n    :type language_slug: str\n\n    :raises ~django.http.Http404: HTTP status 404 if the request is malformed or no page with the given id or url exists.\n\n    :return: JSON with the requested page and a HTTP status 200.\n    :rtype: ~django.http.JsonResponse\n    \"\"\"\n    try:\n        page = get_single_page(request, language_slug)\n    except RuntimeError as e:\n        return JsonResponse({\"error\": str(e)}, status=400)\n    # Get most recent public revision of the page\n    page_translation = page.get_public_translation(language_slug)\n    if page_translation:\n        return JsonResponse(transform_page(page_translation), safe=False)\n\n    raise Http404(\"No Page matches the given url or id.\")\n\n\n@json_response\n# pylint: disable=unused-argument\ndef children(request, region_slug, language_slug):\n    \"\"\"\n    Retrieves all children for a single page\n\n    :param request: The request that has been sent to the Django server\n    :type request: ~django.http.HttpRequest\n\n    :param region_slug: Slug defining the region\n    :type region_slug: str\n\n    :param language_slug: Code to identify the desired language\n    :type language_slug: str\n\n    :raises ~django.http.Http404: HTTP status 404 if the request is malformed or no page with the given id or url exists.\n\n    :return: JSON with the requested page descendants\n    :rtype: ~django.http.JsonResponse\n    \"\"\"\n    depth = int(request.GET.get(\"depth\", 1))\n    try:\n        # try to get a single ancestor page based on the requests query string\n        root_pages = [get_single_page(request, language_slug)]\n    except RuntimeError:\n        # if neither id nor url is set then get all root pages\n        root_pages = Page.get_root_pages(region_slug)\n        # simulate a virtual root node for WP compatibility\n        # so that depth = 1 returns only those pages without parents (immediate children of this virtual root page)\n        # like in wordpress depth = 0 will return no results in this case\n        depth = depth - 1\n    result = []\n    for root in root_pages:\n        descendants = root.get_descendants_max_depth(True, depth)\n        for descendant in descendants:\n            public_translation = descendant.get_public_translation(language_slug)\n            if public_translation:\n                result.append(transform_page(public_translation))\n    return JsonResponse(result, safe=False)\n", "path": "integreat_cms/api/v3/pages.py"}, {"content": "\"\"\"\nExpansion of API-Endpoints for the CMS\n\"\"\"\nfrom django.conf.urls import include, url\n\nfrom .v3.events import events\nfrom .v3.feedback import (\n    page_feedback,\n    search_result_feedback,\n    region_feedback,\n    offer_feedback,\n    offer_list_feedback,\n    event_list_feedback,\n    event_feedback,\n    poi_feedback,\n    map_feedback,\n    imprint_page_feedback,\n    legacy_feedback_endpoint,\n)\nfrom .v3.imprint import imprint\nfrom .v3.languages import languages\nfrom .v3.locations import locations\nfrom .v3.pages import pages, children, single_page\nfrom .v3.pdf_export import pdf_export\nfrom .v3.push_notifications import sent_push_notifications\nfrom .v3.regions import regions, liveregions, hiddenregions, pushnew\nfrom .v3.offers import offers\n\ncontent_api_urlpatterns = [\n    url(r\"^pages/?$\", pages, name=\"api_pages\"),\n    url(r\"^locations/?$\", locations, name=\"api_locations\"),\n    url(r\"^events/?$\", events, name=\"api_events\"),\n    url(\n        r\"^(?:page|post)/?$\",\n        single_page,\n        name=\"api_single_page\",\n    ),\n    url(r\"^children/?$\", children, name=\"api_children\"),\n    url(\n        r\"^pdf/?$\",\n        pdf_export,\n        name=\"api_pdf_export\",\n    ),\n    url(\n        r\"^sent_push_notifications/?$\",\n        sent_push_notifications,\n        name=\"api_sent_push_notifications\",\n    ),\n    url(\n        r\"^(?:imprint|disclaimer)/?$\",\n        imprint,\n        name=\"api_imprint\",\n    ),\n    url(r\"^(?:offers|extras)/?$\", offers, name=\"api_offers\"),\n    url(\n        r\"^feedback/\",\n        include(\n            [\n                url(\n                    r\"^$\",\n                    legacy_feedback_endpoint.legacy_feedback_endpoint,\n                    name=\"api_legacy_feedback_endpoint\",\n                ),\n                url(\n                    r\"^categories/?$\",\n                    region_feedback.region_feedback,\n                    name=\"api_region_feedback\",\n                ),\n                url(\n                    r\"^page/?$\",\n                    page_feedback.page_feedback,\n                    name=\"api_page_feedback\",\n                ),\n                url(\n                    r\"^poi/?$\",\n                    poi_feedback.poi_feedback,\n                    name=\"api_poi_feedback\",\n                ),\n                url(\n                    r\"^event/?$\",\n                    event_feedback.event_feedback,\n                    name=\"api_event_feedback\",\n                ),\n                url(\n                    r\"^events/?$\",\n                    event_list_feedback.event_list_feedback,\n                    name=\"api_event_list_feedback\",\n                ),\n                url(\n                    r\"^imprint-page/?$\",\n                    imprint_page_feedback.imprint_page_feedback,\n                    name=\"api_imprint_page_feedbacks\",\n                ),\n                url(\n                    r\"^map/?$\",\n                    map_feedback.map_feedback,\n                    name=\"api_map_feedback\",\n                ),\n                url(\n                    r\"^search/?$\",\n                    search_result_feedback.search_result_feedback,\n                    name=\"api_search_result_feedback\",\n                ),\n                url(\n                    r\"^(?:extras|offers)/?$\",\n                    offer_list_feedback.offer_list_feedback,\n                    name=\"api_offer_list_feedback\",\n                ),\n                url(\n                    r\"^(?:extra|offer)/?$\",\n                    offer_feedback.offer_feedback,\n                    name=\"api_offer_feedback\",\n                ),\n            ]\n        ),\n    ),\n]\n\nregion_api_urlpatterns = [\n    url(r\"^$\", regions, name=\"api_regions\"),\n    url(r\"^live/?$\", liveregions, name=\"api_regions_live\"),\n    url(r\"^hidden/?$\", hiddenregions, name=\"api_regions_hidden\"),\n    url(r\"^pushnew/?$\", pushnew, name=\"api_pushnew\"),\n]\n\n\nurlpatterns = [\n    url(r\"^api/regions/\", include(region_api_urlpatterns)),\n    url(r\"^wp-json/extensions/v3/sites/\", include(region_api_urlpatterns)),\n    url(\n        r\"^api/(?P<region_slug>[-\\w]+)/\",\n        include(\n            [\n                url(r\"^languages/?$\", languages, name=\"api_languages\"),\n                url(r\"^(?:offers|extras)/?$\", offers, name=\"api_offers\"),\n                url(r\"^(?P<language_slug>[-\\w]+)/\", include(content_api_urlpatterns)),\n            ]\n        ),\n    ),\n    url(\n        r\"^(?P<region_slug>[-\\w]+)/\",\n        include(\n            [\n                url(\n                    r\"^de/wp-json/extensions/v3/languages/?$\",\n                    languages,\n                    name=\"api_languages\",\n                ),\n                url(\n                    r\"^(?P<language_slug>[-\\w]+)/wp-json/extensions/v3/\",\n                    include(content_api_urlpatterns),\n                ),\n            ]\n        ),\n    ),\n]\n", "path": "integreat_cms/api/urls.py"}], "after_files": [{"content": "\"\"\"\npages API endpoint\n\"\"\"\nfrom django.conf import settings\nfrom django.http import JsonResponse, Http404\nfrom django.shortcuts import get_object_or_404\n\nfrom ...cms.models import Region, Page\nfrom ..decorators import json_response\n\n\ndef transform_page(page_translation):\n    \"\"\"\n    Function to create a dict from a single page_translation Object.\n\n    :param page_translation: single page translation object\n    :type page_translation: ~integreat_cms.cms.models.pages.page_translation.PageTranslation\n\n    :return: data necessary for API\n    :rtype: dict\n    \"\"\"\n    if page_translation.page.icon:\n        thumbnail = settings.BASE_URL + page_translation.page.icon.url\n    else:\n        thumbnail = None\n    if page_translation.page.parent:\n        parent = {\n            \"id\": page_translation.page.parent.id,\n            \"url\": page_translation.page.parent.get_translation(\n                page_translation.language.slug\n            ).backend_base_link,\n            \"path\": \"/\"\n            + page_translation.page.parent.get_translation(\n                page_translation.language.slug\n            ).permalink\n            + \"/\",\n        }\n    else:\n        parent = {\n            \"id\": 0,\n            \"url\": None,\n            \"path\": None,\n        }\n    return {\n        \"id\": page_translation.id,\n        \"url\": page_translation.backend_base_link,\n        \"path\": \"/\" + page_translation.permalink + \"/\",\n        \"title\": page_translation.title,\n        \"modified_gmt\": page_translation.combined_last_updated,\n        \"excerpt\": page_translation.text,\n        \"content\": page_translation.combined_text,\n        \"parent\": parent,\n        \"order\": page_translation.page.lft,  # use left edge indicator of mptt model for order\n        \"available_languages\": page_translation.available_languages,\n        \"thumbnail\": thumbnail,\n        \"hash\": None,\n    }\n\n\n@json_response\n# pylint: disable=unused-argument\ndef pages(request, region_slug, language_slug):\n    \"\"\"\n    Function to iterate through all non-archived pages of a region and return them as JSON.\n\n    :param request: Django request\n    :type request: ~django.http.HttpRequest\n    :param region_slug: slug of a region\n    :type region_slug: str\n    :param language_slug: language slug\n    :type language_slug: str\n\n    :return: JSON object according to APIv3 pages endpoint definition\n    :rtype: ~django.http.JsonResponse\n    \"\"\"\n    region = Region.get_current_region(request)\n    result = []\n    for page in region.get_pages():\n        page_translation = page.get_public_translation(language_slug)\n        if page_translation:\n            result.append(transform_page(page_translation))\n    return JsonResponse(\n        result, safe=False\n    )  # Turn off Safe-Mode to allow serializing arrays\n\n\ndef get_single_page(request, language_slug):\n    \"\"\"\n    Helper function returning the desired page or a 404 if the\n    requested page does not exist.\n\n    :param request: The request that has been sent to the Django server\n    :type request: ~django.http.HttpRequest\n\n    :param language_slug: Code to identify the desired language\n    :type language_slug: str\n\n    :raises ~django.http.Http404: HTTP status 404 if the request is malformed or no page with the given id or url exists.\n\n    :raises RuntimeError: If neither the id nor the url parameter is given\n\n    :return: the requested page\n    :rtype: ~integreat_cms.cms.models.pages.page.Page\n    \"\"\"\n    region = Region.get_current_region(request)\n\n    if request.GET.get(\"id\"):\n        page = get_object_or_404(region.pages, id=request.GET.get(\"id\"))\n\n    elif request.GET.get(\"url\"):\n        # Strip leading and trailing slashes to avoid ambiguous urls\n        url = request.GET.get(\"url\").strip(\"/\")\n        # The last path component of the url is the page translation slug\n        page_translation_slug = url.split(\"/\")[-1]\n        # Get page by filtering for translation slug and translation language slug\n        filtered_pages = region.pages.filter(\n            translations__slug=page_translation_slug,\n            translations__language__slug=language_slug,\n        ).distinct()\n\n        if len(filtered_pages) != 1:\n            raise Http404(\"No matching page translation found for url.\")\n        page = filtered_pages[0]\n\n    else:\n        raise RuntimeError(\"Either the id or the url parameter is required.\")\n\n    return page\n\n\n@json_response\n# pylint: disable=unused-argument\ndef single_page(request, region_slug, language_slug):\n    \"\"\"\n    View function returning the desired page as a JSON or a 404 if the\n    requested page does not exist.\n\n    :param request: The request that has been sent to the Django server\n    :type request: ~django.http.HttpRequest\n\n    :param region_slug: Slug defining the region\n    :type region_slug: str\n\n    :param language_slug: Code to identify the desired language\n    :type language_slug: str\n\n    :raises ~django.http.Http404: HTTP status 404 if the request is malformed or no page with the given id or url exists.\n\n    :return: JSON with the requested page and a HTTP status 200.\n    :rtype: ~django.http.JsonResponse\n    \"\"\"\n    try:\n        page = get_single_page(request, language_slug)\n    except RuntimeError as e:\n        return JsonResponse({\"error\": str(e)}, status=400)\n    # Get most recent public revision of the page\n    page_translation = page.get_public_translation(language_slug)\n    if page_translation:\n        return JsonResponse(transform_page(page_translation), safe=False)\n\n    raise Http404(\"No Page matches the given url or id.\")\n\n\n@json_response\n# pylint: disable=unused-argument\ndef children(request, region_slug, language_slug):\n    \"\"\"\n    Retrieves all children for a single page\n\n    :param request: The request that has been sent to the Django server\n    :type request: ~django.http.HttpRequest\n\n    :param region_slug: Slug defining the region\n    :type region_slug: str\n\n    :param language_slug: Code to identify the desired language\n    :type language_slug: str\n\n    :raises ~django.http.Http404: HTTP status 404 if the request is malformed or no page with the given id or url exists.\n\n    :return: JSON with the requested page descendants\n    :rtype: ~django.http.JsonResponse\n    \"\"\"\n    depth = int(request.GET.get(\"depth\", 1))\n    try:\n        # try to get a single ancestor page based on the requests query string\n        root_pages = [get_single_page(request, language_slug)]\n    except RuntimeError:\n        # if neither id nor url is set then get all root pages\n        root_pages = Page.get_root_pages(region_slug)\n        # simulate a virtual root node for WP compatibility\n        # so that depth = 1 returns only those pages without parents (immediate children of this virtual root page)\n        # like in wordpress depth = 0 will return no results in this case\n        depth = depth - 1\n    result = []\n    for root in root_pages:\n        descendants = root.get_descendants_max_depth(True, depth)\n        for descendant in descendants:\n            public_translation = descendant.get_public_translation(language_slug)\n            if public_translation:\n                result.append(transform_page(public_translation))\n    return JsonResponse(result, safe=False)\n\n\n@json_response\n# pylint: disable=unused-argument\ndef parents(request, region_slug, language_slug):\n    \"\"\"\n    Retrieves all ancestors (parent and all nodes up to the root node) of a page\n\n    :param request: The request that has been sent to the Django server\n    :type request: ~django.http.HttpRequest\n\n    :param region_slug: Slug defining the region\n    :type region_slug: str\n\n    :param language_slug: Code to identify the desired language\n    :type language_slug: str\n\n    :raises ~django.http.Http404: HTTP status 404 if the request is malformed or no page with the given id or url exists.\n\n    :return: JSON with the requested page ancestors\n    :rtype: ~django.http.JsonResponse\n    \"\"\"\n    current_page = get_single_page(request, language_slug)\n    result = []\n    for ancestor in current_page.get_ancestors(include_self=False):\n        public_translation = ancestor.get_public_translation(language_slug)\n        if not public_translation:\n            raise Http404(\"No Page matches the given url or id.\")\n        result.append(transform_page(public_translation))\n    return JsonResponse(result, safe=False)\n", "path": "integreat_cms/api/v3/pages.py"}, {"content": "\"\"\"\nExpansion of API-Endpoints for the CMS\n\"\"\"\nfrom django.conf.urls import include, url\n\nfrom .v3.events import events\nfrom .v3.feedback import (\n    page_feedback,\n    search_result_feedback,\n    region_feedback,\n    offer_feedback,\n    offer_list_feedback,\n    event_list_feedback,\n    event_feedback,\n    poi_feedback,\n    map_feedback,\n    imprint_page_feedback,\n    legacy_feedback_endpoint,\n)\nfrom .v3.imprint import imprint\nfrom .v3.languages import languages\nfrom .v3.locations import locations\nfrom .v3.pages import pages, children, parents, single_page\nfrom .v3.pdf_export import pdf_export\nfrom .v3.push_notifications import sent_push_notifications\nfrom .v3.regions import regions, liveregions, hiddenregions, pushnew\nfrom .v3.offers import offers\n\ncontent_api_urlpatterns = [\n    url(r\"^pages/?$\", pages, name=\"api_pages\"),\n    url(r\"^locations/?$\", locations, name=\"api_locations\"),\n    url(r\"^events/?$\", events, name=\"api_events\"),\n    url(\n        r\"^(?:page|post)/?$\",\n        single_page,\n        name=\"api_single_page\",\n    ),\n    url(r\"^children/?$\", children, name=\"api_children\"),\n    url(r\"^parents/?$\", parents, name=\"api_parents\"),\n    url(\n        r\"^pdf/?$\",\n        pdf_export,\n        name=\"api_pdf_export\",\n    ),\n    url(\n        r\"^sent_push_notifications/?$\",\n        sent_push_notifications,\n        name=\"api_sent_push_notifications\",\n    ),\n    url(\n        r\"^(?:imprint|disclaimer)/?$\",\n        imprint,\n        name=\"api_imprint\",\n    ),\n    url(r\"^(?:offers|extras)/?$\", offers, name=\"api_offers\"),\n    url(\n        r\"^feedback/\",\n        include(\n            [\n                url(\n                    r\"^$\",\n                    legacy_feedback_endpoint.legacy_feedback_endpoint,\n                    name=\"api_legacy_feedback_endpoint\",\n                ),\n                url(\n                    r\"^categories/?$\",\n                    region_feedback.region_feedback,\n                    name=\"api_region_feedback\",\n                ),\n                url(\n                    r\"^page/?$\",\n                    page_feedback.page_feedback,\n                    name=\"api_page_feedback\",\n                ),\n                url(\n                    r\"^poi/?$\",\n                    poi_feedback.poi_feedback,\n                    name=\"api_poi_feedback\",\n                ),\n                url(\n                    r\"^event/?$\",\n                    event_feedback.event_feedback,\n                    name=\"api_event_feedback\",\n                ),\n                url(\n                    r\"^events/?$\",\n                    event_list_feedback.event_list_feedback,\n                    name=\"api_event_list_feedback\",\n                ),\n                url(\n                    r\"^imprint-page/?$\",\n                    imprint_page_feedback.imprint_page_feedback,\n                    name=\"api_imprint_page_feedbacks\",\n                ),\n                url(\n                    r\"^map/?$\",\n                    map_feedback.map_feedback,\n                    name=\"api_map_feedback\",\n                ),\n                url(\n                    r\"^search/?$\",\n                    search_result_feedback.search_result_feedback,\n                    name=\"api_search_result_feedback\",\n                ),\n                url(\n                    r\"^(?:extras|offers)/?$\",\n                    offer_list_feedback.offer_list_feedback,\n                    name=\"api_offer_list_feedback\",\n                ),\n                url(\n                    r\"^(?:extra|offer)/?$\",\n                    offer_feedback.offer_feedback,\n                    name=\"api_offer_feedback\",\n                ),\n            ]\n        ),\n    ),\n]\n\nregion_api_urlpatterns = [\n    url(r\"^$\", regions, name=\"api_regions\"),\n    url(r\"^live/?$\", liveregions, name=\"api_regions_live\"),\n    url(r\"^hidden/?$\", hiddenregions, name=\"api_regions_hidden\"),\n    url(r\"^pushnew/?$\", pushnew, name=\"api_pushnew\"),\n]\n\n\nurlpatterns = [\n    url(r\"^api/regions/\", include(region_api_urlpatterns)),\n    url(r\"^wp-json/extensions/v3/sites/\", include(region_api_urlpatterns)),\n    url(\n        r\"^api/(?P<region_slug>[-\\w]+)/\",\n        include(\n            [\n                url(r\"^languages/?$\", languages, name=\"api_languages\"),\n                url(r\"^(?:offers|extras)/?$\", offers, name=\"api_offers\"),\n                url(r\"^(?P<language_slug>[-\\w]+)/\", include(content_api_urlpatterns)),\n            ]\n        ),\n    ),\n    url(\n        r\"^(?P<region_slug>[-\\w]+)/\",\n        include(\n            [\n                url(\n                    r\"^de/wp-json/extensions/v3/languages/?$\",\n                    languages,\n                    name=\"api_languages\",\n                ),\n                url(\n                    r\"^(?P<language_slug>[-\\w]+)/wp-json/extensions/v3/\",\n                    include(content_api_urlpatterns),\n                ),\n            ]\n        ),\n    ),\n]\n", "path": "integreat_cms/api/urls.py"}]}