Merge branch 'develop' of github.com:RocketChat/Rocket.Chat into color-variables

pull/7748/head
Guilherme Gazzo 8 years ago
commit f5c63b46b5
No known key found for this signature in database
GPG Key ID: 1F85C9AD922D0829
  1. 4
      README.md
  2. 1
      packages/rocketchat-file/file.server.js
  3. 2
      packages/rocketchat-migrations/migrations.js
  4. 2
      packages/rocketchat-ui-admin/client/users/adminUsers.js
  5. 4
      packages/rocketchat-ui/client/lib/cordova/urls.js
  6. 16
      packages/rocketchat-ui/client/views/app/room.js
  7. 188
      server/startup/migrations/v097.js
  8. 209
      server/startup/migrations/v099.js

@ -23,6 +23,7 @@
* [Ubuntu 16.04](#ubuntu-1604)
* [Cloudron.io](#cloudronio)
* [Heroku](#heroku)
* [Helm Kubernetes](#helm-kubernetes)
* [Scalingo](#scalingo)
* [Sloppy.io](#sloppyio)
* [Docker](#docker)
@ -137,6 +138,9 @@ Host your own Rocket.Chat server for **FREE** with [One-Click Deploy](https://he
[![Deploy](https://www.herokucdn.com/deploy/button.png)](https://heroku.com/deploy?template=https://github.com/RocketChat/Rocket.Chat/tree/master)
## Helm Kubernetes
Deploy on Kubernetes using the official [helm chart](https://github.com/kubernetes/charts/pull/752).
## Scalingo
Deploy your own Rocket.Chat server instantly on [Scalingo](https://scalingo.com)

@ -105,6 +105,7 @@ RocketChatFile.GridFS = class {
this.store = new Grid(db, mongo);
this.findOneSync = Meteor.wrapAsync(this.store.collection(this.name).findOne.bind(this.store.collection(this.name)));
this.removeSync = Meteor.wrapAsync(this.store.remove.bind(this.store));
this.countSync = Meteor.wrapAsync(this.store._col.count.bind(this.store._col));
this.getFileSync = Meteor.wrapAsync(this.getFile.bind(this));
}

@ -239,7 +239,7 @@ Migrations._migrateTo = function(version, rerun) {
if (rerun) {
log.info('Rerunning version ' + version);
migrate('up', version);
migrate('up', this._findIndexByVersion(version));
log.info('Finished migrating.');
unlock();
return true;

@ -21,7 +21,7 @@ Template.adminUsers.helpers({
}
},
emailAddress() {
return _.map(this.emails, function(e) { e.address; }).join(', ');
return _.map(this.emails, function(e) { return e.address; }).join(', ');
},
flexData() {
return {

@ -1,14 +1,14 @@
Meteor.startup(() => {
if (!Meteor.isCordova) { return; }
// Handle click events for all external URLs
$(document).on('deviceready', () => {
document.addEventListener('deviceready', () => {
// const platform = device.platform.toLowerCase();
$(document).on('click', function(e) {
const $link = $(e.target).closest('a[href]');
if (!($link.length > 0)) { return; }
const url = $link.attr('href');
if (/^https?:\/\/.+/.test(url) === true) {
if (/^https?:\/\/.+/i.test(url) === true) {
window.open(url, '_system');
return e.preventDefault();
}

@ -234,6 +234,8 @@ Template.room.helpers({
let isSocialSharingOpen = false;
let touchMoved = false;
let lastTouchX = null;
let lastTouchY = null;
Template.room.events({
'click, touchend'(e, t) {
@ -249,6 +251,11 @@ Template.room.events({
},
'touchstart .message'(e, t) {
const touches = e.originalEvent.touches;
if (touches && touches.length) {
lastTouchX = touches[0].pageX;
lastTouchY = touches[0].pagey;
}
touchMoved = false;
isSocialSharingOpen = false;
if (e.originalEvent.touches.length !== 1) {
@ -343,7 +350,14 @@ Template.room.events({
},
'touchmove .message'(e, t) {
touchMoved = true;
const touches = e.originalEvent.touches;
if (touches && touches.length) {
const deltaX = Math.abs(lastTouchX - touches[0].pageX);
const deltaY = Math.abs(lastTouchY - touches[0].pageY);
if (deltaX > 5 || deltaY > 5) {
touchMoved = true;
}
}
return Meteor.clearTimeout(t.touchtime);
},

@ -1,192 +1,6 @@
import fs from 'fs';
import path from 'path';
function log(...args) {
console.log('[AVATAR]', ...args);
}
function logError(...args) {
console.error('[AVATAR]', ...args);
}
function insertAvatar({ details, avatarsFileStore, stream, callback = () => {} }) {
return new Promise((resolve) => {
Meteor.defer(() => {
Meteor.runAsUser('rocket.cat', () => {
avatarsFileStore.insert(details, stream, (err) => {
if (err) {
logError({err});
resolve();
} else {
Meteor.setTimeout(() => {
callback();
}, 200);
}
resolve();
});
});
});
});
}
function batch(arr, limit, fn) {
if (!arr.length) {
return Promise.resolve();
}
return Promise.all(arr.splice(0, limit).map((item) => {
return fn(item);
})).then(() => { return batch(arr, limit, fn); });
}
RocketChat.Migrations.add({
version: 97,
up() {
log('Migrating avatars. This might take a while.');
const query = {
$or: [{
's3.path': {
$exists: true
}
}, {
'googleCloudStorage.path': {
$exists: true
}
}]
};
RocketChat.models.Uploads.find(query).forEach((record) => {
if (record.s3) {
RocketChat.models.Uploads.model.direct.update({_id: record._id}, {
$set: {
'store': 'AmazonS3:Uploads',
AmazonS3: {
path: record.s3.path + record._id
}
},
$unset: {
s3: 1
}
}, {multi: true});
} else {
RocketChat.models.Uploads.model.direct.update({_id: record._id}, {
$set: {
store: 'GoogleCloudStorage:Uploads',
GoogleStorage: {
path: record.googleCloudStorage.path + record._id
}
},
$unset: {
googleCloudStorage: 1
}
}, {multi: true});
}
});
RocketChat.models.Uploads.model.direct.update({
store: 'fileSystem'
}, {
$set: {
store: 'FileSystem:Uploads'
}
}, {
multi: true
});
RocketChat.models.Uploads.model.direct.update({
store: 'rocketchat_uploads'
}, {
$set: {
store: 'GridFS:Uploads'
}
}, {
multi: true
});
const avatarOrigins = [
'upload',
'gravatar',
'facebook',
'twitter',
'github',
'google',
'url',
'gitlab',
'linkedin'
];
const avatarsPath = RocketChat.models.Settings.findOne({_id: 'Accounts_AvatarStorePath'}).value;
const avatarStoreType = RocketChat.models.Settings.findOne({_id: 'Accounts_AvatarStoreType'}).value;
Meteor.setTimeout(function() {
const avatarsFileStore = FileUpload.getStore('Avatars');
const oldAvatarGridFS = new RocketChatFile.GridFS({
name: 'avatars'
});
const users = RocketChat.models.Users.find({avatarOrigin: {$in: avatarOrigins}}, {avatarOrigin: 1, username: 1}).fetch();
const usersTotal = users.length;
log('Total users to migrate avatars ->', usersTotal);
let current = 0;
batch(users, 300, (user) => {
const id = `${ user.username }.jpg`;
const gridFSAvatar = oldAvatarGridFS.getFileWithReadStream(id);
log('Migrating', ++current, 'of', usersTotal);
if (gridFSAvatar) {
const details = {
userId: user._id,
type: gridFSAvatar.contentType,
size: gridFSAvatar.length
};
return insertAvatar({
details,
avatarsFileStore,
stream: gridFSAvatar.readStream,
callback() {
oldAvatarGridFS.deleteFile(id);
}
});
}
if (avatarStoreType === 'FileSystem' && avatarsPath && avatarsPath.trim()) {
try {
const filePath = path.join(avatarsPath, id);
const stat = fs.statSync(filePath);
if (stat && stat.isFile()) {
const details = {
userId: user._id,
type: 'image/jpeg',
size: stat.size
};
return insertAvatar({
details,
avatarsFileStore,
stream: fs.createReadStream(filePath),
callback() {
fs.unlinkSync(filePath);
}
});
}
} catch (e) {
logError('Error migrating old avatar', e);
return Promise.resolve();
}
}
}).then(() => {
const avatarsFiles = new Mongo.Collection('avatars.files');
const avatarsChunks = new Mongo.Collection('avatars.chunks');
avatarsFiles.rawCollection().drop();
avatarsChunks.rawCollection().drop();
});
}, 1000);
RocketChat.models.Settings.remove({_id: 'Accounts_AvatarStoreType'});
RocketChat.models.Settings.remove({_id: 'Accounts_AvatarStorePath'});
// Migration moved to 099.js to fix a bug
}
});

@ -1,14 +1,211 @@
/* globals SystemLogger */
import fs from 'fs';
import path from 'path';
function log(...args) {
console.log('[AVATAR]', ...args);
}
function logError(...args) {
console.error('[AVATAR]', ...args);
}
function insertAvatar({ details, avatarsFileStore, stream, callback = () => {} }) {
return new Promise((resolve) => {
Meteor.defer(() => {
Meteor.runAsUser('rocket.cat', () => {
avatarsFileStore.insert(details, stream, (err) => {
if (err) {
logError({err});
resolve();
} else {
Meteor.setTimeout(() => {
callback();
}, 200);
}
resolve();
});
});
});
});
}
function batch(arr, limit, fn) {
if (!arr.length) {
return Promise.resolve();
}
return Promise.all(arr.splice(0, limit).map((item) => {
return fn(item);
})).then(() => { return batch(arr, limit, fn); });
}
RocketChat.Migrations.add({
version: 99,
up() {
return RocketChat.models.Subscriptions.find({ lastActivity: {$exists: false} }).forEach(item => {
return RocketChat.models.Subscriptions.update({
rid: item.rid
log('Migrating avatars. This might take a while.');
const query = {
$or: [{
's3.path': {
$exists: true
}
}, {
$set: {
lastActivity: item.ls
'googleCloudStorage.path': {
$exists: true
}
});
}]
};
RocketChat.models.Uploads.find(query).forEach((record) => {
if (record.s3) {
RocketChat.models.Uploads.model.direct.update({_id: record._id}, {
$set: {
'store': 'AmazonS3:Uploads',
AmazonS3: {
path: record.s3.path + record._id
}
},
$unset: {
s3: 1
}
}, {multi: true});
} else {
RocketChat.models.Uploads.model.direct.update({_id: record._id}, {
$set: {
store: 'GoogleCloudStorage:Uploads',
GoogleStorage: {
path: record.googleCloudStorage.path + record._id
}
},
$unset: {
googleCloudStorage: 1
}
}, {multi: true});
}
});
RocketChat.models.Uploads.model.direct.update({
store: 'fileSystem'
}, {
$set: {
store: 'FileSystem:Uploads'
}
}, {
multi: true
});
RocketChat.models.Uploads.model.direct.update({
store: 'rocketchat_uploads'
}, {
$set: {
store: 'GridFS:Uploads'
}
}, {
multi: true
});
const avatarOrigins = [
'upload',
'gravatar',
'facebook',
'twitter',
'github',
'google',
'url',
'gitlab',
'linkedin'
];
const avatarsPathRecord = RocketChat.models.Settings.findOne({_id: 'Accounts_AvatarStorePath'});
const avatarStoreTypeRecord = RocketChat.models.Settings.findOne({_id: 'Accounts_AvatarStoreType'});
const avatarsPath = avatarsPathRecord ? avatarsPathRecord.value : process.env.AVATARS_PATH;
let avatarStoreType = avatarStoreTypeRecord && avatarStoreTypeRecord.value;
const oldAvatarGridFS = new RocketChatFile.GridFS({
name: 'avatars'
});
if (avatarStoreType == null) {
const count = oldAvatarGridFS.countSync();
if (Match.test(count, Number) && count > 0) {
avatarStoreType = 'GridFS';
} else if (Match.test(avatarsPath, String) && avatarsPath.length > 0) {
avatarStoreType = 'FileSystem';
} else {
SystemLogger.error_box('Can\'t define the avatar\'s storage type.\nIf you have avatars missing and they was stored in your file system\nrun the process including the following environment variables: \n AVATARS_PATH=\'YOUR AVATAR\'S DIRECTORY\'\n MIGRATION_VERSION=99,rerun', 'WARNING');
return;
}
}
Meteor.startup(function() {
Meteor.setTimeout(function() {
const avatarsFileStore = FileUpload.getStore('Avatars');
const users = RocketChat.models.Users.find({avatarOrigin: {$in: avatarOrigins}}, {avatarOrigin: 1, username: 1}).fetch();
const usersTotal = users.length;
log('Total users to migrate avatars ->', usersTotal);
let current = 0;
batch(users, 300, (user) => {
const id = `${ user.username }.jpg`;
const gridFSAvatar = oldAvatarGridFS.getFileWithReadStream(id);
log('Migrating', ++current, 'of', usersTotal);
if (gridFSAvatar) {
const details = {
userId: user._id,
type: gridFSAvatar.contentType,
size: gridFSAvatar.length
};
return insertAvatar({
details,
avatarsFileStore,
stream: gridFSAvatar.readStream,
callback() {
oldAvatarGridFS.deleteFile(id);
}
});
}
if (avatarStoreType === 'FileSystem' && avatarsPath && avatarsPath.trim()) {
try {
const filePath = path.join(avatarsPath, id);
const stat = fs.statSync(filePath);
if (stat && stat.isFile()) {
const details = {
userId: user._id,
type: 'image/jpeg',
size: stat.size
};
return insertAvatar({
details,
avatarsFileStore,
stream: fs.createReadStream(filePath),
callback() {
fs.unlinkSync(filePath);
}
});
}
} catch (e) {
logError('Error migrating old avatar', e);
return Promise.resolve();
}
}
}).then(() => {
const avatarsFiles = new Mongo.Collection('avatars.files');
const avatarsChunks = new Mongo.Collection('avatars.chunks');
avatarsFiles.rawCollection().drop();
avatarsChunks.rawCollection().drop();
RocketChat.models.Settings.remove({_id: 'Accounts_AvatarStoreType'});
RocketChat.models.Settings.remove({_id: 'Accounts_AvatarStorePath'});
});
}, 1000);
});
}
});

Loading…
Cancel
Save