Download the PHP package goldnead/statamic-messaging without Composer
On this page you can find all versions of the php package goldnead/statamic-messaging. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.
Download goldnead/statamic-messaging
More information about goldnead/statamic-messaging
Files in goldnead/statamic-messaging
Package statamic-messaging
Short Description Messaging for Statamic: conversations between people and teams, with anonymous requests, profile release and blocking.
License proprietary
Informations about the package statamic-messaging
Messaging for Statamic
Conversations between a person and a team or another person (goldnead/statamic-teams), with anonymous requests, profile release and blocking. No routes and no UI: a site builds those on ConversationService.
Requirements
- PHP 8.2 or newer (Laravel 13 needs 8.3)
- Statamic 6, Laravel 12.40 or 13
goldnead/statamic-teams, which supplies teams and their members
Installation
The service provider is discovered automatically; the migrations load from the package, there is nothing to publish. The package is not on Packagist yet, and its goldnead/statamic-teams constraint points at a branch, so a site has to declare the repositories entries for both until they are released.
Usage
Everything goes through ConversationService, resolved from the container. Every call is authorised by ConversationPolicy; an unauthorised call throws Illuminate\Auth\Access\AuthorizationException.
Listen to Goldnead\Messaging\Events\MessageSent to notify the other side.
Model
Conversation: optional subject (subject_type,subject_id),recipient_team_idorrecipient_user_id(exactly one),anonymous,profile_shared.ConversationParticipant: roleinitiatorormember, andarchived,blocked,flaggedper person.Message: sender, body,read_at,hidden.
Rules
start()adds the initiator and every other team member. A team with nobody else cannot receive a conversation.- Read and write: the initiator and the recipient (the person, or the members of the recipient team as they are now). A member who has left the team loses the conversation. Admins of other teams get nothing (
ConversationPolicy). - A message is plain text, tags are stripped, and it holds at most
ConversationService::MAX_BODY_LENGTH(10000) characters. Empty or longer text throwsMessagingException. - An anonymous side shows as
Anonyme:r Interessent:inuntil it callsshareProfile(). Only the initiator can. block()stops the blocking person from sending. It changes nothing for the others.- Render with
ConversationService::thread()andConversation::displayName(), never from the raw rows. MessageSentfires once per message, after commit. A listener for anonymous conversations must not put the sender's name or the text in a mail.
Person as recipient
start($initiator, null, $body, $anonymous, $subject, recipientUser: $person) opens a conversation with one person instead of a team. Exactly one of team and person must be given, otherwise MessagingException. The same rules apply: only the two participants read and reply, nobody else, and an anonymous initiator stays masked. If the recipient's account is erased, their messages lose sender and text, the conversation loses the recipient's key, and the other side still reads the thread with the sender shown as Konto gelöscht. Existing installs run the new migration to get recipient_user_id.
Personal data
With goldnead/statamic-accounts installed, MessagingContributor is tagged for export and erasure. Erasing an account deletes the conversations it started. Messages it wrote elsewhere stay, without sender and text.
Encrypted storage
Message texts are stored encrypted (messages.body, Laravel's encrypted cast), so they are unreadable for anyone with database access and in backups. The encryption depends on APP_KEY. A key change is only safe with APP_PREVIOUS_KEYS; otherwise the stored messages are lost. Because the database cannot read the text, never filter, search or sort on body in SQL: load the model and work on the decrypted attribute. When you read or copy rows with DB::table('messages'), you get ciphertext; use the Message model. A bulk Message::query()->update(['body' => ...]) does not apply the cast either, update through the model. Two migrations upgrade an existing install: one widens the column to longText, one encrypts the plain rows that are still there (safe to run twice, migrate:rollback decrypts again).
Control Panel
A moderator's view under Tools > Messages (Nachrichten in German). It shows the shape of the conversations and never their content: no message text, no participant name or key, and no free-text subject id appear on any screen, in any response or in the widget. The initiator of an anonymous conversation is only ever "Initiator".
- Listing: id, created, last activity, recipient (the team's name, or "Person"), anonymous, message count and the flags flagged, blocked and has hidden messages. Filters: anonymous, flagged, blocked, hidden messages, created (date range). There is no search, because everything a search could match is a text or a name.
- Detail: the metadata, a timeline (message sent, which side, hidden or not, with the moderator's reason) and the participants by side with their flags.
- Moderation (needs
manage messaging): hide a message with a reason, show it again, unblock a participant. A message can only be hidden once a participant has flagged the conversation (ConversationParticipant::$flagged); a hidden message drops out ofConversationService::thread(). The same rules are onConversationService::hideMessage(),unhideMessage()andunblock(), which skip the policy because the moderator is not a party to the conversation. Settingflaggedis up to your site. - Dashboard widget
messaging: conversations and messages of the last 7 and 30 days, and the number of flagged conversations.
Permissions: view messaging for everything above, manage messaging (a child of it) for the actions. Run php artisan migrate once: moderation adds hidden_reason and hidden_at to messages.
The compiled assets are committed (resources/dist/build); after changing resources/js or resources/css, run composer install && npm ci && npm run build. CI fails on a stale build.
Realtime in 3 steps
New messages appear in an open conversation without reloading, and the inbox updates while it is open. It uses websockets through Laravel Reverb, so there is no polling and no third-party service. It switches on by itself as soon as the default broadcast connection is reverb or pusher, and stays off, without an error, otherwise.
-
Install and configure the server.
Set
BROADCAST_CONNECTION=reverband make sureREVERB_APP_KEY,REVERB_APP_SECRET,REVERB_APP_ID,REVERB_HOST,REVERB_PORTandREVERB_SCHEMEare set. They describe where PHP reaches the server; the browser connects to the host and port of the page (route/appto Reverb in your proxy), or toMESSAGING_CLIENT_HOST,MESSAGING_CLIENT_PORTandMESSAGING_CLIENT_SCHEMEif it lives elsewhere. -
Put the tag in the template. In the conversation view, and once in the inbox:
Mark where new messages go, and how one looks. Without a template the script builds a plain paragraph.
For the inbox, wrap the parts that should follow along (the list, the unread badges) in
data-messaging-refresh="name". When something arrives for the person, the script fetches the current page again and swaps those regions. - Run the server next to the app:
php artisan reverb:start --host=0.0.0.0 --port=8080(a second process or container; behind a proxy, route the paths/appand/appsto it).
How it works, and what it will not do:
- Every message, hide and restore is announced on private channels:
messaging.conversation.{id}(only participants who are still allowed to read it and have not blocked it) andmessaging.user.{key}(only that person). The addon registers both channels and, when the app has none,/broadcasting/auth. - The broadcast carries two ids and nothing else: no text, no name, nothing about an anonymous identity. The browser fetches the message from
GET /messaging/realtime/{conversation}/messages/{message}, which applies the same rules as the page (a hidden message, a foreign conversation and a stranger all get 404). The sender's name depends on who is looking, which is why the server cannot send one rendering to everybody. - A hidden message disappears from open conversations at once; a restored one returns in place.
- The signal leaves in the same request (
ShouldBroadcastNow) instead of waiting for a queue worker. A dead websocket server is reported and ignored: sending a message never fails because of it. - The client is a prebuilt script (
resources/dist/realtime, Echo and the Pusher client included, about 27 kB gzipped) served by the addon at/messaging/realtime.js. No JavaScript build in your app, no CDN. It reads the CSRF token from<meta name="csrf-token">if there is one. MESSAGING_REALTIME=falseforces it off,trueforces it on.php artisan vendor:publish --tag=messaging-configpublishesconfig/messaging.php(time zone and format of a live message).
Tests
License
Proprietary, see LICENSE.md.
All versions of statamic-messaging with dependencies
goldnead/statamic-teams Version ^0.4
laravel/framework Version ^12.40|^13.0
statamic/cms Version ^6.0