Compare commits

..

15 Commits
1653 ... 1663

Author SHA1 Message Date
yanas
53e784094a Merge pull request #1308 from jitsi/ss_resize_remote
Fix for the size of remote desktop sharing videos
2017-02-08 15:31:06 -06:00
hristoterezov
0e92e48376 fix(ss): resize for remote videos 2017-02-08 14:58:42 -06:00
Lyubomir Marinov
4c9943ac38 Fix an image path on the mobile landing page 2017-02-08 12:41:51 -06:00
Дамян Минков
4bd0fd145d Merge pull request #1293 from jitsi/prosody_plugin_muc_all_owners
prosody plugin to make all users owners/moderators
2017-02-08 13:17:42 +02:00
Lyubomir Marinov
01ae82eb28 No Temasys alert on mobile Web 2017-02-07 21:54:08 -06:00
Lyubomir Marinov
e21eae0933 Prepare for webpack 2 2017-02-07 15:44:37 -06:00
Lyubomir Marinov
2f047c50dc Revert "No Temasys alert on mobile Web"
This reverts commit b09e86352f.
2017-02-07 15:21:34 -06:00
yanas
e397e1a80c Merge pull request #1303 from jitsi/no-temasys-alert-on-mobile
No Temasys alert on mobile (Web)
2017-02-07 15:11:09 -06:00
Lyubomir Marinov
b09e86352f No Temasys alert on mobile Web 2017-02-07 15:08:38 -06:00
Lyubomir Marinov
8687b69167 Consistency
Be consistent about formatting within 1 and the same file.
2017-02-07 08:29:40 -06:00
Lyubomir Marinov
6c5468d904 Simplify the source code
If half the file is written in ES6, it is easier to read if the rest of
the file is in ES6 as well. If ES6 is used, then const is better than
let. If source code is shorter yet as readable as the long version, then
prefer the short version.
2017-02-07 08:29:40 -06:00
Lyubomir Marinov
d6b0f8d4c5 Use functions, do not re-implement them
We have the functions reload and redirect which modify window.location.
Use them and do not directly modify window.location so that we have
fewer places of direct window.location modifications and it is easier to
refactor them.
2017-02-07 08:29:40 -06:00
Lyubomir Marinov
a8cd4ff12c 1, not 2 names for 1 and the same abstraction
window.location calls it reload so util/helpers shouldn't call it
redirect because UI/util/UIUtil has it is own redirect which is the
assign of window.location.
2017-02-07 08:29:40 -06:00
Lyubomir Marinov
8509efc8af Make the Web app aware of its context root 2017-02-07 08:27:23 -06:00
Aaron van Meerten
a5b706a99e Added a prosody plugin for making all users into muc owners in prosody
Included a patch to prosody-trunk which allows owners to kick each other
2017-02-03 11:41:08 -06:00
14 changed files with 267 additions and 95 deletions

View File

