plain 0.62.1__py3-none-any.whl → 0.63.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- plain/CHANGELOG.md +13 -1
- plain/chores/README.md +1 -1
- plain/cli/agent/request.py +1 -1
- plain/internal/middleware/https.py +4 -17
- plain/runtime/global_settings.py +3 -9
- plain/test/README.md +1 -1
- plain/views/README.md +4 -4
- {plain-0.62.1.dist-info → plain-0.63.0.dist-info}/METADATA +1 -1
- {plain-0.62.1.dist-info → plain-0.63.0.dist-info}/RECORD +12 -12
- {plain-0.62.1.dist-info → plain-0.63.0.dist-info}/WHEEL +0 -0
- {plain-0.62.1.dist-info → plain-0.63.0.dist-info}/entry_points.txt +0 -0
- {plain-0.62.1.dist-info → plain-0.63.0.dist-info}/licenses/LICENSE +0 -0
plain/CHANGELOG.md
CHANGED
@@ -1,5 +1,17 @@
|
|
1
1
|
# plain changelog
|
2
2
|
|
3
|
+
## [0.63.0](https://github.com/dropseed/plain/releases/plain@0.63.0) (2025-09-12)
|
4
|
+
|
5
|
+
### What's changed
|
6
|
+
|
7
|
+
- Model manager attribute renamed from `objects` to `query` throughout codebase ([037a239](https://github.com/dropseed/plain/commit/037a239ef4))
|
8
|
+
- Simplified HTTPS redirect middleware by removing `HTTPS_REDIRECT_EXEMPT_PATHS` and `HTTPS_REDIRECT_HOST` settings ([d264cd3](https://github.com/dropseed/plain/commit/d264cd306b))
|
9
|
+
- Database backups are now created automatically during migrations when `DEBUG=True` unless explicitly disabled ([c802307](https://github.com/dropseed/plain/commit/c8023074e9))
|
10
|
+
|
11
|
+
### Upgrade instructions
|
12
|
+
|
13
|
+
- Remove any `HTTPS_REDIRECT_EXEMPT_PATHS` and `HTTPS_REDIRECT_HOST` settings from your configuration - the HTTPS redirect middleware now performs a blanket redirect. For advanced redirect logic, write custom middleware.
|
14
|
+
|
3
15
|
## [0.62.1](https://github.com/dropseed/plain/releases/plain@0.62.1) (2025-09-09)
|
4
16
|
|
5
17
|
### What's changed
|
@@ -126,7 +138,7 @@
|
|
126
138
|
|
127
139
|
- Update your URL patterns from `<int:pk>` to `<int:id>` in your URLconf
|
128
140
|
- Update view code that accesses `self.url_kwargs["pk"]` to use `self.url_kwargs["id"]` instead
|
129
|
-
- Replace any QuerySet filters using `pk` with `id` (e.g., `Model.
|
141
|
+
- Replace any QuerySet filters using `pk` with `id` (e.g., `Model.query.get(pk=1)` becomes `Model.query.get(id=1)`)
|
130
142
|
|
131
143
|
## [0.54.1](https://github.com/dropseed/plain/releases/plain@0.54.1) (2025-07-20)
|
132
144
|
|
plain/chores/README.md
CHANGED
@@ -27,7 +27,7 @@ def clear_expired():
|
|
27
27
|
"""
|
28
28
|
Delete sessions that have expired.
|
29
29
|
"""
|
30
|
-
result = Session.
|
30
|
+
result = Session.query.filter(expires_at__lt=timezone.now()).delete()
|
31
31
|
return f"{result[0]} expired sessions deleted"
|
32
32
|
```
|
33
33
|
|
plain/cli/agent/request.py
CHANGED
@@ -64,7 +64,7 @@ def request(path, method, data, user_id, follow, content_type, headers):
|
|
64
64
|
|
65
65
|
# Get the user
|
66
66
|
try:
|
67
|
-
user = User.
|
67
|
+
user = User.query.get(id=user_id)
|
68
68
|
client.force_login(user)
|
69
69
|
click.secho(
|
70
70
|
f"Authenticated as user {user_id}", fg="green", dim=True
|
@@ -1,5 +1,3 @@
|
|
1
|
-
import re
|
2
|
-
|
3
1
|
from plain.http import ResponseRedirect
|
4
2
|
from plain.runtime import settings
|
5
3
|
|
@@ -8,16 +6,12 @@ class HttpsRedirectMiddleware:
|
|
8
6
|
def __init__(self, get_response):
|
9
7
|
self.get_response = get_response
|
10
8
|
|
11
|
-
# Settings for
|
9
|
+
# Settings for HTTPS
|
12
10
|
self.https_redirect_enabled = settings.HTTPS_REDIRECT_ENABLED
|
13
|
-
self.https_redirect_host = settings.HTTPS_REDIRECT_HOST
|
14
|
-
self.https_redirect_exempt = [
|
15
|
-
re.compile(r) for r in settings.HTTPS_REDIRECT_EXEMPT_PATHS
|
16
|
-
]
|
17
11
|
|
18
12
|
def __call__(self, request):
|
19
13
|
"""
|
20
|
-
|
14
|
+
Perform a blanket HTTP→HTTPS redirect when enabled.
|
21
15
|
"""
|
22
16
|
|
23
17
|
if redirect_response := self.maybe_https_redirect(request):
|
@@ -26,15 +20,8 @@ class HttpsRedirectMiddleware:
|
|
26
20
|
return self.get_response(request)
|
27
21
|
|
28
22
|
def maybe_https_redirect(self, request):
|
29
|
-
if (
|
30
|
-
|
31
|
-
and not request.is_https()
|
32
|
-
and not any(
|
33
|
-
pattern.search(request.path_info)
|
34
|
-
for pattern in self.https_redirect_exempt
|
35
|
-
)
|
36
|
-
):
|
37
|
-
host = self.https_redirect_host or request.get_host()
|
23
|
+
if self.https_redirect_enabled and not request.is_https():
|
24
|
+
host = request.get_host()
|
38
25
|
return ResponseRedirect(
|
39
26
|
f"https://{host}{request.get_full_path()}", status_code=301
|
40
27
|
)
|
plain/runtime/global_settings.py
CHANGED
@@ -37,17 +37,11 @@ DEFAULT_RESPONSE_HEADERS = {
|
|
37
37
|
"X-Frame-Options": "DENY",
|
38
38
|
}
|
39
39
|
|
40
|
-
# Whether to redirect all non-HTTPS requests to HTTPS.
|
40
|
+
# Whether to redirect all non-HTTPS requests to HTTPS (blanket redirect).
|
41
|
+
# For anything more advanced (custom host, path exemptions, etc.), write
|
42
|
+
# your own middleware.
|
41
43
|
HTTPS_REDIRECT_ENABLED = True
|
42
44
|
|
43
|
-
# Regex patterns for paths that should be exempt from HTTPS redirect
|
44
|
-
# Examples: [r"^/health$", r"/api/internal/.*", r"/dev/.*"]
|
45
|
-
HTTPS_REDIRECT_EXEMPT_PATHS: list[str] = []
|
46
|
-
|
47
|
-
# Custom host to redirect to for HTTPS. If None, uses the same host as the request.
|
48
|
-
# Useful for redirecting to a different domain (e.g., "secure.example.com")
|
49
|
-
HTTPS_REDIRECT_HOST = None
|
50
|
-
|
51
45
|
# If your Plain app is behind a proxy that sets a header to specify secure
|
52
46
|
# connections, AND that proxy ensures that user-submitted headers with the
|
53
47
|
# same name are ignored (so that people can't spoof it), set this value to
|
plain/test/README.md
CHANGED
plain/views/README.md
CHANGED
@@ -179,7 +179,7 @@ class ExampleDetailView(DetailView):
|
|
179
179
|
template_name = "detail.html"
|
180
180
|
|
181
181
|
def get_object(self):
|
182
|
-
return MyObjectClass.
|
182
|
+
return MyObjectClass.query.get(
|
183
183
|
id=self.url_kwargs["id"],
|
184
184
|
user=self.request.user, # Limit access
|
185
185
|
)
|
@@ -197,7 +197,7 @@ class ExampleUpdateView(UpdateView):
|
|
197
197
|
success_url = "."
|
198
198
|
|
199
199
|
def get_object(self):
|
200
|
-
return MyObjectClass.
|
200
|
+
return MyObjectClass.query.get(
|
201
201
|
id=self.url_kwargs["id"],
|
202
202
|
user=self.request.user, # Limit access
|
203
203
|
)
|
@@ -211,7 +211,7 @@ class ExampleDeleteView(DeleteView):
|
|
211
211
|
# Just POST to this view to delete the object.
|
212
212
|
|
213
213
|
def get_object(self):
|
214
|
-
return MyObjectClass.
|
214
|
+
return MyObjectClass.query.get(
|
215
215
|
id=self.url_kwargs["id"],
|
216
216
|
user=self.request.user, # Limit access
|
217
217
|
)
|
@@ -221,7 +221,7 @@ class ExampleListView(ListView):
|
|
221
221
|
template_name = "list.html"
|
222
222
|
|
223
223
|
def get_objects(self):
|
224
|
-
return MyObjectClass.
|
224
|
+
return MyObjectClass.query.filter(
|
225
225
|
user=self.request.user, # Limit access
|
226
226
|
)
|
227
227
|
```
|
@@ -1,5 +1,5 @@
|
|
1
1
|
plain/AGENTS.md,sha256=5XMGBpJgbCNIpp60DPXB7bpAtFk8FAzqiZke95T965o,1038
|
2
|
-
plain/CHANGELOG.md,sha256=
|
2
|
+
plain/CHANGELOG.md,sha256=58av1OUbgw-UIYjVJuwoniXmtAD9AgJYII6tWVhmOtc,13821
|
3
3
|
plain/README.md,sha256=5BJyKhf0TDanWVbOQyZ3zsi5Lov9xk-LlJYCDWofM6Y,4078
|
4
4
|
plain/__main__.py,sha256=GK39854Lc_LO_JP8DzY9Y2MIQ4cQEl7SXFJy244-lC8,110
|
5
5
|
plain/debug.py,sha256=XdjnXcbPGsi0J2SpHGaLthhYU5AjhBlkHdemaP4sbYY,758
|
@@ -16,7 +16,7 @@ plain/assets/finders.py,sha256=2k8QZAbfUbc1LykxbzdazTSB6xNxJZnsZaGhWbSFZZs,1452
|
|
16
16
|
plain/assets/fingerprints.py,sha256=D-x0o49sUuWfx86BssDyo87yJCjWrV0go6JGPagvMAE,1365
|
17
17
|
plain/assets/urls.py,sha256=zQUA8bAlh9qVqskPJJrqWd9DjvetOi5jPSqy4vUX0J4,1161
|
18
18
|
plain/assets/views.py,sha256=T_0Qh6v9qBerEBYbhToigwOzsij-x1z_R-1zETQcIh0,9447
|
19
|
-
plain/chores/README.md,sha256=
|
19
|
+
plain/chores/README.md,sha256=xTYKOBnfcLBpVeZgJQ6c35vdxHYaKkfJB8B-6sB3lcs,2054
|
20
20
|
plain/chores/__init__.py,sha256=r9TXtQCH-VbvfnIJ5F8FxgQC35GRWFOfmMZN3q9niLg,67
|
21
21
|
plain/chores/registry.py,sha256=V3WjuekRI22LFvJbqSkUXQtiOtuE2ZK8gKV1TRvxRUI,1866
|
22
22
|
plain/cli/README.md,sha256=5C7vsH0ISxu7q5H6buC25MBOILkI_rzdySitswpQgJw,1032
|
@@ -44,7 +44,7 @@ plain/cli/agent/docs.py,sha256=ubX3ZeRHxVaetLk9fjiN9mJ07GZExC-CHUvQoX2DD7c,2464
|
|
44
44
|
plain/cli/agent/llmdocs.py,sha256=AUpNDb1xSOsSpzGOiFvpzUe4f7PUGMiR9cI13aVZouo,5038
|
45
45
|
plain/cli/agent/md.py,sha256=7r1II8ckubBFOZNGPASWaPmJdgByWFPINLqIOzRetLQ,2581
|
46
46
|
plain/cli/agent/prompt.py,sha256=rugYyQHV7JDNqGrx3_PPShwwqYlnEVbxw8RsczOo8tg,1253
|
47
|
-
plain/cli/agent/request.py,sha256=
|
47
|
+
plain/cli/agent/request.py,sha256=U4acLmpXlbFRjivrPtVv_r8DBts8OXg3m3-qotQxGL4,6547
|
48
48
|
plain/csrf/README.md,sha256=ApWpB-qlEf0LkOKm9Yr-6f_lB9XJEvGFDo_fraw8ghI,2391
|
49
49
|
plain/csrf/middleware.py,sha256=n5_7v6qwFKgiAnKVyJa7RhwHoWepLkPudzIgZtdku5A,5119
|
50
50
|
plain/csrf/views.py,sha256=HwQqfI6KPelHP9gSXhjfZaTLQic71PKsoZ6DPhr1rKI,572
|
@@ -75,7 +75,7 @@ plain/internal/handlers/exception.py,sha256=TbPYtgZ7ITJahUKhQWkptHK28Lb4zh_nOviN
|
|
75
75
|
plain/internal/handlers/wsgi.py,sha256=dgPT29t_F9llB-c5RYU3SHxGuZNaZ83xRjOfuOmtOl8,8209
|
76
76
|
plain/internal/middleware/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
77
77
|
plain/internal/middleware/headers.py,sha256=ENIW1Gwat54hv-ejgp2R8QTZm-PlaI7k44WU01YQrNk,964
|
78
|
-
plain/internal/middleware/https.py,sha256=
|
78
|
+
plain/internal/middleware/https.py,sha256=ctW90yJnn-blMb1lv17IsSWlAThHJ4RDku5XFfFNECM,834
|
79
79
|
plain/internal/middleware/slash.py,sha256=JWcIfGbXEKH00I7STq1AMdHhFGmQHC8lkKENa6280ko,2846
|
80
80
|
plain/logs/README.md,sha256=rzOHfngjizLgXL21g0svC1Cdya2s_gBA_E-IljtHpy8,4069
|
81
81
|
plain/logs/__init__.py,sha256=gFVMcNn5D6z0JrvUJgGsOeYj1NKNtEXhw0MvPDtkN6w,58
|
@@ -97,7 +97,7 @@ plain/preflight/security.py,sha256=oxUZBp2M0bpBfUoLYepIxoex2Y90nyjlrL8XU8UTHYY,2
|
|
97
97
|
plain/preflight/urls.py,sha256=cQ-WnFa_5oztpKdtwhuIGb7pXEml__bHsjs1SWO2YNI,1468
|
98
98
|
plain/runtime/README.md,sha256=sTqXXJkckwqkk9O06XMMSNRokAYjrZBnB50JD36BsYI,4873
|
99
99
|
plain/runtime/__init__.py,sha256=byFYnHrpUCwkpkHtdNhxr9iUdLDCWJjy92HPj30Ilck,2478
|
100
|
-
plain/runtime/global_settings.py,sha256=
|
100
|
+
plain/runtime/global_settings.py,sha256=PjgrsTQc3aQ0YxbZ43Lj2eNrOcP6hf4jBjjQ2lT0MfE,5767
|
101
101
|
plain/runtime/user_settings.py,sha256=OzMiEkE6ZQ50nxd1WIqirXPiNuMAQULklYHEzgzLWgA,11027
|
102
102
|
plain/runtime/utils.py,sha256=p5IuNTzc7Kq-9Ym7etYnt_xqHw5TioxfSkFeq1bKdgk,832
|
103
103
|
plain/signals/README.md,sha256=XefXqROlDhzw7Z5l_nx6Mhq6n9jjQ-ECGbH0vvhKWYg,272
|
@@ -114,7 +114,7 @@ plain/templates/jinja/environments.py,sha256=9plifzvQj--aTN1cCpJ2WdzQxZJpzB8S_4h
|
|
114
114
|
plain/templates/jinja/extensions.py,sha256=AEmmmHDbdRW8fhjYDzq9eSSNbp9WHsXenD8tPthjc0s,1351
|
115
115
|
plain/templates/jinja/filters.py,sha256=ft5XUr4OLeQayn-MSxrycpFLyyN_yEo7j5WhWMwpTOs,1445
|
116
116
|
plain/templates/jinja/globals.py,sha256=VMpuMZvwWOmb5MbzKK-ox-QEX_WSsXFxq0mm8biJgaU,558
|
117
|
-
plain/test/README.md,sha256=
|
117
|
+
plain/test/README.md,sha256=tNzaVjma0sgowIrViJguCgVy8A2d8mUKApZO2RxTYyU,1140
|
118
118
|
plain/test/__init__.py,sha256=MhNHtp7MYBl9kq-pMRGY11kJ6kU1I6vOkjNkit1TYRg,94
|
119
119
|
plain/test/client.py,sha256=OcL8wQDOu6PUNYvwcmslT5IGt81ffcPsXn05n-2n9oA,25128
|
120
120
|
plain/test/encoding.py,sha256=YJBOIE-BQRA5yl4zHnQy-9l67mJDTFmfy1DQXK0Wk-Q,3199
|
@@ -151,7 +151,7 @@ plain/utils/text.py,sha256=42hJv06sadbWfsaAHNhqCQaP1W9qZ69trWDTS-Xva7k,9496
|
|
151
151
|
plain/utils/timesince.py,sha256=a_-ZoPK_s3Pt998CW4rWp0clZ1XyK2x04hCqak2giII,5928
|
152
152
|
plain/utils/timezone.py,sha256=6u0sE-9RVp0_OCe0Y1KiYYQoq5THWLokZFQYY8jf78g,6221
|
153
153
|
plain/utils/tree.py,sha256=wdWzmfsgc26YDF2wxhAY3yVxXTixQYqYDKE9mL3L3ZY,4383
|
154
|
-
plain/views/README.md,sha256=
|
154
|
+
plain/views/README.md,sha256=caUSKUhCSs5hdxHC5wIVzKkumPXiuNoOFRnIs3CUHfo,7215
|
155
155
|
plain/views/__init__.py,sha256=a-N1nkklVohJTtz0yD1MMaS0g66HviEjsKydNVVjvVc,392
|
156
156
|
plain/views/base.py,sha256=CC9UvMZeAjVvi90vGjoZzsQ0jnhbg3-7qCKQ8-Pb6cg,4184
|
157
157
|
plain/views/errors.py,sha256=jbNCJIzowwCsEvqyJ3opMeZpPDqTyhtrbqb0VnAm2HE,1263
|
@@ -160,8 +160,8 @@ plain/views/forms.py,sha256=ESZOXuo6IeYixp1RZvPb94KplkowRiwO2eGJCM6zJI0,2400
|
|
160
160
|
plain/views/objects.py,sha256=v3Vgvdoc1s0QW6JNWWrO5XXy9zF7vgwndgxX1eOSQoE,4999
|
161
161
|
plain/views/redirect.py,sha256=Xpb3cB7nZYvKgkNqcAxf9Jwm2SWcQ0u2xz4oO5M3vP8,1909
|
162
162
|
plain/views/templates.py,sha256=oAlebEyfES0rzBhfyEJzFmgLkpkbleA6Eip-8zDp-yk,1863
|
163
|
-
plain-0.
|
164
|
-
plain-0.
|
165
|
-
plain-0.
|
166
|
-
plain-0.
|
167
|
-
plain-0.
|
163
|
+
plain-0.63.0.dist-info/METADATA,sha256=ndV-xFNXqqO_AvIMwNJXShZ5EgbN5Al9i7MbtiGiDWI,4488
|
164
|
+
plain-0.63.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
165
|
+
plain-0.63.0.dist-info/entry_points.txt,sha256=nn4uKTRRZuEKOJv3810s3jtSMW0Gew7XDYiKIvBRR6M,93
|
166
|
+
plain-0.63.0.dist-info/licenses/LICENSE,sha256=m0D5O7QoH9l5Vz_rrX_9r-C8d9UNr_ciK6Qwac7o6yo,3175
|
167
|
+
plain-0.63.0.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|