mirror of
https://github.com/opensourcepos/opensourcepos.git
synced 2026-09-13 22:08:16 -04:00
Add three missing plugin hook points for sale documents so plugins can
inject buttons into invoice, quote, and work order views alongside the
existing receipt hook. All four pass ['saleId' => $sale_id_num] and
follow the same naming pattern (view:sales_{type}_buttons).
Exempt plugins/*/webhook from CSRF filtering to support server-to-server
provider callbacks. Also convert the CSRF except list from a string to an
array — the previous 'login|migrate' string produced a single unanchored
pattern, making login/anything CSRF-exempt. Separate array entries anchor
each one individually. Plugin webhook handlers are responsible for their
own authentication.
Add WhatsApp Business Cloud API plugin (app/Plugins/WhatsAppPlugin/):
- Free-form messaging page registered as the 'whatsapp' office module
with its own permission, plus per-customer modal and thread view
- "Send via WhatsApp" button on all four sale document types via the new
hooks; renders only when the customer has a phone number
- PDF delivery using the same sales/{type}_email view core uses for email
- Inbound webhook at plugins/whatsapp/webhook authenticated by
X-Hub-Signature-256 HMAC; fails closed on missing/bad signature;
always returns 200 to suppress Meta retries
- Out-of-order status callbacks cannot downgrade sent → delivered → read
- Conversation log table via plugin migration; dropped on uninstall with
version reset so re-install recreates it cleanly
- Access token and app secret encrypted at rest in plugin_config; no
writes to app_config or initial_schema.sql
- utf8mb4_unicode_520_ci collation throughout (MySQL and MariaDB compat)
- Language file stubs for all existing locales; English strings complete
- README covering credentials, install, webhook setup, and uninstall
35 lines
1.8 KiB
PHP
35 lines
1.8 KiB
PHP
<?php
|
|
/**
|
|
* Renders a WhatsApp conversation thread.
|
|
*
|
|
* @var array $messages Array of message objects (oldest first).
|
|
*/
|
|
?>
|
|
<div class="whatsapp-conversation" style="max-height: 360px; overflow-y: auto; border: 1px solid #e5e5e5; border-radius: 4px; padding: 8px; background: #efeae2;">
|
|
<?php if (empty($messages)): ?>
|
|
<p class="text-muted" style="text-align: center; margin: 12px 0;"><?= lang('WhatsAppPlugin.no_messages') ?></p>
|
|
<?php else: ?>
|
|
<?php foreach ($messages as $message): ?>
|
|
<?php $outbound = $message->direction === 'out'; ?>
|
|
<div style="text-align: <?= $outbound ? 'right' : 'left' ?>; margin: 4px 0;">
|
|
<div style="display: inline-block; max-width: 75%; padding: 6px 10px; border-radius: 8px; text-align: left; background: <?= $outbound ? '#dcf8c6' : '#ffffff' ?>; box-shadow: 0 1px 0.5px rgba(0,0,0,0.13);">
|
|
<?php if ($message->type === 'document'): ?>
|
|
<div><span class="glyphicon glyphicon-file"> </span><?= esc($message->filename) ?></div>
|
|
<?php if (! empty($message->body)): ?>
|
|
<div><?= nl2br(esc($message->body)) ?></div>
|
|
<?php endif; ?>
|
|
<?php else: ?>
|
|
<?= nl2br(esc($message->body)) ?>
|
|
<?php endif; ?>
|
|
<div style="font-size: 10px; color: #888; margin-top: 2px;">
|
|
<?= esc($message->created_at) ?>
|
|
<?php if ($outbound && ! empty($message->status)): ?>
|
|
· <?= esc(lang('WhatsAppPlugin.status_' . $message->status)) ?>
|
|
<?php endif; ?>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<?php endforeach; ?>
|
|
<?php endif; ?>
|
|
</div>
|