Commit 31b60b1f authored by Dominik George's avatar Dominik George

New upstream version 0.14.1~rc

parent a0449080
Movim Changelog
================
v0.15 (trunk)
v0.14.1 (trunk)
---------------------------
* Replace ZeroMQ sockets with WebSockets, remove reactphp/zmq and php-zmq dependency
* Make Movim compatible with PHP 7.3
* Display icon + infobox when the chatroom is public
* Display the Git HEAD commit hash in ?infos if available
* Don't reload the page when opening chat from Search
* Don't reload the discussion when the WS is reconnecting but only append the new messages
* Add slide-to-close feature in Chats to quickly close one-to-one discussions on touch devices
v0.14
---------------------------
......
0.14rc2
\ No newline at end of file
0.14.1rc
\ No newline at end of file
......@@ -156,6 +156,9 @@ class Info extends Model
case 'muc_moderated':
$this->mucmoderated = true;
break;
case 'muc_semianonymous':
$this->mucsemianonymous = true;
break;
}
}
......
......@@ -19,8 +19,9 @@ WebSocket.prototype.register = function(host) {
var MovimWebsocket = {
connection: null,
attached: [],
registered: [],
attached: [], // Launched when the socket is connected to the daemon
started: [], // Launched when the linker is started
registered: [], // Launched when the linker is connected to XMPP
attempts: 1,
pong: false,
closed: false,
......@@ -40,11 +41,17 @@ var MovimWebsocket = {
}
},
launchStarted : function() {
for(var i = 0; i < MovimWebsocket.started.length; i++) {
MovimWebsocket.started[i]();
}
},
init : function() {
if (SECURE_WEBSOCKET) {
var uri = 'wss:' + BASE_URI + '/ws/';
var uri = 'wss:' + BASE_URI + 'ws/';
} else {
var uri = 'ws:' + BASE_URI + '/ws/';
var uri = 'ws:' + BASE_URI + 'ws/';
}
if (this.connection
......@@ -76,6 +83,15 @@ var MovimWebsocket = {
MovimWebsocket.launchRegistered();
}
if (obj.func == 'started') {
// If the linker was started but we're not on the login page
if (!['login', 'account', 'accountnext'].includes(MovimUtils.urlParts().page)) {
MovimUtils.disconnect();
} else {
MovimWebsocket.launchStarted();
}
}
if (obj.func == 'disconnected') {
MovimUtils.disconnect();
}
......@@ -187,6 +203,12 @@ var MovimWebsocket = {
}
},
start : function(func) {
if (typeof(func) === "function") {
this.started.push(func);
}
},
clearAttached : function() {
this.attached = [];
},
......
......@@ -10,7 +10,7 @@ class AccountController extends Base
function dispatch()
{
requestURL('http://localhost:1560/disconnect/', 2, ['sid' => SESSION_ID]);
requestAPI('disconnect', 2, ['sid' => SESSION_ID]);
$this->page->setTitle(__('page.account_creation'));
}
......
......@@ -14,7 +14,7 @@ class DisconnectController extends Base
function dispatch()
{
// Just in case
requestURL('http://localhost:1560/disconnect/', 2, ['sid' => SESSION_ID]);
requestAPI('disconnect', 2, ['sid' => SESSION_ID]);
Session::dispose();
// Fresh cookie
......
......@@ -532,7 +532,7 @@ define('DEFAULT_HTTP_USER_AGENT', 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:31.0)
/*
* @desc Request a simple url
*/
function requestURL($url, $timeout = 10, $post = false, $json = false)
function requestURL(string $url, int $timeout = 10, $post = false, bool $json = false)
{
$ch = curl_init($url);
......@@ -552,25 +552,14 @@ function requestURL($url, $timeout = 10, $post = false, $json = false)
curl_setopt ($ch, CURLOPT_POSTFIELDS, $post);
}
$rs = [];
$content = curl_exec($ch);
$rs['content'] = $content;
$rs['errno'] = curl_errno($ch);
$rs['errmsg'] = curl_error($ch);
$rs['header'] = curl_getinfo($ch);
if ($rs['errno'] == 0) {
return $rs['content'];
}
return false;
return curl_errno($ch) == 0 ? $content : false;
}
/*
* Request the headers of a URL
*/
function requestHeaders($url, $timeout = 2)
function requestHeaders(string $url, $timeout = 2)
{
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
......@@ -582,13 +571,31 @@ function requestHeaders($url, $timeout = 2)
curl_setopt($ch, CURLOPT_NOBODY, 1);
curl_setopt($ch, CURLOPT_USERAGENT, DEFAULT_HTTP_USER_AGENT);
$rs = [];
curl_exec($ch);
return curl_getinfo($ch);
}
/**
* Request the internal API
*/
function requestAPI(string $action, int $timeout = 2, $post = false)
{
$ch = curl_init('http:/'.$action);
curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $timeout);
curl_setopt($ch, CURLOPT_UNIX_SOCKET_PATH, API_SOCKET);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
if (is_array($post)) {
curl_setopt ($ch, CURLOPT_POST, 1);
curl_setopt ($ch, CURLOPT_POSTFIELDS, $post);
}
$content = curl_exec($ch);
return curl_errno($ch) == 0 ? $content : false;
}
/*
* @desc Get the URI of a smiley
*/
......
......@@ -48,12 +48,12 @@
<body dir="<?php $this->dir();?>"
class="<?php if (!$this->public && \App\User::me()->nightmode) { ?>nightmode<?php } ?>">
<noscript>
<style type="text/css">
nav {display:none;} #content {display: none;}
</style>
<div class="warning" style="width: 500px; margin: 0 auto;">
<?php echo __('global.no_js'); ?>
</div>
<style type="text/css">main {display: none;}</style>
<ul class="list" style="color: white;">
<li>
<p class="center"><?php echo __('global.no_js'); ?></p>
</li>
</ul>
</noscript>
<div id="hiddendiv"></div>
<div id="snackbar" class="snackbar"></div>
......
<section class="scroll">
<form name="register" data-sessionid="{$attributes->sessionid}" data-node="{$attributes->node}">
{$form}
{autoescape="off"}
{$form}
{/autoescape}
</form>
</section>
<div>
......
......@@ -99,7 +99,7 @@ class AccountNext extends \Movim\Widget\Base
{
Notification::append(null, $this->__('error.service_unavailable'));
requestURL('http://localhost:1560/disconnect/', 2, ['sid' => SESSION_ID]);
requestAPI('disconnect', 2, ['sid' => SESSION_ID]);
$this->rpc('MovimUtils.redirect', $this->route('account'));
}
......
......@@ -102,6 +102,7 @@ class Blog extends \Movim\Widget\Base
->get();
if ($this->_messages->isNotEmpty()) {
$this->title = $this->_messages->first()->title;
$this->description = !empty($this->_messages->first()->contentcleaned)
? $this->_messages->first()->contentcleaned
: $this->_messages->first()->title;
......
......@@ -406,13 +406,13 @@ class Chat extends \Movim\Widget\Base
*/
function ajaxHttpCorrect($to, $message)
{
$m = $this->user->messages()
$replace = $this->user->messages()
->where('jidto', $to)
->orderBy('published', 'desc')
->first();
if ($m) {
$this->ajaxHttpSendMessage($to, $message, false, false, $m);
if ($replace) {
$this->ajaxHttpSendMessage($to, $message, false, false, $replace);
}
}
......@@ -469,7 +469,7 @@ class Chat extends \Movim\Widget\Base
* @param string jid
* @param string time
*/
function ajaxGetHistory($jid, $date, $muc = false)
function ajaxGetHistory($jid, $date, $muc = false, $prepend = true)
{
if (!$this->validateJid($jid)) return;
......@@ -478,7 +478,7 @@ class Chat extends \Movim\Widget\Base
$query->where('jidfrom', $jid)
->orWhere('jidto', $jid);
})
->where('published', '<', date(SQL_DATE, strtotime($date)));
->where('published', $prepend ? '<' : '>', date(SQL_DATE, strtotime($date)));
$messages = $muc
......@@ -490,7 +490,11 @@ class Chat extends \Movim\Widget\Base
->get();
if ($messages->count() > 0) {
Notification::append(false, $this->__('message.history', $messages->count()));
if ($prepend) {
Notification::append(false, $this->__('message.history', $messages->count()));
} else {
$messages = $messages->reverse();
}
foreach($messages as $message) {
if (!$message->isOTR()) {
......@@ -498,7 +502,7 @@ class Chat extends \Movim\Widget\Base
}
}
$this->rpc('Chat.appendMessagesWrapper', $this->_wrapper, true);
$this->rpc('Chat.appendMessagesWrapper', $this->_wrapper, $prepend);
$this->_wrapper = [];
}
}
......
......@@ -54,7 +54,9 @@
{/if}
{if="$conference && $conference->name"}
<p class="line" title="{$room}">{$conference->name}</p>
<p class="line" title="{$room}">
{$conference->name}
</p>
{else}
<p class="line">{$room}</p>
{/if}
......@@ -63,10 +65,24 @@
<p>{$c->__('button.connecting')}</p>
{elseif="$conference && $conference->subject"}
<p class="line" title="{$conference->subject}">
{if="$conference->info && !$conference->info->mucsemianonymous"}
<span title="{$c->__('room.public_muc_text')}">
{$c->__('room.public_muc')} <i class="material-icons">wifi_tethering</i>
</span>
{/if}
{$conference->subject}
</p>
{else}
<p class="line">{$room}</p>
<p class="line">
{if="$conference->info && !$conference->info->mucsemianonymous"}
<span title="{$c->__('room.public_muc_text')}">
{$c->__('room.public_muc')} <i class="material-icons">wifi_tethering</i>
</span>
{/if}
{$room}
</p>
{/if}
</li>
</ul>
......@@ -140,7 +156,7 @@
<span
title="{$c->__('button.close')}"
class="control icon active"
onclick="Chats_ajaxClose('{$jid|echapJS}'); MovimTpl.hidePanel();">
onclick="Chats_ajaxClose('{$jid|echapJS}', true);">
<i class="material-icons">close</i>
</span>
<p class="line">
......
......@@ -254,17 +254,6 @@ var Chat = {
MovimUtils.textareaAutoheight(textarea);
textarea.focus();
},
notify : function(title, body, image)
{
if (document_focus == false) {
movim_title_inc();
movim_desktop_notification(title, body, image);
}
},
empty : function()
{
Chat_ajaxGet();
},
setBubbles : function(left, right, date, separator) {
var div = document.createElement('div');
......@@ -278,8 +267,6 @@ var Chat = {
Chat.date = div.firstChild.cloneNode(true);
div.innerHTML = separator;
Chat.separator = div.firstChild.cloneNode(true);
Chat.setScrollBehaviour();
},
setScrollBehaviour : function() {
var discussion = document.querySelector('#chat_widget div.contained');
......@@ -289,7 +276,8 @@ var Chat = {
Chat_ajaxGetHistory(
Chat.getTextarea().dataset.jid,
Chat.currentDate,
Chat.getTextarea().dataset.muc);
Chat.getTextarea().dataset.muc,
true);
}
Chat.lastHeight = this.clientHeight;
......@@ -372,6 +360,8 @@ var Chat = {
discussion.querySelector('.placeholder').classList.add('show');
}
}
Chat.setScrollBehaviour();
},
appendMessage : function(idjidtime, data, prepend) {
if (data.body == null) return;
......@@ -697,14 +687,18 @@ var Chat = {
MovimWebsocket.attach(function() {
var jid = MovimUtils.urlParts().params[0];
var room = MovimUtils.urlParts().params[1];
var room = (MovimUtils.urlParts().params[1] === 'room');
if (jid) {
MovimTpl.showPanel();
if (room) {
Chat_ajaxGetRoom(jid, Boolean(document.getElementById(MovimUtils.cleanupId(jid) + '-conversation')));
if (Boolean(document.getElementById(MovimUtils.cleanupId(jid) + '-conversation'))) {
Chat_ajaxGetHistory(jid, Chat.currentDate, room, false);
} else {
Chat_ajaxGet(jid, Boolean(document.getElementById(MovimUtils.cleanupId(jid) + '-conversation')));
MovimTpl.showPanel();
if (room) {
Chat_ajaxGetRoom(jid);
} else {
Chat_ajaxGet(jid);
}
}
}
});
......
......@@ -138,7 +138,7 @@ class Chats extends \Movim\Widget\Base
}
}
function ajaxClose($jid)
function ajaxClose($jid, $closeDiscussion = false)
{
$notif = new Notification;
$notif->ajaxClear('chat|'.$jid);
......@@ -150,10 +150,11 @@ class Chats extends \Movim\Widget\Base
\App\Cache::c('chats', $chats);
$this->rpc('MovimTpl.remove', '#' . cleanupId($jid . '_chat_item'));
$this->rpc('Chats.refresh');
$this->rpc('Chat.empty');
$this->rpc('MovimTpl.hidePanel');
if ($closeDiscussion) {
$this->rpc('Chat_ajaxGet');
}
}
function prepareChats($emptyItems = false)
......
#chats_widget_list ~ .placeholder {
ul#chats_widget_list {
overflow-x: hidden;
}
ul#chats_widget_list ~ .placeholder {
display: none;
}
#chats_widget_list:empty ~ .placeholder {
display: block;
ul#chats_widget_list li.close {
opacity: 0.5;
}
ul#chats_widget_list:empty ~ .placeholder {
display: block;
}
var Chats = {
startX: 0,
startY: 0,
refresh: function() {
var list = document.querySelector('#chats_widget_list');
list.innerHTML = list.innerHTML.trim();
......@@ -20,14 +23,45 @@ var Chats = {
items[i].onmousedown = function(e) {
if (e.which == 2) {
Notification_ajaxClear('chat|' + this.dataset.jid);
Notification.current('chat');
Chats_ajaxClose(this.dataset.jid);
delete document.querySelector('#chat_widget').dataset.jid;
MovimTpl.hidePanel();
Chats_ajaxClose(this.dataset.jid, (MovimUtils.urlParts().params[0] === this.dataset.jid));
e.preventDefault();
}
}
items[i].addEventListener('touchstart', function(event) {
Chats.startX = event.targetTouches[0].pageX;
Chats.startY = event.targetTouches[0].pageY;
}, true);
items[i].addEventListener('touchmove', function(event) {
moveX = Math.abs(parseInt(event.targetTouches[0].pageX - Chats.startX));
moveY = Math.abs(parseInt(event.targetTouches[0].pageY - Chats.startY));
if (moveX > 15 && moveX > moveY && moveY < 15) {
if (moveX > this.offsetWidth/2) {
this.classList.add('close');
} else {
this.classList.remove('close');
}
this.style.transform = 'matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, '
+ parseInt(event.targetTouches[0].pageX - Chats.startX)
+', 0, 0, 1)';
}
}, true);
items[i].addEventListener('touchend', function(event) {
move = Math.abs(parseInt(event.changedTouches[0].pageX - Chats.startX));
if (move > this.offsetWidth/2) {
this.style.display = 'none';
Chats_ajaxClose(this.dataset.jid, (MovimUtils.urlParts().params[0] === this.dataset.jid));
}
this.style.transform = '';
this.classList.remove('close');
Chats.startX = Chats.startY = 0;
}, true);
}
items[i].classList.remove('active');
......@@ -46,5 +80,4 @@ var Chats = {
MovimWebsocket.attach(function() {
Chats_ajaxGet();
Notification.current('chat');
});
......@@ -9,7 +9,10 @@ class Infos extends Base
function display()
{
$configuration = Configuration::get();
$connected = (int)requestURL('http://localhost:1560/started/', 2);
$connected = (int)requestAPI('started');
$gitHeadPath = DOCUMENT_ROOT . '/.git/refs/heads/master';
$hash = file_exists($gitHeadPath) ? substr(file_get_contents($gitHeadPath), 0, 7) : 'release';
$infos = [
'url' => BASE_URI,
......@@ -20,9 +23,10 @@ class Infos extends Base
'php_version' => phpversion(),
'version' => APP_VERSION,
'population' => User::count(),
'linked' => (int)requestURL('http://localhost:1560/linked/', 2),
'linked' => (int)requestAPI('linked'),
'started' => $connected,
'connected' => $connected
'connected' => $connected,
'commit' => $hash
];
$this->view->assign('json', json_encode($infos));
......
......@@ -80,7 +80,7 @@ class Login extends Base
}
$this->view->assign('pop', User::count());
$this->view->assign('connected', (int)requestURL('http://localhost:1560/started/', 2));
$this->view->assign('connected', (int)requestAPI('started', 2));
$this->view->assign('error', $this->prepareError());
if (isset($_SERVER['PHP_AUTH_USER'])
......
......@@ -53,7 +53,7 @@ body main {
#login_widget li.info p {
text-align: center;
max-height: 100%;
padding: 0 2rem;
padding: 0;
}
#login_widget span.info {
......
......@@ -62,7 +62,6 @@ var Login = {
MovimWebsocket.attach(function()
{
Login.quickLogin();
Login.init();
// We enable the form
......@@ -73,6 +72,10 @@ MovimWebsocket.attach(function()
}
});
MovimWebsocket.start(function() {
Login.quickLogin();
});
MovimWebsocket.register(function()
{
if (localStorage.getItem('quickKey') != null) {
......@@ -81,11 +84,11 @@ MovimWebsocket.register(function()
localStorage.getItem('quickLogin'),
localStorage.getItem('quickKey')
);
}
form = document.querySelector('form[name="login"]');
if (Login.submitted) {
Login_ajaxLogin(MovimUtils.formToJson('login'));
} else {
form = document.querySelector('form[name="login"]');
if (Login.submitted) {
Login_ajaxLogin(MovimUtils.formToJson('login'));
}
}
});
......
......@@ -52,7 +52,7 @@ class Notifications extends \Movim\Widget\Base
Drawer::fill($this->prepareNotifications());
\App\Cache::c('notifs_since', date(SQL_DATE));
$this->ajaxSetCounter();
Notification::ajaxClear('comments');
(new Notification)->ajaxClear('comments');
}
public function ajaxSetCounter()
......
......@@ -60,6 +60,9 @@
class="moderator"
{/if}>
{$count} <i class="material-icons">people</i>
{if="$value->info && !$value->info->mucsemianonymous"}
<i class="material-icons">wifi_tethering</i>
{/if}
</span>
{elseif="isset($info) && $info->occupants > 0"}
<span title="{$c->__('communitydata.sub', $info->occupants)}"
......@@ -67,6 +70,9 @@
class="moderator"
{/if}>
{$info->occupants} <i class="material-icons">people</i>
{if="$value->info && !$value->info->mucsemianonymous"}
<i class="material-icons">wifi_tethering</i>
{/if}
</span>
{/if}
{if="isset($info) && $info->description"}
......
......@@ -27,7 +27,7 @@
<i class="material-icons">comment</i>
</span>
{/if}
{if="$value->mucrole =='moderator'"}
{if="$value->mucrole == 'moderator'"}
<span class="control icon yellow">
<i class="material-icons">star</i>
</span>
......
......@@ -36,3 +36,5 @@ nick = Your nickname
invite = Invite a contact
invited = Invitation sent
invite_code = Send this link to your contacts
public_muc = Public
public_muc_text = "The participants of this chatroom can see each other's profile"
......@@ -34,7 +34,7 @@
<span class="control icon active gray" onclick="MovimUtils.reload('{$c->route('contact', $value->jid)}')">
<i class="material-icons">person</i>
</span>
<span class="control icon active gray" onclick="Search_ajaxChat('{$value->jid}')">
<span class="control icon active gray" onclick="Search.chat('{$value->jid}')">
<i class="material-icons">comment</i>
</span>
{if="$value->presence && $value->presence->capability && $value->presence->capability->isJingle()"}
......
......@@ -38,7 +38,7 @@
<span class="control icon active gray" onclick="MovimUtils.reload('{$c->route('contact', $value->jid)}')">
<i class="material-icons">person</i>
</span>
<span class="control icon active gray" onclick="Search_ajaxChat('{$value->jid}')">
<span class="control icon active gray" onclick="Search.chat('{$value->jid}')">
<i class="material-icons">comment</i>
</span>
<p class="normal line">{$value->truename}</p>
......
......@@ -32,6 +32,16 @@ var Search = {
}
},
chat : function(jid) {
if (MovimUtils.urlParts().page === 'chat') {
Drawer_ajaxClear();
Chats_ajaxOpen(jid);
Chat_ajaxGet(jid);
} else {
Search_ajaxChat(jid);
}
},
searchSomething : function(value) {
clearTimeout(Search.timer);
......
<?php
use Movim\Migration;
use Illuminate\Database\Schema\Blueprint;
class AddMucSemiAnonymousToInfosTable extends Migration
{
public function up()
{
$this->schema->table('infos', function(Blueprint $table) {
$table->boolean('mucsemianonymous')->default(false);
});
}
public function down()
{
$this->schema->table('infos', function(Blueprint $table) {
$table->dropColumn('mucsemianonymous');
});
}
}
......@@ -13,7 +13,6 @@ $bootstrap->boot();