@@ -10,7 +10,7 @@ import Recorder from './modules/recorder/Recorder';
import mediaDeviceHelper from './modules/devices/mediaDeviceHelper';
import {reportError} from './modules/util/helpers';
import { reload, reportError } from './modules/util/helpers';
import UIEvents from './service/UI/UIEvents';
import UIUtil from './modules/UI/util/UIUtil';
@@ -203,10 +203,8 @@ function maybeRedirectToWelcomePage(options) {
// save whether current user is guest or not, before navigating
// to close page
window.sessionStorage.setItem('guest', APP.tokenData.isGuest);
if (options.feedbackSubmitted)
window.location.pathname = "close.html";
else
window.location.pathname = "close2.html";
assignWindowLocationPathname(
options.feedbackSubmitted ? "close.html" : "close2.html");
return;
}
@@ -219,11 +217,42 @@ function maybeRedirectToWelcomePage(options) {
if (config.enableWelcomePage) {
setTimeout(() => {
APP.settings.setWelcomePageEnabled(true);
window.location.pathname = "/";
assignWindowLocationPathname('./');
}, 3000);
}
}
/**
* Assigns a specific pathname to window.location.pathname taking into account
* the context root of the Web app.
*
* @param {string} pathname - The pathname to assign to
* window.location.pathname. If the specified pathname is relative, the context
* root of the Web app will be prepended to the specified pathname before
* assigning it to window.location.pathname.
* @return {void}
*/
function assignWindowLocationPathname(pathname) {
const windowLocation = window.location;
if (!pathname.startsWith('/')) {
// XXX To support a deployment in a sub-directory, assume that the room
// (name) is the last non-directory component of the path (name).
let contextRoot = windowLocation.pathname;
contextRoot
= contextRoot.substring(0, contextRoot.lastIndexOf('/') + 1);
// A pathname equal to ./ specifies the current directory. It will be
// fine but pointless to include it because contextRoot is the current
// directory.
pathname.startsWith('./') && (pathname = pathname.substring(2));
pathname = contextRoot + pathname;
}
windowLocation.pathname = pathname;
}
/**
* Create local tracks of specified types.
* @param {Object} options
@@ -323,7 +352,7 @@ class ConferenceConnector {
case ConferenceErrors.NOT_ALLOWED_ERROR:
{
// let's show some auth not allowed page
window.location.pathname = "authError.html";
assignWindowLocationPathname('authError.html');
}
break;
@@ -388,7 +417,7 @@ class ConferenceConnector {
APP.UI.notifyMaxUsersLimitReached();
break;
case ConferenceErrors.INCOMPATIBLE_SERVER_VERSIONS:
window.location.reload();
reload();
break;
default:
this._handleConferenceFailed(err, ...params);
@@ -1445,7 +1474,7 @@ export default {
APP.UI.addListener(UIEvents.LOGOUT, () => {
AuthHandler.logout(room).then(url => {
if (url) {
window.location.href = url;
UIUtil.redirect(url);
} else {
this.hangup(true);
}

View File

@@ -54,7 +54,7 @@ const IndicatorFontSizes = {
/**
* Returns the available video width.
*/
getAvailableVideoWidth: function () {
getAvailableVideoWidth() {
return window.innerWidth;
},
@@ -70,7 +70,7 @@ const IndicatorFontSizes = {
*
* @param el the element
*/
getTextWidth: function (el) {
getTextWidth(el) {
return (el.clientWidth + 1);
},
@@ -79,7 +79,7 @@ const IndicatorFontSizes = {
*
* @param el the element
*/
getTextHeight: function (el) {
getTextHeight(el) {
return (el.clientHeight + 1);
},
@@ -88,14 +88,14 @@ const IndicatorFontSizes = {
*
* @param id the identifier of the audio element.
*/
playSoundNotification: function (id) {
playSoundNotification(id) {
document.getElementById(id).play();
},
/**
* Escapes the given text.
*/
escapeHtml: function (unsafeText) {
escapeHtml(unsafeText) {
return $('<div/>').text(unsafeText).html();
},
@@ -105,11 +105,11 @@ const IndicatorFontSizes = {
* @param {string} safe string which contains escaped html
* @returns {string} unescaped html string.
*/
unescapeHtml: function (safe) {
unescapeHtml(safe) {
return $('<div />').html(safe).text();
},
imageToGrayScale: function (canvas) {
imageToGrayScale(canvas) {
var context = canvas.getContext('2d');
var imgData = context.getImageData(0, 0, canvas.width, canvas.height);
var pixels = imgData.data;
@@ -156,7 +156,7 @@ const IndicatorFontSizes = {
* @param key the tooltip data-i18n key
* @param position the position of the tooltip in relation to the element
*/
setTooltip: function (element, key, position) {
setTooltip(element, key, position) {
if (element !== null) {
element.setAttribute('data-tooltip', TOOLTIP_POSITIONS[position]);
element.setAttribute('data-i18n', '[content]' + key);
@@ -170,7 +170,7 @@ const IndicatorFontSizes = {
*
* @param element the element to remove the tooltip from
*/
removeTooltip: function (element) {
removeTooltip(element) {
element.removeAttribute('data-tooltip', '');
element.removeAttribute('data-i18n','');
element.removeAttribute('content','');
@@ -183,7 +183,7 @@ const IndicatorFontSizes = {
* @returns {string|*}
* @private
*/
_getTooltipText: function (element) {
_getTooltipText(element) {
let title = element.getAttribute('content');
let shortcut = element.getAttribute('shortcut');
if(shortcut) {
@@ -198,7 +198,7 @@ const IndicatorFontSizes = {
* @param container the container to which new child element will be added
* @param newChild the new element that will be inserted into the container
*/
prependChild: function (container, newChild) {
prependChild(container, newChild) {
var firstChild = container.childNodes[0];
if (firstChild) {
container.insertBefore(newChild, firstChild);
@@ -214,7 +214,7 @@ const IndicatorFontSizes = {
* @returns {boolean} {true} to indicate that the given toolbar button
* is enabled, {false} - otherwise
*/
isButtonEnabled: function (name) {
isButtonEnabled(name) {
return interfaceConfig.TOOLBAR_BUTTONS.indexOf(name) !== -1
|| interfaceConfig.MAIN_TOOLBAR_BUTTONS.indexOf(name) !== -1;
},
@@ -226,7 +226,7 @@ const IndicatorFontSizes = {
* @returns {boolean} {true} to indicate that the given setting section
* is enabled, {false} - otherwise
*/
isSettingEnabled: function (name) {
isSettingEnabled(name) {
return interfaceConfig.SETTINGS_SECTIONS.indexOf(name) !== -1;
},
@@ -235,7 +235,7 @@ const IndicatorFontSizes = {
*
* @returns {boolean}
*/
isAuthenticationEnabled: function() {
isAuthenticationEnabled() {
return interfaceConfig.AUTHENTICATION_ENABLE;
},
@@ -303,7 +303,7 @@ const IndicatorFontSizes = {
}
},
hideDisabledButtons: function (mappings) {
hideDisabledButtons(mappings) {
var selector = Object.keys(mappings)
.map(function (buttonName) {
return UIUtil.isButtonEnabled(buttonName)
@@ -313,7 +313,7 @@ const IndicatorFontSizes = {
$(selector).hide();
},
redirect (url) {
redirect(url) {
window.location.href = url;
},
@@ -368,7 +368,7 @@ const IndicatorFontSizes = {
* @param {Object} attrs object with properties
* @returns {String} string of html element attributes
*/
attrsToString: function (attrs) {
attrsToString(attrs) {
return Object.keys(attrs).map(
key => ` ${key}="${attrs[key]}"`
).join(' ');

View File

@@ -23,6 +23,8 @@ export default class LargeVideoManager {
this.eventEmitter = emitter;
this.state = VIDEO_CONTAINER_TYPE;
// FIXME: We are passing resizeContainer as parameter which is calling
// Container.resize. Probably there's better way to implement this.
this.videoContainer = new VideoContainer(
() => this.resizeContainer(VIDEO_CONTAINER_TYPE), emitter);
this.addContainer(VIDEO_CONTAINER_TYPE, this.videoContainer);

View File

@@ -164,12 +164,20 @@ export class VideoContainer extends LargeContainer {
return getStreamOwnerId(this.stream);
}
constructor (onPlay, emitter) {
/**
* Creates new VideoContainer instance.
* @param resizeContainer {Function} function that takes care of the size
* of the video container.
* @param emitter {EventEmitter} the event emitter that will be used by
* this instance.
*/
constructor (resizeContainer, emitter) {
super();
this.stream = null;
this.videoType = null;
this.localFlipX = true;
this.emitter = emitter;
this.resizeContainer = resizeContainer;
this.isVisible = false;
@@ -199,8 +207,8 @@ export class VideoContainer extends LargeContainer {
this.avatarHeight = $("#dominantSpeakerAvatar").height();
var onPlayCallback = function (event) {
if (typeof onPlay === 'function') {
onPlay(event);
if (typeof resizeContainer === 'function') {
resizeContainer(event);
}
this.wasVideoRendered = true;
}.bind(this);
@@ -336,8 +344,14 @@ export class VideoContainer extends LargeContainer {
* @param {string} videoType video type
*/
setStream (stream, videoType) {
if (this.stream === stream) {
// Handles the use case for the remote participants when the
// videoType is received with delay after turning on/off the
// desktop sharing.
if(this.videoType !== videoType) {
this.videoType = videoType;
this.resizeContainer();
}
return;
} else {
// The stream has changed, so the image will be lost on detach

View File

@@ -1,6 +1,6 @@
const logger = require("jitsi-meet-logger").getLogger(__filename);
import { redirect } from '../util/helpers';
import { replace } from '../util/helpers';
/**
* The modules stores information about the URL used to start the conference and
@@ -68,6 +68,6 @@ export default class ConferenceUrl {
*/
reload() {
logger.info("Reloading the conference using URL: " + this.originalURL);
redirect(this.originalURL);
replace(this.originalURL);
}
}

View File

@@ -2,12 +2,13 @@ const logger = require("jitsi-meet-logger").getLogger(__filename);
/**
* Create deferred object.
*
* @returns {{promise, resolve, reject}}
*/
export function createDeferred () {
let deferred = {};
export function createDeferred() {
const deferred = {};
deferred.promise = new Promise(function (resolve, reject) {
deferred.promise = new Promise((resolve, reject) => {
deferred.resolve = resolve;
deferred.reject = reject;
});
@@ -18,63 +19,58 @@ export function createDeferred () {
/**
* Reload page.
*/
export function reload () {
export function reload() {
window.location.reload();
}
/**
* Redirects to new URL.
* Redirects to a specific new URL by replacing the current location (in the
* history).
*
* @param {string} url the URL pointing to the location where the user should
* be redirected to.
*/
export function redirect (url) {
export function replace(url) {
window.location.replace(url);
}
/**
* Prints the error and reports it to the global error handler.
*
* @param e {Error} the error
* @param msg {string} [optional] the message printed in addition to the error
*/
export function reportError (e, msg = "") {
export function reportError(e, msg = "") {
logger.error(msg, e);
if(window.onerror)
window.onerror(msg, null, null,
null, e);
window.onerror && window.onerror(msg, null, null, null, e);
}
/**
* Creates a debounced function that delays invoking func until after wait
* milliseconds have elapsed since the last time the debounced
* function was invoked
* milliseconds have elapsed since the last time the debounced function was
* invoked.
*
* @param fn
* @param wait
* @param options
* @returns {function(...[*])}
*/
export function debounce(fn, wait = 0, options = {}) {
let leading = options.leading || false;
let trailing = true;
let isCalled = false;
if (typeof options.trailing !== 'undefined') {
trailing = options.trailing;
}
const leading = options.leading || false;
const trailing
= (typeof options.trailing === 'undefined') || options.trailing;
let called = false;
return (...args) => {
if(!isCalled) {
if (leading) {
fn(...args);
}
if (!called) {
leading && fn(...args);
setTimeout(() => {
isCalled = false;
if (trailing) {
fn(...args);
}
called = false;
trailing && fn(...args);
}, wait);
isCalled = true;
called = true;
}
};
}

View File

@@ -0,0 +1,13 @@
-- Copyright (c) 2015 &yet <https://andyet.com>
-- https://github.com/otalk/mod_muc_allowners/blob/9a86266a25ed32ade150742cc79f5a1669765a8f/mod_muc_allowners.lua
--
-- Used under the terms of the MIT License
-- https://github.com/otalk/mod_muc_allowners/blob/9a86266a25ed32ade150742cc79f5a1669765a8f/LICENSE
local muc_service = module:depends("muc");
local room_mt = muc_service.room_mt;
room_mt.get_affiliation = function (room, jid)
return "owner";
end

View File

@@ -0,0 +1,21 @@
--- muc.lib.lua 2016-10-26 18:26:53.432377291 +0000
+++ muc.lib.lua 2016-10-26 18:41:40.754426072 +0000
@@ -1256,15 +1256,16 @@
if actor == true then
actor = nil -- So we can pass it safely to 'publicise_occupant_status' below
else
+ local actor_affiliation = self:get_affiliation(actor);
+
-- Can't do anything to other owners or admins
local occupant_affiliation = self:get_affiliation(occupant.bare_jid);
- if occupant_affiliation == "owner" or occupant_affiliation == "admin" then
+ if (occupant_affiliation == "owner" and actor_affiliation ~= "owner") or (occupant_affiliation == "admin" and actor_affiliation ~= "admin" and actor_affiliation ~= "owner") then
return nil, "cancel", "not-allowed";
end
-- If you are trying to give or take moderator role you need to be an owner or admin
if occupant.role == "moderator" or role == "moderator" then
- local actor_affiliation = self:get_affiliation(actor);
if actor_affiliation ~= "owner" and actor_affiliation ~= "admin" then
return nil, "cancel", "not-allowed";
end

View File

@@ -38,7 +38,7 @@ export class AbstractApp extends Component {
}
/**
* Initializes a new App instance.
* Initializes a new AbstractApp instance.
*
* @param {Object} props - The read-only React Component props with which
* the new instance is to be initialized.
@@ -334,13 +334,7 @@ export class AbstractApp extends Component {
// (2) A replace function would be provided to the Route in case it
// chose to redirect to another path.
this._onRouteEnter(route, nextState, pathname => {
// FIXME In order to minimize the modifications related to the
// removal of react-router, the Web implementation is provided
// bellow because the replace function is used on Web only at the
// time of this writing. Provide a platform-agnostic implementation.
// It should likely find the best Route matching the specified
// pathname and navigate to it.
window.location.pathname = pathname;
this._openURL(pathname);
// Do not proceed with the route because it chose to redirect to
// another path.

View File

@@ -14,6 +14,27 @@ export class App extends AbstractApp {
*/
static propTypes = AbstractApp.propTypes
/**
* Initializes a new App instance.
*
* @param {Object} props - The read-only React Component props with which
* the new instance is to be initialized.
*/
constructor(props) {
super(props);
this.state = {
...this.state,
/**
* The context root of window.location i.e. this Web App.
*
* @type {string}
*/
windowLocationContextRoot: this._getWindowLocationContextRoot()
};
}
/**
* Inits the app before component will mount.
*
@@ -35,6 +56,22 @@ export class App extends AbstractApp {
return window.location;
}
/**
* Gets the context root of this Web App from window.location.
*
* @private
* @returns {string} The context root of window.location i.e. this Web App.
*/
_getWindowLocationContextRoot() {
const pathname = this._getWindowLocation().pathname;
const contextRootEndIndex = pathname.lastIndexOf('/');
return (
contextRootEndIndex === -1
? '/'
: pathname.substring(0, contextRootEndIndex + 1));
}
/**
* Navigates to a specific Route (via platform-specific means).
*
@@ -53,6 +90,7 @@ export class App extends AbstractApp {
= path.replace(
/:room/g,
store.getState()['features/base/conference'].room);
path = this._routePath2WindowLocationPathname(path);
// Navigate to the specified Route.
const windowLocation = this._getWindowLocation();
@@ -69,4 +107,22 @@ export class App extends AbstractApp {
windowLocation.pathname = path;
}
}
/**
* Converts a specific Route path to a window.location.pathname.
*
* @param {string} path - A Route path to be converted to/represeted as a
* window.location.pathname.
* @private
* @returns {string} A window.location.pathname-compatible representation of
* the specified Route path.
*/
_routePath2WindowLocationPathname(path) {
let pathname = this.state.windowLocationContextRoot;
pathname.endsWith('/') || (pathname += '/');
pathname += path.startsWith('/') ? path.substring(1) : path;
return pathname;
}
}

View File

@@ -74,7 +74,7 @@ class UnsupportedMobileBrowser extends Component {
<div className = { `${ns}__body` }>
<img
className = { `${ns}__logo` }
src = '/images/logo-blue.svg' />
src = 'images/logo-blue.svg' />
<p className = { `${ns}__text` }>
You need <strong>Jitsi Meet</strong> to join a
conversation on your mobile
@@ -97,9 +97,40 @@ class UnsupportedMobileBrowser extends Component {
</button>
</a>
</div>
{
this._renderStyle()
}
</div>
);
}
/**
* Renders an HTML style element with CSS specific to
* this UnsupportedMobileBrowser.
*
* @private
* @returns {ReactElement}
*/
_renderStyle() {
// Temasys provide lib-jitsi-meet/modules/RTC/adapter.screenshare.js
// which detects whether the browser supports WebRTC. If the browser
// does not support WebRTC, it displays an alert in the form of a yellow
// bar at the top of the page. The alert notifies the user that the
// browser does not support WebRTC and, if Temasys provide a plugin for
// the browser, the alert contains a button to initiate installing the
// browser. When Temasys do not provide a plugin for the browser, we do
// not want the alert on the unsupported-browser page because the
// notification about the lack of WebRTC support is the whole point of
// the unsupported-browser page.
return (
<style type = 'text/css'>
{
'iframe[name="adapterjs-alert"] { display: none; }'
}
</style>
);
}
}
/**

View File

@@ -76,15 +76,18 @@ class WelcomePage extends AbstractWelcomePage {
}
/**
* Returns the domain name.
* Returns the URL of this WelcomePage for display purposes. For
* historic/legacy reasons, the return value is referred to as domain.
*
* @private
* @returns {string} Domain name.
* @returns {string} The URL of this WelcomePage for display purposes.
*/
_getDomain() {
const windowLocation = window.location;
// As the returned URL is for display purposes, do not return the
// userinfo, query and fragment URI parts.
const wl = window.location;
return `${windowLocation.protocol}//${windowLocation.host}/`;
return `${wl.protocol}//${wl.host}${wl.pathname}`;
}
/**

View File

@@ -10,26 +10,22 @@
* Builds and returns the room name.
*/
function getRoomName () { // eslint-disable-line no-unused-vars
var getroomnode = config.getroomnode;
var path = window.location.pathname;
var roomName;
// determinde the room node from the url
// TODO: just the roomnode or the whole bare jid?
if (config.getroomnode && typeof config.getroomnode === 'function') {
// Determine the room node from the URL.
if (getroomnode && typeof getroomnode === 'function') {
// custom function might be responsible for doing the pushstate
roomName = config.getroomnode(path);
roomName = getroomnode.call(config, path);
} else {
/* fall back to default strategy
* this is making assumptions about how the URL->room mapping happens.
* It currently assumes deployment at root, with a rewrite like the
* following one (for nginx):
location ~ ^/([a-zA-Z0-9]+)$ {
rewrite ^/(.*)$ / break;
}
*/
if (path.length > 1) {
roomName = path.substr(1).toLowerCase();
}
// Fall back to the default strategy of making assumptions about how the
// URL maps to the room (name). It currently assumes a deployment in
// which the last non-directory component of the path (name) is the
// room.
roomName
= path.substring(path.lastIndexOf('/') + 1).toLowerCase()
|| undefined;
}
return roomName;

View File

@@ -33,6 +33,23 @@ if (minimize) {
NODE_ENV: JSON.stringify('production')
}
}));
// While webpack will automatically insert UglifyJsPlugin when minimize is
// true, the defaults of UglifyJsPlugin in webpack 1 and webpack 2 are
// different. Explicitly state what we want even if we want defaults in
// order to prepare for webpack 2.
plugins.push(new webpack.optimize.UglifyJsPlugin({
compress: {
// It is nice to see warnings from UglifyJsPlugin that something is
// unused and, consequently, is removed. The default is false in
// webpack 2.
warnings: true
},
// Use the source map to map error message locations to modules. The
// default is false in webpack 2.
sourceMap: true
}));
}
// The base Webpack configuration to bundle the JavaScript artifacts of
@@ -56,7 +73,7 @@ var config = {
// as well.
exclude: node_modules,
loader: 'babel',
loader: 'babel-loader',
query: {
// XXX The require.resolve bellow solves failures to locate the
// presets when lib-jitsi-meet, for example, is npm linked in
@@ -74,20 +91,20 @@ var config = {
// to be available in such a form by multiple jitsi-meet
// dependencies including AUI, lib-jitsi-meet.
loader: 'expose?$!expose?jQuery',
loader: 'expose-loader?$!expose-loader?jQuery',
test: /\/node_modules\/jquery\/.*\.js$/
}, {
// Disable AMD for the Strophe.js library or its imports will fail
// at runtime.
loader: 'imports?define=>false&this=>window',
loader: 'imports-loader?define=>false&this=>window',
test: strophe
}, {
// Allow CSS to be imported into JavaScript.
loaders: [
'style',
'css'
'style-loader',
'css-loader'
],
test: /\.css$/
}, {
@@ -95,7 +112,7 @@ var config = {
// by CSS into the output path.
include: aui_css,
loader: 'file',
loader: 'file-loader',
query: {
context: aui_css,
name: '[path][name].[ext]'
@@ -104,7 +121,7 @@ var config = {
}, {
// Enable the import of JSON files.
loader: 'json',
loader: 'json-loader',
exclude: node_modules,
test: /\.json$/
} ],