flaskbb-plugin-conversations 3.0.0__tar.gz → 3.0.2__tar.gz
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.
- {flaskbb_plugin_conversations-3.0.0 → flaskbb_plugin_conversations-3.0.2}/PKG-INFO +1 -1
- {flaskbb_plugin_conversations-3.0.0 → flaskbb_plugin_conversations-3.0.2}/conversations/__init__.py +8 -1
- {flaskbb_plugin_conversations-3.0.0 → flaskbb_plugin_conversations-3.0.2}/conversations/forms.py +1 -0
- flaskbb_plugin_conversations-3.0.2/conversations/migrations/202602062122_1770409336_update_sqlalchemy_2.py +76 -0
- {flaskbb_plugin_conversations-3.0.0 → flaskbb_plugin_conversations-3.0.2}/conversations/templates/conversations/conversation.html +2 -2
- {flaskbb_plugin_conversations-3.0.0 → flaskbb_plugin_conversations-3.0.2}/conversations/templates/conversations/conversation_list.html +2 -2
- {flaskbb_plugin_conversations-3.0.0 → flaskbb_plugin_conversations-3.0.2}/conversations/templates/conversations/message_layout.html +8 -7
- {flaskbb_plugin_conversations-3.0.0 → flaskbb_plugin_conversations-3.0.2}/conversations/views.py +4 -0
- {flaskbb_plugin_conversations-3.0.0 → flaskbb_plugin_conversations-3.0.2}/pyproject.toml +1 -1
- {flaskbb_plugin_conversations-3.0.0 → flaskbb_plugin_conversations-3.0.2}/tests/test_views.py +36 -2
- flaskbb_plugin_conversations-3.0.0/conversations/migrations/202602062122_1770409336_update_sqlalchemy_2.py +0 -46
- {flaskbb_plugin_conversations-3.0.0 → flaskbb_plugin_conversations-3.0.2}/.gitignore +0 -0
- {flaskbb_plugin_conversations-3.0.0 → flaskbb_plugin_conversations-3.0.2}/LICENSE +0 -0
- {flaskbb_plugin_conversations-3.0.0 → flaskbb_plugin_conversations-3.0.2}/README.md +0 -0
- {flaskbb_plugin_conversations-3.0.0 → flaskbb_plugin_conversations-3.0.2}/conversations/migrations/201802171929_0625bc75ab77_add_conversations.py +0 -0
- {flaskbb_plugin_conversations-3.0.0 → flaskbb_plugin_conversations-3.0.2}/conversations/migrations/202607311750_1785520254_move_message_quota_setting.py +0 -0
- {flaskbb_plugin_conversations-3.0.0 → flaskbb_plugin_conversations-3.0.2}/conversations/migrations/__init__.py +0 -0
- {flaskbb_plugin_conversations-3.0.0 → flaskbb_plugin_conversations-3.0.2}/conversations/models.py +0 -0
- {flaskbb_plugin_conversations-3.0.0 → flaskbb_plugin_conversations-3.0.2}/conversations/py.typed +0 -0
- {flaskbb_plugin_conversations-3.0.0 → flaskbb_plugin_conversations-3.0.2}/conversations/templates/conversations/_inject_navlink.html +0 -0
- {flaskbb_plugin_conversations-3.0.0 → flaskbb_plugin_conversations-3.0.2}/conversations/templates/conversations/_inject_new_message_button.html +0 -0
- {flaskbb_plugin_conversations-3.0.0 → flaskbb_plugin_conversations-3.0.2}/conversations/templates/conversations/_inject_new_message_link.html +0 -0
- {flaskbb_plugin_conversations-3.0.0 → flaskbb_plugin_conversations-3.0.2}/conversations/templates/conversations/drafts.html +0 -0
- {flaskbb_plugin_conversations-3.0.0 → flaskbb_plugin_conversations-3.0.2}/conversations/templates/conversations/inbox.html +0 -0
- {flaskbb_plugin_conversations-3.0.0 → flaskbb_plugin_conversations-3.0.2}/conversations/templates/conversations/message_form.html +0 -0
- {flaskbb_plugin_conversations-3.0.0 → flaskbb_plugin_conversations-3.0.2}/conversations/templates/conversations/sent.html +0 -0
- {flaskbb_plugin_conversations-3.0.0 → flaskbb_plugin_conversations-3.0.2}/conversations/templates/conversations/trash.html +0 -0
- {flaskbb_plugin_conversations-3.0.0 → flaskbb_plugin_conversations-3.0.2}/conversations/translations/en/LC_MESSAGES/messages.po +0 -0
- {flaskbb_plugin_conversations-3.0.0 → flaskbb_plugin_conversations-3.0.2}/conversations/utils.py +0 -0
- {flaskbb_plugin_conversations-3.0.0 → flaskbb_plugin_conversations-3.0.2}/tests/conftest.py +0 -0
- {flaskbb_plugin_conversations-3.0.0 → flaskbb_plugin_conversations-3.0.2}/tests/test_message_models.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: flaskbb-plugin-conversations
|
|
3
|
-
Version: 3.0.
|
|
3
|
+
Version: 3.0.2
|
|
4
4
|
Summary: A private messaging plugin for FlaskBB
|
|
5
5
|
Project-URL: Homepage, https://github.com/flaskbb/flaskbb-plugin-conversations
|
|
6
6
|
Project-URL: Repository, https://github.com/flaskbb/flaskbb-plugin-conversations
|
{flaskbb_plugin_conversations-3.0.0 → flaskbb_plugin_conversations-3.0.2}/conversations/__init__.py
RENAMED
|
@@ -14,7 +14,7 @@ from typing import Any, cast
|
|
|
14
14
|
from flask import Flask
|
|
15
15
|
from flask_login import current_user
|
|
16
16
|
from flaskbb.forum.models import Post
|
|
17
|
-
from flaskbb.settings.definitions import IntSetting, SettingGroup
|
|
17
|
+
from flaskbb.settings.definitions import BoolSetting, IntSetting, SettingGroup
|
|
18
18
|
from flaskbb.user.models import User
|
|
19
19
|
from flaskbb.utils.helpers import real, render_template
|
|
20
20
|
from pluggy import HookimplMarker
|
|
@@ -29,6 +29,13 @@ SETTINGS = SettingGroup(
|
|
|
29
29
|
name="Conversations Settings",
|
|
30
30
|
description="Settings for the conversations plugin.",
|
|
31
31
|
settings=(
|
|
32
|
+
BoolSetting(
|
|
33
|
+
key="MESSAGE_QUOTA_ENABLED",
|
|
34
|
+
value=True,
|
|
35
|
+
name="Enable Private Message Quota",
|
|
36
|
+
description="Limit the amount of messages a user can have. "
|
|
37
|
+
"If disabled, users can have unlimited messages.",
|
|
38
|
+
),
|
|
32
39
|
IntSetting(
|
|
33
40
|
key="MESSAGE_QUOTA",
|
|
34
41
|
value=50,
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"""Update SQLAlchemy 2
|
|
2
|
+
|
|
3
|
+
Revision ID: 1770409336
|
|
4
|
+
Revises: 0625bc75ab77
|
|
5
|
+
Create Date: 2026-02-06 21:22:16.464657
|
|
6
|
+
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import sqlalchemy as sa
|
|
10
|
+
from alembic import op
|
|
11
|
+
|
|
12
|
+
# revision identifiers, used by Alembic.
|
|
13
|
+
revision = "1770409336"
|
|
14
|
+
down_revision = "0625bc75ab77"
|
|
15
|
+
branch_labels = ()
|
|
16
|
+
depends_on = None
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def upgrade():
|
|
20
|
+
# Version 1.x stored the 16 UUID bytes, sa.Uuid() stores 32 hex characters
|
|
21
|
+
# on MySQL and SQLite
|
|
22
|
+
bind = op.get_bind()
|
|
23
|
+
if bind.dialect.name == "sqlite":
|
|
24
|
+
# the column has NUMERIC affinity until the rebuild, which would turn
|
|
25
|
+
# hex strings like "1234e567..." into numbers - the "x" prevents that
|
|
26
|
+
op.execute(
|
|
27
|
+
"UPDATE conversations SET shared_id = 'x' || lower(hex(shared_id)) "
|
|
28
|
+
"WHERE typeof(shared_id) = 'blob'"
|
|
29
|
+
)
|
|
30
|
+
elif bind.dialect.name == "mysql":
|
|
31
|
+
shared_id = next(
|
|
32
|
+
column
|
|
33
|
+
for column in sa.inspect(bind).get_columns("conversations")
|
|
34
|
+
if column["name"] == "shared_id"
|
|
35
|
+
)
|
|
36
|
+
if isinstance(shared_id["type"], sa.BINARY):
|
|
37
|
+
op.alter_column(
|
|
38
|
+
"conversations",
|
|
39
|
+
"shared_id",
|
|
40
|
+
existing_type=shared_id["type"],
|
|
41
|
+
type_=sa.VARBINARY(32),
|
|
42
|
+
existing_nullable=False,
|
|
43
|
+
)
|
|
44
|
+
op.execute("UPDATE conversations SET shared_id = LOWER(HEX(shared_id))")
|
|
45
|
+
|
|
46
|
+
with op.batch_alter_table("conversations", schema=None) as batch_op:
|
|
47
|
+
batch_op.alter_column(
|
|
48
|
+
"shared_id",
|
|
49
|
+
existing_type=sa.NUMERIC(precision=16),
|
|
50
|
+
type_=sa.Uuid(),
|
|
51
|
+
existing_nullable=False,
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
if bind.dialect.name == "sqlite":
|
|
55
|
+
op.execute(
|
|
56
|
+
"UPDATE conversations SET shared_id = substr(shared_id, 2) WHERE shared_id LIKE 'x%'"
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
with op.batch_alter_table("messages", schema=None) as batch_op:
|
|
60
|
+
batch_op.alter_column("message", existing_type=sa.TEXT(), nullable=True)
|
|
61
|
+
# ### end Alembic commands ###
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def downgrade():
|
|
65
|
+
with op.batch_alter_table("messages", schema=None) as batch_op:
|
|
66
|
+
batch_op.alter_column("message", existing_type=sa.TEXT(), nullable=False)
|
|
67
|
+
|
|
68
|
+
with op.batch_alter_table("conversations", schema=None) as batch_op:
|
|
69
|
+
batch_op.alter_column(
|
|
70
|
+
"shared_id",
|
|
71
|
+
existing_type=sa.Uuid(),
|
|
72
|
+
type_=sa.NUMERIC(precision=16),
|
|
73
|
+
existing_nullable=False,
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
# ### end Alembic commands ###
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
<div class="author-title"><h5>{{ message.user.primary_group.name }}</h5></div>
|
|
34
34
|
|
|
35
35
|
{% if message.user.avatar %}
|
|
36
|
-
<div class="author-avatar"><img src="{{ message.user.
|
|
36
|
+
<div class="author-avatar"><img src="{{ message.user.avatar_url }}" alt="avatar"></div>
|
|
37
37
|
{% endif %}
|
|
38
38
|
|
|
39
39
|
<div class="author-registered">{% trans %}Joined{% endtrans %}: {{ message.user.date_joined|format_date }}</div>
|
|
@@ -84,7 +84,7 @@
|
|
|
84
84
|
<div class="author-title"><h5>{{ message.user.primary_group.name }}</h5></div>
|
|
85
85
|
|
|
86
86
|
{% if message.user.avatar %}
|
|
87
|
-
<div class="author-avatar"><img src="{{ message.user.
|
|
87
|
+
<div class="author-avatar"><img src="{{ message.user.avatar_url }}" alt="avatar"></div>
|
|
88
88
|
{% endif %}
|
|
89
89
|
|
|
90
90
|
<div class="author-registered">{% trans %}Joined{% endtrans %}: {{ message.user.date_joined|format_date }}</div>
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
|
|
10
10
|
<div class="col-auto">
|
|
11
11
|
<span class="badge bg-info" title="The amount of all conversations" data-bs-toggle="tooltip">
|
|
12
|
-
{{ current_user.message_count or 0 }}/{{ flaskbb_config["CONVERSATIONS_MESSAGE_QUOTA"] }}
|
|
12
|
+
{{ current_user.message_count or 0 }}{% if flaskbb_config["CONVERSATIONS_MESSAGE_QUOTA_ENABLED"] is not sameas false %}/{{ flaskbb_config["CONVERSATIONS_MESSAGE_QUOTA"] }}{% endif %}
|
|
13
13
|
</span>
|
|
14
14
|
</div>
|
|
15
15
|
</div>
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
<!-- avatar -->
|
|
22
22
|
<div class="col-md-1 col-sm-2 col-2 conversation-avatar">
|
|
23
23
|
{% if conversation.from_user and conversation.from_user.avatar %}
|
|
24
|
-
<img src="{{ conversation.from_user.
|
|
24
|
+
<img src="{{ conversation.from_user.avatar_url }}" class="rounded" alt="avatar" width="65px" height="65px" />
|
|
25
25
|
{% else %}
|
|
26
26
|
<img src="{{ url_for('static', filename='avatar80x80.png') }}" class="rounded" alt="avatar" width="65px" height="65px" />
|
|
27
27
|
{% endif %}
|
|
@@ -40,15 +40,16 @@
|
|
|
40
40
|
|
|
41
41
|
const editor = document.querySelector(".flaskbb-editor");
|
|
42
42
|
fetch(button.dataset.rawUrl)
|
|
43
|
-
.then((response) =>
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
43
|
+
.then((response) => {
|
|
44
|
+
// an expired session redirects to the login page
|
|
45
|
+
if (response.redirected || !response.ok) {
|
|
46
|
+
throw new Error(`could not load ${button.dataset.rawUrl}`);
|
|
47
|
+
}
|
|
48
|
+
return response.text();
|
|
49
49
|
})
|
|
50
|
+
.then((quote) => window.app.insertQuote(editor, quote))
|
|
50
51
|
.catch((error) => {
|
|
51
|
-
console.error("
|
|
52
|
+
console.error("could not quote the message", error);
|
|
52
53
|
});
|
|
53
54
|
});
|
|
54
55
|
</script>
|
{flaskbb_plugin_conversations-3.0.0 → flaskbb_plugin_conversations-3.0.2}/conversations/views.py
RENAMED
|
@@ -50,6 +50,10 @@ def check_message_box_space(redirect_to: str | None = None):
|
|
|
50
50
|
will redirect to the ``conversations_bp.inbox``
|
|
51
51
|
endpoint.
|
|
52
52
|
"""
|
|
53
|
+
# an unset value (settings not yet upgraded) keeps the quota enforced
|
|
54
|
+
if flaskbb_config["CONVERSATIONS_MESSAGE_QUOTA_ENABLED"] is False:
|
|
55
|
+
return None
|
|
56
|
+
|
|
53
57
|
quota = flaskbb_config["CONVERSATIONS_MESSAGE_QUOTA"]
|
|
54
58
|
if get_message_count(real(current_user).id) >= quota:
|
|
55
59
|
flash(
|
{flaskbb_plugin_conversations-3.0.0 → flaskbb_plugin_conversations-3.0.2}/tests/test_views.py
RENAMED
|
@@ -12,6 +12,7 @@ from flask_login import login_user, logout_user
|
|
|
12
12
|
from flaskbb.extensions import db
|
|
13
13
|
|
|
14
14
|
from conversations import views
|
|
15
|
+
from conversations.forms import ConversationForm
|
|
15
16
|
from conversations.models import Conversation
|
|
16
17
|
|
|
17
18
|
|
|
@@ -63,7 +64,11 @@ def test_delete_returns_to_the_page_it_was_sent_from(application, conversation,
|
|
|
63
64
|
|
|
64
65
|
|
|
65
66
|
def test_reply_over_the_quota_loads_the_inbox(application, conversation, user, monkeypatch):
|
|
66
|
-
monkeypatch.setattr(
|
|
67
|
+
monkeypatch.setattr(
|
|
68
|
+
views,
|
|
69
|
+
"flaskbb_config",
|
|
70
|
+
{"CONVERSATIONS_MESSAGE_QUOTA_ENABLED": True, "CONVERSATIONS_MESSAGE_QUOTA": 0},
|
|
71
|
+
)
|
|
67
72
|
|
|
68
73
|
response, messages = _htmx_post(
|
|
69
74
|
application,
|
|
@@ -113,7 +118,11 @@ def no_csrf(application):
|
|
|
113
118
|
|
|
114
119
|
@pytest.fixture
|
|
115
120
|
def no_quota(monkeypatch):
|
|
116
|
-
monkeypatch.setattr(
|
|
121
|
+
monkeypatch.setattr(
|
|
122
|
+
views,
|
|
123
|
+
"flaskbb_config",
|
|
124
|
+
{"CONVERSATIONS_MESSAGE_QUOTA_ENABLED": True, "CONVERSATIONS_MESSAGE_QUOTA": 0},
|
|
125
|
+
)
|
|
117
126
|
|
|
118
127
|
|
|
119
128
|
def _send(application, view, actor, recipient, **kwargs):
|
|
@@ -164,3 +173,28 @@ def test_draft_over_the_quota_is_not_sent(
|
|
|
164
173
|
assert conversation_msgs.draft
|
|
165
174
|
# sending would have put a copy into the recipient's inbox
|
|
166
175
|
assert Conversation.count() == 1
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def test_disabled_quota_allows_sending_over_the_limit(
|
|
179
|
+
application, default_settings, no_csrf, user, admin_user, monkeypatch
|
|
180
|
+
):
|
|
181
|
+
monkeypatch.setattr(
|
|
182
|
+
views,
|
|
183
|
+
"flaskbb_config",
|
|
184
|
+
{"CONVERSATIONS_MESSAGE_QUOTA_ENABLED": False, "CONVERSATIONS_MESSAGE_QUOTA": 0},
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
_response, messages = _send(
|
|
188
|
+
application, views.NewConversation.as_view("new_conversation"), user, admin_user
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
assert QUOTA_EXCEEDED not in messages
|
|
192
|
+
assert Conversation.count() == 2
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
def test_recipient_field_opts_into_the_user_lookup(application):
|
|
196
|
+
with application.test_request_context():
|
|
197
|
+
field = ConversationForm(meta={"csrf": False}).to_user()
|
|
198
|
+
|
|
199
|
+
assert "data-user-lookup" in field
|
|
200
|
+
assert 'autocomplete="off"' in field
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
"""Update SQLAlchemy 2
|
|
2
|
-
|
|
3
|
-
Revision ID: 1770409336
|
|
4
|
-
Revises: 0625bc75ab77
|
|
5
|
-
Create Date: 2026-02-06 21:22:16.464657
|
|
6
|
-
|
|
7
|
-
"""
|
|
8
|
-
|
|
9
|
-
import sqlalchemy as sa
|
|
10
|
-
from alembic import op
|
|
11
|
-
|
|
12
|
-
# revision identifiers, used by Alembic.
|
|
13
|
-
revision = "1770409336"
|
|
14
|
-
down_revision = "0625bc75ab77"
|
|
15
|
-
branch_labels = ()
|
|
16
|
-
depends_on = None
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
def upgrade():
|
|
20
|
-
# ### commands auto generated by Alembic - please adjust! ###
|
|
21
|
-
with op.batch_alter_table("conversations", schema=None) as batch_op:
|
|
22
|
-
batch_op.alter_column(
|
|
23
|
-
"shared_id",
|
|
24
|
-
existing_type=sa.NUMERIC(precision=16),
|
|
25
|
-
type_=sa.Uuid(),
|
|
26
|
-
existing_nullable=False,
|
|
27
|
-
)
|
|
28
|
-
|
|
29
|
-
with op.batch_alter_table("messages", schema=None) as batch_op:
|
|
30
|
-
batch_op.alter_column("message", existing_type=sa.TEXT(), nullable=True)
|
|
31
|
-
# ### end Alembic commands ###
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
def downgrade():
|
|
35
|
-
with op.batch_alter_table("messages", schema=None) as batch_op:
|
|
36
|
-
batch_op.alter_column("message", existing_type=sa.TEXT(), nullable=False)
|
|
37
|
-
|
|
38
|
-
with op.batch_alter_table("conversations", schema=None) as batch_op:
|
|
39
|
-
batch_op.alter_column(
|
|
40
|
-
"shared_id",
|
|
41
|
-
existing_type=sa.Uuid(),
|
|
42
|
-
type_=sa.NUMERIC(precision=16),
|
|
43
|
-
existing_nullable=False,
|
|
44
|
-
)
|
|
45
|
-
|
|
46
|
-
# ### end Alembic commands ###
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{flaskbb_plugin_conversations-3.0.0 → flaskbb_plugin_conversations-3.0.2}/conversations/models.py
RENAMED
|
File without changes
|
{flaskbb_plugin_conversations-3.0.0 → flaskbb_plugin_conversations-3.0.2}/conversations/py.typed
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{flaskbb_plugin_conversations-3.0.0 → flaskbb_plugin_conversations-3.0.2}/conversations/utils.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|