Create page to manage assets and change favicons

pull/1557/head
Rodrigo Nascimento 10 years ago
parent d0736e7dab
commit 2435165fef
  1. 1
      .meteor/packages
  2. 1
      .meteor/versions
  3. 5
      i18n/en.i18n.json
  4. 1
      packages/rocketchat-assets/.npm/package/.gitignore
  5. 7
      packages/rocketchat-assets/.npm/package/README
  6. 7
      packages/rocketchat-assets/.npm/package/npm-shrinkwrap.json
  7. 28
      packages/rocketchat-assets/package.js
  8. 165
      packages/rocketchat-assets/server/assets.coffee
  9. 3
      packages/rocketchat-authorization/server/startup.coffee
  10. 13
      packages/rocketchat-lib/server/functions/settings.coffee
  11. 39
      packages/rocketchat-theme/assets/stylesheets/base.less
  12. 30
      packages/rocketchat-ui-admin/admin/admin.coffee
  13. 25
      packages/rocketchat-ui-admin/admin/admin.html
  14. 14
      packages/rocketchat-ui-master/master/main.html

@ -123,3 +123,4 @@ yasaricli:slugify
yasinuslu:blaze-meta
# sanjo:jasmine
# velocity:html-reporter
rocketchat:assets

@ -122,6 +122,7 @@ reactive-dict@1.1.3
reactive-var@1.0.6
reload@1.1.4
retry@1.0.4
rocketchat:assets@0.0.1
rocketchat:authorization@0.0.1
rocketchat:autolinker@0.0.1
rocketchat:channel-settings@0.0.1

@ -168,10 +168,14 @@
"Install_FxOs_done" : "Great! You can now use Rocket.Chat via the icon on your homescreen. Have fun with Rocket.Chat!",
"Install_FxOs_error" : "Sorry, that did not work as intended! The following error appeared:",
"Install_FxOs_follow_instructions" : "Please confirm the app installation on your device (press \"Install\" when prompted).",
"Invalid_asset" : "Invalid asset",
"Invalid_confirm_pass" : "The password confirmation does not match password",
"Invalid_Secret_URL" : "Invalid Secret URL",
"Invalid_secret_URL_message" : "The URL provided is invalid.",
"Invalid_email" : "The e-mail entered is invalid",
"Invalid_file_height" : "Invalid file height",
"Invalid_file_type" : "Invalid file type",
"Invalid_file_width" : "Invalid file width",
"Invalid_name" : "The name must not be empty",
"Invalid_pass" : "The password must not be empty",
"Invalid_room_name" : "<strong>%s</strong> is not a valid room name,<br/> use only letters, numbers and dashes",
@ -419,6 +423,7 @@
"Unread_Rooms" : "Unread Rooms",
"Unread_Rooms_Mode" : "Unread Rooms Mode",
"Upload_file_question" : "Upload file?",
"Uploading_file" : "Uploading file...",
"Use_Emojis" : "Use Emojis",
"Use_initials_avatar" : "Use your username initials",
"use_menu" : "Use the side menu to access your rooms and chats",

@ -0,0 +1,7 @@
This directory and the files immediately inside it are automatically generated
when you change this package's NPM dependencies. Commit the files in this
directory (npm-shrinkwrap.json, .gitignore, and this README) to source control
so that others run the same versions of sub-dependencies.
You should NOT check in the node_modules directory that Meteor automatically
creates; if you are using git, the .gitignore file tells git to ignore it.

@ -0,0 +1,7 @@
{
"dependencies": {
"image-size": {
"version": "0.4.0"
}
}
}

@ -0,0 +1,28 @@
Package.describe({
name: 'rocketchat:assets',
version: '0.0.1',
summary: '',
git: ''
});
Package.onUse(function(api) {
api.versionsFrom('1.0');
api.use([
'coffeescript',
'underscore',
'webapp',
'rocketchat:file',
'rocketchat:lib@0.0.1'
]);
api.addFiles('server/assets.coffee', 'server');
});
Npm.depends({
"image-size": "0.4.0"
});
Package.onTest(function(api) {
});

