martin-bts / askbot-devel

Askbot is a Django/Python Q&A forum. **Contributors README**: https://github.com/ASKBOT/askbot-devel#how-to-contribute. Commercial hosting of Askbot and support are available at https://askbot.com
Other
0 stars 2 forks source link

fragile messaging system #105

Closed martin-bts closed 5 years ago

martin-bts commented 5 years ago

Askbot ships with its own messaging system which mimics the behaviour of Django's old messaging system that was removed with Django 1.4.

I'm replacing it in the feature branch https://github.com/martin-bts/askbot-devel/tree/switch_to_django_messages

sebastian-philipp commented 5 years ago

:+1:

martin-bts commented 5 years ago

So far I have found three distinct messaging systems in Askbot:

  1. session based messaging for anonymous users
  2. database based messaging for authenticated users
  3. the group_messages app

Authenticated users can use session based messaging for a bunch of use cases where (it looks like) today messages are sent through the database.

The Askbot messaging systems all allow sending notifications to different users, which the Django messaging system does not. Further, the database based messaging allows sending notifications to offline users.

The group_messages app suppports a specific message type ONE_TIME, which seems to fit the spirit of notifications. Group_messages is an Askbot (sub)project and apparently only implemented as far as needed, i.e. ONE_TIME is an option, but it is never used thus making a message ONE_TIME only makes them not show up in certain (most) result sets.

However, when dropping Askbot's session based and database based messaging systems, group_messages can be used to cover remaining use cases that Django messages do not cover.

create an additional message type

AFAICT group_messages work a lot like "site-local" emails. A message of type ONE_TIME is probably meant to show up in the inbox and get deleted when the user reads the message. In our use cases we want messages that never show up in the inbox but get displayed as user notifications, i.e. yellow banner at the top of the page. As this is probably a different intent as a read-once message, we shall create a new message type. Since messages can already be typed this is simple to facilitate.

When created, messages default to type STORED and the MessagesManager explicitly looks for/creates messages of type STORED. There is a good chance that a new message type has close to no side effects.

displaying group_messages as user notifications

This seems easy enough. Context_processors provide lists of Messages and we iterate over the lists while rendering Templates with Jinja. I duplicated this Askbot method to also render Django messages and similarly we can duplicate this method again where we create the list of messages via a query to group messages.

use case: messages to different user

At the moment when Askbot sends a notification to a different user it has a corresponding User object and then calls user.message_set.create() to send a message to that user. Given that we do have a User object, we have all required information to send the message. We can draw inspiration from how the Askbot test suite sends messages.

To make it work, we need a MessagesManager that can create messages with our newly created message type, because the currently available MessagesManager as the type STORED hardcoded.

use case: notifications to offline users

I think this is only a theoretical corner case, because offline users do not have sessions that create notifications. Whenever a notification is to be sent to an offline user, it's the "messages to different user" use case.

HOWEVER, to be on the safe side, we should write a custom storage backend for the Django messages, that uses group_messages to store the notifications, using our newly created message type. We then derive from storage.fallback.FallbackStorage and extend it by an additional fallback to our custom group_messages storage backend. Through this, sending user notifications happens using cookies, if those are unavailable through the session and if thats not available (i.e. the user is offline) through a group message

Then we should have all cases covered.

martin-bts commented 5 years ago

This does not cover the case of sending a message to a different anonymous user.

martin-bts commented 5 years ago

I finished my work on messaging. The main contribution is probably refined knowledge and (soon) documentation on how messages work and why we have them, e.g. see https://askbot.org/en/question/16762/how-does-system-to-user-messaging-system-works-in-askbot/ .

The entire messaging problem can be structured like this:

  1. The running Askbot app gives feedback to users through on-screen notifications, e.g. "Thank you for your feedback!"
  2. The running Askbot app gives feedback to users through Ajax responses, e.g. "5 Karma required to upvode"
  3. The model notifies the user that it was updated through on-screen notifications, e.g. "You have received badge XYZ."

That covers 100% of all use cases. Askbot does not have a running dedicated facility for user-to-user messages. There is a lot of work on that with the group_messaging app, but it isn't active at the moment.

The use cases 1. and 2. are 100% synchronous, i.e. Askbot sends a notification because the user just did something inside the Askbot app that triggers on-screen feedback. Use case 3., sending model update information to the user is asynchronouos, i.e. these notifications are not an immediate result of what the receiver did and it is even possible that the user does not have a running session at the time the notification is sent.

Django messages can immediately be used for use case 1. Django messages work for anonymous and authenticated users alike.

Use case 2. mostly sends notifications with the Ajax response for immediate rendering on the client side. This works for anonymous and authenticated users alike. However, handling Ajax requests may additionally store notifications. These are notifications send to the user as an immediate result of their actions, analogous to use case 1., which means we can also use Django messages here.

Analysing Askbot to ensure every other use of messages is indeed the same and only use case 3. was the hardest part of this work. Thanks again @evgenyfadeev for doing this with me.

martin-bts commented 5 years ago

https://github.com/martin-bts/askbot-devel/pull/107