Skip to content

Commit 5818259

Browse files
committed
change post by email to be premium only
1 parent 5520546 commit 5818259

File tree

5 files changed

+91
-9
lines changed

5 files changed

+91
-9
lines changed

main/models.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,10 @@ def footer_note_as_html(self):
169169
def post_count(self):
170170
return Post.objects.filter(owner=self).count()
171171

172+
@property
173+
def has_premium_features(self):
174+
return self.is_premium or self.is_grandfathered
175+
172176
@property
173177
def class_status(self):
174178
if self.is_premium or self.is_grandfathered:

main/templates/main/guides_postbyemail.html

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,12 @@ <h1>Post by email</h1>
1111
Mataroa supports publishing blog posts via email, inspired by <a href="https://posthaven.com/">Posthaven</a>.
1212
</p>
1313

14+
{% if request.user.is_authenticated and not request.user.is_premium %}
15+
<p>
16+
This is a premium feature. <a href="{% url 'billing_overview' %}">Upgrade to Premium</a> to use it.
17+
</p>
18+
{% endif %}
19+
1420
{% if request.user.is_authenticated and not request.user.email %}
1521
<h2>Requirements</h2>
1622
<p>
@@ -52,19 +58,16 @@ <h2>Notes</h2>
5258
will be ignored.
5359
</li>
5460
<li>
55-
Emails to <code>post@</code> are published immediately. Use
56-
<code>draft@</code> to save as a draft instead.
57-
</li>
58-
<li>
59-
Drafts are unlisted but still accessible via their URL.
61+
Drafts do not appear in a blog's home page but are accessible
62+
directly from their URL.
6063
</li>
6164
<li>
6265
Only the plain text version of the email is used for the post body.
6366
HTML formatting in your email will not be preserved.
6467
</li>
6568
<li>
6669
The post slug is automatically generated from the email subject
67-
(post title).
70+
(which becomes the post title).
6871
</li>
6972
</ul>
7073

main/templates/main/landing.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ <h2>Features</h2>
3232
<li>No platform lock-in</li>
3333
<ul>
3434
<li><a href="{% url 'export_index' %}">Export blog anytime</a></li>
35-
<li><a href="https://hey.mataroa.blog/blog/monthly-auto-exports-via-email/">Monthly auto-exports via email</a> (premium feature)</li>
35+
<li><a href="https://hey.mataroa.blog/blog/monthly-auto-exports-via-email/">Monthly auto-exports via email</a></li>
3636
<li><a href="https://hey.mataroa.blog/blog/redirect-to-new-domain/">Redirect to new domain</a></li>
3737
</ul>
3838
<li>
@@ -66,7 +66,7 @@ <h2>Pricing</h2>
6666
<li>Free — most features</li>
6767
<ul>
6868
<li>No custom domain</li>
69-
<li>No auto-exports via email</li>
69+
<li>No post by email</li>
7070
</ul>
7171
<li>Premium — all features</li>
7272
<ul>

main/tests/test_blog.py

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -430,7 +430,7 @@ class PostmarkWebhookTestCase(TestCase):
430430

431431
def setUp(self):
432432
self.user = models.User.objects.create(
433-
username="alice", email="[email protected]"
433+
username="alice", email="[email protected]", is_premium=True
434434
)
435435

436436
def test_postmark_webhook_create_post_success(self):
@@ -551,3 +551,60 @@ def test_postmark_webhook_cannot_post_to_another_users_blog(self):
551551
self.assertFalse(models.Post.objects.exists())
552552
# no email sent
553553
self.assertEqual(len(mail.outbox), 0)
554+
555+
def test_postmark_webhook_non_premium_user_cannot_post(self):
556+
"""Non-premium users should receive an upgrade email instead of creating a post."""
557+
non_premium_user = models.User.objects.create(
558+
username="freemium", email="[email protected]", is_premium=False
559+
)
560+
561+
data = {
562+
"From": "[email protected]",
563+
"To": f"post@{non_premium_user.username}.{settings.CANONICAL_HOST}",
564+
"Subject": "My Post Attempt",
565+
"TextBody": "This should not create a post.",
566+
"Headers": [],
567+
}
568+
569+
response = self.client.post(
570+
reverse("postmark_webhook"),
571+
data=json.dumps(data),
572+
content_type="application/json",
573+
)
574+
575+
self.assertEqual(response.status_code, 200)
576+
# no post created
577+
self.assertFalse(models.Post.objects.exists())
578+
# upgrade email sent
579+
self.assertEqual(len(mail.outbox), 1)
580+
self.assertEqual(mail.outbox[0].to, ["[email protected]"])
581+
self.assertIn("premium feature", mail.outbox[0].body.lower())
582+
self.assertIn("https://mataroa.blog/billing/overview/", mail.outbox[0].body)
583+
584+
def test_postmark_webhook_non_premium_user_cannot_create_draft(self):
585+
"""Non-premium users should receive an upgrade email when trying to create a draft."""
586+
non_premium_user = models.User.objects.create(
587+
username="freemium", email="[email protected]", is_premium=False
588+
)
589+
590+
data = {
591+
"From": "[email protected]",
592+
"To": f"draft@{non_premium_user.username}.{settings.CANONICAL_HOST}",
593+
"Subject": "My Draft Attempt",
594+
"TextBody": "This should not create a draft.",
595+
"Headers": [],
596+
}
597+
598+
response = self.client.post(
599+
reverse("postmark_webhook"),
600+
data=json.dumps(data),
601+
content_type="application/json",
602+
)
603+
604+
self.assertEqual(response.status_code, 200)
605+
# no post created
606+
self.assertFalse(models.Post.objects.exists())
607+
# upgrade email sent
608+
self.assertEqual(len(mail.outbox), 1)
609+
self.assertEqual(mail.outbox[0].to, ["[email protected]"])
610+
self.assertIn("premium feature", mail.outbox[0].body.lower())

main/views/general.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1272,6 +1272,24 @@ def postmark_webhook(request):
12721272
)
12731273
return HttpResponse(status=500)
12741274

1275+
# check if user is premium
1276+
if not user.has_premium_features:
1277+
logger.warning(f"Post by email attempted by non-premium user: {user.username}")
1278+
body = "This is a premium feature. Please upgrade to Premium to use it. https://mataroa.blog/billing/overview/"
1279+
extra_headers = {}
1280+
if message_id:
1281+
extra_headers["In-Reply-To"] = message_id
1282+
extra_headers["References"] = message_id
1283+
email = mail.EmailMessage(
1284+
subject=subject,
1285+
body=body,
1286+
from_email=settings.DEFAULT_FROM_EMAIL,
1287+
to=[from_email],
1288+
headers=extra_headers,
1289+
)
1290+
email.send()
1291+
return HttpResponse(status=200)
1292+
12751293
# check inbound host
12761294
to_email_parts = to_email.split("@")
12771295
email_prefix = to_email_parts[0]

0 commit comments

Comments
 (0)