chore: remove chat module
This commit is contained in:
parent
c0380cdd06
commit
4576ab77d7
33 changed files with 0 additions and 4475 deletions
|
|
@ -1,23 +0,0 @@
|
|||
|
||||
import frappe
|
||||
from frappe import _
|
||||
|
||||
session = frappe.session
|
||||
|
||||
def authenticate(user, raise_err = True):
|
||||
if session.user == 'Guest':
|
||||
if not frappe.db.exists('Chat Token', user):
|
||||
if raise_err:
|
||||
frappe.throw(_("Sorry, you're not authorized."))
|
||||
else:
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
else:
|
||||
if user != session.user:
|
||||
if raise_err:
|
||||
frappe.throw(_("Sorry, you're not authorized."))
|
||||
else:
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
// Copyright (c) 2017, Frappe Technologies and contributors
|
||||
// For license information, please see license.txt
|
||||
|
||||
frappe.ui.form.on('Chat Message', {
|
||||
onload: function(frm) {
|
||||
if(frm.doc.type == 'File') {
|
||||
frm.set_df_property('content', 'read_only', 1);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
@ -1,91 +0,0 @@
|
|||
{
|
||||
"beta": 1,
|
||||
"creation": "2017-11-10 11:10:40.011099",
|
||||
"doctype": "DocType",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"room_type",
|
||||
"type",
|
||||
"user",
|
||||
"room",
|
||||
"content",
|
||||
"mentions",
|
||||
"urls"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"fieldname": "room_type",
|
||||
"fieldtype": "Select",
|
||||
"in_list_view": 1,
|
||||
"label": "Room Type",
|
||||
"options": "Direct\nGroup\nVisitor",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "type",
|
||||
"fieldtype": "Data",
|
||||
"label": "Type",
|
||||
"options": "Content\nFile"
|
||||
},
|
||||
{
|
||||
"fieldname": "user",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 1,
|
||||
"label": "User",
|
||||
"options": "User",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "room",
|
||||
"fieldtype": "Link",
|
||||
"label": "Room",
|
||||
"options": "Chat Room",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "content",
|
||||
"fieldtype": "Text",
|
||||
"label": "Content",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "mentions",
|
||||
"fieldtype": "Code",
|
||||
"hidden": 1,
|
||||
"label": "Mentions"
|
||||
},
|
||||
{
|
||||
"fieldname": "urls",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 1,
|
||||
"label": "URLs"
|
||||
}
|
||||
],
|
||||
"modified": "2020-09-18 17:26:09.703215",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Chat",
|
||||
"name": "Chat Message",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "System Manager",
|
||||
"share": 1,
|
||||
"write": 1
|
||||
}
|
||||
],
|
||||
"quick_entry": 1,
|
||||
"search_fields": "content, user",
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"title_field": "content",
|
||||
"track_changes": 1,
|
||||
"track_seen": 1
|
||||
}
|
||||
|
|
@ -1,215 +0,0 @@
|
|||
# imports - standard imports
|
||||
import json
|
||||
|
||||
# imports - third-party imports
|
||||
import requests
|
||||
from bs4 import BeautifulSoup as Soup
|
||||
|
||||
# imports - module imports
|
||||
from frappe.model.document import Document
|
||||
from frappe import _, _dict
|
||||
import frappe
|
||||
|
||||
# imports - frappe module imports
|
||||
from frappe.chat import authenticate
|
||||
from frappe.chat.util import (
|
||||
get_if_empty,
|
||||
check_url,
|
||||
dictify,
|
||||
get_emojis,
|
||||
safe_json_loads,
|
||||
get_user_doc,
|
||||
squashify
|
||||
)
|
||||
|
||||
session = frappe.session
|
||||
|
||||
class ChatMessage(Document):
|
||||
pass
|
||||
|
||||
def get_message_urls(content):
|
||||
soup = Soup(content, 'html.parser')
|
||||
anchors = soup.find_all('a')
|
||||
urls = [ ]
|
||||
|
||||
for anchor in anchors:
|
||||
text = anchor.text
|
||||
|
||||
if check_url(text):
|
||||
urls.append(text)
|
||||
|
||||
return urls
|
||||
|
||||
def get_message_mentions(content):
|
||||
mentions = [ ]
|
||||
tokens = content.split(' ')
|
||||
|
||||
for token in tokens:
|
||||
if token.startswith('@'):
|
||||
what = token[1:]
|
||||
if frappe.db.exists('User', what):
|
||||
mentions.append(what)
|
||||
else:
|
||||
if frappe.db.exists('User', token):
|
||||
mentions.append(token)
|
||||
|
||||
return mentions
|
||||
|
||||
def get_message_meta(content):
|
||||
'''
|
||||
Assumes content to be HTML. Sanitizes the content
|
||||
into a dict of metadata values.
|
||||
'''
|
||||
meta = _dict(
|
||||
links = [ ],
|
||||
mentions = [ ]
|
||||
)
|
||||
|
||||
meta.content = content
|
||||
meta.urls = get_message_urls(content)
|
||||
meta.mentions = get_message_mentions(content)
|
||||
|
||||
return meta
|
||||
|
||||
def sanitize_message_content(content):
|
||||
emojis = get_emojis()
|
||||
|
||||
tokens = content.split(' ')
|
||||
for token in tokens:
|
||||
if token.startswith(':') and token.endswith(':'):
|
||||
what = token[1:-1]
|
||||
|
||||
# Expensive, I know.
|
||||
for emoji in emojis:
|
||||
for alias in emoji.aliases:
|
||||
if what == alias:
|
||||
content = content.replace(token, emoji.emoji)
|
||||
|
||||
return content
|
||||
|
||||
def get_new_chat_message_doc(user, room, content, type = "Content", link = True):
|
||||
user = get_user_doc(user)
|
||||
room = frappe.get_doc('Chat Room', room)
|
||||
|
||||
meta = get_message_meta(content)
|
||||
mess = frappe.new_doc('Chat Message')
|
||||
mess.room = room.name
|
||||
mess.room_type = room.type
|
||||
mess.content = sanitize_message_content(content)
|
||||
mess.type = type
|
||||
mess.user = user.name
|
||||
|
||||
mess.mentions = json.dumps(meta.mentions)
|
||||
mess.urls = ','.join(meta.urls)
|
||||
mess.save(ignore_permissions = True)
|
||||
|
||||
if link:
|
||||
room.update(dict(
|
||||
last_message = mess.name
|
||||
))
|
||||
room.save(ignore_permissions = True)
|
||||
|
||||
return mess
|
||||
|
||||
def get_new_chat_message(user, room, content, type = "Content"):
|
||||
mess = get_new_chat_message_doc(user, room, content, type)
|
||||
|
||||
resp = dict(
|
||||
name = mess.name,
|
||||
user = mess.user,
|
||||
room = mess.room,
|
||||
room_type = mess.room_type,
|
||||
content = json.loads(mess.content) if mess.type in ["File"] else mess.content,
|
||||
urls = mess.urls,
|
||||
mentions = json.loads(mess.mentions),
|
||||
creation = mess.creation,
|
||||
seen = json.loads(mess._seen) if mess._seen else [ ],
|
||||
)
|
||||
|
||||
return resp
|
||||
|
||||
@frappe.whitelist(allow_guest = True)
|
||||
def send(user, room, content, type = "Content"):
|
||||
mess = get_new_chat_message(user, room, content, type)
|
||||
|
||||
frappe.publish_realtime('frappe.chat.message:create', mess, room = room,
|
||||
after_commit = True)
|
||||
|
||||
@frappe.whitelist(allow_guest = True)
|
||||
def seen(message, user = None):
|
||||
authenticate(user)
|
||||
|
||||
has_message = frappe.db.exists('Chat Message', message)
|
||||
|
||||
if has_message:
|
||||
mess = frappe.get_doc('Chat Message', message)
|
||||
mess.add_seen(user)
|
||||
mess.load_from_db()
|
||||
room = mess.room
|
||||
resp = dict(message = message, data = dict(seen = json.loads(mess._seen) if mess._seen else []))
|
||||
|
||||
frappe.publish_realtime('frappe.chat.message:update', resp, room = room, after_commit = True)
|
||||
|
||||
def history(room, fields = None, limit = 10, start = None, end = None):
|
||||
room = frappe.get_doc('Chat Room', room)
|
||||
mess = frappe.get_all('Chat Message',
|
||||
filters = [
|
||||
('Chat Message', 'room', '=', room.name),
|
||||
('Chat Message', 'room_type', '=', room.type)
|
||||
],
|
||||
fields = fields if fields else [
|
||||
'name', 'room_type', 'room', 'content', 'type', 'user', 'mentions', 'urls', 'creation', '_seen'
|
||||
],
|
||||
order_by = 'creation'
|
||||
)
|
||||
|
||||
if not fields or 'seen' in fields:
|
||||
for m in mess:
|
||||
m['seen'] = json.loads(m._seen) if m._seen else [ ]
|
||||
del m['_seen']
|
||||
if not fields or 'content' in fields:
|
||||
for m in mess:
|
||||
m['content'] = json.loads(m.content) if m.type in ["File"] else m.content
|
||||
|
||||
frappe.enqueue('frappe.chat.doctype.chat_message.chat_message.mark_messages_as_seen',
|
||||
message_names=[m.name for m in mess], user=frappe.session.user)
|
||||
|
||||
return mess
|
||||
|
||||
def mark_messages_as_seen(message_names, user):
|
||||
'''
|
||||
Marks chat messages as seen, updates the _seen for each message
|
||||
(should be run in background process)
|
||||
'''
|
||||
for name in message_names:
|
||||
seen = frappe.db.get_value('Chat Message', name, '_seen') or '[]'
|
||||
seen = json.loads(seen)
|
||||
seen.append(user)
|
||||
seen = json.dumps(seen)
|
||||
frappe.db.set_value('Chat Message', name, '_seen', seen, update_modified=False)
|
||||
|
||||
frappe.db.commit()
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def get(name, rooms = None, fields = None):
|
||||
rooms, fields = safe_json_loads(rooms, fields)
|
||||
|
||||
has_message = frappe.db.exists('Chat Message', name)
|
||||
|
||||
if has_message:
|
||||
dmess = frappe.get_doc('Chat Message', name)
|
||||
data = dict(
|
||||
name = dmess.name,
|
||||
user = dmess.user,
|
||||
room = dmess.room,
|
||||
room_type = dmess.room_type,
|
||||
content = json.loads(dmess.content) if dmess.type in ["File"] else dmess.content,
|
||||
type = dmess.type,
|
||||
urls = dmess.urls,
|
||||
mentions = dmess.mentions,
|
||||
creation = dmess.creation,
|
||||
seen = get_if_empty(dmess._seen, [ ])
|
||||
)
|
||||
|
||||
return data
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
frappe.listview_settings['Chat Message'] = {
|
||||
filters: [
|
||||
['Chat Message', 'user', '==', frappe.session.user, true]
|
||||
// I need an or_filter here.
|
||||
// ['Chat Room', 'owner', '==', frappe.session.user, true],
|
||||
// ['Chat Room', frappe.session.user, 'in', 'users', true]
|
||||
]
|
||||
};
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
/* eslint semi: "never" */
|
||||
frappe.ui.form.on('Chat Profile', {
|
||||
refresh: function (form) {
|
||||
if ( form.doc.name !== frappe.session.user ) {
|
||||
form.disable_save()
|
||||
form.set_read_only(true)
|
||||
// There's one more that faris@frappe.io told me to add here. form.refresh_fields()?
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
@ -1,98 +0,0 @@
|
|||
{
|
||||
"autoname": "field:user",
|
||||
"beta": 1,
|
||||
"creation": "2017-11-13 18:26:57.943027",
|
||||
"doctype": "DocType",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"user",
|
||||
"status",
|
||||
"chat_background",
|
||||
"notifications",
|
||||
"message_preview",
|
||||
"notification_tones",
|
||||
"conversation_tones",
|
||||
"settings",
|
||||
"enable_chat"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"fieldname": "user",
|
||||
"fieldtype": "Link",
|
||||
"label": "User",
|
||||
"options": "User",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"default": "Online",
|
||||
"fieldname": "status",
|
||||
"fieldtype": "Select",
|
||||
"in_list_view": 1,
|
||||
"label": "Status",
|
||||
"options": "Online\nAway\nBusy\nOffline"
|
||||
},
|
||||
{
|
||||
"fieldname": "chat_background",
|
||||
"fieldtype": "Attach Image",
|
||||
"label": "Chat Background"
|
||||
},
|
||||
{
|
||||
"fieldname": "notifications",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Notifications"
|
||||
},
|
||||
{
|
||||
"default": "1",
|
||||
"fieldname": "message_preview",
|
||||
"fieldtype": "Check",
|
||||
"label": "Message Preview"
|
||||
},
|
||||
{
|
||||
"default": "1",
|
||||
"fieldname": "notification_tones",
|
||||
"fieldtype": "Check",
|
||||
"label": "Notification Tones"
|
||||
},
|
||||
{
|
||||
"default": "1",
|
||||
"fieldname": "conversation_tones",
|
||||
"fieldtype": "Check",
|
||||
"label": "Conversation Tones"
|
||||
},
|
||||
{
|
||||
"fieldname": "settings",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Settings"
|
||||
},
|
||||
{
|
||||
"default": "1",
|
||||
"fieldname": "enable_chat",
|
||||
"fieldtype": "Check",
|
||||
"label": "Enable Chat"
|
||||
}
|
||||
],
|
||||
"in_create": 1,
|
||||
"modified": "2019-11-07 13:21:36.414961",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Chat",
|
||||
"name": "Chat Profile",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "System Manager",
|
||||
"share": 1,
|
||||
"write": 1
|
||||
}
|
||||
],
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1
|
||||
}
|
||||
|
|
@ -1,98 +0,0 @@
|
|||
# imports - module imports
|
||||
from frappe.model.document import Document
|
||||
from frappe import _
|
||||
import frappe
|
||||
|
||||
# imports - frappe module imports
|
||||
from frappe.core.doctype.version.version import get_diff
|
||||
from frappe.chat.doctype.chat_room import chat_room
|
||||
from frappe.chat.util import (
|
||||
safe_json_loads,
|
||||
filter_dict,
|
||||
dictify
|
||||
)
|
||||
|
||||
session = frappe.session
|
||||
|
||||
class ChatProfile(Document):
|
||||
def on_update(self):
|
||||
if not self.is_new():
|
||||
b, a = self.get_doc_before_save(), self
|
||||
diff = dictify(get_diff(a, b))
|
||||
if diff:
|
||||
user = session.user
|
||||
|
||||
fields = [changed[0] for changed in diff.changed]
|
||||
|
||||
if 'status' in fields:
|
||||
rooms = chat_room.get(user, filters = ['Chat Room', 'type', '=', 'Direct'])
|
||||
update = dict(user = user, data = dict(status = self.status))
|
||||
|
||||
for room in rooms:
|
||||
frappe.publish_realtime('frappe.chat.profile:update', update, room = room.name, after_commit = True)
|
||||
|
||||
if 'enable_chat' in fields:
|
||||
update = dict(user = user, data = dict(enable_chat = bool(self.enable_chat)))
|
||||
frappe.publish_realtime('frappe.chat.profile:update', update, user = user, after_commit = True)
|
||||
|
||||
def authenticate(user):
|
||||
if user != session.user:
|
||||
frappe.throw(_("Sorry, you're not authorized."))
|
||||
|
||||
@frappe.whitelist()
|
||||
def get(user, fields = None):
|
||||
duser = frappe.get_doc('User', user)
|
||||
|
||||
if frappe.db.exists('Chat Profile', user):
|
||||
dprof = frappe.get_doc('Chat Profile', user)
|
||||
|
||||
# If you're adding something here, make sure the client recieves it.
|
||||
profile = dict(
|
||||
# User
|
||||
name = duser.name,
|
||||
email = duser.email,
|
||||
first_name = duser.first_name,
|
||||
last_name = duser.last_name,
|
||||
username = duser.username,
|
||||
avatar = duser.user_image,
|
||||
bio = duser.bio,
|
||||
# Chat Profile
|
||||
status = dprof.status,
|
||||
chat_background = dprof.chat_background,
|
||||
message_preview = bool(dprof.message_preview),
|
||||
notification_tones = bool(dprof.notification_tones),
|
||||
conversation_tones = bool(dprof.conversation_tones),
|
||||
enable_chat = bool(dprof.enable_chat)
|
||||
)
|
||||
profile = filter_dict(profile, fields)
|
||||
|
||||
return dictify(profile)
|
||||
|
||||
@frappe.whitelist()
|
||||
def create(user, exists_ok = False, fields = None):
|
||||
authenticate(user)
|
||||
|
||||
exists_ok, fields = safe_json_loads(exists_ok, fields)
|
||||
|
||||
try:
|
||||
dprof = frappe.new_doc('Chat Profile')
|
||||
dprof.user = user
|
||||
dprof.save(ignore_permissions = True)
|
||||
except frappe.DuplicateEntryError:
|
||||
frappe.clear_messages()
|
||||
if not exists_ok:
|
||||
frappe.throw(_('Chat Profile for User {0} exists.').format(user))
|
||||
|
||||
profile = get(user, fields = fields)
|
||||
|
||||
return profile
|
||||
|
||||
@frappe.whitelist()
|
||||
def update(user, data):
|
||||
authenticate(user)
|
||||
|
||||
data = safe_json_loads(data)
|
||||
|
||||
dprof = frappe.get_doc('Chat Profile', user)
|
||||
dprof.update(data)
|
||||
dprof.save(ignore_permissions = True)
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
frappe.listview_settings['Chat Profile'] =
|
||||
{
|
||||
get_indicator: function (doc)
|
||||
{
|
||||
const status = frappe.utils.squash(frappe.chat.profile.STATUSES.filter(
|
||||
s => s.name === doc.status
|
||||
));
|
||||
|
||||
return [__(status.name), status.color, `status,=,${status.name}`]
|
||||
}
|
||||
};
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
// Copyright (c) 2017, Frappe Technologies and contributors
|
||||
// For license information, please see license.txt
|
||||
|
||||
frappe.ui.form.on('Chat Room', {
|
||||
refresh: function (form) {
|
||||
|
||||
}
|
||||
});
|
||||
|
|
@ -1,100 +0,0 @@
|
|||
{
|
||||
"autoname": "CR.#####",
|
||||
"beta": 1,
|
||||
"creation": "2017-11-08 15:27:21.156667",
|
||||
"doctype": "DocType",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"type",
|
||||
"room_name",
|
||||
"avatar",
|
||||
"last_message",
|
||||
"message_count",
|
||||
"owner",
|
||||
"user_list",
|
||||
"users"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"default": "Direct",
|
||||
"fieldname": "type",
|
||||
"fieldtype": "Select",
|
||||
"in_list_view": 1,
|
||||
"label": "Type",
|
||||
"options": "Direct\nGroup\nVisitor",
|
||||
"reqd": 1,
|
||||
"set_only_once": 1
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:doc.type==\"Group\"",
|
||||
"fieldname": "room_name",
|
||||
"fieldtype": "Data",
|
||||
"label": "Name"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:doc.type==\"Group\"",
|
||||
"fieldname": "avatar",
|
||||
"fieldtype": "Attach Image",
|
||||
"hidden": 1,
|
||||
"label": "Avatar"
|
||||
},
|
||||
{
|
||||
"fieldname": "last_message",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 1,
|
||||
"label": "Last Message"
|
||||
},
|
||||
{
|
||||
"fieldname": "message_count",
|
||||
"fieldtype": "Int",
|
||||
"hidden": 1,
|
||||
"label": "Message Count"
|
||||
},
|
||||
{
|
||||
"fieldname": "owner",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 1,
|
||||
"label": "Owner",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "user_list",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Users"
|
||||
},
|
||||
{
|
||||
"fieldname": "users",
|
||||
"fieldtype": "Table",
|
||||
"label": "Users",
|
||||
"options": "Chat Room User"
|
||||
}
|
||||
],
|
||||
"image_field": "avatar",
|
||||
"modified": "2019-11-07 13:20:24.625329",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Chat",
|
||||
"name": "Chat Room",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "System Manager",
|
||||
"set_user_permissions": 1,
|
||||
"share": 1,
|
||||
"write": 1
|
||||
}
|
||||
],
|
||||
"search_fields": "room_name",
|
||||
"show_name_in_global_search": 1,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"title_field": "room_name",
|
||||
"track_changes": 1
|
||||
}
|
||||
|
|
@ -1,227 +0,0 @@
|
|||
# imports - module imports
|
||||
from frappe.model.document import Document
|
||||
from frappe import _
|
||||
import frappe
|
||||
|
||||
# imports - frappe module imports
|
||||
from frappe.chat import authenticate
|
||||
from frappe.core.doctype.version.version import get_diff
|
||||
from frappe.chat.doctype.chat_message import chat_message
|
||||
from frappe.chat.util import (
|
||||
safe_json_loads,
|
||||
dictify,
|
||||
listify,
|
||||
squashify,
|
||||
get_if_empty
|
||||
)
|
||||
|
||||
session = frappe.session
|
||||
|
||||
|
||||
def is_direct(owner, other, bidirectional=False):
|
||||
def get_room(owner, other):
|
||||
room = frappe.get_all('Chat Room', filters=[
|
||||
['Chat Room', 'type', 'in', ('Direct', 'Visitor')],
|
||||
['Chat Room', 'owner', '=', owner],
|
||||
['Chat Room User', 'user', '=', other]
|
||||
], distinct=True)
|
||||
|
||||
return room
|
||||
|
||||
exists = len(get_room(owner, other)) == 1
|
||||
if bidirectional:
|
||||
exists = exists or len(get_room(other, owner)) == 1
|
||||
|
||||
return exists
|
||||
|
||||
|
||||
def get_chat_room_user_set(users, filter_=None):
|
||||
seen, uset = set(), list()
|
||||
|
||||
for u in users:
|
||||
if filter_(u) and u.user not in seen:
|
||||
uset.append(u)
|
||||
seen.add(u.user)
|
||||
|
||||
return uset
|
||||
|
||||
|
||||
class ChatRoom(Document):
|
||||
def validate(self):
|
||||
if self.is_new():
|
||||
users = get_chat_room_user_set(self.users, filter_=lambda u: u.user != session.user)
|
||||
self.update(dict(
|
||||
users=users
|
||||
))
|
||||
|
||||
if self.type == "Direct":
|
||||
if len(self.users) != 1:
|
||||
frappe.throw(_('{0} room must have atmost one user.').format(self.type))
|
||||
|
||||
other = squashify(self.users)
|
||||
|
||||
if self.is_new():
|
||||
if is_direct(self.owner, other.user, bidirectional=True):
|
||||
frappe.throw(_('Direct room with {0} already exists.').format(other.user))
|
||||
|
||||
if self.type == "Group" and not self.room_name:
|
||||
frappe.throw(_('Group name cannot be empty.'))
|
||||
|
||||
def on_update(self):
|
||||
if not self.is_new():
|
||||
before = self.get_doc_before_save()
|
||||
if not before: return
|
||||
|
||||
after = self
|
||||
diff = dictify(get_diff(before, after))
|
||||
if diff:
|
||||
update = {}
|
||||
for changed in diff.changed:
|
||||
field, old, new = changed
|
||||
|
||||
if field == 'last_message':
|
||||
new = chat_message.get(new)
|
||||
|
||||
update.update({field: new})
|
||||
|
||||
if diff.added or diff.removed:
|
||||
update.update(dict(users=[u.user for u in self.users]))
|
||||
|
||||
update = dict(room=self.name, data=update)
|
||||
|
||||
frappe.publish_realtime('frappe.chat.room:update', update, room=self.name,
|
||||
after_commit=True)
|
||||
|
||||
|
||||
@frappe.whitelist(allow_guest=True)
|
||||
def get(user=None, token=None, rooms=None, fields=None, filters=None):
|
||||
# There is this horrible bug out here.
|
||||
# Looks like if frappe.call sends optional arguments (not in right order),
|
||||
# the argument turns to an empty string.
|
||||
# I'm not even going to think searching for it.
|
||||
# Hence, the hack was get_if_empty (previous assign_if_none)
|
||||
# - Achilles Rasquinha achilles@frappe.io
|
||||
data = user or token
|
||||
authenticate(data)
|
||||
|
||||
rooms, fields, filters = safe_json_loads(rooms, fields, filters)
|
||||
|
||||
rooms = listify(get_if_empty(rooms, []))
|
||||
fields = listify(get_if_empty(fields, []))
|
||||
|
||||
const = [] # constraints
|
||||
if rooms:
|
||||
const.append(['Chat Room', 'name', 'in', rooms])
|
||||
if filters:
|
||||
if isinstance(filters[0], list):
|
||||
const = const + filters
|
||||
else:
|
||||
const.append(filters)
|
||||
|
||||
default = ['name', 'type', 'room_name', 'creation', 'owner', 'avatar']
|
||||
handle = ['users', 'last_message']
|
||||
|
||||
param = [f for f in fields if f not in handle]
|
||||
|
||||
rooms = frappe.get_all('Chat Room',
|
||||
or_filters=[
|
||||
['Chat Room', 'owner', '=', frappe.session.user],
|
||||
['Chat Room User', 'user', '=', frappe.session.user]
|
||||
],
|
||||
filters=const,
|
||||
fields=param + ['name'] if param else default,
|
||||
distinct=True
|
||||
)
|
||||
|
||||
if not fields or 'users' in fields:
|
||||
for i, r in enumerate(rooms):
|
||||
droom = frappe.get_doc('Chat Room', r.name)
|
||||
rooms[i]['users'] = []
|
||||
|
||||
for duser in droom.users:
|
||||
rooms[i]['users'].append(duser.user)
|
||||
|
||||
if not fields or 'last_message' in fields:
|
||||
for i, r in enumerate(rooms):
|
||||
droom = frappe.get_doc('Chat Room', r.name)
|
||||
if droom.last_message:
|
||||
rooms[i]['last_message'] = chat_message.get(droom.last_message)
|
||||
else:
|
||||
rooms[i]['last_message'] = None
|
||||
|
||||
rooms = squashify(dictify(rooms))
|
||||
|
||||
return rooms
|
||||
|
||||
|
||||
@frappe.whitelist(allow_guest=True)
|
||||
def create(kind, token, users=None, name=None):
|
||||
authenticate(token)
|
||||
|
||||
users = safe_json_loads(users)
|
||||
create = True
|
||||
|
||||
if kind == 'Visitor':
|
||||
room = squashify(frappe.db.sql("""
|
||||
SELECT name
|
||||
FROM `tabChat Room`
|
||||
WHERE owner=%s
|
||||
""", (frappe.session.user), as_dict=True))
|
||||
|
||||
if room:
|
||||
room = frappe.get_doc('Chat Room', room.name)
|
||||
create = False
|
||||
|
||||
if create:
|
||||
room = frappe.new_doc('Chat Room')
|
||||
room.type = kind
|
||||
room.owner = frappe.session.user
|
||||
room.room_name = name
|
||||
|
||||
dusers = []
|
||||
|
||||
if kind != 'Visitor':
|
||||
if users:
|
||||
users = listify(users)
|
||||
for user in users:
|
||||
duser = frappe.new_doc('Chat Room User')
|
||||
duser.user = user
|
||||
dusers.append(duser)
|
||||
|
||||
room.users = dusers
|
||||
else:
|
||||
dsettings = frappe.get_single('Website Settings')
|
||||
room.room_name = dsettings.chat_room_name
|
||||
|
||||
users = [user for user in room.users] if hasattr(room, 'users') else []
|
||||
|
||||
for user in dsettings.chat_operators:
|
||||
if user.user not in users:
|
||||
# appending user to room.users will remove the user from chat_operators
|
||||
# this is undesirable, create a new Chat Room User instead
|
||||
chat_room_user = {"doctype": "Chat Room User", "user": user.user}
|
||||
room.append('users', chat_room_user)
|
||||
|
||||
room.save(ignore_permissions=True)
|
||||
|
||||
room = get(token=token, rooms=room.name)
|
||||
if room:
|
||||
users = [room.owner] + [u for u in room.users]
|
||||
|
||||
for user in users:
|
||||
frappe.publish_realtime('frappe.chat.room:create', room, user=user, after_commit=True)
|
||||
|
||||
return room
|
||||
|
||||
|
||||
@frappe.whitelist(allow_guest=True)
|
||||
def history(room, user, fields=None, limit=10, start=None, end=None):
|
||||
if frappe.get_doc('Chat Room', room).type != 'Visitor':
|
||||
authenticate(user)
|
||||
|
||||
fields = safe_json_loads(fields)
|
||||
|
||||
mess = chat_message.history(room, limit=limit, start=start, end=end)
|
||||
mess = squashify(mess)
|
||||
|
||||
return dictify(mess)
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
frappe.listview_settings['Chat Room'] = {
|
||||
filters: [
|
||||
['Chat Room', 'owner', '=', frappe.session.user, true],
|
||||
['Chat Room User', 'user', '=', frappe.session.user, true]
|
||||
]
|
||||
};
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
{
|
||||
"beta": 1,
|
||||
"creation": "2017-11-08 15:24:21.029314",
|
||||
"doctype": "DocType",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"user",
|
||||
"is_admin"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"fieldname": "user",
|
||||
"fieldtype": "Link",
|
||||
"in_list_view": 1,
|
||||
"label": "User",
|
||||
"options": "User",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "is_admin",
|
||||
"fieldtype": "Check",
|
||||
"label": "Admin"
|
||||
}
|
||||
],
|
||||
"in_create": 1,
|
||||
"istable": 1,
|
||||
"modified": "2019-11-07 13:21:05.297337",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Chat",
|
||||
"name": "Chat Room User",
|
||||
"owner": "Administrator",
|
||||
"permissions": [],
|
||||
"quick_entry": 1,
|
||||
"read_only": 1,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1
|
||||
}
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
# imports - module imports
|
||||
from frappe.model.document import Document
|
||||
import frappe
|
||||
|
||||
session = frappe.session
|
||||
|
||||
class ChatRoomUser(Document):
|
||||
pass
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
// Copyright (c) 2018, Frappe Technologies and contributors
|
||||
// For license information, please see license.txt
|
||||
|
||||
frappe.ui.form.on('Chat Token', {
|
||||
refresh: function(frm) {
|
||||
|
||||
}
|
||||
});
|
||||
|
|
@ -1,57 +0,0 @@
|
|||
{
|
||||
"autoname": "field:token",
|
||||
"beta": 1,
|
||||
"creation": "2018-03-26 18:20:13.825652",
|
||||
"doctype": "DocType",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"token",
|
||||
"ip_address",
|
||||
"country"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"fieldname": "token",
|
||||
"fieldtype": "Data",
|
||||
"in_list_view": 1,
|
||||
"label": "Token",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "ip_address",
|
||||
"fieldtype": "Data",
|
||||
"label": "IP Address"
|
||||
},
|
||||
{
|
||||
"fieldname": "country",
|
||||
"fieldtype": "Data",
|
||||
"label": "Country"
|
||||
}
|
||||
],
|
||||
"in_create": 1,
|
||||
"modified": "2019-11-07 13:21:24.514558",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Chat",
|
||||
"name": "Chat Token",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "System Manager",
|
||||
"share": 1,
|
||||
"write": 1
|
||||
}
|
||||
],
|
||||
"quick_entry": 1,
|
||||
"read_only": 1,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1
|
||||
}
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2018, Frappe Technologies and contributors
|
||||
# License: MIT. See LICENSE
|
||||
|
||||
import frappe
|
||||
from frappe.model.document import Document
|
||||
|
||||
class ChatToken(Document):
|
||||
pass
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
# imports - module imports
|
||||
from frappe.chat.util.util import (
|
||||
get_user_doc,
|
||||
squashify,
|
||||
safe_json_loads,
|
||||
filter_dict,
|
||||
get_if_empty,
|
||||
listify,
|
||||
dictify,
|
||||
check_url,
|
||||
create_test_user,
|
||||
get_emojis
|
||||
)
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
# imports - standard imports
|
||||
import unittest
|
||||
|
||||
# imports - module imports
|
||||
from frappe.chat.util import (
|
||||
get_user_doc,
|
||||
safe_json_loads
|
||||
)
|
||||
import frappe
|
||||
|
||||
class TestChatUtil(unittest.TestCase):
|
||||
def test_safe_json_loads(self):
|
||||
number = safe_json_loads("1")
|
||||
self.assertEqual(type(number), int)
|
||||
|
||||
number = safe_json_loads("1.0")
|
||||
self.assertEqual(type(number), float)
|
||||
|
||||
string = safe_json_loads("foobar")
|
||||
self.assertEqual(type(string), str)
|
||||
|
||||
array = safe_json_loads('[{ "foo": "bar" }]')
|
||||
self.assertEqual(type(array), list)
|
||||
|
||||
objekt = safe_json_loads('{ "foo": "bar" }')
|
||||
self.assertEqual(type(objekt), dict)
|
||||
|
||||
true, null = safe_json_loads("true", "null")
|
||||
self.assertEqual(true, True)
|
||||
self.assertEqual(null, None)
|
||||
|
||||
def test_get_user_doc(self):
|
||||
# Needs more test cases.
|
||||
user = get_user_doc()
|
||||
self.assertEqual(user.name, frappe.session.user)
|
||||
|
|
@ -1,108 +0,0 @@
|
|||
# imports - standard imports
|
||||
import json
|
||||
from collections.abc import MutableMapping, MutableSequence, Sequence
|
||||
|
||||
# imports - third-party imports
|
||||
import requests
|
||||
from urllib.parse import urlparse
|
||||
|
||||
# imports - module imports
|
||||
import frappe
|
||||
from frappe.exceptions import DuplicateEntryError
|
||||
from frappe.model.document import Document
|
||||
|
||||
session = frappe.session
|
||||
|
||||
|
||||
def get_user_doc(user = None):
|
||||
if isinstance(user, Document):
|
||||
return user
|
||||
|
||||
user = user or session.user
|
||||
user = frappe.get_doc('User', user)
|
||||
|
||||
return user
|
||||
|
||||
def squashify(what):
|
||||
if isinstance(what, Sequence) and len(what) == 1:
|
||||
return what[0]
|
||||
|
||||
return what
|
||||
|
||||
def safe_json_loads(*args):
|
||||
results = []
|
||||
|
||||
for arg in args:
|
||||
try:
|
||||
arg = json.loads(arg)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
results.append(arg)
|
||||
|
||||
return squashify(results)
|
||||
|
||||
def filter_dict(what, keys, ignore = False):
|
||||
copy = dict()
|
||||
|
||||
if keys:
|
||||
for k in keys:
|
||||
if k not in what and not ignore:
|
||||
raise KeyError('{key} not in dict.'.format(key = k))
|
||||
else:
|
||||
copy.update({
|
||||
k: what[k]
|
||||
})
|
||||
else:
|
||||
copy = what.copy()
|
||||
|
||||
return copy
|
||||
|
||||
def get_if_empty(a, b):
|
||||
if not a:
|
||||
a = b
|
||||
return a
|
||||
|
||||
def listify(arg):
|
||||
if not isinstance(arg, list):
|
||||
arg = [arg]
|
||||
return arg
|
||||
|
||||
def dictify(arg):
|
||||
if isinstance(arg, MutableSequence):
|
||||
for i, a in enumerate(arg):
|
||||
arg[i] = dictify(a)
|
||||
elif isinstance(arg, MutableMapping):
|
||||
arg = frappe._dict(arg)
|
||||
|
||||
return arg
|
||||
|
||||
def check_url(what, raise_err = False):
|
||||
if not urlparse(what).scheme:
|
||||
if raise_err:
|
||||
raise ValueError('{what} not a valid URL.')
|
||||
else:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def create_test_user(module):
|
||||
try:
|
||||
test_user = frappe.new_doc('User')
|
||||
test_user.first_name = '{module}'.format(module = module)
|
||||
test_user.email = 'testuser.{module}@example.com'.format(module = module)
|
||||
test_user.save()
|
||||
except DuplicateEntryError:
|
||||
frappe.log('Test User Chat Profile exists.')
|
||||
|
||||
def get_emojis():
|
||||
redis = frappe.cache()
|
||||
emojis = redis.hget('frappe_emojis', 'emojis')
|
||||
|
||||
if not emojis:
|
||||
resp = requests.get('http://git.io/frappe-emoji')
|
||||
if resp.ok:
|
||||
emojis = resp.json()
|
||||
redis.hset('frappe_emojis', 'emojis', emojis)
|
||||
|
||||
return dictify(emojis)
|
||||
|
|
@ -1,42 +0,0 @@
|
|||
|
||||
import frappe
|
||||
from frappe.chat.util import filter_dict, safe_json_loads
|
||||
|
||||
from frappe.sessions import get_geo_ip_country
|
||||
|
||||
@frappe.whitelist(allow_guest = True)
|
||||
def settings(fields = None):
|
||||
fields = safe_json_loads(fields)
|
||||
|
||||
dsettings = frappe.get_single('Website Settings')
|
||||
response = dict(
|
||||
socketio = dict(
|
||||
port = frappe.conf.socketio_port
|
||||
),
|
||||
enable = bool(dsettings.chat_enable),
|
||||
enable_from = dsettings.chat_enable_from,
|
||||
enable_to = dsettings.chat_enable_to,
|
||||
room_name = dsettings.chat_room_name,
|
||||
welcome_message = dsettings.chat_welcome_message,
|
||||
operators = [
|
||||
duser.user for duser in dsettings.chat_operators
|
||||
]
|
||||
)
|
||||
|
||||
if fields:
|
||||
response = filter_dict(response, fields)
|
||||
|
||||
return response
|
||||
|
||||
@frappe.whitelist(allow_guest = True)
|
||||
def token():
|
||||
dtoken = frappe.new_doc('Chat Token')
|
||||
|
||||
dtoken.token = frappe.generate_hash()
|
||||
dtoken.ip_address = frappe.local.request_ip
|
||||
country = get_geo_ip_country(dtoken.ip_address)
|
||||
if country:
|
||||
dtoken.country = country['iso_code']
|
||||
dtoken.save(ignore_permissions = True)
|
||||
|
||||
return dtoken.token
|
||||
|
|
@ -1 +0,0 @@
|
|||
import "./frappe/chat";
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,461 +0,0 @@
|
|||
// Author - Achilles Rasquinha <achilles@frappe.io>
|
||||
// http://codeguide.co - @mdo (Author of Bootstrap)
|
||||
|
||||
@import "../css/font-awesome.css";
|
||||
@import "../css/octicons/octicons.css";
|
||||
|
||||
// Typography
|
||||
@font-weight-bold: 700;
|
||||
@font-weight-heavy: 900;
|
||||
|
||||
@chat-toggle-height: 40px;
|
||||
|
||||
@fab-box-shadow: 0 5px 15px rgba(0, 0, 0, .25);
|
||||
@fab-size: 48px;
|
||||
@fab-size-lg: 56px;
|
||||
@fab-margin: 20px;
|
||||
|
||||
@chat-popper-margin: @fab-margin;
|
||||
@chat-popper-panel-width: 350px;
|
||||
@chat-popper-panel-height: 500px;
|
||||
// z-index greater than FAB, lesser than modal.
|
||||
@chat-popper-z-index: 1035;
|
||||
|
||||
// BS modal's box-shadow
|
||||
@chat-popper-panel-box-shadow: @fab-box-shadow;
|
||||
|
||||
// https://github.com/twbs/bootstrap/blob/v3.3.7/less/variables.less#L278
|
||||
// Keep z-index of the ChatPopper higher than others, lower than modal background.
|
||||
|
||||
@chat-room-list-content-max-width: 180px;
|
||||
|
||||
@chat-form-font-size: 12px;
|
||||
@chat-form-menu-border-radius: 4px;
|
||||
@chat-form-list-group-height: 150px; // Hints
|
||||
|
||||
// Typography
|
||||
.font-bold { font-weight: @font-weight-bold; }
|
||||
.font-heavy { font-weight: @font-weight-heavy; }
|
||||
|
||||
// Utilities
|
||||
.cursor-pointer { cursor: pointer; }
|
||||
|
||||
// Hacks and Fixes
|
||||
// suggested by rushabh@frappe.io. Thanks, Rushabh!
|
||||
// .avatar { padding: 2px; }
|
||||
|
||||
.frappe-fab
|
||||
{
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
border-radius: 50%;
|
||||
box-shadow: @fab-box-shadow;
|
||||
margin: @fab-margin;
|
||||
width: @fab-size;
|
||||
height: @fab-size;
|
||||
|
||||
&.frappe-fab-lg
|
||||
{
|
||||
width: @fab-size-lg;
|
||||
height: @fab-size-lg;
|
||||
}
|
||||
}
|
||||
|
||||
.navbar
|
||||
{
|
||||
.frappe-chat-toggle
|
||||
{
|
||||
height: @chat-toggle-height;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.octicon { margin-top: 3px; } // Hack, somewhat.
|
||||
}
|
||||
|
||||
.frappe-chat
|
||||
{
|
||||
& > .frappe-chat-popper
|
||||
{
|
||||
position: fixed;
|
||||
bottom: 0px;
|
||||
right: 0px;
|
||||
margin: @chat-popper-margin;
|
||||
z-index: @chat-popper-z-index;
|
||||
|
||||
& > .frappe-chat-popper-collapse
|
||||
{
|
||||
& > .panel
|
||||
{
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: @chat-popper-panel-width;
|
||||
height: @chat-popper-panel-height;
|
||||
box-shadow: @chat-popper-panel-box-shadow;
|
||||
|
||||
.vcenter
|
||||
{
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
|
||||
.panel-heading
|
||||
{
|
||||
.panel-title
|
||||
{
|
||||
.media-heading
|
||||
{
|
||||
font-size: 12px;
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
.media-left {
|
||||
padding-right: 10px;
|
||||
}
|
||||
|
||||
.media-subtitle
|
||||
{
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.frappe-chat-action-bar
|
||||
{
|
||||
form
|
||||
{
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.btn-action
|
||||
{
|
||||
margin-left: 5px !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.frappe-chat-room-list
|
||||
{
|
||||
height: 100%;
|
||||
overflow-y: auto;
|
||||
padding: 0 1px 0 1px;
|
||||
|
||||
& > li > a
|
||||
{
|
||||
border-radius: 0px !important;
|
||||
}
|
||||
|
||||
.media
|
||||
{
|
||||
.media-heading, .media-subtitle
|
||||
{
|
||||
max-width: @chat-room-list-content-max-width;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
& > .panel.panel-bg
|
||||
{
|
||||
background-size: 350px 500px;
|
||||
background-image: url(/assets/frappe/images/chat/wallpaper-default.jpg);
|
||||
}
|
||||
|
||||
& > .panel.panel-span
|
||||
{
|
||||
position: fixed;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
bottom: 0px;
|
||||
right: 0px;
|
||||
overflow: auto;
|
||||
border-radius: 0px;
|
||||
|
||||
.panel-heading
|
||||
{
|
||||
border-radius: 0px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.panel
|
||||
{
|
||||
margin-bottom: 0px !important;
|
||||
|
||||
.chat-form
|
||||
{
|
||||
.form-control
|
||||
{
|
||||
font-size: @chat-form-font-size;
|
||||
}
|
||||
|
||||
.dropdown-menu
|
||||
{
|
||||
border-radius: @chat-form-menu-border-radius;
|
||||
}
|
||||
|
||||
// Hints
|
||||
.hint-list.list-group
|
||||
{
|
||||
margin: 0px;
|
||||
max-height: @chat-form-list-group-height;
|
||||
overflow-y: auto;
|
||||
|
||||
.hint-list-item.list-group-item:first-child, .hint-list-item.list-group-item:last-child
|
||||
{
|
||||
border-radius: 0px !important;
|
||||
|
||||
a { text-decoration: none }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@chat-color-grey: #8D99A6;
|
||||
|
||||
@chat-base-font-size: 12px;
|
||||
@chat-base-font-size-lg: 14px;
|
||||
|
||||
@chat-base-spacing: 5px;
|
||||
|
||||
// ChatForm
|
||||
@chat-form-border: 1px solid #D1D8DD;
|
||||
|
||||
// ChatList
|
||||
@chat-list-bg-color: #FAFBFC;
|
||||
|
||||
// ChatList.Item
|
||||
@chat-list-item-padding: @chat-base-spacing @chat-base-spacing * 2;
|
||||
|
||||
// ChatBubble
|
||||
@chat-bubble-padding: @chat-base-spacing @chat-base-spacing * 2;
|
||||
@chat-bubble-min-width: 25%;
|
||||
@chat-bubble-max-width: 75%;
|
||||
|
||||
@chat-bubble-box-shadow: 0px 0.1px 0.5px 0px rgba(0,0,0,0.5);
|
||||
|
||||
@chat-bubble-border-size: 1px;
|
||||
@chat-bubble-border-radius: @chat-base-spacing;
|
||||
|
||||
@chat-bubble-l-color: #EBEFF2;
|
||||
@chat-bubble-r-color: #EBF7CF;
|
||||
|
||||
@chat-bubble-l-groupable-margin-left: 40px;
|
||||
|
||||
@chat-bubble-author-font-size: @chat-base-font-size;
|
||||
|
||||
@chat-bubble-content-margin-bottom: @chat-base-spacing;
|
||||
|
||||
@chat-bubble-meta-font-size: @chat-base-spacing * 2;
|
||||
|
||||
@chat-bubble-check-font-size: @chat-base-font-size;
|
||||
|
||||
.frappe-chat-popper-collapse
|
||||
{
|
||||
& > .panel
|
||||
{
|
||||
& > .panel-heading
|
||||
{
|
||||
padding: @chat-base-spacing @chat-base-spacing * 2;
|
||||
|
||||
.btn-back
|
||||
{
|
||||
margin-right: @chat-base-spacing;
|
||||
}
|
||||
|
||||
.avatar
|
||||
{
|
||||
width: 32px; height: 32px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
.chat-room-footer
|
||||
{
|
||||
.chat-form
|
||||
{
|
||||
border-top: @chat-form-border;
|
||||
|
||||
.input-group-btn
|
||||
{
|
||||
.btn
|
||||
{
|
||||
background: white;
|
||||
border-radius: 0px;
|
||||
}
|
||||
}
|
||||
|
||||
.form-control
|
||||
{
|
||||
line-height: 27px; // HACK: Makes input and placeholder centered within textarea. Also takes care of the input-btn
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
resize: none;
|
||||
padding-left: 0px;
|
||||
padding-right: 0px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.fa
|
||||
{
|
||||
font-size: @chat-base-font-size-lg;
|
||||
transition: color 0.5s; // Change, with grace. :)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.chat-list
|
||||
{
|
||||
height: 100%;
|
||||
// background: @chat-list-bg-color;
|
||||
overflow-y: scroll;
|
||||
|
||||
.chat-list-item
|
||||
{
|
||||
.avatar
|
||||
{
|
||||
vertical-align: top;
|
||||
|
||||
.standard-image
|
||||
{
|
||||
background-color: white;
|
||||
}
|
||||
}
|
||||
|
||||
.cursor-pointer;
|
||||
|
||||
border: none !important;
|
||||
padding: @chat-list-item-padding;
|
||||
background: transparent;
|
||||
|
||||
.chat-bubble
|
||||
{
|
||||
max-width: @chat-bubble-max-width;
|
||||
display: inline-block;
|
||||
padding: @chat-bubble-padding;
|
||||
border-radius: @chat-bubble-border-radius;
|
||||
|
||||
-webkit-box-shadow: @chat-bubble-box-shadow;
|
||||
-moz-box-shadow: @chat-bubble-box-shadow;
|
||||
box-shadow: @chat-bubble-box-shadow;
|
||||
|
||||
@media (max-width : 768px) {
|
||||
min-width: @chat-bubble-min-width;
|
||||
}
|
||||
|
||||
&.chat-bubble-l
|
||||
{
|
||||
&.chat-groupable
|
||||
{
|
||||
margin-left: @chat-bubble-l-groupable-margin-left;
|
||||
}
|
||||
|
||||
// background-color: @chat-bubble-l-color;
|
||||
background-color: white;
|
||||
|
||||
.chat-bubble-meta
|
||||
{
|
||||
& > .chat-bubble-creation, & > .chat-bubble-check i
|
||||
{
|
||||
color: darken(@chat-bubble-l-color, 50%) !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.chat-bubble-r
|
||||
{
|
||||
text-align: right;
|
||||
background-color: @chat-bubble-r-color;
|
||||
|
||||
.chat-bubble-meta
|
||||
{
|
||||
& > .chat-bubble-creation, & > .chat-bubble-check i
|
||||
{
|
||||
color: darken(@chat-bubble-r-color, 50%) !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.chat-bubble-author
|
||||
{
|
||||
font-size: @chat-bubble-author-font-size;
|
||||
|
||||
a
|
||||
{
|
||||
.font-bold;
|
||||
|
||||
text-decoration: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
.chat-bubble-content
|
||||
{
|
||||
margin-bottom: @chat-bubble-content-margin-bottom;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
.chat-bubble-meta
|
||||
{
|
||||
font-size: @chat-bubble-meta-font-size;
|
||||
|
||||
& > .chat-bubble-check
|
||||
{
|
||||
margin-left: @chat-base-spacing;
|
||||
|
||||
i
|
||||
{
|
||||
font-size: @chat-bubble-check-font-size;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.chat-list-notification
|
||||
{
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.chat-list-notification-content
|
||||
{
|
||||
color: white;
|
||||
background-color: #8D99A6;
|
||||
display: inline-block;
|
||||
/* padding: 5px; */
|
||||
border-radius: 20px;
|
||||
opacity: 0.5;
|
||||
font-size: 10px;
|
||||
padding: 5px;
|
||||
// background-color: white;
|
||||
}
|
||||
|
||||
// v12 fixes for visitor chat
|
||||
.panel-default > .panel-heading {
|
||||
background-color: #f7fafc;
|
||||
border-color: #ced5db;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
textarea.form-control {
|
||||
resize: none;
|
||||
height: 3.2em;
|
||||
}
|
||||
|
||||
@media (max-width: 767px) {
|
||||
.nav-stacked > li + li {
|
||||
margin-top: 0px;
|
||||
margin-left: 2px;
|
||||
}
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Loading…
Add table
Reference in a new issue