Skip to content

Commit

Permalink
Merge pull request #2745 from RocketChat/improvements/assets
Browse files Browse the repository at this point in the history
Assets improvements for mobile
  • Loading branch information
rodrigok committed Apr 4, 2016
2 parents 0101ab5 + 0e93d6e commit 9f44a4f
Show file tree
Hide file tree
Showing 27 changed files with 230 additions and 100 deletions.
3 changes: 2 additions & 1 deletion packages/rocketchat-assets/package.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ Package.onUse(function(api) {
'underscore',
'webapp',
'rocketchat:file',
'rocketchat:lib'
'rocketchat:lib',
'webapp-hashing'
]);

api.addFiles('server/assets.coffee', 'server');
Expand Down
230 changes: 172 additions & 58 deletions packages/rocketchat-assets/server/assets.coffee
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
sizeOf = Npm.require 'image-size'
mime = Npm.require 'mime-types'
crypto = Npm.require 'crypto'

mime.extensions['image/vnd.microsoft.icon'] = ['ico']

Expand All @@ -9,109 +10,81 @@ mime.extensions['image/vnd.microsoft.icon'] = ['ico']

assets =
'logo':
label: 'logo (svg, png)'
defaultUrl: 'images/logo/logo.svg?v=3'
label: 'logo (svg, png, jpg)'
defaultUrl: 'images/logo/logo.svg'
constraints:
type: 'image'
extensions: ['svg', 'png']
extensions: ['svg', 'png', 'jpg', 'jpeg']
width: undefined
height: undefined
'favicon.ico':
'favicon':
label: 'favicon.ico'
defaultUrl: 'favicon.ico?v=3'
defaultUrl: 'favicon.ico'
constraints:
type: 'image'
extensions: ['ico']
width: undefined
height: undefined
'favicon.svg':
'favicon':
label: 'favicon.svg'
defaultUrl: 'images/logo/icon.svg?v=3'
defaultUrl: 'images/logo/icon.svg'
constraints:
type: 'image'
extensions: ['svg']
width: undefined
height: undefined
'favicon_64.png':
'favicon_64':
label: 'favicon.png (64x64)'
defaultUrl: 'images/logo/favicon-64x64.png?v=3'
defaultUrl: 'images/logo/favicon-64x64.png'
constraints:
type: 'image'
extensions: ['png']
width: 64
height: 64
'favicon_96.png':
'favicon_96':
label: 'favicon.png (96x96)'
defaultUrl: 'images/logo/favicon-96x96.png?v=3'
defaultUrl: 'images/logo/favicon-96x96.png'
constraints:
type: 'image'
extensions: ['png']
width: 96
height: 96
'favicon_128.png':
'favicon_128':
label: 'favicon.png (128x128)'
defaultUrl: 'images/logo/favicon-128x128.png?v=3'
defaultUrl: 'images/logo/favicon-128x128.png'
constraints:
type: 'image'
extensions: ['png']
width: 128
height: 128
'favicon_192.png':
'favicon_192':
label: 'favicon.png (192x192)'
defaultUrl: 'images/logo/android-chrome-192x192.png?v=3'
defaultUrl: 'images/logo/android-chrome-192x192.png'
constraints:
type: 'image'
extensions: ['png']
width: 192
height: 192
'favicon_256.png':
'favicon_256':
label: 'favicon.png (256x256)'
defaultUrl: 'images/logo/favicon-256x256.png?v=3'
defaultUrl: 'images/logo/favicon-256x256.png'
constraints:
type: 'image'
extensions: ['png']
width: 256
height: 256


RocketChat.settings.addGroup 'Assets'
for key, value of assets
RocketChat.settings.add "Assets_#{key}", '', { type: 'asset', group: 'Assets', fileConstraints: value.constraints, i18nLabel: value.label, asset: key }


Meteor.methods
unsetAsset: (asset) ->
unless Meteor.userId()
throw new Meteor.Error 'invalid-user', "[methods] unsetAsset -> Invalid user"

hasPermission = RocketChat.authz.hasPermission Meteor.userId(), 'manage-assets'
unless hasPermission
throw new Meteor.Error 'manage-assets-not-allowed', "[methods] unsetAsset -> Manage assets not allowed"

if not assets[asset]?
throw new Meteor.Error "Invalid_asset"

RocketChatAssetsInstance.deleteFile asset
RocketChat.settings.clearById "Assets_#{asset}"


Meteor.methods
RocketChat.Assets = new class
setAsset: (binaryContent, contentType, asset) ->
unless Meteor.userId()
throw new Meteor.Error 'invalid-user', "[methods] setAsset -> Invalid user"

hasPermission = RocketChat.authz.hasPermission Meteor.userId(), 'manage-assets'
unless hasPermission
throw new Meteor.Error 'manage-assets-not-allowed', "[methods] unsetAsset -> Manage assets not allowed"

if not assets[asset]?
throw new Meteor.Error "Invalid_asset"

if mime.extension(contentType) not in assets[asset].constraints.extensions
extension = mime.extension(contentType)
if extension not in assets[asset].constraints.extensions
throw new Meteor.Error "Invalid_file_type", contentType