@ -0,0 +1,165 @@
sizeOf = Npm.require 'image-size'
@RocketChatAssetsInstance = new RocketChatFile.GridFS
name: 'assets'
assets =
'favicon.ico':
label: 'favicon.ico'
defaultUrl: 'favicon.ico?v=3'
constraints:
type: 'image'
contentType: 'image/vnd.microsoft.icon'
extention: 'ico'
width: undefined
height: undefined
'favicon.svg':
label: 'favicon.svg'
defaultUrl: '/images/logo/icon.svg?v=3'
constraints:
type: 'image'
contentType: 'image/svg+xml'
extention: 'svg'
width: undefined
height: undefined
'favicon_64.png':
label: 'favicon.png (64x64)'
defaultUrl: 'images/logo/favicon-64x64.png?v=3'
constraints:
type: 'image'
contentType: 'image/png'
extention: 'png'
width: 64
height: 64
'favicon_96.png':
label: 'favicon.png (96x96)'
defaultUrl: 'images/logo/favicon-96x96.png?v=3'
constraints:
type: 'image'
contentType: 'image/png'
extention: 'png'
width: 96
height: 96
'favicon_128.png':
label: 'favicon.png (128x128)'
defaultUrl: 'images/logo/favicon-128x128.png?v=3'
constraints:
type: 'image'
contentType: 'image/png'
extention: 'png'
width: 128
height: 128
'favicon_192.png':
label: 'favicon.png (192x192)'
defaultUrl: 'images/logo/android-chrome-192x192.png?v=3'
constraints:
type: 'image'
contentType: 'image/png'
extention: 'png'
width: 192
height: 192
'favicon_256.png':
label: 'favicon.png (256x256)'
defaultUrl: 'images/logo/favicon-256x256.png?v=3'
constraints:
type: 'image'
contentType: 'image/png'
extention: '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
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 contentType isnt assets[asset].constraints.contentType
throw new Meteor.Error "Invalid_file_type"
file = new Buffer(binaryContent, 'binary')
if assets[asset].constraints.width? or assets[asset].constraints.height?
dimensions = sizeOf file
if assets[asset].constraints.width? and assets[asset].constraints.width isnt dimensions.width
throw new Meteor.Error "Invalid_file_width"
if assets[asset].constraints.height? and assets[asset].constraints.height isnt dimensions.height
throw new Meteor.Error "Invalid_file_height"
rs = RocketChatFile.bufferToStream file
RocketChatAssetsInstance.deleteFile asset
ws = RocketChatAssetsInstance.createWriteStream asset, contentType
ws.on 'end', Meteor.bindEnvironment ->
Meteor.setTimeout ->
RocketChat.settings.updateById "Assets_#{asset}", "/assets/#{asset}"
, 200
rs.pipe ws
return
WebApp.connectHandlers.use '/assets/', (req, res, next) ->
params =
asset: decodeURIComponent(req.url.replace(/^\//, '').replace(/\?.*$/, ''))
file = RocketChatAssetsInstance.getFileWithReadStream params.asset
# res.setHeader 'Content-Disposition', 'inline'
if not file?
if assets[params.asset]?.defaultUrl?
res.writeHead 301,
Location: Meteor.absoluteUrl(assets[params.asset].defaultUrl)
else
res.writeHead 404
res.end()
return
reqModifiedHeader = req.headers["if-modified-since"];
if reqModifiedHeader?
if reqModifiedHeader == file.uploadDate?.toUTCString()
res.setHeader 'Last-Modified', reqModifiedHeader
res.writeHead 304
res.end()
return
res.setHeader 'Cache-Control', 'public, max-age=0'
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
file.readStream.pipe res
return

@ -89,6 +89,9 @@ Meteor.startup ->
{ _id: 'access-permissions',
roles : ['admin']}
{ _id: 'manage-assets',
roles : ['admin']}
]
#alanning:roles

@ -91,6 +91,19 @@ RocketChat.settings.updateById = (_id, value) ->
return RocketChat.models.Settings.updateValueById _id, value
###
# Update a setting by id
# @param {String} _id
###
RocketChat.settings.clearById = (_id) ->
# console.log '[functions] RocketChat.settings.clearById -> '.green, 'arguments:', arguments
if not _id?
return false
return RocketChat.models.Settings.updateValueById _id, undefined
###
# Update a setting by id
###

@ -1729,6 +1729,45 @@ a.github-fork {
width: 100%;
padding: 0;
}
.settings-file-preview {
display: flex;
align-items: center;
input[type=file] {
position: absolute !important;
width: 100%;
top: 0;
left: 0;
height: 100%;
opacity: 0;
z-index: 10000;
cursor: pointer;
* {
cursor: pointer;
}
}
.preview {
height: 50px;
width: 100px;
border-radius: 4px;
overflow: hidden;
box-shadow: 0 0 1px rgba(0,0,0,.5) inset;
background-size: contain;
background-position: center center;
background-repeat: no-repeat;
&.no-file {
background-color: #fafafa;
display: flex;
align-items: center;
justify-content: center;
color: #ccc;
font-size: 24px;
}
}
}
}
.page-static {

@ -40,6 +40,9 @@ Template.admin.helpers
selectedOption: (_id, val) ->
return RocketChat.settings.get(_id) is val
random: ->
return Random.id()
Template.admin.events
"click .submit .save": (e, t) ->
group = FlowRouter.getParam('group')
@ -100,6 +103,33 @@ Template.admin.events
swal config, ->
Meteor.call 'removeOAuthService', name
"click .delete-asset": ->
Meteor.call 'unsetAsset', @asset
"change input[type=file]": ->
e = event.originalEvent or event
files = e.target.files
if not files or files.length is 0
files = e.dataTransfer?.files or []
for blob in files
toastr.info TAPi18n.__ 'Uploading_file'
if @fileConstraints.contentType isnt blob.type
toastr.error TAPi18n.__ 'Invalid_file_type'
return
reader = new FileReader()
reader.readAsBinaryString(blob)
reader.onloadend = =>
Meteor.call 'setAsset', reader.result, blob.type, @asset, (err, data) ->
if err?
toastr.error TAPi18n.__ err.error
console.log err.error
return
toastr.success TAPi18n.__ 'File_uploaded'
Template.admin.onRendered ->
Tracker.afterFlush ->

@ -43,13 +43,16 @@
<input type="text" name="{{_id}}" value="{{value}}" placeholder="{{placeholder}}" />
{{/if}}
{{/if}}
{{#if $eq type 'int'}}
<input type="number" name="{{_id}}" value="{{value}}" placeholder="{{placeholder}}" />
{{/if}}
{{#if $eq type 'boolean'}}
<label><input type="radio" name="{{_id}}" value="1" checked="{{$eq value true}}" /> {{_ "True"}}</label>
<label><input type="radio" name="{{_id}}" value="0" checked="{{$eq value false}}" /> {{_ "False"}}</label>
{{/if}}
{{#if $eq type 'select'}}
<select name="{{_id}}">
{{#each values}}
@ -57,9 +60,31 @@
{{/each}}
</select>
{{/if}}
{{#if $eq type 'color'}}
<input type="text" class="minicolors" name="{{_id}}" value="{{value}}" />
{{/if}}
{{#if $eq type 'asset'}}
{{#if value}}
<div class="settings-file-preview">
<div class="preview" style="background-image:url({{value}}?_dc={{random}});"></div>
<div class="action">
<button type="button" class="button red delete-asset"><i class="icon-trash"></i>{{_ 'Delete'}}</button>
</div>
</div>
{{else}}
<div class="settings-file-preview">
<div class="preview no-file"><i class="icon-upload"></i></div>
<div class="action">
<div class="button primary"><i class="icon-trash"></i>{{_ 'Select_file'}}
<input type="file" accept="{{fileConstraints.contentType}}" />
</div>
</div>
</div>
{{/if}}
{{/if}}
{{#if description}}
<div class="settings-description">{{{description}}}</div>
{{/if}}

@ -13,18 +13,18 @@
<meta name="msapplication-config" content="/images/logo/browserconfig.xml?v=3">
<meta name="theme-color" content="#04436a">
<link rel="manifest" href="/images/logo/manifest.json?v=3">
<link rel="icon" sizes="any" type="image/svg+xml" href="/images/logo/icon.svg?v=3">
<link rel="icon" sizes="256x256" type="image/png" href="/images/logo/favicon-256x256.png?v=3">
<link rel="icon" sizes="192x192" type="image/png" href="/images/logo/android-chrome-192x192.png?v=3">
<link rel="icon" sizes="128x128" type="image/png" href="/images/logo/favicon-128x128.png?v=3">
<link rel="icon" sizes="96x96" type="image/png" href="/images/logo/favicon-96x96.png?v=3">
<link rel="icon" sizes="64x64" type="image/png" href="/images/logo/favicon-64x64.png?v=3">
<link rel="icon" sizes="any" type="image/svg+xml" href="/assets/favicon.svg?v=3">
<link rel="icon" sizes="256x256" type="image/png" href="/assets/favicon_256.png?v=3">
<link rel="icon" sizes="192x192" type="image/png" href="/assets/favicon_192.png?v=3">
<link rel="icon" sizes="128x128" type="image/png" href="/assets/favicon_128.png?v=3">
<link rel="icon" sizes="96x96" type="image/png" href="/assets/favicon_96.png?v=3">
<link rel="icon" sizes="64x64" type="image/png" href="/assets/favicon_64.png?v=3">
<!--
<link rel="icon" sizes="48x48" type="image/png" href="/images/logo/favicon-48x48.png?v=3">
<link rel="icon" sizes="32x32" type="image/png" href="/images/logo/favicon-32x32.png?v=3">
<link rel="icon" sizes="16x16" type="image/png" href="/images/logo/favicon-16x16.png?v=3">
-->
<link rel="shortcut icon" sizes="16x16 32x32 48x48" type="image/x-icon" href="/favicon.ico?v=3" />
<link rel="shortcut icon" sizes="16x16 32x32 48x48" type="image/x-icon" href="/assets/favicon.ico?v=3" />
<link rel="apple-touch-icon" sizes="57x57" href="/images/logo/apple-touch-icon-57x57.png?v=3">
<link rel="apple-touch-icon" sizes="60x60" href="/images/logo/apple-touch-icon-60x60.png?v=3">
<link rel="apple-touch-icon" sizes="72x72" href="/images/logo/apple-touch-icon-72x72.png?v=3">

Loading…
Cancel
Save