Yannick Warnier 10 年之前
父節點
當前提交
311a7ca43e

+ 9 - 1
app/Resources/public/css/base.css

@@ -5953,4 +5953,12 @@ ul.exercise-draggable-answer li {
 
 ._jsPlumb_endpoint {
     z-index: 50;
-}
+}
+
+/*** Video chat ***/
+video#chat-local-video,
+div#chat-remote-video video {
+    display: block;
+    height: auto;
+    width: 100%;
+}

+ 2 - 1
composer.json

@@ -71,7 +71,8 @@
         "bower-asset/mediaelement": "@stable",
         "bower-asset/modernizr": "2.8.*",
         "bower-asset/jqueryui-timepicker-addon": "@stable",
-        "bower-asset/imageMap-resizer": "0.5.3"
+        "bower-asset/imageMap-resizer": "0.5.3",
+        "bower-asset/simplewebrtc": "@stable"
     },
     "require-dev": {
         "behat/behat": "2.5.*@stable",

+ 69 - 0
main/inc/ajax/chat.ajax.php

@@ -69,6 +69,75 @@ switch ($action) {
         $status = isset($_REQUEST['status']) ? intval($_REQUEST['status']) : 0;
         $chat->setUserStatus($status);
         break;
+    case 'start_video':
+        $room = VideoChat::getChatRoomByUsers(api_get_user_id(), $to_user_id);
+
+        if ($room !== false) {
+            $videoChatLink = Display::url(
+                Display::tag(
+                    'i',
+                    null,
+                    ['class' => 'fa fa-video-camera']
+                ) . " " . get_lang('StartVideoChat'),
+                api_get_path(WEB_LIBRARY_JS_PATH) . "chat/video.php?room={$room['room_name']}"
+            );
+
+            $chat->send(api_get_user_id(), $to_user_id, $videoChatLink, false);
+
+            echo Display::tag('p', $videoChatLink, ['class' => 'lead']);
+            break;
+        }
+
+        $form = new FormValidator('start_video_chat');
+        $form->addText('chat_room_name', get_lang('ChatRoomName'), false);
+        $form->addHidden('to', $to_user_id);
+        $form->addButtonSend(get_lang('Create'));
+
+        $template = new Template();
+        $template->assign('form', $form->returnForm());
+
+        echo $template->fetch('default/javascript/chat/start_video.tpl');
+        break;
+    case 'create_room':
+        $room = VideoChat::getChatRoomByUsers(api_get_user_id(), $to_user_id);
+        $createdRoom = false;
+
+        if ($room === false) {
+            $roomName = isset($_REQUEST['room_name']) ? Security::remove_XSS($_REQUEST['room_name']) : null;
+
+            if (VideoChat::nameExists($roomName)) {
+                echo Display::return_message(get_lang('TheVideoChatRoomXNameAlreadyExists'), 'error');
+
+                break;
+            }
+
+            $createdRoom = VideoChat::createRoom($roomName, api_get_user_id(), $to_user_id);
+        } else {
+            $roomName = $room['room_name'];
+            $createdRoom = true;
+        }
+
+        if ($createdRoom === false) {
+            echo Display::return_message(get_lang('ChatRoomNotCreated'), 'error');
+            break;
+        }
+
+        $videoChatLink = Display::url(
+            Display::tag(
+                'i',
+                null,
+                ['class' => 'fa fa-video-camera']
+            ) . " " . get_lang('StartVideoChat'),
+            api_get_path(WEB_LIBRARY_JS_PATH) . "chat/video.php?room=$roomName"
+        );
+
+        $chat->send(api_get_user_id(), $to_user_id, $videoChatLink, false);
+
+        echo Display::tag('p', $videoChatLink, ['class' => 'lead']);
+        break;
+    case 'notify_not_support':
+        $chat->send(api_get_user_id(), $to_user_id, get_lang('TheXUserBrowserDoesNotSupportWebRTC'));
+        break;
     default:
         echo '';
 }

+ 101 - 0
main/inc/lib/VideoChat.php

@@ -0,0 +1,101 @@
+<?php
+/* For licensing terms, see /license.txt */
+/**
+ * VideoChat class
+ * 
+ * This class provides methods for video chat management.
+ *
+ * @author Angel Fernando Quiroz Campos <angel.quiroz@beeznest.com>
+ */
+class VideoChat
+{
+
+    /**
+     * Get the video chat info by its users
+     * @param int $user1 User id
+     * @param int $user2 Other user id
+     * @return array The video chat info. Otherwise return false
+     */
+    public static function getChatRoomByUsers($user1, $user2)
+    {
+        $user1 = intval($user1);
+        $user2 = intval($user2);
+
+        if (empty($user1) || empty($user2)) {
+            return false;
+        }
+
+        return Database::select(
+            '*',
+            Database::get_main_table(TABLE_MAIN_CHAT_VIDEO),
+            [
+                'where' => [
+                    '(from_user = ? AND to_user = ?)' => [$user1, $user2],
+                    'OR (from_user = ? AND to_user = ?)' => [$user2, $user1]
+                ]
+            ],
+            'first'
+        );
+    }
+
+    /**
+     * Create a video chat
+     * @param string $name The video chat name
+     * @param int $fromUser The sender user
+     * @param int $toUser The receiver user
+     * @return int The created video chat id. Otherwise return false
+     */
+    public static function createRoom($name, $fromUser, $toUser)
+    {
+        return Database::insert(
+            Database::get_main_table(TABLE_MAIN_CHAT_VIDEO),
+            [
+                'from_user' => intval($fromUser),
+                'to_user' => intval($toUser),
+                'room_name' => $name,
+                'datetime' => api_get_utc_datetime()
+            ]
+        );
+    }
+
+    /**
+     * Check if the video chat exists by its room name
+     * @param string $name The video chat name
+     * @return boolean
+     */
+    public static function nameExists($name)
+    {
+        $resultData = Database::select(
+            'COUNT(1) AS count',
+            Database::get_main_table(TABLE_MAIN_CHAT_VIDEO),
+            [
+                'where' => ['room_name = ?' => $name]
+            ],
+            'first'
+        );
+
+        if ($resultData !== false) {
+            return $resultData['count'] > 0;
+        }
+
+        return false;
+    }
+
+    /**
+     * Get the video chat info by its room name
+     * @param string $name The video chat name
+     * @return array The video chat info. Otherwise return false
+     */
+    public static function getChatRoomByName($name)
+    {
+        return Database::select(
+            '*',
+            Database::get_main_table(TABLE_MAIN_CHAT_VIDEO),
+            [
+                'where' => ['room_name = ?' => $name]
+            ],
+            'first'
+        );
+    }
+
+}

+ 9 - 5
main/inc/lib/chat.lib.php

@@ -172,7 +172,7 @@ class Chat extends Model
      * @param string Message
      * @return void Prints "1"
      */
-    public function send($from_user_id, $to_user_id, $message)
+    public function send($from_user_id, $to_user_id, $message, $printResult = true)
     {
         $user_friend_relation = SocialManager::get_relation_between_contacts($from_user_id, $to_user_id);
         if ($user_friend_relation == USER_RELATION_TYPE_FRIEND) {
@@ -207,11 +207,15 @@ class Chat extends Model
                 $this->save($params);
             }
 
-            echo "1";
-            exit;
+            if ($printResult) {
+                echo "1";
+                exit;
+            }
         } else {
-            echo "0";
-            exit;
+            if ($printResult) {
+                echo "0";
+                exit;
+            }
         }
     }
 