file = new Buffer(binaryContent, 'binary')

if assets[asset].constraints.width? or assets[asset].constraints.height?
dimensions = sizeOf file

Expand All @@ -126,28 +99,168 @@ Meteor.methods
ws = RocketChatAssetsInstance.createWriteStream asset, contentType
ws.on 'end', Meteor.bindEnvironment ->
Meteor.setTimeout ->
RocketChat.settings.updateById "Assets_#{asset}", "/assets/#{asset}"
RocketChat.settings.updateById "Assets_#{asset}", {
url: "/assets/#{asset}.#{extension}"
defaultUrl: assets[asset].defaultUrl
}
, 200

rs.pipe ws
return

unsetAsset: (asset) ->
if not assets[asset]?
throw new Meteor.Error "Invalid_asset"

RocketChatAssetsInstance.deleteFile asset

RocketChat.settings.updateById "Assets_#{asset}", {defaultUrl: assets[asset].defaultUrl}
return

refreshClients: ->
process.emit('message', {refresh: 'client'})


RocketChat.settings.addGroup 'Assets'
for key, value of assets
do (key, value) ->
RocketChat.settings.add "Assets_#{key}", {defaultUrl: value.defaultUrl}, { type: 'asset', group: 'Assets', fileConstraints: value.constraints, i18nLabel: value.label, asset: key, public: true }

Meteor.startup ->
forEachAsset = (key, value) ->
RocketChat.settings.get "Assets_#{key}", (settingKey, settingValue) ->
if settingValue is undefined
value.cache = undefined
return

file = RocketChatAssetsInstance.getFileWithReadStream key
if not file
value.cache = undefined
return

data = []
file.readStream.on 'data', Meteor.bindEnvironment (chunk) ->
data.push chunk

file.readStream.on 'end', Meteor.bindEnvironment ->
data = Buffer.concat(data)
hash = crypto.createHash('sha1').update(data).digest('hex')
extension = settingValue.url.split('.').pop()
value.cache =
path: "assets/#{key}.#{extension}"
cacheable: false
sourceMapUrl: undefined
where: 'client'
type: 'asset'
content: data
extension: extension
url: "/assets/#{key}.#{extension}?#{hash}"
size: file.length
uploadDate: file.uploadDate
contentType: file.contentType
hash: hash


forEachAsset(key, value) for key, value of assets

calculateClientHash = WebAppHashing.calculateClientHash
WebAppHashing.calculateClientHash = (manifest, includeFilter, runtimeConfigOverride) ->
for key, value of assets
if not value.cache? && not value.defaultUrl?
continue

manifestItem = _.find manifest, (item) ->
return item.path is key

cache = {}
if value.cache
cache =
path: value.cache.path
cacheable: value.cache.cacheable
sourceMapUrl: value.cache.sourceMapUrl
where: value.cache.where
type: value.cache.type
url: value.cache.url
size: value.cache.size
hash: value.cache.hash

WebAppInternals.staticFiles["/__cordova/assets/#{key}"] = value.cache
WebAppInternals.staticFiles["/__cordova/assets/#{key}.#{value.cache.extension}"] = value.cache
else
extension = value.defaultUrl.split('.').pop()
cache =
path: "assets/#{key}.#{extension}"
cacheable: false
sourceMapUrl: undefined
where: 'client'
type: 'asset'
url: "/assets/#{key}.#{extension}?v3"
# size: value.cache.size
hash: 'v3'

WebAppInternals.staticFiles["/__cordova/assets/#{key}"] = WebAppInternals.staticFiles["/__cordova/#{value.defaultUrl}"]
WebAppInternals.staticFiles["/__cordova/assets/#{key}.#{extension}"] = WebAppInternals.staticFiles["/__cordova/#{value.defaultUrl}"]


if manifestItem?
index = manifest.indexOf(manifestItem)

manifest[index] = cache
else
manifest.push cache

return calculateClientHash.call this, manifest, includeFilter, runtimeConfigOverride


Meteor.methods
refreshClients: ->
unless Meteor.userId()
throw new Meteor.Error 'invalid-user', "[methods] unsetAsset -> Invalid user"

hasPermission = RocketChat.authz.hasPermission Meteor.userId(), 'manage-assets'
unless hasPermission
throw new Meteor.Error 'manage-assets-not-allowed', "[methods] unsetAsset -> Manage assets not allowed"

RocketChat.Assets.refreshClients


unsetAsset: (asset) ->
unless Meteor.userId()
throw new Meteor.Error 'invalid-user', "[methods] unsetAsset -> Invalid user"

hasPermission = RocketChat.authz.hasPermission Meteor.userId(), 'manage-assets'
unless hasPermission
throw new Meteor.Error 'manage-assets-not-allowed', "[methods] unsetAsset -> Manage assets not allowed"

RocketChat.Assets.unsetAsset asset


setAsset: (binaryContent, contentType, asset) ->
unless Meteor.userId()
throw new Meteor.Error 'invalid-user', "[methods] setAsset -> Invalid user"

