Cleanout communication server.

I don't think anyone's actively using this anymore, and most
communication has moved to (also embeddable) things like Gitter, etc.
This commit is contained in:
Dannon Baker
2020-10-22 09:58:34 -04:00
parent 449da442b7
commit 434d406bde
28 changed files with 17 additions and 1655 deletions
@@ -33,15 +33,6 @@ export const getUserPreferencesModel = () => {
submit_icon: "fa-trash",
shouldRender: config.enable_oidc,
},
communication: {
title: _l("Change Communication Settings"),
id: "edit-preferences-communication",
description: _l("Enable or disable the communication feature to chat with other users."),
url: `api/users/${Galaxy.user.id}/communication/inputs`,
icon: "fa-comments-o",
redirect: "user",
shouldRender: !!config.enable_communication_server,
},
permissions: {
title: _l("Set Dataset Permissions for New Histories"),
id: "edit-preferences-permissions",
@@ -1,99 +0,0 @@
/** Real-time Communication feature **/
import $ from "jquery";
import Backbone from "backbone";
import _ from "underscore";
import _l from "utils/localization";
import Modal from "mvc/ui/ui-modal";
import { getGalaxyInstance } from "app";
export const CommunicationServerView = Backbone.View.extend({
initialize: function () {
this.modal = null;
},
/** makes bootstrap modal and iframe inside it */
makeModalIframe: function (e) {
const Galaxy = getGalaxyInstance();
// make modal
const host = Galaxy.config.communication_server_host;
const port = Galaxy.config.communication_server_port;
const username = _.escape(Galaxy.user.attributes.username);
const persistent_communication_rooms = _.escape(Galaxy.config.persistent_communication_rooms);
const query_string = `?username=${username}&persistent_communication_rooms=${persistent_communication_rooms}`;
const src = `${host}:${port}${query_string}`;
let $el_chat_modal_header = null;
let $el_chat_modal_body = null;
const iframe_template = `<iframe class="h-100 w-100" src="${src}"> </iframe>`;
const header_template =
'<i class="fa fa-comment" aria-hidden="true" title="Communicate with other users"></i>' +
'<i class="fa fa-expand expand-compress-modal" aria-hidden="true" title="Maximize"></i>' +
'<i class="fa fa-times close-modal" aria-hidden="true" title="Close"></i>';
const frame_height = 350;
const frame_width = 600;
const class_names = "ui-modal chat-modal";
// deletes the chat modal if already present and create one
if ($(".chat-modal").length > 0) {
$(".chat-modal").remove();
}
// creates a modal
CommunicationServerView.modal = new Modal.View({
body: iframe_template,
height: frame_height,
width: frame_width,
closing_events: true,
title_separator: false,
cls: class_names,
});
// shows modal
CommunicationServerView.modal.show();
$el_chat_modal_header = $(".chat-modal .modal-header");
$el_chat_modal_body = $(".chat-modal .modal-body");
// adjusts the css of bootstrap modal for chat
$el_chat_modal_header.addClass("modal-header-body");
$el_chat_modal_body.addClass("modal-header-body");
$el_chat_modal_header.find("h4").remove();
$el_chat_modal_header.removeAttr("min-height padding border");
$el_chat_modal_header.append(header_template);
// click event of the close button for chat
$(".close-modal").click((e) => {
$(".chat-modal").css("display", "none");
});
// click event of expand and compress icon
$(".expand-compress-modal").click((e) => {
if ($(".expand-compress-modal").hasClass("fa-expand")) {
$(".chat-modal .modal-dialog").width("1000px");
$(".chat-modal .modal-body").height("575px");
$(".expand-compress-modal").removeClass("fa-expand").addClass("fa-compress");
$(".expand-compress-modal").attr("title", "Minimize");
$(".expand-compress-modal").css("margin-left", "96.2%");
} else {
$(".chat-modal .modal-dialog").width(`${frame_width}px`);
$(".chat-modal .modal-body").height(`${frame_height}px`);
$(".expand-compress-modal").removeClass("fa-compress").addClass("fa-expand");
$(".expand-compress-modal").attr("title", "Maximize");
$(".expand-compress-modal").css("margin-left", "93.2%");
}
});
return this;
},
/**renders the chat icon as a nav item*/
render: function () {
return {
id: "show-chat-online",
icon: "fa-comment-o",
tooltip: _l("Chat online"),
visible: false,
onclick: this.makeModalIframe,
};
},
});
-7
View File
@@ -1,7 +1,6 @@
import axios from "axios";
import { getGalaxyInstance } from "app";
import _l from "utils/localization";
import { CommunicationServerView } from "layout/communication-server-view";
const POST_LOGOUT_URL = "root/login?is_logout_redirect=true";
@@ -54,12 +53,6 @@ export function fetchMenu(options = {}) {
const Galaxy = getGalaxyInstance();
const menu = [];
//
// Chat server tab
//
const extendedNavItem = new CommunicationServerView();
menu.push(extendedNavItem.render());
//
// Analyze data tab.
//
-31
View File
@@ -105,7 +105,6 @@ const View = Backbone.View.extend({
this.renderInactivityBox();
}
this.renderPanels();
this._checkCommunicationServerOnline();
return this;
},
@@ -181,36 +180,6 @@ const View = Backbone.View.extend({
toString: function () {
return "PageLayoutView";
},
/** Check if the communication server is online and show the icon otherwise hide the icon */
_checkCommunicationServerOnline: function () {
const Galaxy = getGalaxyInstance();
const host = Galaxy.config.communication_server_host;
const port = Galaxy.config.communication_server_port;
const preferences = Galaxy.user.attributes.preferences;
const $chat_icon_element = $("#show-chat-online");
/** Check if the user has deactivated the communication in it's personal settings */
if (preferences && ["1", "true"].indexOf(preferences.communication_server) != -1) {
// See if the configured communication server is available
$.ajax({
url: `${host}:${port}`,
})
.success((data) => {
// enable communication only when a user is logged in
if (Galaxy.user.id !== null) {
if ($chat_icon_element.css("visibility") === "hidden") {
$chat_icon_element.css("visibility", "visible");
}
}
})
.error((data) => {
// hide the communication icon if the communication server is not available
$chat_icon_element.css("visibility", "hidden");
});
} else {
$chat_icon_element.css("visibility", "hidden");
}
},
});
export default { View: View };
-2
View File
@@ -291,7 +291,6 @@ define({
"Associate OpenIDs with your account.": false,
"Customize your Toolbox by displaying or omitting sets of Tools.": false,
"Access your current API key or create a new one.": false,
"Enable or disable the communication feature to chat with other users.": false,
"Allows you to change your login credentials.": false,
"User Preferences": false,
"Sign out": false,
@@ -300,7 +299,6 @@ define({
"Manage Toolbox filters": false,
"Manage API key": false,
"Set dataset permissions for new histories": false,
"Change communication settings": false,
"Change password": false,
"Manage information": false,
// ---------------------------------------------------------------------------- history-list
-20
View File
@@ -1465,26 +1465,6 @@ div.toolSectionTitle {
display: none;
}
// communication channel bootstrap modal
.chat-modal {
overflow: hidden;
}
.modal-header-body {
padding: 2px;
}
.close-modal {
float: right;
cursor: pointer;
}
.expand-compress-modal {
cursor: pointer;
font-size: 12px;
margin-left: 93.2%;
}
// password complexity monitor
#change_password,
#registrationForm {
+13 -41
View File
@@ -2636,6 +2636,19 @@
:Type: float
~~~~~~~~~~~~~~~~~
``tool_id_boost``
~~~~~~~~~~~~~~~~~
:Description:
Boosts are used to customize this instance's toolbox search. The
higher the boost, the more importance the scoring algorithm gives
to the given field. Section refers to the tool group in the tool
panel. Rest of the fields are tool's attributes.
:Default: ``9.0``
:Type: float
~~~~~~~~~~~~~~~~~~~~~~
``tool_section_boost``
~~~~~~~~~~~~~~~~~~~~~~
@@ -4141,47 +4154,6 @@
:Type: str
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
``enable_communication_server``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
:Description:
Galaxy real time communication server settings
:Default: ``false``
:Type: bool
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
``communication_server_host``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
:Description:
Galaxy real time communication server settings
:Default: ``http://localhost``
:Type: str
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
``communication_server_port``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
:Description:
Galaxy real time communication server settings
:Default: ``7070``
:Type: int
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
``persistent_communication_rooms``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
:Description:
persistent_communication_rooms is a comma-separated list of rooms
that should be always available.
:Default: ``None``
:Type: str
~~~~~~~~~~~~~~
``use_pbkdf2``
~~~~~~~~~~~~~~
Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

-137
View File
@@ -1,137 +0,0 @@
Galaxy Communication Server
===========================
A problem faced by administrators, especially those of smaller galaxy sites, is how to connect users with documentation on workflows and analysis procedures, and then helping them through running these steps. For sites where the admins and the tool developers are the same groups, finding time for developing the required training resources and providing the level of hands-on guidance required can be an impossible challenge in time management.
In order to solve this, a simple chat server has been added to Galaxy to provide realtime communication between users. The hope is that this will decrease the burden on administrators:
- *Troubleshooting*: When users have trouble with a tool, they can ask in chat if
anyone can help, rather than coming to administrators and tool developers
first.
- *Sharing*: Your galaxy users can easily talk to coworkers, maybe learn about
new analyses they've each developed
- *Dedicated Discussion*: The chat implementation provides the ability to
designate some fixed chat rooms which will always be visible to users, these
can be used to help guide user discussion to topics like NGS.
.. image:: communication_server.png
Pre-requisites
--------------
This service will need to be deployed on a host that has:
- Access to the Galaxy database
- A port exposed
- A non-default ``id_secret`` set in your ``galaxy.yml``
We say "access to galaxy database", this service could run on an NFS mounted
copy of Galaxy, if the host it is deployed on can access the database of the
main galaxy instance. This is required, because the chat service validates
connecting users to prevent impersonation.
Deploying the Server
--------------------
The chat server is a very new feature in Galaxy and has been disabled by
default at a number of levels, as not all users may wish to use this feature.
Configuration is managed in ``galaxy.yml``:
.. code-block:: yaml
# Galaxy real time communication server settings
enable_communication_server: True
communication_server_host: http://localhost
communication_server_port: 7070
# persistent_communication_rooms is a comma-separated list of rooms that should be always available.
persistent_communication_rooms: NGS,RNA-Seq,Genome Annotation
As you can see in this configuration, it is pointing at a service running on
the same host as Galaxy, talking to port 7070.
Once you have configured this portion, you will want to install the required
dependencies, and launch the chat server. This is run as a separate flask based
service.
Installing the Dependencies
---------------------------
Most admins will find it convenient to activate the Galaxy virtualenv, and run
the following command:
.. code-block:: console
(.venv)$ pip install flask flask-login flask-socketio eventlet
You can then launch the server:
.. code-block:: console
(.venv)$ python ./scripts/communication/communication_server.py --port 7070 --host localhost
Next we'll configure user-level access.
Enabling the Chat Interace
--------------------------
Users who wish to enable the chat can do so in their user preferences.
.. image:: chat-0.png
There is a new element at the top of the list, which controls chat preferences
.. image:: chat-1.png
And a short dialog to enable it
.. image:: chat-2.png
After which, a new icon becomes visible next to "Analyse Data" on your Galaxy
header.
.. image:: chat-3.png
Opening the chat interface, lastly we notice that chat defaults to an offline
state. Clicking the red chat bubble icon will turn it green and allow the user
to engage with others.
.. image:: chat-4.png
Finally users can connect with one another within Galaxy.
.. image:: communication_server_rooms.png
Security Considerations
-----------------------
This service does:
- Ensure authenticated access by decoding their Galaxy ``galaxysession``
cookie.
- Prevent user spoofing
This service does not:
- enforce rate-limiting of messages
- run any sort of text/obscenity filtering rules
- prevent user spoofing if the user is an administrator and impersonating
another user
Keeping the Chat Server Running
-------------------------------
Here is an example supervisord configuration for keeping the chat server running:
.. code-block:: console
[program:galaxy_chat_server]
directory = GALAXY_ROOT
command = python ./scripts/communication/communication_server.py --port 7070 --host localhost
autostart = true
autorestart = unexpected
user = GALAXY_USER
startsecs = 2
redirect_stderr = true
Configuration with virtualenvs will be slightly different.
-1
View File
@@ -620,7 +620,6 @@ class GalaxyAppConfiguration(BaseAppConfiguration, CommonConfigurationMixin):
self.email_domain_blocklist_content = self._load_list_from_file(self._in_config_dir(self.email_domain_blocklist_file)) if self.email_domain_blocklist_file else None
self.email_domain_allowlist_content = self._load_list_from_file(self._in_config_dir(self.email_domain_allowlist_file)) if self.email_domain_allowlist_file else None
self.persistent_communication_rooms = listify(self.persistent_communication_rooms, do_strip=True)
# The transfer manager and deferred job queue
self.enable_beta_job_managers = string_as_bool(kwargs.get('enable_beta_job_managers', 'False'))
# These are not even beta - just experiments - don't use them unless
+4
View File
@@ -312,6 +312,10 @@ OPTION_ACTIONS = {
'tool_submission_burst_at': _DeprecatedAndDroppedAction(),
'toolform_upgrade': _DeprecatedAndDroppedAction(),
'enable_beta_mulled_containers': _DeprecatedAndDroppedAction(),
'enable_communication_server': _DeprecatedAndDroppedAction(),
'communication_server_host': _DeprecatedAndDroppedAction(),
'communication_server_port': _DeprecatedAndDroppedAction(),
'persistent_communication_rooms': _DeprecatedAndDroppedAction(),
}
@@ -2019,19 +2019,6 @@ galaxy:
# commented out line below).
#amqp_internal_connection: sqlalchemy+sqlite:///./database/control.sqlite?isolation_level=IMMEDIATE
# Galaxy real time communication server settings
#enable_communication_server: false
# Galaxy real time communication server settings
#communication_server_host: http://localhost
# Galaxy real time communication server settings
#communication_server_port: 7070
# persistent_communication_rooms is a comma-separated list of rooms
# that should be always available.
#persistent_communication_rooms: null
# Allow disabling pbkdf2 hashing of passwords for legacy situations.
# This should normally be left enabled unless there is a specific
# reason to disable it.
-4
View File
@@ -58,10 +58,6 @@ class ConfigSerializer(base.ModelSerializer):
'lims_doc_url' : _defaults_to("https://usegalaxy.org/u/rkchak/p/sts"),
'default_locale' : _use_config,
'enable_openid' : _use_config,
'enable_communication_server' : _use_config,
'communication_server_port' : _use_config,
'communication_server_host' : _use_config,
'persistent_communication_rooms' : _use_config,
'enable_tool_recommendations' : _use_config,
'tool_recommendation_model_path' : _use_config,
'admin_tool_recommendations_path' : _use_config,
@@ -156,7 +156,6 @@ class RemoteUser:
'/user/logout',
'/user/toolbox_filters',
'/user/set_default_permissions',
'/user/change_communication',
)
admin_accessible_paths = (
-27
View File
@@ -752,33 +752,6 @@ class UserAPIController(BaseAPIController, UsesTagsMixin, CreatesApiKeysMixin, B
'help': ' An API key will allow you to access via web API. Please note that this key acts as an alternate means to access your account and should be treated with the same care as your login password.'}]
return {'message': message, 'inputs': inputs}
@expose_api
def get_communication(self, trans, id, payload={}, **kwd):
"""
Build communication server inputs.
"""
user = self._get_user(trans, id)
return {'inputs': [{'name': 'enable',
'type': 'boolean',
'label': 'Enable communication',
'value': user.preferences.get('communication_server', 'false')}]}
@expose_api
def set_communication(self, trans, id, payload={}, **kwd):
"""
Allows the user to activate/deactivate the communication server.
"""
user = self._get_user(trans, id)
enable = payload.get('enable', 'false')
if enable == 'true':
message = 'Your communication server has been activated.'
else:
message = 'Your communication server has been disabled.'
user.preferences['communication_server'] = enable
trans.sa_session.add(user)
trans.sa_session.flush()
return {'message': message}
@expose_api
def get_custom_builds(self, trans, id, payload={}, **kwd):
"""
@@ -3085,33 +3085,6 @@ mapping:
will automatically create and use a separate sqlite database located in your
<galaxy>/database folder (indicated in the commented out line below).
enable_communication_server:
type: bool
default: false
required: false
desc: |
Galaxy real time communication server settings
communication_server_host:
type: str
default: http://localhost
required: false
desc: |
Galaxy real time communication server settings
communication_server_port:
type: int
default: 7070
required: false
desc: |
Galaxy real time communication server settings
persistent_communication_rooms:
type: str
required: false
desc: |
persistent_communication_rooms is a comma-separated list of rooms that should be always available.
use_pbkdf2:
type: bool
default: true
-10
View File
@@ -164,16 +164,6 @@ class UsersApiTestCase(ApiTestCase):
search_response = get(url).json()
assert "cat1" in search_response
def test_communication(self):
user = self._setup_user(TEST_USER_EMAIL)
url = self.__url("communication/inputs", user)
self.assertEqual(self.__filter(get(url).json(), "enable", "value"), "false")
put(url, data=json.dumps(dict(enable="true")))
self.assertEqual(self.__filter(get(url).json(), "enable", "value"), "true")
def __filter(self, response, name, attr):
return [r[attr] for r in response["inputs"] if r["name"] == name][0]
def __url(self, action, user):
return self._api_url("users/{}/{}".format(user["id"], action), params=dict(key=self.master_api_key))
@@ -1,223 +0,0 @@
#!/usr/bin/env python
"""
Server for realtime communication between Galaxy uers.
At first you need to install a few requirements.
. GALAXY_ROOT/.venv/bin/activate # activate Galaxy's virtualenv
pip install flask flask-login flask-socketio eventlet # install the requirements
As a next step start the communication server with something like this:
./scripts/communication/communication_server.py --port 7070 --host localhost
Please make sure the host and the port matches the ones in ./config/galaxy.ini and
to set the `secret_id`.
This communication server can be controlled on three different levels:
1. The admin can activate/deactivate the communication server via ./config/galaxy.ini. [off by default]
2. The user can activate/deactivate it in their own personal-settings via Galaxy user preferences. [off by default]
3. The user can activate/deactivate communications for a session directly in the communication window. [on by default]
"""
import argparse
import hashlib
import logging
import os
import sys
from datetime import timedelta
from functools import update_wrapper
import flask.ext.login as flask_login
from flask import (
current_app,
Flask,
make_response,
request,
send_file
)
from flask.ext.login import current_user
from flask_socketio import (
disconnect,
emit,
join_room,
leave_room,
SocketIO
)
sys.path.insert(1, os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir, os.pardir, 'lib')))
import galaxy.config
from galaxy.security.idencoding import IdEncodingHelper
from galaxy.util.sanitize_html import sanitize_html
from galaxy.util.script import app_properties_from_args, populate_config_args
logging.basicConfig()
log = logging.getLogger(__name__)
parser = argparse.ArgumentParser(description='Real-time communication server for Galaxy.')
parser.add_argument('--port', type=int, default="7070", help='Port number on which the server should run.')
parser.add_argument('--host', default='localhost', help='Hostname of the communication server.')
populate_config_args(parser)
args = parser.parse_args()
# With the config file we can load the full app properties
app_properties = app_properties_from_args(args)
config = galaxy.config.Configuration(**app_properties)
model = galaxy.config.init_models_from_config(config)
sa_session = model.context.current
# We need the ID secret for configuring the security helper to decrypt
# galaxysession cookies.
if "id_secret" not in app_properties:
log.warning('No ID_SECRET specified. Please set the "id_secret" in your galaxy.ini.')
id_secret = app_properties.get('id_secret', 'dangerous_default')
security_helper = IdEncodingHelper(id_secret=id_secret)
# And get access to the models
# Login manager to manage current_user functionality
login_manager = flask_login.LoginManager()
app = Flask(__name__)
app.config['SECRET_KEY'] = id_secret
login_manager.init_app(app)
socketio = SocketIO(app)
@login_manager.request_loader
def findUserByCookie(request):
cookie_value = request.cookies.get('galaxysession')
if not cookie_value:
return None
session_key = security_helper.decode_guid(cookie_value)
user_session = sa_session.query(model.GalaxySession).filter_by(session_key=session_key).first()
if user_session:
return user_session.user
return None
# Taken from flask.pocoo.org/snippets/56/
def crossdomain(origin=None, methods=None, headers=None,
max_age=21600, attach_to_all=True,
automatic_options=True):
if methods is not None:
methods = ', '.join(sorted(x.upper() for x in methods))
if headers is not None and not isinstance(headers, str):
headers = ', '.join(x.upper() for x in headers)
if not isinstance(origin, str):
origin = ', '.join(origin)
if isinstance(max_age, timedelta):
max_age = max_age.total_seconds()
def get_methods():
if methods is not None:
return methods
options_resp = current_app.make_default_options_response()
return options_resp.headers['allow']
def decorator(f):
def wrapped_function(*args, **kwargs):
if automatic_options and request.method == 'OPTIONS':
resp = current_app.make_default_options_response()
else:
resp = make_response(f(*args, **kwargs))
if not attach_to_all and request.method != 'OPTIONS':
return resp
h = resp.headers
h['Access-Control-Allow-Origin'] = origin
h['Access-Control-Allow-Methods'] = get_methods()
h['Access-Control-Max-Age'] = str(max_age)
if headers is not None:
h['Access-Control-Allow-Headers'] = headers
return resp
f.provide_automatic_options = False
return update_wrapper(wrapped_function, f)
return decorator
script_dir = os.path.dirname(os.path.realpath(__file__))
communication_directory = os.path.join(script_dir, 'template')
@app.route('/')
@crossdomain(origin='*')
def index():
return send_file(os.path.join(communication_directory, 'communication.html'))
@app.route('/communication.js')
def static_script():
return send_file(os.path.join(communication_directory, 'communication.js'))
@app.route('/communication.css')
def static_style():
return send_file(os.path.join(communication_directory, 'communication.css'))
@socketio.on('event connect', namespace='/chat')
def event_connect(message):
log.info(f"{current_user.username} connected")
@socketio.on('event broadcast', namespace='/chat')
def event_broadcast(message):
message = sanitize_html(message['data'])
log.debug(f"{current_user.username} broadcast '{message}'")
emit('event response',
{'data': message, 'user': current_user.username, 'gravatar': hashlib.md5(current_user.email).hexdigest()}, broadcast=True)
@socketio.on('event room', namespace='/chat')
def send_room_message(message):
data = sanitize_html(message['data'])
room = sanitize_html(message['room'])
log.debug(f"{current_user.username} sent '{message}' to {room}")
emit('event response room',
{'data': data, 'user': current_user.username, 'gravatar': hashlib.md5(current_user.email).hexdigest(), 'chatroom': room}, room=room)
@socketio.on('event disconnect', namespace='/chat')
def event_disconnect(message):
log.info("%s disconnected" % current_user.username)
disconnect()
@socketio.on('join', namespace='/chat')
def join(message):
room = sanitize_html(message['room'])
log.debug(f"{current_user.username} joined {room}")
join_room(room)
emit('event response room',
{'data': room, 'userjoin': current_user.username}, broadcast=True)
@socketio.on('leave', namespace='/chat')
def leave(message):
room = sanitize_html(message['room'])
log.debug(f"{current_user.username} left {room}")
leave_room(room)
emit('event response room',
{'data': room, 'userleave': current_user.username}, broadcast=True)
if __name__ == '__main__':
socketio.run(app, host=args.host, port=args.port)
@@ -1,180 +0,0 @@
html, body {
height: 100%;
font-size: 12px;
}
body {
overflow: hidden;
}
.body-container {
opacity: 0;
transition: opacity 1s;
}
.body-container-loaded {
opacity: 1;
}
/* Styles for message text box */
.message-textarea {
height: 50px !important;
width: 99.5%;
padding: 6px 3px;
font-size: inherit;
}
/* Styles for top right icons */
.right_icons {
margin-left: 95%;
}
.chat-room-textbx {
width: 34%;
height: 10%;
padding: 6px 3px;
font-size: inherit;
}
ul, li,
.anchor {
cursor: pointer;
color: black;
}
.nav>li>a:hover, .nav>li>a:focus {
background-color: #F7F8FA;
}
.messages {
overflow-y: auto;
height: 100%;
overflow-x: hidden;
/*background-color: #F7F8FA;*/
}
.send_message {
margin-top: 5px;
margin-left: 2px;
}
.user_message {
background-color: #F7F8FA;
margin: 2px 0px 4px 2px;
padding: 2px;
box-shadow: 0px 1px 1px rgba(0, 0, 0, 0.5);
border-radius: 2px;
}
.user_name {
font-weight: 900;
padding-left: 0px;
}
.date_time {
font-style: italic;
padding-right: 0px;
font-size: 11px;
}
.message {
padding-top: 2px;
}
.tab-content {
height: 64%;
}
.tab-pane {
height: 100%;
padding: 5px 20px 5px 5px;
}
#chat_room_tab {
margin-left: 2%;
}
.row {
/* Override the margins, otherwise goes outside of bounds */
margin-right: 0px;
margin-left: 0px;
}
.nav li a {
padding: 7px 7px;
}
ul > li i {
padding-left: 4px;
}
.join-room {
margin-left: -2.3%;
}
.global-rooms {
overflow-y: auto;
height: 200px;
margin-top: 1%;
}
.global-room {
margin: 0px 2px 2px 0px;
padding: 3px 6px;
font-size: inherit;
}
a:hover, a:visited {
text-decoration: none;
}
/****styles for overflow tabs****/
.wrapper {
position: relative;
margin: 0 auto;
overflow: hidden;
height: 45px;
}
.list {
position: absolute;
left: 0px;
top: 0px;
min-width: 3000px;
margin-top: 0px;
}
.list li {
display: table-cell;
position: relative;
text-align: center;
color: #efefef;
vertical-align: middle;
}
.scroller {
text-align: center;
cursor: pointer;
display: none;
padding: 5px;
padding-top: 9px;
vertical-align: middle;
background-color: #fff;
}
.scroller-right {
float: right;
}
.scroller-left {
float: left;
}
.message-height,
.notification-padding {
padding-right: 0px;
}
@@ -1,58 +0,0 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Chat</title>
<link rel="stylesheet" type="text/css" href="http://netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css">
<link rel="stylesheet" type="text/css" href="//cdn.jsdelivr.net/jquery.mcustomscrollbar/3.0.6/jquery.mCustomScrollbar.min.css">
<link rel="stylesheet" type="text/css" href="communication.css">
</head>
<body class="body-container">
<div class="right_icons">
<i id="online_status" class="anchor fa fa-comments" aria-hidden="true" title=""></i>
<i id="chat_history" class="anchor fa fa-history" aria-hidden="true" title="Show chat history"></i>
</div>
<div class="scroller scroller-left"><i class="fa fa-chevron-left" aria-hidden="true" title="Move left"></i></div>
<div class="scroller scroller-right"><i class="fa fa-chevron-right" aria-hidden="true" title="Move right"></i></div>
<div class="wrapper">
<ul class="nav nav-tabs list" id="chat_tabs">
<li class="active">
<a data-target="#all_chat_tab" data-toggle="tab" aria-expanded='true' title="All Chats">All Chats</a>
</li>
<li class="create-room">
<a data-target="#chat_room_tab" data-toggle="tab" aria-expanded='false' title="Chat Room">Chat Room</a>
</li>
</ul>
</div>
<div class="tab-content">
<div class="tab-pane active fade in" id="all_chat_tab">
<div id="all_messages" class="row mCustomScrollbar">
</div>
</div>
<div class="tab-pane fade" id="chat_room_tab">
<div id="join_room" class="join-room">
<input id="txtbox_chat_room" type="text" class="form-control chat-room-textbx" value=""
placeholder="Type a room name to join...">
<br>
<div class="persistent-rooms-visibility">
<div class="room-text">Or click any available room to join</div>
<div id="global_rooms" class="global-rooms"></div>
</div>
</div>
</div>
</div>
<div class="send_message">
<textarea id="send_data" class="form-control clearable message-textarea" placeholder="Type your message...">
</textarea>
</div>
<script type="text/javascript" src="https://code.jquery.com/jquery-1.10.2.js"></script>
<script type="text/javascript" src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js"></script>
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/socket.io/1.3.5/socket.io.min.js"></script>
<script type="text/javascript" src="http://malihu.github.io/custom-scrollbar/jquery.mCustomScrollbar.concat.min.js"></script>
<script src="https://use.fontawesome.com/89a733ecb7.js"></script>
<script type="text/javascript" src="communication.js"></script>
</body>
</html>
@@ -1,759 +0,0 @@
// the namespace chat events connect to
var namespace = '/chat',
socket = io.connect(window.location.protocol + '//' + document.domain + ':' + location.port + namespace);
// socketio events
var events_module = {
// event handler for sent data from server
event_response: function (socket) {
socket.on('event response', function ( msg ) {
var message = "",
uid = utils.get_userid(),
$el_all_messages = $( '#all_messages' ),
$el_tab_li = $( "a[data-target='#all_chat_tab']" );
// builds the message to be displayed
message = utils.build_message( msg );
// append only for non empty messages
if (msg.data.length > 0) {
utils.append_message( $el_all_messages, message );
utils.vertical_center_align_gravatar( $( '#all_messages .message' ) );
// adding message to build full chat history
utils.store_append_message( uid, $el_all_messages.html() );
}
// updates the user session storage with all the messages
sessionStorage[uid] = $el_all_messages.html();
// show the last item by scrolling to the end
utils.fancyscroll_to_last( $("#all_chat_tab") );
// Alert user if needed
if ( uid !== msg.user ) {
utils.show_notification( $el_tab_li );
}
});
},
// event handler for room messages
event_response_room: function (socket) {
socket.on('event response room', function ( msg ) {
var $el_all_messages = $( '#all_messages' ),
message = {},
uid = utils.get_userid(),
tab_counter = 0,
$el_tab_li = null,
server_text_name = 'Notification';
// response when user joins
if (msg.userjoin) {
message = {
data: msg.userjoin + " has joined " + msg.data,
user: server_text_name,
};
utils.append_message( $el_all_messages, utils.build_message( message ) );
utils.fancyscroll_to_last( $("#all_chat_tab") );
// shows notification when message is from other user
if ( uid !== msg.userjoin ) {
$el_tab_li = $( "a[data-target='#all_chat_tab']" );
utils.show_notification( $el_tab_li );
}
} // response when user leaves
else if ( msg.userleave ) {
message = {
data: msg.userleave + " has left " + msg.data,
user: server_text_name,
};
utils.append_message( $el_all_messages, utils.build_message( message ) );
utils.fancyscroll_to_last( $("#all_chat_tab") );
// shows notification when message is from other user
if ( uid !== msg.userleave ) {
$el_tab_li = $( "a[data-target='#all_chat_tab']" );
utils.show_notification( $el_tab_li );
}
}
else { // normal message sharing when connected
var room = utils.check_room_by_roomname( click_events.connected_room, msg.chatroom );
var $el_room_msg = $( '#all_messages_' + room.id );
utils.append_message( $el_room_msg, utils.build_message( msg ) );
utils.vertical_center_align_gravatar( $( '#all_messages_' + room.id + ' .message' ) );
utils.fancyscroll_to_last( $( "#galaxy_tabroom_" + room.id ) );
// if the pushed message is for some other user, show notification
if (uid !== msg.data) {
$el_tab_li = $( "a[data-target='#galaxy_tabroom_" + room.id + "'" + " ]" );
utils.show_notification( $el_tab_li );
}
}
});
},
// event handler for new connections
event_connect: function ( socket ) {
socket.on('connect', function () {
var send_data = {};
send_data.data = 'connected';
socket.emit( 'event connect', send_data );
});
}
}
// all the click events of buttons
var click_events = {
// on form load, user is connected, so the value is true
is_connected: true,
active_tab: "#all_chat_tab",
connected_room: [],
tab_counter : 0,
broadcast_data: function ( socket ) {
$('#send_data').keydown(function ( e ) {
var $el_active_li = $( '.nav-tabs>li.active' ),
$el_send_data = $( '#send_data' ),
message = "";
message = $el_send_data.val();
message = message.trim(); // removes whitespaces
// return false if entered is pressed without any message
if( message.length == 0 && ( e.keyCode == 13 || e.which == 13 ) ) {
return false;
}
if( click_events.is_connected ) {
var send_data = {},
event_name = "";
if ( e.keyCode == 13 || e.which == 13 ) { // if enter is pressed
// if the tab is all chats
if ( click_events.active_tab === '#all_chat_tab' ) {
send_data.data = message;
event_name = 'event broadcast';
}
else { // if the tab belongs to a room
send_data.data = message;
send_data.room = $el_active_li.children().attr( "title" );
event_name = 'event room';
}
socket.emit( event_name, send_data );
$el_send_data.val('');
return false;
}
}
});
},
// sets the current active tab
active_element: function () {
$('.nav-tabs>li').click(function ( e ) {
var tab_content_id = null,
message_area_div_id = "",
message_area_id = "",
$el_create_textbox = $('#txtbox_chat_room');
if( e.target.attributes['data-target'] ) {
// sets the active tab
click_events.active_tab = e.target.attributes['data-target'].nodeValue;
// removes the background color
$( this ).children().css( 'background-color', '' );
// hides the message textarea for create room tab
utils.show_hide_textarea( $( this ).children() );
// finds the tab content and scrolls to last
message_area_div_id = $( this ).find('a').attr( 'data-target' );
if( message_area_div_id ) { // if it has message area
utils.fancyscroll_to_last( $( message_area_div_id ) );
}
}
});
},
create_room: function() {
$(".create-room").click(function( e ) {
// create global chat rooom links and
// registers their click events
$( ".global-rooms" ).html( "" );
utils.create_global_chatroom_links();
click_events.open_global_chat_room();
});
},
// event for connected and disconneted states
// toggles the connection status icon
connect_disconnect: function ( socket ) {
$('#online_status').click(function () {
var $el_online_status = $( '#online_status' ),
$el_input_text = $( '#send_data' ),
send_data = {},
connected_message = 'Type your message...',
uid = utils.get_userid();
if ( click_events.is_connected ) { // if connected, disconnect
click_events.make_disconnect( uid, $el_input_text, $el_online_status );
}
else { // if disconnected, reconnect
socket.connect();
click_events.is_connected = true;
sessionStorage['connected'] = true;
utils.update_online_status( $el_online_status, click_events.is_connected );
$el_input_text.prop( 'disabled', false );
$el_input_text.val('');
$el_input_text.prop( 'placeholder', connected_message );
}
});
},
// shows full chat history
show_chat_history: function () {
$('#chat_history').click(function ( e ) {
utils.fill_messages( localStorage[ utils.get_userid() ] );
});
},
// makes disconnect and raises disconnect event at the server
make_disconnect: function ( uid, $el_input_text, $el_online_status ) {
var send_data = {};
var disconnected_message = 'You are now disconnected. To send/receive messages, please connect';
click_events.is_connected = false;
socket.emit( 'event disconnect', send_data );
sessionStorage.removeItem( uid );
sessionStorage[ 'connected' ] = false;
utils.update_online_status( $el_online_status, click_events.is_connected );
$el_input_text.val( '' );
$el_input_text.prop( 'placeholder', disconnected_message );
$el_input_text.prop( 'disabled', true );
},
// for creating/joining user created chat room
create_chat_room: function () {
var $el_txtbox_chat_room = $( '#txtbox_chat_room' ),
$el_chat_room_tab = $( '#chat_room_tab' ),
$el_chat_tabs = $( '#chat_tabs' ),
$el_tab_content = $( '.tab-content' ),
$el_msg_box = $( '#send_data' );
$el_txtbox_chat_room.keydown(function ( e ) {
var chat_room_name = $el_txtbox_chat_room.val();
if ( ( e.which === 13 || e.keyCode === 13 ) && chat_room_name.length > 0 ) { // if enter is pressed
chat_room_name = chat_room_name.trim(); // removes the leading and trailing whitespaces
utils.create_new_tab( chat_room_name, $el_txtbox_chat_room, $el_chat_room_tab, $el_chat_tabs, $el_tab_content, $el_msg_box );
$el_txtbox_chat_room.val( "" ) // clears create room textbox
return false;
}
});
},
// for chat rooms/ group chat
leave_close_room: function () {
var tab_counter = "",
room_name = "",
room_index = 0,
self = click_events;
$('.close-room').click(function ( e ) {
e.stopPropagation();
// gets room name from the title of anchor tag
room_name = e.target.parentElement.title;
// leaves room
socket.emit( 'leave', { room: room_name } );
// removes tab and its content
$( '.tab-content ' + $( this ).parent().attr( 'data-target' ) ).remove();
$( this ).parent().parent().remove();
room_index = utils.delete_from_rooms( self.connected_room, room_name );
delete self.connected_room[ room_index ];
// selects the last tab and makes it active
$('#chat_tabs a:last').tab( 'show' );
// hides or shows textarea
utils.show_hide_textarea( $('#chat_tabs a:last') );
// adjusts the left/right scrollers
// when a tab is deleted
utils.adjust_scrollers();
return false;
});
},
// registers overflow tabs left/right click events
overflow_left_right_scroll: function() {
var $el_scroll_right = $('.scroller-right'),
$el_scroll_left = $('.scroller-left');
$('.scroller-right').click(function() {
$el_scroll_left.fadeIn('slow');
$el_scroll_right.fadeOut('slow');
// performs animation when the scroller is clicked
// and shifts the tab list to the right end
$('.list').animate( {left: "+=" + utils.get_width_hidden_list() + "px"}, 'slow', function() { } );
});
$el_scroll_left.click(function() {
$el_scroll_right.fadeIn('slow');
$el_scroll_left.fadeOut('slow');
// performs animation when the scroller is clicked
// and shifts the tab list to the left end
$('.list').animate( {left: "-=" + utils.get_list_left_position() + "px"}, 'slow', function() { } );
});
},
// opens a global chat room
open_global_chat_room: function() {
var $el_txtbox_chat_room = $( '#txtbox_chat_room' ),
$el_chat_room_tab = $( '#chat_room_tab' ),
$el_chat_tabs = $( '#chat_tabs' ),
$el_tab_content = $( '.tab-content' ),
$el_msg_box = $( '#send_data' ),
chat_room_name = '';
// creates a tab for persistent chat room upon clicking
$('.global-room').click(function( e ) {
e.stopPropagation();
chat_room_name = $(this)[0].title;
utils.create_new_tab( chat_room_name, $el_txtbox_chat_room, $el_chat_room_tab, $el_chat_tabs, $el_tab_content, $el_msg_box );
return false;
});
},
// deletes chat log on keypress ctrl + l
// for the active chat window
delete_chat: function() {
$(document).keydown(function( e ) {
var message_area_div_id = "",
message_area_id = "",
$el_active_li = $( '.nav-tabs>li.active' );
// detects keypress of ctrl+l
if ( ( e.which == 76 || e.keyCode == 76 ) && e.ctrlKey ) {
message_area_div_id = $el_active_li.find('a').attr( 'data-target' );
if( message_area_div_id === "#all_chat_tab" ) {
// deletes log for the main chat tab
$('#all_chat_tab').find('#all_messages').html('');
}
else {
// deletes log for the other tabs
message_area_id = $( message_area_div_id + ' .messages' ).attr( 'id' );
if( message_area_id ) { // deletes the chat log
utils.clear_message_area( $( '#' + message_area_id ) );
}
}
}
});
},
// event for body resize
event_window_resize: function() {
$( window ).resize(function() {
var body_width = $('.body-container').width();
if ( body_width > 600 ) {
$('.right_icons').css('margin-left', '97%');
$('.tab-content').height( '79%' );
}
else {
$('.right_icons').css('margin-left', '95%');
$('.tab-content').height( '65%' );
}
// adjusts the vertical alignment of the gravatars
utils.gravatar_align();
});
}
}
// utility methods
var utils = {
notification_color_code: '#FFD700',
connected_color_code: "#00FF00",
disconnected_color_code: "#FF0000",
// fill in all messages
fill_messages: function ( collection ) {
var uid = utils.get_userid(),
message_html = $.parseHTML( collection ),
$el_all_messages = $( '#all_messages' );
// clears the previous items
this.clear_message_area( $el_all_messages );
if ( collection ) {
$el_all_messages.append( $( '<div' + '/' + '>' ).html( message_html ) );
}
// show the last item by scrolling to the end
utils.fancyscroll_to_last( $("#all_chat_tab") );
utils.gravatar_align();
},
// get the current username of logged in user
get_userid: function () {
var query_string_start = location.search.indexOf( '?' ) + 1,
query_string_list = location.search.slice( query_string_start ).split( '&' );
return query_string_list[0].split( '=' )[1];
},
// append message
append_message: function ( $el, message ) {
$el.append( message );
},
// builds message for self
build_message: function ( original_message ) {
var from_uid = original_message.user,
message_data = original_message.data,
message_user = "",
message_text = "",
uid = utils.get_userid();
var user = {
username: original_message.user,
gravatar: original_message.gravatar,
}
if (from_uid === uid) {
// for our own user we override the text_name
user.username = "me";
}
return this.build_message_from_template( user, message_data );
},
// builds template for message display
build_message_from_template: function ( user, original_message ) {
var gravatar_col_content = '' +
'<img src="https://s.gravatar.com/avatar/' + user.gravatar + '?s=32&d=identicon" />' +
'';
var message_col_content = '<div class="row">'+
' <div class="col-xs-6 user_name">' +
user.username +
' </div>' +
' <div class="col-xs-6 text-right date_time" title="'+ this.get_date() +'">' +
this.get_time() +
' </div>' +
'</div>' +
'<div class="row user_message">' +
unescape( original_message ) +
'</div>';
if ( user.username === "me" ){
return '<div class="row message">' +
'<div class="col-xs-11 col-md-12 message-height">' +
message_col_content +
'</div>' +
'<div class="col-xs-1 col-md vertical-align">' +
gravatar_col_content +
'</div>' +
'</div>';
} else if ( user.username === "Notification" ){
return '<div class="row message">' +
'<div class="col-xs-11 col-md-12 notification-padding">' +
message_col_content +
'</div>' +
'</div>';
} else {
return '<div class="row message">' +
'<div class="col-xs-1 col-md vertical-align">' +
gravatar_col_content +
'</div>' +
'<div class="col-xs-11 col-md-12 message-height">' +
message_col_content +
'</div>' +
'</div>';
}
},
// adds an information about the online status
update_online_status: function ( $el, connected ) {
var connected_message = "You are online. Press to be offline.",
disconnected_message = "You are offline. Press to be online.";
if ( connected ) {
$el.prop( "title", connected_message ).css( "color", this.connected_color_code );
}
else {
$el.prop( "title", disconnected_message ).css( "color", this.disconnected_color_code );
}
},
// gets the current time
get_time: function () {
var currentdate = new Date(),
datetime = "",
hours = 0,
minutes = 0;
hours = ( currentdate.getHours() < 10 ) ? ( "0" + currentdate.getHours() ) : currentdate.getHours();
minutes = ( currentdate.getMinutes() < 10 ) ? ( "0" + currentdate.getMinutes() ) : currentdate.getMinutes();
datetime = hours + ":" + minutes;
return datetime;
},
// gets the current date
get_date: function () {
var currentdate = new Date(),
day,
month;
month = ( (currentdate.getMonth() + 1 ) < 10) ? ( "0" + ( currentdate.getMonth() + 1 ) ) : ( currentdate.getMonth() + 1 );
day = ( currentdate.getDate() < 10 ) ? ( "0" + currentdate.getDate() ) : currentdate.getDate();
return month + "/" + day + "/" + currentdate.getFullYear();
},
// clears the message area or chat logs
// from the currently active tab
clear_message_area: function ( $el ) {
$el.html( "" );
},
// shows notification and animating when a message is being
// pushed to an inactive tab(s)
show_notification: function ( $el ) {
if ( !$el.parent().hasClass( 'active' ) ) {
$el.css( 'background-color', this.notification_color_code );
for (var i = 2; i >= 1; i--) {
// shows the animation
$el.fadeOut( 150 ).fadeIn( 150 );
}
}
},
// adjusts the scrollers when a
// tab is added or deleted
adjust_scrollers: function() {
if ( $( '.wrapper' ).width() < this.get_list_width() ) {
$( '.scroller-right' ).show();
}
else {
$( '.scroller-right' ).hide();
}
if ( this.get_list_left_position() < 0 ) {
$( '.scroller-left' ).show();
}
else {
$( '.item' ).animate( {left: "-=" + this.get_list_left_position() + "px"}, 'slow' );
$( '.scroller-left' ).hide();
}
},
// gets the widht of all li elements
get_list_width: function() {
var total_width = 0;
$( '.list li' ).each(function() {
var li_width = $( this ).width();
total_width += li_width;
});
return total_width;
},
// gets the hidden width of the tab
get_width_hidden_list: function() {
var scroll_icon_width = 50;
return ( $( '.wrapper' ).width() - this.get_list_width() - this.get_list_left_position() - scroll_icon_width );
},
get_list_left_position: function() {
return $( '.list' ).position().left;
},
// checks the session storage on page load
// and updates connected status
checks_session_storage: function() {
var uid = utils.get_userid(),
$el_all_messages = $( '#all_messages' ),
$el_online_status = $( '#online_status' ),
$el_textarea = $( '#send_data' );
if ( sessionStorage[ 'connected' ] ) {
if ( sessionStorage[ 'connected' ] === 'true' || sessionStorage[ 'connected' ] === true ) {
utils.update_online_status( $el_online_status, true );
click_events.is_connected = true;
}
else {
click_events.make_disconnect( uid, $el_textarea, $el_online_status );
utils.clear_message_area( $el_all_messages );
}
}
else {
utils.update_online_status( $el_online_status, true );
click_events.is_connected = true;
}
},
// show/hide textarea message box
show_hide_textarea: function( $selector ) {
var $el_textarea = $( '#send_data' );
if ( $selector.attr( "data-target" ) === "#chat_room_tab" ) {
$el_textarea.css( 'display', 'none' );
}
else {
$el_textarea.css('display', 'block');
utils.set_focus_textarea( $el_textarea );
}
},
// save chat logs
store_append_message: function( key, data ) {
localStorage[key] = data;
},
create_global_chatroom_links: function() {
// global_rooms
var $el_room_container = $('#global_rooms'),
room_name = "",
room_template = "",
persistent_communication_rooms = [],
persistent_rooms_length = 0,
$el_persistent_rooms_visible = $(".persistent-rooms-visibility");
// gets an array of persistent communication rooms
persistent_communication_rooms = this.get_persistent_rooms();
persistent_rooms_length = persistent_communication_rooms.length;
if( persistent_rooms_length > 0 ) {
$el_persistent_rooms_visible.css('display', 'block');
// creates html template for persistent rooms
for(var room_counter = 0; room_counter < persistent_rooms_length; room_counter++ ) {
room_name = persistent_communication_rooms[room_counter];
room_template = "<button type='button' class='btn btn-primary global-room' title='" + room_name +
"'>" + room_name + "</button>";
$el_room_container.append(room_template);
}
}
else {
$el_persistent_rooms_visible.css('display', 'none');
}
},
// creates a new tab and joins a chat room
create_new_tab: function( chat_room_name, $el_txtbox_chat_room, $el_chat_room_tab, $el_chat_tabs, $el_tab_content, $el_msg_box) {
var self = click_events,
tab_room_header_template = "",
tab_room_body_template = "",
tab_id = "",
message_area_id = "",
$el_active_li = $('ul>li.active'),
$el_textarea = $('#send_data'),
is_room = utils.check_room_by_roomname( self.connected_room, chat_room_name);
// checks if the room is already open and displays it if open
// otherwise create a new one
if( is_room ) {
$el_chat_tabs.find( "a[data-target='#galaxy_tabroom_" + is_room.id + "']" ).tab('show');
}
else {
// emits join room event
socket.emit('join', {room: chat_room_name});
// removes the active class from the chat creating tab
$el_chat_room_tab.removeClass('fade active in').addClass('fade');
$el_active_li.removeClass('active');
// create tab and message area ids for the
// new tab header and tab content elements
tab_id = "galaxy_tabroom_" + self.tab_counter;
message_area_id = "all_messages_" + self.tab_counter;
self.connected_room.push({
id: self.tab_counter,
name: chat_room_name
});
// create chat room tab header for new room
tab_room_header_template = "<li class='active'><a title='" + chat_room_name + "' data-target='#" + tab_id +
"' data-toggle='tab' aria-expanded='false'>" + chat_room_name +
"<i class='fa fa-times anchor close-room' title='Close room'></i></a></li>";
$el_chat_tabs.append( tab_room_header_template );
// create chat room tab body for new room
tab_room_body_template = "<div class='tab-pane active fade in' id='" + tab_id +
"'><div id='" + message_area_id + "'" + " class='messages'></div></div>";
$el_tab_content.append(tab_room_body_template);
// registers leave room event
self.leave_close_room();
utils.create_fancy_scroll( $( '#' + tab_id ), "#"+tab_id );
click_events.tab_counter++;
}
self.active_element();
// displays the textarea
$el_msg_box.css('display', 'block');
utils.set_focus_textarea( $el_textarea );
// adjusts the left/right scrollers when a tab is created
utils.adjust_scrollers();
},
// returns persistent communication rooms from the iframe url
get_persistent_rooms: function() {
var query_string_start = location.search.indexOf('?') + 1,
query_string_list = location.search.slice(query_string_start).split('&'),
persistent_rooms_list = [];
if( query_string_list[1] ) {
// unescapes the list
query_string_list = unescape(query_string_list[1]);
persistent_rooms_list = query_string_list.split('=')[1];
if( persistent_rooms_list && persistent_rooms_list.length > 0 ) {
return query_string_list.split('=')[1].split(',');
}
else {
return [];
}
}
else {
return [];
}
},
// returns the room information if it exists
check_room_by_roomname: function( dictionary, room_name ) {
for(var ctr = 0; ctr < dictionary.length; ctr++) {
if( dictionary[ctr] ) {
if( room_name === dictionary[ctr].name ) {
return dictionary[ctr];
}
}
}
},
// returns the index of the room to be deleted
// from the connected room list if the room is left by the user
delete_from_rooms: function( dictionary, item ) {
for(var ctr = 0; ctr < dictionary.length; ctr++) {
if( dictionary[ctr] ) {
if( item === dictionary[ctr].name ) {
return ctr;
}
}
}
},
// sets focus inside the textarea and clears it too
set_focus_textarea: function( $el ) {
$el.val("");
$el.focus();
},
// smooth transition of body element upon first load
load_transition: function() {
$('.body-container').addClass('body-container-loaded');
},
// creates fancy scroll for the messages area
create_fancy_scroll: function( $el, element_id ) {
$el.mCustomScrollbar({
theme:"minimal"
});
$( element_id + ' .mCSB_dragger_bar' ).css( 'background-color', 'black' );
},
// scrolls the fancy scroll to the last element
fancyscroll_to_last: function( $el ) {
$el.mCustomScrollbar( "scrollTo", "bottom" );
},
// vertically aligns the gravatar icon in the center
vertical_center_align_gravatar: function( $el ) {
var usermsg_length = $el.find('.message-height').length,
usermsg_height = ( $( $el.find('.message-height')[ usermsg_length - 1 ] ).height() / 2 ),
gravatar_height = ( $( $el.find( '.vertical-align img' )[0] ).height() / 2 );
if( gravatar_height === 0 ) {
gravatar_height = 16;
}
$( $el.find( '.vertical-align img' )[ usermsg_length - 1 ] ).css( 'padding-top', usermsg_height - gravatar_height );
},
gravatar_align: function() {
var active_tab_target = $('li.active a').attr('data-target').split('_'),
tab_number = "",
selector = "",
scroll_selector = "",
usermsg_height = 0,
gravatar_height = 0;
// finds the active tab's suffix number
tab_number = active_tab_target[active_tab_target.length - 1];
if( isNaN( tab_number ) ) {
selector = "#all_messages";
scroll_selector = "#all_chat_tab";
}
else {
selector = "#all_messages_" + tab_number;
scroll_selector = "#galaxy_tabroom_" + tab_number;
}
$( selector + ' .message' ).each(function( index ) {
usermsg_height = ( $( this ).find( '.message-height' ).height() / 2 ),
gravatar_height = ( $( this ).find( '.vertical-align img' ).height() / 2 );
$( this ).find( '.vertical-align img' ).css( 'padding-top', (usermsg_height - gravatar_height) );
});
// scrolls to the last element
utils.fancyscroll_to_last( $( scroll_selector ) );
}
}
// this event fires when all the resources of the page
// have been loaded
$(window).load(function() {
var uid = utils.get_userid(),
$el_textarea = $('#send_data'),
$el_all_messages = $('#all_messages'),
$el_chat_tabs = $('#chat_tabs'),
main_tab_id = "#all_chat_tab",
$el_persistent_rooms_visible = $(".persistent-rooms-visibility");
// build tabs
$el_chat_tabs.tab();
// registers response event
events_module.event_response(socket);
// registers room response event
events_module.event_response_room(socket);
// registers connect event
events_module.event_connect(socket);
// registers create room event
click_events.create_chat_room(socket);
// broadcast the data
click_events.broadcast_data(socket);
// disconnet the user from the chat server
click_events.connect_disconnect(socket);
// registers event for showing chat log
click_events.show_chat_history();
click_events.active_element();
click_events.leave_close_room();
// register tab overflow scroll events
click_events.overflow_left_right_scroll();
// registers event for creating persistent rooms
click_events.create_room();
// registers event for deleting chat log
click_events.delete_chat();
// updates online status text
// by checking if user was connected or not
utils.checks_session_storage();
utils.fill_messages(sessionStorage[uid]);
// sets focus to the textarea
utils.set_focus_textarea( $el_textarea );
// sets smooth transition
utils.load_transition();
utils.create_fancy_scroll( $( main_tab_id ), main_tab_id );
// scrolls to the last of the element
utils.fancyscroll_to_last( $( main_tab_id ) );
$el_persistent_rooms_visible.css('display', 'none');
click_events.event_window_resize();
});
-1
View File
@@ -147,7 +147,6 @@ CUSTOM = {
'tool_filters': listify,
'tool_label_filters': listify,
'tool_section_filters': listify,
'persistent_communication_rooms': listify,
}
-4
View File
@@ -138,10 +138,6 @@ class ScriptsIntegrationTestCase(integration_util.IntegrationTestCase):
def test_admin_cleanup_datasets(self):
self._scripts_check_argparse_help("cleanup_datasets/admin_cleanup_datasets.py")
@skip_unless_module("flask_socketio")
def test_communication_server(self):
self._scripts_check_argparse_help("communication/communication_server.py")
def test_secret_decoder_ring(self):
script = "secret_decoder_ring.py"
self._scripts_check_argparse_help(script)
-1
View File
@@ -76,7 +76,6 @@ class ExpectedValues:
'mulled_channels': listify_strip,
'object_store_store_by': 'uuid',
'password_expiration_period': timedelta,
'persistent_communication_rooms': listify_strip,
'pretty_datetime_format': expand_pretty_datetime_format,
'statsd_host': '', # TODO: do we need '' as the default?
'tool_config_file': listify_strip,