+ 1 - 0
main/inc/lib/database.constants.inc.php

@@ -328,6 +328,7 @@ define('TABLE_MAIN_SKILL_REL_PROFILE', 'skill_rel_profile');
 
 define('TABLE_MAIN_CHAT', 'chat');
 define('TABLE_TIMELINE', 'timeline');
+define('TABLE_MAIN_CHAT_VIDEO', 'chat_video');
 
 // Gradebook model
 define('TABLE_GRADE_MODEL', 'grade_model');

+ 8 - 5
main/inc/lib/javascript/chat/css/chat.css

@@ -8,7 +8,7 @@
     display: block;
 }
 .chatboxheadmain {	
-	padding:7px 7px 7px 0px;
+	padding: 5px;
 	color: #333333;		
 	background-color: #EBEEF4;		
     border-top: 1px solid rgba(29, 49, 91, 0.3);
@@ -45,20 +45,23 @@
 	width:16px;
 	display:inline;
 	float:left;
-	padding: 0px 4px 0px 4px;	
 }
 
 .chatboxtitle {
 	font-weight: bold;
 	float: left;
 	font-size: 13px;
-	width: 160px;
+	width: 130px;
 	cursor:pointer;
+        overflow: hidden;
+        padding-left: 2px;
+        text-overflow: ellipsis;
+        white-space: nowrap;
 }
 
 .chatboxhead {
 	/* background-color: #222; */
-	padding:7px 7px 7px 0px;
+	padding: 5px;
 	color: #ffffff;
 	border-right:1px solid #222;
 	border-left:1px solid #222;
@@ -99,7 +102,7 @@
 	font-size: 13px;
 	color: #333333;
 	height:200px;
-	width:209px;
+	width:225px;
 	overflow-y:auto;
 	overflow-x:auto;
 	padding:7px;

+ 97 - 13
main/inc/lib/javascript/chat/js/chat.js

@@ -352,20 +352,99 @@ function createChatBox(user_id, chatboxtitle, minimizeChatBox, online) {
 
 	user_is_online = return_online_user(user_id, online);
 
-	$("<div />" ).attr("id","chatbox_"+user_id)
-	.addClass("chatbox")
-	.html('<div class="chatboxhead">\n\
-			'+user_is_online+'	\n\
-			<div class="chatboxtitle">'+chatboxtitle+'</div>\n\
-			<div class="chatboxoptions">\n\
-				<a class="togglelink" rel="'+user_id+'" href="javascript:void(0)" > _ </a>&nbsp;\n\
-				<a class="closelink" rel="'+user_id+'" href="javascript:void(0)">X</a></div>\n\
-				<br clear="all"/></div>\n\
-			<div class="chatboxcontent"></div>\n\
-			<div class="chatboxinput"><textarea class="chatboxtextarea" onkeydown="javascript:return checkChatBoxInputKey(event,this,\''+user_id+'\');"></textarea></div>')
-	.appendTo($( "body" ));
+        var chatbox = $('<div>')
+            .attr({
+                id: 'chatbox_' + user_id
+            })
+            .addClass('chatbox')
+            .css('bottom', 0);
+
+        var chatboxHead = $('<div>')
+            .addClass('chatboxhead')
+            .append(user_is_online);
+
+        $('<div>')
+            .addClass('chatboxtitle')
+            .append(chatboxtitle)
+            .appendTo(chatboxHead);
+
+        var chatboxoptions = $('<div>')
+            .addClass('chatboxoptions')
+            .appendTo(chatboxHead);
+
+        if (!!Modernizr.prefixed('RTCPeerConnection', window)) {
+            $('<a>')
+                .addClass('btn btn-xs')
+                .attr({
+                    href: '#'
+                })
+                .html('<i class="fa fa-video-camera"></i>')
+                .on('click', function(e) {
+                    e.preventDefault();
+
+                    var createForm = $.get(
+                        ajax_url,
+                        {
+                            action: 'start_video',
+                            to: user_id
+                        }
+                    );
+
+                    $.when(createForm).done(function(response) {
+                        $('#global-modal')
+                            .find('.modal-dialog')
+                            .removeClass('modal-lg');
 
-	$("#chatbox_"+user_id).css('bottom', '0px');
+                        $('#global-modal')
+                            .find('.modal-body')
+                            .html(response);
+
+                        $('#global-modal').modal('show');
+                    });
+                })
+                .appendTo(chatboxoptions);
+        }
+
+        $('<a>')
+            .addClass('btn btn-xs togglelink')
+            .attr({
+                href: 'javascript:void(0)',
+                rel: user_id
+            })
+            .html('<i class="fa fa-toggle-down"></i>')
+            .appendTo(chatboxoptions);
+
+        $('<a>')
+            .addClass('btn btn-xs closelink')
+            .attr({
+                href: 'javascript:void(0)',
+                rel: user_id
+            })
+            .html('<i class="fa fa-close"></i>')
+            .appendTo(chatboxoptions);
+
+        $('<br>')
+            .attr('clear', 'all')
+            .appendTo(chatboxHead);
+
+        var chatboxContent = $('<div>')
+            .addClass('chatboxcontent');
+
+        var chatboxInput = $('<div>')
+            .addClass('chatboxinput');
+
+        $('<textarea>')
+            .addClass('chatboxtextarea')
+            .on('keydown', function(e) {
+                return checkChatBoxInputKey(e.originalEvent, this, user_id);
+            })
+            .appendTo(chatboxInput);
+
+        chatbox
+            .append(chatboxHead)
+            .append(chatboxContent)
+            .append(chatboxInput)
+            .appendTo('body');
 
 	chatBoxeslength = 0;
 
@@ -398,6 +477,7 @@ function createChatBox(user_id, chatboxtitle, minimizeChatBox, online) {
 		}
 
 		if (minimize == 1) {
+                        $('.togglelink').html('<i class="fa fa-toggle-up"></i>');
 			$('#chatbox_'+user_id+' .chatboxcontent').css('display','none');
 			$('#chatbox_'+user_id+' .chatboxinput').css('display','none');
 		}
@@ -481,6 +561,8 @@ function toggleChatBoxGrowth(user_id) {
 		$('#chatbox_'+user_id+' .chatboxcontent').css('display','block');
 		$('#chatbox_'+user_id+' .chatboxinput').css('display','block');
 		$("#chatbox_"+user_id+" .chatboxcontent").scrollTop($("#chatbox_"+user_id+" .chatboxcontent")[0].scrollHeight);
+
+                $('.togglelink').html('<i class="fa fa-toggle-down"></i>');
 	} else {
 		var newCookie = user_id;
 		if ($.cookie('chatbox_minimized')) {
@@ -489,6 +571,8 @@ function toggleChatBoxGrowth(user_id) {
 		$.cookie('chatbox_minimized',newCookie);
 		$('#chatbox_'+user_id+' .chatboxcontent').css('display','none');
 		$('#chatbox_'+user_id+' .chatboxinput').css('display','none');
+
+                $('.togglelink').html('<i class="fa fa-toggle-up"></i>');
 	}
 }
 

+ 43 - 0
main/inc/lib/javascript/chat/video.php

@@ -0,0 +1,43 @@
+<?php
+/* For licensing terms, see /license.txt */
+require_once '../../../global.inc.php';
+
+if (api_is_anonymous()) {
+    api_not_allowed(true);
+}
+
+$roomName = isset($_GET['room']) ? $_GET['room'] : null;
+
+$room = VideoChat::getChatRoomByName($roomName);
+
+if ($room === false) {
+    Header::location(api_get_path(WEB_PATH));
+}
+
+$isSender = $room['from_user'] == api_get_user_id();
+$isReceiver = $room['to_user'] == api_get_user_id();
+
+if (!$isSender && !$isReceiver) {
+    Header::location(api_get_path(WEB_PATH));
+}
+
+if ($isSender) {
+    $chatUser = api_get_user_info($room['to_user']);
+} elseif ($isReceiver) {
+    $chatUser = api_get_user_info($room['from_user']);
+}
+
+$htmlHeadXtra[] = '<script type="text/javascript" src="'
+    . api_get_path(WEB_PATH) . 'web/assets/simplewebrtc/latest.js'
+    . '"></script>' . "\n";
+
+$template = new Template();
+$template->assign('room_name', $room['room_name']);
+$template->assign('chat_user', $chatUser);
+
+$content = $template->fetch('default/chat/video.tpl');
+
+$template->assign('header', $room['room_name']);
+$template->assign('content', $content);
+$template->assign('message', Display::return_message(get_lang('BroswerDoesNotSupportWebRTC'), 'warning'));
+$template->display_one_col_template();

+ 1 - 0
main/install/data.sql

@@ -1627,6 +1627,7 @@ INSERT INTO sequence_type_entity VALUES
 (2,'Quiz', 'Quiz and Tests','c_quiz'),
 (3,'LpItem', 'Items of a Learning Path','c_lp_item');
 
+<<<<<<< HEAD
 -- Version 1.10.0.40
 
 INSERT INTO settings_current

+ 58 - 0
main/template/default/chat/video.tpl

@@ -0,0 +1,58 @@
+<p class="lead">{{ "ChatWithXUser"|get_lang|format(chat_user.complete_name) }}</p>
+<div class="row" id="chat-video-panel">
+    <div class="col-sm-3">
+        <div class="thumbnail">
+            <video id="chat-local-video" class="skip"></video>
+        </div>
+    </div>
+    <div class="col-sm-9">
+        <div class="thumbnail" id="chat-remote-video"></div>
+    </div>
+</div>
+
+<script>
+    (function() {
+        var VideoChat = {
+            init: function() {
+                var isCompatible = !!Modernizr.prefixed('RTCPeerConnection', window);
+
+                var notifyNotSupport = function() {
+                    $.get('{{ _p.web_ajax }}chat.ajax.php', {
+                        action: 'notify_not_support',
+                        to: {{ chat_user.id }}
+                    });
+                };
+
+                var startVideoChat = function() {
+                    var webRTC = new SimpleWebRTC({
+                        localVideoEl: 'chat-local-video',
+                        remoteVideosEl: 'chat-remote-video',
+                        autoRequestMedia: true
+                    });
+
+                    webRTC.on('readyToCall', function() {
+                        webRTC.joinRoom('{{ room_name }}');
+                    });
+                    webRTC.on('videoAdded', function (video, peer) {
+                        $(video).addClass('skip');
+                    });
+                };
+
+                if (!isCompatible) {
+                    notifyNotSupport();
+
+                    $('#chat-video-panel').remove();
+                    return;
+                }
+
+                $('#messages').remove();
+
+                startVideoChat();
+            }
+        };
+
+        $(document).on('ready', function() {
+            VideoChat.init();
+        });
+    })();
+</script>

+ 20 - 0
main/template/default/javascript/chat/start_video.tpl

@@ -0,0 +1,20 @@
+{{ form }}
+
+<script>
+    $('form[name="start_video_chat"]').on('submit', function(e) {
+        e.preventDefault();
+
+        var createChatRoom = $.post(
+            '{{ _p.web_ajax }}chat.ajax.php',
+            {
+                room_name: $(this).find('input[name="chat_room_name"]').val(),
+                to: $(this).find('input[name="to"]').val(),
+                action: 'create_room'
+            }
+        );
+
+        $.when(createChatRoom).done(function(response) {
+            $('#global-modal').find('.modal-body').html(response);
+        });
+    });
+</script>

+ 162 - 0
src/Chamilo/CoreBundle/Entity/ChatVideo.php

@@ -0,0 +1,162 @@
+<?php
+
+namespace Chamilo\CoreBundle\Entity;
+
+use Doctrine\ORM\Mapping as ORM;
+
+/**
+ * Chat
+ *
+ * @ORM\Table(
+ *      name="chat_video", indexes={
+ *          @ORM\Index(name="idx_chat_video_to_user", columns={"to_user"}),
+ *          @ORM\Index(name="idx_chat_video_from_user", columns={"from_user"}),
+ *          @ORM\Index(name="idx_chat_video_users", columns={"from_user", "to_user"}),
+ *          @ORM\Index(name="idx_chat_video_room_name", columns={"room_name"})
+ *      }
+ * )
+ * @ORM\Entity
+ */
+class ChatVideo
+{
+    /**
+     * @var integer
+     *
+     * @ORM\Column(name="from_user", type="integer", nullable=true)
+     */
+    private $fromUser;
+
+    /**
+     * @var integer
+     *
+     * @ORM\Column(name="to_user", type="integer", nullable=true)
+     */
+    private $toUser;
+
+    /**
+     * @var string
+     *
+     * @ORM\Column(name="room_name", type="text", nullable=false)
+     */
+    private $roomName;
+
+    /**
+     * @var \DateTime
+     *
+     * @ORM\Column(name="datetime", type="datetime", nullable=false)
+     */
+    private $datetime;
+
+    /**
+     * @var integer
+     *
+     * @ORM\Column(name="id", type="integer")
+     * @ORM\Id
+     * @ORM\GeneratedValue(strategy="IDENTITY")
+     */
+    private $id;
+
+
+
+    /**
+     * Set fromUser
+     *
+     * @param integer $fromUser
+     * @return Chat
+     */
+    public function setFromUser($fromUser)
+    {
+        $this->fromUser = $fromUser;
+
+        return $this;
+    }
+
+    /**
+     * Get fromUser
+     *
+     * @return integer
+     */
+    public function getFromUser()
+    {
+        return $this->fromUser;
+    }
+
+    /**
+     * Set toUser
+     *
+     * @param integer $toUser
+     * @return Chat
+     */
+    public function setToUser($toUser)
+    {
+        $this->toUser = $toUser;
+
+        return $this;
+    }
+
+    /**
+     * Get toUser
+     *
+     * @return integer
+     */
+    public function getToUser()
+    {
+        return $this->toUser;
+    }
+
+    /**
+     * Set room_name
+     *
+     * @param string $roomName
+     * @return Chat
+     */
+    public function setRoomName($roomName)
+    {
+        $this->roomName = $roomName;
+
+        return $this;
+    }
+
+    /**
+     * Get room_name
+     *
+     * @return string
+     */
+    public function getRoomName()
+    {
+        return $this->roomName;
+    }
+
+    /**
+     * Set datetime
+     *
+     * @param \DateTime $datetime
+     * @return Chat
+     */
+    public function setDatetime($datetime)
+    {
+        $this->datetime = $datetime;
+
+        return $this;
+    }
+
+    /**
+     * Get datetime
+     *
+     * @return \DateTime
+     */
+    public function getDatetime()
+    {
+        return $this->datetime;
+    }
+
+    /**
+     * Get id
+     *
+     * @return integer
+     */
+    public function getId()
+    {
+        return $this->id;
+    }
+}

+ 66 - 0
src/Chamilo/CoreBundle/Migrations/Schema/V110/Version20150505142900.php

@@ -0,0 +1,66 @@
+<?php
+/* For licensing terms, see /license.txt */
+
+namespace Chamilo\CoreBundle\Migrations\Schema\V110;
+
+use Chamilo\CoreBundle\Migrations\AbstractMigrationChamilo;
+use Doctrine\DBAL\Schema\Schema;
+use Doctrine\DBAL\Types\Type;
+
+/**
+ * Class Version20150505142900
+ *
+ * @package Chamilo\CoreBundle\Migrations\Schema\v1
+ */
+class Version20150505142900 extends AbstractMigrationChamilo
+{
+    /**
+     * @param Schema $schema
+     *
+     * @throws \Doctrine\DBAL\Schema\SchemaException
+     */
+    public function up(Schema $schema)
+    {
+        // Create table for video chat
+        $chatVideoTable = $schema->createTable('chat_video');
+        $chatVideoTable->addColumn(
+            'id',
+            Type::INTEGER,
+            ['unsigned' => true, 'autoincrement' => true, 'notnull' => true]
+        );
+        $chatVideoTable->addColumn(
+            'from_user',
+            Type::INTEGER,
+            ['unsigned' => true, 'notnull' => true]
+        );
+        $chatVideoTable->addColumn(
+            'to_user',
+            Type::INTEGER,
+            ['unsigned' => true, 'notnull' => true]
+        );
+        $chatVideoTable->addColumn(
+            'room_name',
+            Type::STRING,
+            ['length' => 255, 'notnull' => true]
+        );
+        $chatVideoTable->addColumn(
+            'datetime',
+            Type::DATETIME,
+            ['notnull' => true]
+        );
+        $chatVideoTable->setPrimaryKey(['id']);
+        $chatVideoTable->addIndex(['from_user'], 'idx_chat_video_from_user');
+        $chatVideoTable->addIndex(['to_user'], 'idx_chat_video_to_user');
+        $chatVideoTable->addIndex(['from_user', 'to_user'], 'idx_chat_video_users');
+        $chatVideoTable->addIndex(['room_name'], 'idx_chat_video_room_name');
+    }
+
+    /**
+     * We don't allow downgrades yet
+     * @param Schema $schema
+     */
+    public function down(Schema $schema)
+    {
+        $schema->dropTable('chat_video');
+    }
+}