hasPermission = RocketChat.authz.hasPermission Meteor.userId(), 'manage-assets'
unless hasPermission
throw new Meteor.Error 'manage-assets-not-allowed', "[methods] unsetAsset -> Manage assets not allowed"

RocketChat.Assets.setAsset binaryContent, contentType, asset
return


WebApp.connectHandlers.use '/assets/', Meteor.bindEnvironment (req, res, next) ->
params =
asset: decodeURIComponent(req.url.replace(/^\//, '').replace(/\?.*$/, ''))

file = RocketChatAssetsInstance.getFileWithReadStream params.asset
asset: decodeURIComponent(req.url.replace(/^\//, '').replace(/\?.*$/, '')).replace(/\.[^.]*$/, '')

# res.setHeader 'Content-Disposition', 'inline'
file = assets[params.asset]?.cache

if not file?
if assets[params.asset]?.defaultUrl?
res.writeHead 301,
Location: Meteor.absoluteUrl(assets[params.asset].defaultUrl)
req.url = '/'+assets[params.asset].defaultUrl
WebAppInternals.staticFilesMiddleware WebAppInternals.staticFiles, req, res, next
else
res.writeHead 404
res.end()
res.end()

return

reqModifiedHeader = req.headers["if-modified-since"];
Expand All @@ -162,7 +275,8 @@ WebApp.connectHandlers.use '/assets/', Meteor.bindEnvironment (req, res, next) -
res.setHeader 'Expires', '-1'
res.setHeader 'Last-Modified', file.uploadDate?.toUTCString() or new Date().toUTCString()
res.setHeader 'Content-Type', file.contentType
res.setHeader 'Content-Length', file.length
res.setHeader 'Content-Length', file.size

file.readStream.pipe res
res.writeHead 200
res.end(file.content)
return
3 changes: 1 addition & 2 deletions packages/rocketchat-lib/i18n/de.i18n.json
Original file line number Diff line number Diff line change
Expand Up @@ -400,7 +400,6 @@
"Layout" : "Layout",
"Layout_Home_Body" : "Inhalt der Startseite",
"Layout_Home_Title" : "Titel der Startseite",
"Layout_Login_Header" : "Kopfzeile der Anmeldeseite",
"Layout_Login_Terms" : "Anmeldebedingungen",
"Layout_Privacy_Policy" : "Datenschutzbestimmungen",
"Layout_Sidenav_Footer" : "Seitenfußzeile",
Expand Down Expand Up @@ -1011,4 +1010,4 @@
"Your_Open_Source_solution" : "Deine eigene Open-Source-Chat-Lösung.",
"Your_password_is_wrong" : "Falsches Passwort",
"Your_push_was_sent_to_s_devices" : "Die Push-Nachricht wurde an %s Geräte gesendet."
}
}
3 changes: 2 additions & 1 deletion packages/rocketchat-lib/i18n/en.i18n.json
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@
"Application_added" : "Application added",
"Application_Name" : "Application Name",
"Application_updated" : "Application updated",
"Apply_and_refresh_all_clients" : "Apply and refresh all clients",
"Archive" : "Archive",
"Archive_Unarchive" : "Archive / Unarchive",
"are_also_typing" : "are also typing",
Expand Down Expand Up @@ -185,6 +186,7 @@
"Clear_all_unreads_question" : "Clear all unreads?",
"Client_ID" : "Client ID",
"Client_Secret" : "Client Secret",
"Clients_will_refresh_in_a_few_seconds" : "Clients will refresh in a few seconds",
"close" : "close",
"Closed" : "Closed",
"coming_soon" : "coming soon",
Expand Down Expand Up @@ -407,7 +409,6 @@
"Layout" : "Layout",
"Layout_Home_Body" : "Home Body",
"Layout_Home_Title" : "Home Title",
"Layout_Login_Header" : "Login Header",
"Layout_Login_Terms" : "Login Terms",
"Layout_Privacy_Policy" : "Privacy Policy",
"Layout_Sidenav_Footer" : "Side Navigation Footer",
Expand Down
3 changes: 1 addition & 2 deletions packages/rocketchat-lib/i18n/es.i18n.json
Original file line number Diff line number Diff line change
Expand Up @@ -288,7 +288,6 @@
"Layout" : "Diseño",
"Layout_Home_Body" : "Cuerpo de pagina de inicio",
"Layout_Home_Title" : "Titulo de pagina de inicio",
"Layout_Login_Header" : "Encabezado de Inicio de Sesión ",
"Layout_Login_Terms" : "Términos de Inicio de Sesión ",
"Layout_Privacy_Policy" : "Política de Privacidad",
"Layout_Sidenav_Footer" : "Pie de Pagina de la Barra de Navegación Lateral",
Expand Down Expand Up @@ -728,4 +727,4 @@
"Your_entry_has_been_deleted" : "Tu entrada ha sido eliminada",
"Your_file_has_been_deleted" : "Tu archivo ha sido eliminado.",
"Your_Open_Source_solution" : "Tu propia solución de chat de código abierto"
}
}
Loading

0 comments on commit 9f44a4f

Please sign in to comment.