livechat 0.3.6 → 0.4.0
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +21 -0
- data/README.md +47 -1
- data/app/controllers/livechat/attachments_controller.rb +61 -0
- data/app/controllers/livechat/conversations_controller.rb +1 -17
- data/app/controllers/livechat/messages_controller.rb +2 -1
- data/app/controllers/livechat/visitor_controller.rb +13 -2
- data/app/helpers/livechat/inbox_helper.rb +32 -0
- data/app/models/livechat/conversation.rb +22 -6
- data/app/models/livechat/message.rb +110 -2
- data/app/views/layouts/livechat/application.html.erb +9 -1
- data/app/views/livechat/conversations/index.html.erb +3 -1
- data/app/views/livechat/conversations/show.html.erb +14 -5
- data/config/locales/livechat.ar.yml +8 -0
- data/config/locales/livechat.bg.yml +8 -0
- data/config/locales/livechat.bn.yml +8 -0
- data/config/locales/livechat.de.yml +8 -0
- data/config/locales/livechat.el.yml +8 -0
- data/config/locales/livechat.en.yml +8 -0
- data/config/locales/livechat.es.yml +8 -0
- data/config/locales/livechat.fr.yml +8 -0
- data/config/locales/livechat.hi.yml +8 -0
- data/config/locales/livechat.hr.yml +8 -0
- data/config/locales/livechat.id.yml +8 -0
- data/config/locales/livechat.it.yml +8 -0
- data/config/locales/livechat.ja.yml +8 -0
- data/config/locales/livechat.ko.yml +8 -0
- data/config/locales/livechat.lb.yml +8 -0
- data/config/locales/livechat.nl.yml +8 -0
- data/config/locales/livechat.pl.yml +8 -0
- data/config/locales/livechat.pt.yml +8 -0
- data/config/locales/livechat.ro.yml +8 -0
- data/config/locales/livechat.ru.yml +8 -0
- data/config/locales/livechat.th.yml +8 -0
- data/config/locales/livechat.tr.yml +8 -0
- data/config/locales/livechat.uk.yml +8 -0
- data/config/locales/livechat.ur.yml +8 -0
- data/config/locales/livechat.vi.yml +8 -0
- data/config/locales/livechat.zh-CN.yml +8 -0
- data/config/routes.rb +6 -0
- data/lib/generators/livechat/install/templates/initializer.rb +15 -0
- data/lib/livechat/channels.rb +27 -0
- data/lib/livechat/configuration.rb +28 -0
- data/lib/livechat/dashboard.js +73 -5
- data/lib/livechat/engine.rb +7 -0
- data/lib/livechat/version.rb +1 -1
- data/lib/livechat/widget.js +210 -10
- data/lib/livechat/widget.rb +6 -1
- data/lib/livechat.rb +31 -0
- metadata +3 -1
|
@@ -17,6 +17,13 @@ el:
|
|
|
17
17
|
error_send: "Δεν ήταν δυνατή η αποστολή. Δοκιμάστε ξανά."
|
|
18
18
|
error_rate_limited: "Πάρα πολλά μηνύματα. Περιμένετε λίγο και δοκιμάστε ξανά."
|
|
19
19
|
unread_aria: "μη αναγνωσμένα μηνύματα"
|
|
20
|
+
attach: "Επισύναψη αρχείων"
|
|
21
|
+
remove_file: "Αφαίρεση αρχείου"
|
|
22
|
+
attachment_error: "Ορισμένα αρχεία δεν ήταν δυνατό να σταλούν."
|
|
23
|
+
attachments:
|
|
24
|
+
too_many: "Πάρα πολλά αρχεία (έως %{count})."
|
|
25
|
+
too_large: "Το %{name} είναι πολύ μεγάλο."
|
|
26
|
+
type_rejected: "Ο τύπος του %{name} δεν επιτρέπεται."
|
|
20
27
|
statuses:
|
|
21
28
|
open: "Ανοιχτή"
|
|
22
29
|
resolved: "Επιλυμένη"
|
|
@@ -36,6 +43,7 @@ el:
|
|
|
36
43
|
reopen: "Άνοιγμα ξανά"
|
|
37
44
|
reply_placeholder: "Γράψτε την απάντησή σας…"
|
|
38
45
|
send: "Αποστολή"
|
|
46
|
+
attach: "Επισύναψη αρχείων"
|
|
39
47
|
newer: "Νεότερα"
|
|
40
48
|
older: "Παλαιότερα"
|
|
41
49
|
mail:
|
|
@@ -17,6 +17,13 @@ en:
|
|
|
17
17
|
error_send: "Could not send. Please try again."
|
|
18
18
|
error_rate_limited: "Too many messages. Please wait a moment and try again."
|
|
19
19
|
unread_aria: "unread messages"
|
|
20
|
+
attach: "Attach files"
|
|
21
|
+
remove_file: "Remove file"
|
|
22
|
+
attachment_error: "Some files could not be sent."
|
|
23
|
+
attachments:
|
|
24
|
+
too_many: "Too many files (max %{count})."
|
|
25
|
+
too_large: "%{name} is too large."
|
|
26
|
+
type_rejected: "%{name}'s type isn't allowed."
|
|
20
27
|
statuses:
|
|
21
28
|
open: "Open"
|
|
22
29
|
resolved: "Resolved"
|
|
@@ -36,6 +43,7 @@ en:
|
|
|
36
43
|
reopen: "Reopen"
|
|
37
44
|
reply_placeholder: "Write your reply…"
|
|
38
45
|
send: "Send"
|
|
46
|
+
attach: "Attach files"
|
|
39
47
|
newer: "Newer"
|
|
40
48
|
older: "Older"
|
|
41
49
|
mail:
|
|
@@ -17,6 +17,13 @@ es:
|
|
|
17
17
|
error_send: "No se pudo enviar. Inténtalo de nuevo."
|
|
18
18
|
error_rate_limited: "Demasiados mensajes. Espera un momento e inténtalo de nuevo."
|
|
19
19
|
unread_aria: "mensajes sin leer"
|
|
20
|
+
attach: "Adjuntar archivos"
|
|
21
|
+
remove_file: "Quitar archivo"
|
|
22
|
+
attachment_error: "No se pudieron enviar algunos archivos."
|
|
23
|
+
attachments:
|
|
24
|
+
too_many: "Demasiados archivos (máx. %{count})."
|
|
25
|
+
too_large: "%{name} es demasiado grande."
|
|
26
|
+
type_rejected: "El tipo de %{name} no está permitido."
|
|
20
27
|
statuses:
|
|
21
28
|
open: "Abierta"
|
|
22
29
|
resolved: "Resuelta"
|
|
@@ -36,6 +43,7 @@ es:
|
|
|
36
43
|
reopen: "Reabrir"
|
|
37
44
|
reply_placeholder: "Escribe tu respuesta…"
|
|
38
45
|
send: "Enviar"
|
|
46
|
+
attach: "Adjuntar archivos"
|
|
39
47
|
newer: "Más recientes"
|
|
40
48
|
older: "Más antiguos"
|
|
41
49
|
mail:
|
|
@@ -17,6 +17,13 @@ fr:
|
|
|
17
17
|
error_send: "Échec de l'envoi. Veuillez réessayer."
|
|
18
18
|
error_rate_limited: "Trop de messages. Veuillez patienter un instant et réessayer."
|
|
19
19
|
unread_aria: "messages non lus"
|
|
20
|
+
attach: "Joindre des fichiers"
|
|
21
|
+
remove_file: "Retirer le fichier"
|
|
22
|
+
attachment_error: "Certains fichiers n'ont pas pu être envoyés."
|
|
23
|
+
attachments:
|
|
24
|
+
too_many: "Trop de fichiers (max %{count})."
|
|
25
|
+
too_large: "%{name} est trop volumineux."
|
|
26
|
+
type_rejected: "Le type de %{name} n'est pas autorisé."
|
|
20
27
|
statuses:
|
|
21
28
|
open: "Ouverte"
|
|
22
29
|
resolved: "Résolue"
|
|
@@ -36,6 +43,7 @@ fr:
|
|
|
36
43
|
reopen: "Rouvrir"
|
|
37
44
|
reply_placeholder: "Écrivez votre réponse…"
|
|
38
45
|
send: "Envoyer"
|
|
46
|
+
attach: "Joindre des fichiers"
|
|
39
47
|
newer: "Plus récents"
|
|
40
48
|
older: "Plus anciens"
|
|
41
49
|
mail:
|
|
@@ -17,6 +17,13 @@ hi:
|
|
|
17
17
|
error_send: "भेजा नहीं जा सका। कृपया फिर से कोशिश करें।"
|
|
18
18
|
error_rate_limited: "बहुत सारे संदेश। कृपया थोड़ी देर रुककर फिर से कोशिश करें।"
|
|
19
19
|
unread_aria: "अपठित संदेश"
|
|
20
|
+
attach: "फ़ाइलें संलग्न करें"
|
|
21
|
+
remove_file: "फ़ाइल हटाएं"
|
|
22
|
+
attachment_error: "कुछ फ़ाइलें भेजी नहीं जा सकीं।"
|
|
23
|
+
attachments:
|
|
24
|
+
too_many: "बहुत सारी फ़ाइलें (अधिकतम %{count})।"
|
|
25
|
+
too_large: "%{name} बहुत बड़ी है।"
|
|
26
|
+
type_rejected: "%{name} का प्रकार अनुमत नहीं है।"
|
|
20
27
|
statuses:
|
|
21
28
|
open: "खुली"
|
|
22
29
|
resolved: "सुलझी हुई"
|
|
@@ -36,6 +43,7 @@ hi:
|
|
|
36
43
|
reopen: "फिर से खोलें"
|
|
37
44
|
reply_placeholder: "अपना जवाब लिखें…"
|
|
38
45
|
send: "भेजें"
|
|
46
|
+
attach: "फ़ाइलें संलग्न करें"
|
|
39
47
|
newer: "नए"
|
|
40
48
|
older: "पुराने"
|
|
41
49
|
mail:
|
|
@@ -17,6 +17,13 @@ hr:
|
|
|
17
17
|
error_send: "Slanje nije uspjelo. Pokušajte ponovno."
|
|
18
18
|
error_rate_limited: "Previše poruka. Pričekajte trenutak i pokušajte ponovno."
|
|
19
19
|
unread_aria: "nepročitane poruke"
|
|
20
|
+
attach: "Priloži datoteke"
|
|
21
|
+
remove_file: "Ukloni datoteku"
|
|
22
|
+
attachment_error: "Neke datoteke nije bilo moguće poslati."
|
|
23
|
+
attachments:
|
|
24
|
+
too_many: "Previše datoteka (najviše %{count})."
|
|
25
|
+
too_large: "%{name} je prevelika."
|
|
26
|
+
type_rejected: "Vrsta datoteke %{name} nije dopuštena."
|
|
20
27
|
statuses:
|
|
21
28
|
open: "Otvoren"
|
|
22
29
|
resolved: "Riješen"
|
|
@@ -36,6 +43,7 @@ hr:
|
|
|
36
43
|
reopen: "Ponovno otvori"
|
|
37
44
|
reply_placeholder: "Napišite svoj odgovor…"
|
|
38
45
|
send: "Pošalji"
|
|
46
|
+
attach: "Priloži datoteke"
|
|
39
47
|
newer: "Novije"
|
|
40
48
|
older: "Starije"
|
|
41
49
|
mail:
|
|
@@ -17,6 +17,13 @@ id:
|
|
|
17
17
|
error_send: "Tidak dapat mengirim. Silakan coba lagi."
|
|
18
18
|
error_rate_limited: "Terlalu banyak pesan. Mohon tunggu sebentar dan coba lagi."
|
|
19
19
|
unread_aria: "pesan belum dibaca"
|
|
20
|
+
attach: "Lampirkan file"
|
|
21
|
+
remove_file: "Hapus file"
|
|
22
|
+
attachment_error: "Beberapa file tidak dapat dikirim."
|
|
23
|
+
attachments:
|
|
24
|
+
too_many: "Terlalu banyak file (maks. %{count})."
|
|
25
|
+
too_large: "%{name} terlalu besar."
|
|
26
|
+
type_rejected: "Tipe %{name} tidak diizinkan."
|
|
20
27
|
statuses:
|
|
21
28
|
open: "Terbuka"
|
|
22
29
|
resolved: "Selesai"
|
|
@@ -36,6 +43,7 @@ id:
|
|
|
36
43
|
reopen: "Buka kembali"
|
|
37
44
|
reply_placeholder: "Tulis balasan Anda…"
|
|
38
45
|
send: "Kirim"
|
|
46
|
+
attach: "Lampirkan file"
|
|
39
47
|
newer: "Lebih baru"
|
|
40
48
|
older: "Lebih lama"
|
|
41
49
|
mail:
|
|
@@ -17,6 +17,13 @@ it:
|
|
|
17
17
|
error_send: "Invio non riuscito. Riprova."
|
|
18
18
|
error_rate_limited: "Troppi messaggi. Attendi un momento e riprova."
|
|
19
19
|
unread_aria: "messaggi non letti"
|
|
20
|
+
attach: "Allega file"
|
|
21
|
+
remove_file: "Rimuovi file"
|
|
22
|
+
attachment_error: "Non è stato possibile inviare alcuni file."
|
|
23
|
+
attachments:
|
|
24
|
+
too_many: "Troppi file (max %{count})."
|
|
25
|
+
too_large: "%{name} è troppo grande."
|
|
26
|
+
type_rejected: "Il tipo di %{name} non è consentito."
|
|
20
27
|
statuses:
|
|
21
28
|
open: "Aperta"
|
|
22
29
|
resolved: "Risolta"
|
|
@@ -36,6 +43,7 @@ it:
|
|
|
36
43
|
reopen: "Riapri"
|
|
37
44
|
reply_placeholder: "Scrivi la tua risposta…"
|
|
38
45
|
send: "Invia"
|
|
46
|
+
attach: "Allega file"
|
|
39
47
|
newer: "Più recenti"
|
|
40
48
|
older: "Più vecchi"
|
|
41
49
|
mail:
|
|
@@ -17,6 +17,13 @@ ja:
|
|
|
17
17
|
error_send: "送信できませんでした。もう一度お試しください。"
|
|
18
18
|
error_rate_limited: "メッセージが多すぎます。少し待ってからもう一度お試しください。"
|
|
19
19
|
unread_aria: "件の未読メッセージ"
|
|
20
|
+
attach: "ファイルを添付"
|
|
21
|
+
remove_file: "ファイルを削除"
|
|
22
|
+
attachment_error: "一部のファイルを送信できませんでした。"
|
|
23
|
+
attachments:
|
|
24
|
+
too_many: "ファイルが多すぎます(最大%{count}件)。"
|
|
25
|
+
too_large: "%{name}が大きすぎます。"
|
|
26
|
+
type_rejected: "%{name}の種類は許可されていません。"
|
|
20
27
|
statuses:
|
|
21
28
|
open: "対応中"
|
|
22
29
|
resolved: "解決済み"
|
|
@@ -36,6 +43,7 @@ ja:
|
|
|
36
43
|
reopen: "再開"
|
|
37
44
|
reply_placeholder: "返信を入力…"
|
|
38
45
|
send: "送信"
|
|
46
|
+
attach: "ファイルを添付"
|
|
39
47
|
newer: "新しい"
|
|
40
48
|
older: "古い"
|
|
41
49
|
mail:
|
|
@@ -17,6 +17,13 @@ ko:
|
|
|
17
17
|
error_send: "전송하지 못했습니다. 다시 시도해 주세요."
|
|
18
18
|
error_rate_limited: "메시지가 너무 많습니다. 잠시 후 다시 시도해 주세요."
|
|
19
19
|
unread_aria: "읽지 않은 메시지"
|
|
20
|
+
attach: "파일 첨부"
|
|
21
|
+
remove_file: "파일 제거"
|
|
22
|
+
attachment_error: "일부 파일을 보내지 못했습니다."
|
|
23
|
+
attachments:
|
|
24
|
+
too_many: "파일이 너무 많습니다 (최대 %{count}개)."
|
|
25
|
+
too_large: "%{name}이(가) 너무 큽니다."
|
|
26
|
+
type_rejected: "%{name}의 형식은 허용되지 않습니다."
|
|
20
27
|
statuses:
|
|
21
28
|
open: "진행 중"
|
|
22
29
|
resolved: "해결됨"
|
|
@@ -36,6 +43,7 @@ ko:
|
|
|
36
43
|
reopen: "다시 열기"
|
|
37
44
|
reply_placeholder: "답장을 입력하세요…"
|
|
38
45
|
send: "보내기"
|
|
46
|
+
attach: "파일 첨부"
|
|
39
47
|
newer: "최신"
|
|
40
48
|
older: "이전"
|
|
41
49
|
mail:
|
|
@@ -17,6 +17,13 @@ lb:
|
|
|
17
17
|
error_send: "Konnt net geschéckt ginn. Probéiert w.e.g. nach eng Kéier."
|
|
18
18
|
error_rate_limited: "Ze vill Noriichten. Waart w.e.g. e Moment a probéiert nach eng Kéier."
|
|
19
19
|
unread_aria: "ongelies Noriichten"
|
|
20
|
+
attach: "Dateien uschléissen"
|
|
21
|
+
remove_file: "Datei ewechhuelen"
|
|
22
|
+
attachment_error: "E puer Dateie konnten net geschéckt ginn."
|
|
23
|
+
attachments:
|
|
24
|
+
too_many: "Ze vill Dateien (max %{count})."
|
|
25
|
+
too_large: "%{name} ass ze grouss."
|
|
26
|
+
type_rejected: "Den Typ vun %{name} ass net erlaabt."
|
|
20
27
|
statuses:
|
|
21
28
|
open: "Op"
|
|
22
29
|
resolved: "Geléist"
|
|
@@ -36,6 +43,7 @@ lb:
|
|
|
36
43
|
reopen: "Nees opmaachen"
|
|
37
44
|
reply_placeholder: "Schreift Är Äntwert…"
|
|
38
45
|
send: "Schécken"
|
|
46
|
+
attach: "Dateien uschléissen"
|
|
39
47
|
newer: "Méi nei"
|
|
40
48
|
older: "Méi al"
|
|
41
49
|
mail:
|
|
@@ -17,6 +17,13 @@ nl:
|
|
|
17
17
|
error_send: "Versturen is niet gelukt. Probeer het opnieuw."
|
|
18
18
|
error_rate_limited: "Te veel berichten. Wacht even en probeer het opnieuw."
|
|
19
19
|
unread_aria: "ongelezen berichten"
|
|
20
|
+
attach: "Bestanden bijvoegen"
|
|
21
|
+
remove_file: "Bestand verwijderen"
|
|
22
|
+
attachment_error: "Sommige bestanden konden niet worden verzonden."
|
|
23
|
+
attachments:
|
|
24
|
+
too_many: "Te veel bestanden (max. %{count})."
|
|
25
|
+
too_large: "%{name} is te groot."
|
|
26
|
+
type_rejected: "Het type van %{name} is niet toegestaan."
|
|
20
27
|
statuses:
|
|
21
28
|
open: "Open"
|
|
22
29
|
resolved: "Opgelost"
|
|
@@ -36,6 +43,7 @@ nl:
|
|
|
36
43
|
reopen: "Heropenen"
|
|
37
44
|
reply_placeholder: "Schrijf je antwoord…"
|
|
38
45
|
send: "Versturen"
|
|
46
|
+
attach: "Bestanden bijvoegen"
|
|
39
47
|
newer: "Nieuwer"
|
|
40
48
|
older: "Ouder"
|
|
41
49
|
mail:
|
|
@@ -17,6 +17,13 @@ pl:
|
|
|
17
17
|
error_send: "Nie udało się wysłać. Spróbuj ponownie."
|
|
18
18
|
error_rate_limited: "Zbyt wiele wiadomości. Poczekaj chwilę i spróbuj ponownie."
|
|
19
19
|
unread_aria: "nieprzeczytane wiadomości"
|
|
20
|
+
attach: "Załącz pliki"
|
|
21
|
+
remove_file: "Usuń plik"
|
|
22
|
+
attachment_error: "Nie udało się wysłać niektórych plików."
|
|
23
|
+
attachments:
|
|
24
|
+
too_many: "Zbyt wiele plików (maks. %{count})."
|
|
25
|
+
too_large: "%{name} jest za duży."
|
|
26
|
+
type_rejected: "Typ pliku %{name} jest niedozwolony."
|
|
20
27
|
statuses:
|
|
21
28
|
open: "Otwarta"
|
|
22
29
|
resolved: "Rozwiązana"
|
|
@@ -36,6 +43,7 @@ pl:
|
|
|
36
43
|
reopen: "Wznów"
|
|
37
44
|
reply_placeholder: "Napisz odpowiedź…"
|
|
38
45
|
send: "Wyślij"
|
|
46
|
+
attach: "Załącz pliki"
|
|
39
47
|
newer: "Nowsze"
|
|
40
48
|
older: "Starsze"
|
|
41
49
|
mail:
|
|
@@ -17,6 +17,13 @@ pt:
|
|
|
17
17
|
error_send: "Não foi possível enviar. Tente novamente."
|
|
18
18
|
error_rate_limited: "Muitas mensagens. Aguarde um momento e tente novamente."
|
|
19
19
|
unread_aria: "mensagens não lidas"
|
|
20
|
+
attach: "Anexar arquivos"
|
|
21
|
+
remove_file: "Remover arquivo"
|
|
22
|
+
attachment_error: "Não foi possível enviar alguns arquivos."
|
|
23
|
+
attachments:
|
|
24
|
+
too_many: "Muitos arquivos (máx. %{count})."
|
|
25
|
+
too_large: "%{name} é muito grande."
|
|
26
|
+
type_rejected: "O tipo de %{name} não é permitido."
|
|
20
27
|
statuses:
|
|
21
28
|
open: "Aberta"
|
|
22
29
|
resolved: "Resolvida"
|
|
@@ -36,6 +43,7 @@ pt:
|
|
|
36
43
|
reopen: "Reabrir"
|
|
37
44
|
reply_placeholder: "Escreva sua resposta…"
|
|
38
45
|
send: "Enviar"
|
|
46
|
+
attach: "Anexar arquivos"
|
|
39
47
|
newer: "Mais recentes"
|
|
40
48
|
older: "Mais antigas"
|
|
41
49
|
mail:
|
|
@@ -17,6 +17,13 @@ ro:
|
|
|
17
17
|
error_send: "Nu s-a putut trimite. Vă rugăm să încercați din nou."
|
|
18
18
|
error_rate_limited: "Prea multe mesaje. Așteptați puțin și încercați din nou."
|
|
19
19
|
unread_aria: "mesaje necitite"
|
|
20
|
+
attach: "Atașează fișiere"
|
|
21
|
+
remove_file: "Elimină fișierul"
|
|
22
|
+
attachment_error: "Unele fișiere nu au putut fi trimise."
|
|
23
|
+
attachments:
|
|
24
|
+
too_many: "Prea multe fișiere (max. %{count})."
|
|
25
|
+
too_large: "%{name} este prea mare."
|
|
26
|
+
type_rejected: "Tipul fișierului %{name} nu este permis."
|
|
20
27
|
statuses:
|
|
21
28
|
open: "Deschisă"
|
|
22
29
|
resolved: "Rezolvată"
|
|
@@ -36,6 +43,7 @@ ro:
|
|
|
36
43
|
reopen: "Redeschide"
|
|
37
44
|
reply_placeholder: "Scrieți răspunsul…"
|
|
38
45
|
send: "Trimite"
|
|
46
|
+
attach: "Atașează fișiere"
|
|
39
47
|
newer: "Mai noi"
|
|
40
48
|
older: "Mai vechi"
|
|
41
49
|
mail:
|
|
@@ -17,6 +17,13 @@ ru:
|
|
|
17
17
|
error_send: "Не удалось отправить. Пожалуйста, попробуйте ещё раз."
|
|
18
18
|
error_rate_limited: "Слишком много сообщений. Подождите немного и попробуйте ещё раз."
|
|
19
19
|
unread_aria: "непрочитанные сообщения"
|
|
20
|
+
attach: "Прикрепить файлы"
|
|
21
|
+
remove_file: "Удалить файл"
|
|
22
|
+
attachment_error: "Некоторые файлы не удалось отправить."
|
|
23
|
+
attachments:
|
|
24
|
+
too_many: "Слишком много файлов (макс. %{count})."
|
|
25
|
+
too_large: "%{name} слишком большой."
|
|
26
|
+
type_rejected: "Тип файла %{name} не разрешён."
|
|
20
27
|
statuses:
|
|
21
28
|
open: "Открыт"
|
|
22
29
|
resolved: "Завершён"
|
|
@@ -36,6 +43,7 @@ ru:
|
|
|
36
43
|
reopen: "Возобновить"
|
|
37
44
|
reply_placeholder: "Напишите ответ…"
|
|
38
45
|
send: "Отправить"
|
|
46
|
+
attach: "Прикрепить файлы"
|
|
39
47
|
newer: "Новее"
|
|
40
48
|
older: "Старше"
|
|
41
49
|
mail:
|
|
@@ -17,6 +17,13 @@ th:
|
|
|
17
17
|
error_send: "ส่งไม่สำเร็จ โปรดลองอีกครั้ง"
|
|
18
18
|
error_rate_limited: "ข้อความมากเกินไป โปรดรอสักครู่แล้วลองอีกครั้ง"
|
|
19
19
|
unread_aria: "ข้อความที่ยังไม่ได้อ่าน"
|
|
20
|
+
attach: "แนบไฟล์"
|
|
21
|
+
remove_file: "นำไฟล์ออก"
|
|
22
|
+
attachment_error: "ไม่สามารถส่งไฟล์บางไฟล์ได้"
|
|
23
|
+
attachments:
|
|
24
|
+
too_many: "ไฟล์มากเกินไป (สูงสุด %{count})"
|
|
25
|
+
too_large: "%{name} มีขนาดใหญ่เกินไป"
|
|
26
|
+
type_rejected: "ไม่อนุญาตประเภทของ %{name}"
|
|
20
27
|
statuses:
|
|
21
28
|
open: "เปิดอยู่"
|
|
22
29
|
resolved: "แก้ไขแล้ว"
|
|
@@ -36,6 +43,7 @@ th:
|
|
|
36
43
|
reopen: "เปิดอีกครั้ง"
|
|
37
44
|
reply_placeholder: "เขียนคำตอบของคุณ…"
|
|
38
45
|
send: "ส่ง"
|
|
46
|
+
attach: "แนบไฟล์"
|
|
39
47
|
newer: "ใหม่กว่า"
|
|
40
48
|
older: "เก่ากว่า"
|
|
41
49
|
mail:
|
|
@@ -17,6 +17,13 @@ tr:
|
|
|
17
17
|
error_send: "Gönderilemedi. Lütfen tekrar deneyin."
|
|
18
18
|
error_rate_limited: "Çok fazla mesaj. Lütfen biraz bekleyip tekrar deneyin."
|
|
19
19
|
unread_aria: "okunmamış mesaj"
|
|
20
|
+
attach: "Dosya ekle"
|
|
21
|
+
remove_file: "Dosyayı kaldır"
|
|
22
|
+
attachment_error: "Bazı dosyalar gönderilemedi."
|
|
23
|
+
attachments:
|
|
24
|
+
too_many: "Çok fazla dosya (en fazla %{count})."
|
|
25
|
+
too_large: "%{name} çok büyük."
|
|
26
|
+
type_rejected: "%{name} dosyasının türüne izin verilmiyor."
|
|
20
27
|
statuses:
|
|
21
28
|
open: "Açık"
|
|
22
29
|
resolved: "Çözüldü"
|
|
@@ -36,6 +43,7 @@ tr:
|
|
|
36
43
|
reopen: "Yeniden aç"
|
|
37
44
|
reply_placeholder: "Yanıtınızı yazın…"
|
|
38
45
|
send: "Gönder"
|
|
46
|
+
attach: "Dosya ekle"
|
|
39
47
|
newer: "Daha yeni"
|
|
40
48
|
older: "Daha eski"
|
|
41
49
|
mail:
|
|
@@ -17,6 +17,13 @@ uk:
|
|
|
17
17
|
error_send: "Не вдалося надіслати. Будь ласка, спробуйте ще раз."
|
|
18
18
|
error_rate_limited: "Забагато повідомлень. Зачекайте трохи та спробуйте ще раз."
|
|
19
19
|
unread_aria: "непрочитані повідомлення"
|
|
20
|
+
attach: "Прикріпити файли"
|
|
21
|
+
remove_file: "Видалити файл"
|
|
22
|
+
attachment_error: "Деякі файли не вдалося надіслати."
|
|
23
|
+
attachments:
|
|
24
|
+
too_many: "Забагато файлів (максимум %{count})."
|
|
25
|
+
too_large: "%{name} завеликий."
|
|
26
|
+
type_rejected: "Тип файлу %{name} не дозволено."
|
|
20
27
|
statuses:
|
|
21
28
|
open: "Відкрита"
|
|
22
29
|
resolved: "Завершена"
|
|
@@ -36,6 +43,7 @@ uk:
|
|
|
36
43
|
reopen: "Відновити"
|
|
37
44
|
reply_placeholder: "Напишіть відповідь…"
|
|
38
45
|
send: "Надіслати"
|
|
46
|
+
attach: "Прикріпити файли"
|
|
39
47
|
newer: "Новіші"
|
|
40
48
|
older: "Старіші"
|
|
41
49
|
mail:
|
|
@@ -17,6 +17,13 @@ ur:
|
|
|
17
17
|
error_send: "بھیجا نہیں جا سکا۔ براہ کرم دوبارہ کوشش کریں۔"
|
|
18
18
|
error_rate_limited: "بہت زیادہ پیغامات۔ براہ کرم تھوڑا انتظار کریں اور دوبارہ کوشش کریں۔"
|
|
19
19
|
unread_aria: "غیر پڑھے گئے پیغامات"
|
|
20
|
+
attach: "فائلیں منسلک کریں"
|
|
21
|
+
remove_file: "فائل ہٹائیں"
|
|
22
|
+
attachment_error: "کچھ فائلیں نہیں بھیجی جا سکیں۔"
|
|
23
|
+
attachments:
|
|
24
|
+
too_many: "بہت زیادہ فائلیں (زیادہ سے زیادہ %{count})۔"
|
|
25
|
+
too_large: "%{name} بہت بڑی ہے۔"
|
|
26
|
+
type_rejected: "%{name} کی قسم کی اجازت نہیں ہے۔"
|
|
20
27
|
statuses:
|
|
21
28
|
open: "کھلی"
|
|
22
29
|
resolved: "حل شدہ"
|
|
@@ -36,6 +43,7 @@ ur:
|
|
|
36
43
|
reopen: "دوبارہ کھولیں"
|
|
37
44
|
reply_placeholder: "اپنا جواب لکھیں…"
|
|
38
45
|
send: "بھیجیں"
|
|
46
|
+
attach: "فائلیں منسلک کریں"
|
|
39
47
|
newer: "نئے"
|
|
40
48
|
older: "پرانے"
|
|
41
49
|
mail:
|
|
@@ -17,6 +17,13 @@ vi:
|
|
|
17
17
|
error_send: "Không thể gửi. Vui lòng thử lại."
|
|
18
18
|
error_rate_limited: "Quá nhiều tin nhắn. Vui lòng đợi một lát rồi thử lại."
|
|
19
19
|
unread_aria: "tin nhắn chưa đọc"
|
|
20
|
+
attach: "Đính kèm tệp"
|
|
21
|
+
remove_file: "Xóa tệp"
|
|
22
|
+
attachment_error: "Không thể gửi một số tệp."
|
|
23
|
+
attachments:
|
|
24
|
+
too_many: "Quá nhiều tệp (tối đa %{count})."
|
|
25
|
+
too_large: "%{name} quá lớn."
|
|
26
|
+
type_rejected: "Loại tệp %{name} không được phép."
|
|
20
27
|
statuses:
|
|
21
28
|
open: "Đang mở"
|
|
22
29
|
resolved: "Đã giải quyết"
|
|
@@ -36,6 +43,7 @@ vi:
|
|
|
36
43
|
reopen: "Mở lại"
|
|
37
44
|
reply_placeholder: "Viết câu trả lời của bạn…"
|
|
38
45
|
send: "Gửi"
|
|
46
|
+
attach: "Đính kèm tệp"
|
|
39
47
|
newer: "Mới hơn"
|
|
40
48
|
older: "Cũ hơn"
|
|
41
49
|
mail:
|
|
@@ -17,6 +17,13 @@
|
|
|
17
17
|
error_send: "发送失败,请重试。"
|
|
18
18
|
error_rate_limited: "消息过多,请稍等片刻再重试。"
|
|
19
19
|
unread_aria: "条未读消息"
|
|
20
|
+
attach: "附加文件"
|
|
21
|
+
remove_file: "移除文件"
|
|
22
|
+
attachment_error: "部分文件无法发送。"
|
|
23
|
+
attachments:
|
|
24
|
+
too_many: "文件过多(最多 %{count} 个)。"
|
|
25
|
+
too_large: "%{name} 太大。"
|
|
26
|
+
type_rejected: "不允许 %{name} 的类型。"
|
|
20
27
|
statuses:
|
|
21
28
|
open: "进行中"
|
|
22
29
|
resolved: "已解决"
|
|
@@ -36,6 +43,7 @@
|
|
|
36
43
|
reopen: "重新打开"
|
|
37
44
|
reply_placeholder: "输入您的回复…"
|
|
38
45
|
send: "发送"
|
|
46
|
+
attach: "附加文件"
|
|
39
47
|
newer: "较新"
|
|
40
48
|
older: "较旧"
|
|
41
49
|
mail:
|
data/config/routes.rb
CHANGED
|
@@ -13,6 +13,12 @@ Livechat::Engine.routes.draw do
|
|
|
13
13
|
post 'read', to: 'visitor#read'
|
|
14
14
|
end
|
|
15
15
|
|
|
16
|
+
# Message attachments, gated by the engine (never a public blob URL). One
|
|
17
|
+
# route for both sides — the controller decides whether you're the visitor
|
|
18
|
+
# who owns the thread or an agent. Above the conversations catch-all, and
|
|
19
|
+
# id-constrained so it never shadows a numeric conversation path.
|
|
20
|
+
get 'attachments/:id', to: 'attachments#show', as: :attachment, constraints: { id: /\d+/ }
|
|
21
|
+
|
|
16
22
|
# The inbox. Flat, human URLs: the mount path IS the conversation list.
|
|
17
23
|
resources :conversations, path: '', only: %i[index show], constraints: { id: /\d+/ } do
|
|
18
24
|
collection do
|
|
@@ -62,6 +62,21 @@ Livechat.configure do |config|
|
|
|
62
62
|
# Per-IP throttle for the widget endpoints (Rails 7.2+; ignored on 7.1).
|
|
63
63
|
# config.rate_limit = { to: 30, within: 1.minute }
|
|
64
64
|
|
|
65
|
+
# File attachments. On by default, but only take effect where the app has
|
|
66
|
+
# Active Storage (run `rails active_storage:install` once). Files are served
|
|
67
|
+
# through the engine — gated the same as the chat — never a public blob URL.
|
|
68
|
+
# config.attach_files = true
|
|
69
|
+
# config.max_attachments = 5
|
|
70
|
+
# config.max_attachment_size = 10.megabytes
|
|
71
|
+
# config.allowed_attachment_types = nil # e.g. %w[image/png image/jpeg application/pdf]
|
|
72
|
+
|
|
73
|
+
# Realtime push over Action Cable. Off by default — the widget and inbox
|
|
74
|
+
# poll, which needs nothing from your app. Turn this on to push new messages
|
|
75
|
+
# the instant they arrive; polling stays the fallback. Requires Action Cable
|
|
76
|
+
# mounted (the default `mount ActionCable... => "/cable"` in your routes).
|
|
77
|
+
# config.action_cable = true
|
|
78
|
+
# config.action_cable_url = "/cable"
|
|
79
|
+
|
|
65
80
|
# Keep in sync with the `mount` in config/routes.rb.
|
|
66
81
|
# config.mount_path = "/livechat"
|
|
67
82
|
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Loaded from the engine only when Action Cable is present (see engine.rb), so
|
|
4
|
+
# a host without a cable never pays for it and never trips Zeitwerk eager-load
|
|
5
|
+
# on a missing ActionCable constant.
|
|
6
|
+
module Livechat
|
|
7
|
+
# One channel for both sides of the chat. It carries no payload of its own —
|
|
8
|
+
# a broadcast is a nudge ("something changed on this stream"); the client
|
|
9
|
+
# then refetches through the same gated endpoint it already polls, so the
|
|
10
|
+
# rendering, dedup and read-tracking logic stays in one place.
|
|
11
|
+
#
|
|
12
|
+
# Authorization IS the signature: the server only ever signs a stream name
|
|
13
|
+
# it already let this subscriber see — the widget gets its conversation
|
|
14
|
+
# stream from the gated /conversation endpoint, an agent gets conversation
|
|
15
|
+
# and inbox streams from the (authorized) inbox pages. A subscriber that
|
|
16
|
+
# can't present a validly signed livechat: stream is rejected.
|
|
17
|
+
class StreamChannel < ActionCable::Channel::Base
|
|
18
|
+
def subscribed
|
|
19
|
+
stream = Livechat.verify_stream(params[:signed_stream])
|
|
20
|
+
if stream&.start_with?('livechat:')
|
|
21
|
+
stream_from stream
|
|
22
|
+
else
|
|
23
|
+
reject
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -65,6 +65,28 @@ module Livechat
|
|
|
65
65
|
# Rails' rate limiter (Rails 7.2+; ignored on 7.1). nil disables it.
|
|
66
66
|
attr_accessor :rate_limit
|
|
67
67
|
|
|
68
|
+
# Let visitors and agents attach files to messages. Requires Active
|
|
69
|
+
# Storage in the host app (rails active_storage:install); silently
|
|
70
|
+
# ignored when it isn't present, so the widget keeps working. Set false
|
|
71
|
+
# to turn attachments off even where Active Storage exists.
|
|
72
|
+
attr_accessor :attach_files
|
|
73
|
+
|
|
74
|
+
# Cap on attachments per message, on the size of each file (bytes), and
|
|
75
|
+
# an optional content-type allowlist (nil accepts any type). Enforced on
|
|
76
|
+
# the server; a rejected upload comes back as a validation error.
|
|
77
|
+
attr_accessor :max_attachments, :max_attachment_size, :allowed_attachment_types
|
|
78
|
+
|
|
79
|
+
# Push new messages over Action Cable instead of waiting for the next
|
|
80
|
+
# poll. Opt-in and off by default: polling stays the transport, so a
|
|
81
|
+
# host that never mounts a cable keeps working; when on (and Action Cable
|
|
82
|
+
# is present) a new message nudges the widget and the inbox to refresh at
|
|
83
|
+
# once. Requires `/cable` mounted in the host's routes.
|
|
84
|
+
attr_accessor :action_cable
|
|
85
|
+
|
|
86
|
+
# Where the host mounts Action Cable. Only consulted when action_cable is
|
|
87
|
+
# on. Keep in sync with the `mount ActionCable... => "/cable"` in routes.
|
|
88
|
+
attr_accessor :action_cable_url
|
|
89
|
+
|
|
68
90
|
# Where the engine is mounted. The widget calls paths under it, so keep
|
|
69
91
|
# this in sync with the `mount` line in your routes.
|
|
70
92
|
attr_accessor :mount_path
|
|
@@ -88,6 +110,12 @@ module Livechat
|
|
|
88
110
|
@on_agent_message = ->(_message) {}
|
|
89
111
|
@rate_limit = { to: 30, within: 60 }
|
|
90
112
|
@mount_path = '/livechat'
|
|
113
|
+
@attach_files = true
|
|
114
|
+
@max_attachments = 5
|
|
115
|
+
@max_attachment_size = 10 * 1024 * 1024
|
|
116
|
+
@allowed_attachment_types = nil
|
|
117
|
+
@action_cable = false
|
|
118
|
+
@action_cable_url = '/cable'
|
|
91
119
|
end
|
|
92
120
|
|
|
93
121
|
def widget_endpoint = "#{mount_path.chomp('/')}/widget"
|