Skip to content
Snippets Groups Projects
Commit cb6cf3c5 authored by Gary Chapman's avatar Gary Chapman
Browse files

Merge branch 'develop' into feature/read-only-rooms

# Conflicts:
#	packages/rocketchat-channel-settings/client/views/channelSettings.coffee
#	packages/rocketchat-channel-settings/client/views/channelSettings.html
#	packages/rocketchat-channel-settings/server/methods/saveRoomSettings.coffee
#	packages/rocketchat-lib/server/methods/addUserToRoom.coffee
#	packages/rocketchat-ui/views/app/room.coffee
#	server/methods/createChannel.coffee
#	server/methods/createPrivateGroup.coffee
#	server/methods/joinRoom.coffee
#	server/startup/roomPublishes.coffee
parents 37efedd7 562c7621
No related branches found
No related tags found
No related merge requests found
Showing
with 636 additions and 17 deletions
...@@ -93,6 +93,7 @@ rocketchat:slashcommands-me ...@@ -93,6 +93,7 @@ rocketchat:slashcommands-me
rocketchat:slashcommands-mute rocketchat:slashcommands-mute
rocketchat:slashcommands-topic rocketchat:slashcommands-topic
rocketchat:slashcommands-unarchive rocketchat:slashcommands-unarchive
rocketchat:smarsh-connector
rocketchat:spotify rocketchat:spotify
rocketchat:statistics rocketchat:statistics
rocketchat:streamer rocketchat:streamer
......
...@@ -189,6 +189,7 @@ rocketchat:slashcommands-mute@0.0.1 ...@@ -189,6 +189,7 @@ rocketchat:slashcommands-mute@0.0.1
rocketchat:slashcommands-open@0.0.1 rocketchat:slashcommands-open@0.0.1
rocketchat:slashcommands-topic@0.0.1 rocketchat:slashcommands-topic@0.0.1
rocketchat:slashcommands-unarchive@0.0.1 rocketchat:slashcommands-unarchive@0.0.1
rocketchat:smarsh-connector@0.0.1
rocketchat:sms@0.0.1 rocketchat:sms@0.0.1
rocketchat:spotify@0.0.1 rocketchat:spotify@0.0.1
rocketchat:statistics@0.0.1 rocketchat:statistics@0.0.1
......
...@@ -21,7 +21,7 @@ const pkgdef :Spk.PackageDefinition = ( ...@@ -21,7 +21,7 @@ const pkgdef :Spk.PackageDefinition = (
appVersion = 38, # Increment this for every release. appVersion = 38, # Increment this for every release.
appMarketingVersion = (defaultText = "0.37.1"), appMarketingVersion = (defaultText = "0.38.0"),
# Human-readable representation of appVersion. Should match the way you # Human-readable representation of appVersion. Should match the way you
# identify versions of your app in documentation and marketing. # identify versions of your app in documentation and marketing.
......
## NEXT ## NEXT
## 0.38.0, 2016-Aug-30
- Action links improvements
- Add global event unread-changed-by-subscription
- Add role to disable/enable channel preview (#4127)
- Add room setting to require code to join Room (#4126)
- Add the timer for disconnecting, one minute after going in the background it'll disconnect
- Add Ubuntu 16.04-under 30 seconds snap deployment using SNAPS
- Added File Uploaded text on attachments to i18n
- Added option to populate Rocket Chat with LDAP users (import them) (#4054)
- Changes rtl check in ChatMessages class (#4049)
- Check message timestamp before notifying users
- Do not check for last admin while updating a user
- Don't send offline emails to users who aren't active
- Fix mispelling for seriliazedDescriptor
- Fix multiple notifications (closes #3517) (#4074)
- Fix offering Sandstorm grains without a title
- Fix the verbs in Sandstorm activity events
- Fix user update check for last admin
- Fixed buttons margins and upload file list
- Formatting and adding some missing permissions to standard roles
- Handle locations when disabled
- Improve lazy loading of custom fields and translations
- Improve stream broadcast connection (#4119)
- Improvements/login and registration (#4073)
- Less borders (#4101)
- Make sure Sandstorm.notify is always called for DMs
- Open room correctly after creation and new messages
- Set gitlabs scope to 'api', the only support scope.
- Set message.ts if empty on sendMessage method
- Update moment locales
- Update to Autolinker.js 0.28.0
- Update to depend only on the gMaps API key, add i18n strings for geolocaiotn sharing
- Updated loginform a11y and UX - labels instead of placeholders (#4075)
## 0.37.1, 2016-Aug-17 ## 0.37.1, 2016-Aug-17
- Allow deletion of records with same id on settings - Allow deletion of records with same id on settings
......
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
* [Sandstorm.io](#sandstormio) * [Sandstorm.io](#sandstormio)
* [DPlatform](#dplatform) * [DPlatform](#dplatform)
* [IndieHosters](#indiehosters) * [IndieHosters](#indiehosters)
* [Ubuntu 16.04](#ubuntu-1604)
* [Cloudron.io](#cloudronio) * [Cloudron.io](#cloudronio)
* [Nitrous.io](#nitrousio) * [Nitrous.io](#nitrousio)
* [Heroku](#heroku) * [Heroku](#heroku)
...@@ -28,7 +29,6 @@ ...@@ -28,7 +29,6 @@
* [Raspberry Pi 2](#raspberry-pi-2) * [Raspberry Pi 2](#raspberry-pi-2)
* [Koozali SME](#koozali-sme) * [Koozali SME](#koozali-sme)
* [Ubuntu VPS](#ubuntu-vps) * [Ubuntu VPS](#ubuntu-vps)
* [Ubuntu Software Center](#ubuntu-software-center)
* [About Rocket.Chat](#about-rocketchat) * [About Rocket.Chat](#about-rocketchat)
* [On the News](#on-the-news) * [On the News](#on-the-news)
* [Features](#features) * [Features](#features)
...@@ -90,6 +90,15 @@ Get your Rocket.Chat instance hosted in a "as a Service" style. You register and ...@@ -90,6 +90,15 @@ Get your Rocket.Chat instance hosted in a "as a Service" style. You register and
[![Rocket.Chat on IndieHosters](https://indie.host/signup.png)](https://indiehosters.net/shop/product/rocket-chat-21) [![Rocket.Chat on IndieHosters](https://indie.host/signup.png)](https://indiehosters.net/shop/product/rocket-chat-21)
## Ubuntu 16.04
[![Ubuntu Apps Explorer](https://raw.githubusercontent.com/Sing-Li/bbug/master/images/uappexplorer.png)](https://uappexplorer.com/app/rocketchat-server.rocketchat)
Deploy from shell:
`snap install rocketchat-server`
In under 30 seconds, your Rocket.Chat server will be up and running at `http://host-ip:3000`
## Cloudron.io ## Cloudron.io
Install Rocket.Chat on [Cloudron](https://cloudron.io) Smartserver: Install Rocket.Chat on [Cloudron](https://cloudron.io) Smartserver:
...@@ -172,11 +181,6 @@ Add Rocket.Chat to this world famous time tested small enterprise server today: ...@@ -172,11 +181,6 @@ Add Rocket.Chat to this world famous time tested small enterprise server today:
## Ubuntu VPS ## Ubuntu VPS
Follow these [deployment instructions](https://rocket.chat/docs/installation/manual-installation/ubuntu/) Follow these [deployment instructions](https://rocket.chat/docs/installation/manual-installation/ubuntu/)
## Ubuntu Software Center
Easy one click install right from your Ubuntu Desktop (coming soon)
[![Ubuntu Software Center](https://raw.githubusercontent.com/Sing-Li/bbug/master/images/ubuntusoft.png)]()
# About Rocket.Chat # About Rocket.Chat
Rocket.Chat is a Web Chat Server, developed in JavaScript, using the [Meteor](https://www.meteor.com/install) fullstack framework. Rocket.Chat is a Web Chat Server, developed in JavaScript, using the [Meteor](https://www.meteor.com/install) fullstack framework.
...@@ -246,7 +250,7 @@ It is a great solution for communities and companies wanting to privately host t ...@@ -246,7 +250,7 @@ It is a great solution for communities and companies wanting to privately host t
- Audio calls - Audio calls
- Multi-users Audio Conference - Multi-users Audio Conference
- Screensharing - Screensharing
- XMPP bridge ([try it](https://demo.rocket.chat/channel/xmppbridge)) - XMPP bridge ([try it](https://demo.rocket.chat/channel/general))
- REST APIs - REST APIs
- Remote Locations Video Monitoring - Remote Locations Video Monitoring
- Native real-time APIs for Microsoft C#, Visual Basic, F# and other .NET supported languages ([Get it!](https://www.nuget.org/packages/Rocket.Chat.Net/0.0.12-pre)) - Native real-time APIs for Microsoft C#, Visual Basic, F# and other .NET supported languages ([Get it!](https://www.nuget.org/packages/Rocket.Chat.Net/0.0.12-pre))
......
Meteor.startup(function() { Meteor.startup(function() {
Tracker.autorun(function() { Tracker.autorun(function() {
if (Meteor.user() && Meteor.user().emails && Meteor.user().emails[0] && Meteor.user().emails[0].verified !== true && RocketChat.settings.get('Accounts_EmailVerification') === true && !Session.get('Accounts_EmailVerification_Warning')) { var user = Meteor.user();
if (user && user.emails && user.emails[0] && user.emails[0].verified !== true && RocketChat.settings.get('Accounts_EmailVerification') === true && !Session.get('Accounts_EmailVerification_Warning')) {
toastr.warning(TAPi18n.__('You_have_not_verified_your_email')); toastr.warning(TAPi18n.__('You_have_not_verified_your_email'));
Session.set('Accounts_EmailVerification_Warning', true); Session.set('Accounts_EmailVerification_Warning', true);
} }
......
Meteor.startup(function() {
Tracker.autorun(function() {
var user, utcOffset;
user = Meteor.user();
if (user && user.statusConnection === 'online') {
utcOffset = moment().utcOffset() / 60;
if (user.utcOffset !== utcOffset) {
Meteor.call('userSetUtcOffset', utcOffset);
}
}
});
});
Tracker.autorun ->
user = Meteor.user()
if user?.statusConnection is 'online'
utcOffset = moment().utcOffset() / 60
if user.utcOffset isnt utcOffset
Meteor.call 'updateUserUtcOffset', utcOffset
\ No newline at end of file
...@@ -110,6 +110,8 @@ Accounts.registerLoginHandler(function(loginRequest) { ...@@ -110,6 +110,8 @@ Accounts.registerLoginHandler(function(loginRequest) {
if (username) { if (username) {
newUser.username = username; newUser.username = username;
} }
} else if (loginResult.profile.username) {
newUser.username = loginResult.profile.username;
} }
var userId = Accounts.insertUserDoc({}, newUser); var userId = Accounts.insertUserDoc({}, newUser);
......
AutoCompleteRecords = new Mongo.Collection("autocompleteRecords")
isServerSearch = (rule) -> _.isString(rule.collection)
validateRule = (rule) ->
if rule.subscription? and not Match.test(rule.collection, String)
throw new Error("Collection name must be specified as string for server-side search")
# XXX back-compat message, to be removed
if rule.callback?
console.warn("autocomplete no longer supports callbacks; use event listeners instead.")
isWholeField = (rule) ->
# either '' or null both count as whole field.
return !rule.token
getRegExp = (rule) ->
unless isWholeField(rule)
# Expressions for the range from the last word break to the current cursor position
new RegExp('(^|\\b|\\s)' + rule.token + '([\\w.]*)$')
else
# Whole-field behavior - word characters or spaces
new RegExp('(^)(.*)$')
getFindParams = (rule, filter, limit) ->
# This is a different 'filter' - the selector from the settings
# We need to extend so that we don't copy over rule.filter
selector = _.extend({}, rule.filter || {})
options = { limit: limit }
# Match anything, no sort, limit X
return [ selector, options ] unless filter
if rule.sort and rule.field
sortspec = {}
# Only sort if there is a filter, for faster performance on a match of anything
sortspec[rule.field] = 1
options.sort = sortspec
if _.isFunction(rule.selector)
# Custom selector
_.extend(selector, rule.selector(filter))
else
selector[rule.field] = {
$regex: if rule.matchAll then filter else "^" + filter
# default is case insensitive search - empty string is not the same as undefined!
$options: if (typeof rule.options is 'undefined') then 'i' else rule.options
}
return [ selector, options ]
getField = (obj, str) ->
obj = obj[key] for key in str.split(".")
return obj
class @AutoComplete
@KEYS: [
40, # DOWN
38, # UP
13, # ENTER
27, # ESCAPE
9 # TAB
]
constructor: (settings) ->
@limit = settings.limit || 5
@position = settings.position || "bottom"
@rules = settings.rules
validateRule(rule) for rule in @rules
@expressions = (getRegExp(rule) for rule in @rules)
@matched = -1
@loaded = true
# Reactive dependencies for current matching rule and filter
@ruleDep = new Deps.Dependency
@filterDep = new Deps.Dependency
@loadingDep = new Deps.Dependency
# autosubscribe to the record set published by the server based on the filter
# This will tear down server subscriptions when they are no longer being used.
@sub = null
@comp = Deps.autorun =>
# Stop any existing sub immediately, don't wait
@sub?.stop()
return unless (rule = @matchedRule()) and (filter = @getFilter()) isnt null
# subscribe only for server-side collections
unless isServerSearch(rule)
@setLoaded(true) # Immediately loaded
return
[ selector, options ] = getFindParams(rule, filter, @limit)
# console.debug 'Subscribing to <%s> in <%s>.<%s>', filter, rule.collection, rule.field
@setLoaded(false)
subName = rule.subscription || "autocomplete-recordset"
@sub = Meteor.subscribe(subName,
selector, options, rule.collection, => @setLoaded(true))
teardown: ->
# Stop the reactive computation we started for this autocomplete instance
@comp.stop()
# reactive getters and setters for @filter and the currently matched rule
matchedRule: ->
@ruleDep.depend()
if @matched >= 0 then @rules[@matched] else null
setMatchedRule: (i) ->
@matched = i
@ruleDep.changed()
getFilter: ->
@filterDep.depend()
return @filter
setFilter: (x) ->
@filter = x
@filterDep.changed()
return @filter
isLoaded: ->
@loadingDep.depend()
return @loaded
setLoaded: (val) ->
return if val is @loaded # Don't cause redraws unnecessarily
@loaded = val
@loadingDep.changed()
onKeyUp: ->
return unless @$element # Don't try to do this while loading
startpos = @element.selectionStart
val = @getText().substring(0, startpos)
###
Matching on multiple expressions.
We always go from a matched state to an unmatched one
before going to a different matched one.
###
i = 0
breakLoop = false
while i < @expressions.length
matches = val.match(@expressions[i])
# matching -> not matching
if not matches and @matched is i
@setMatchedRule(-1)
breakLoop = true
# not matching -> matching
if matches and @matched is -1
@setMatchedRule(i)
breakLoop = true
# Did filter change?
if matches and @filter isnt matches[2]
@setFilter(matches[2])
breakLoop = true
break if breakLoop
i++
onKeyDown: (e) ->
return if @matched is -1 or (@constructor.KEYS.indexOf(e.keyCode) < 0)
switch e.keyCode
when 9, 13 # TAB, ENTER
if @select() # Don't jump fields or submit if select successful
e.preventDefault()
e.stopPropagation()
# preventDefault needed below to avoid moving cursor when selecting
when 40 # DOWN
e.preventDefault()
@next()
when 38 # UP
e.preventDefault()
@prev()
when 27 # ESCAPE
@$element.blur()
@hideList()
return
onFocus: ->
# We need to run onKeyUp after the focus resolves,
# or the caret position (selectionStart) will not be correct
Meteor.defer => @onKeyUp()
onBlur: ->
# We need to delay this so click events work
# TODO this is a bit of a hack; see if we can't be smarter
Meteor.setTimeout =>
@hideList()
, 500
onItemClick: (doc, e) => @processSelection(doc, @rules[@matched])
onItemHover: (doc, e) ->
@tmplInst.$(".-autocomplete-item").removeClass("selected")
$(e.target).closest(".-autocomplete-item").addClass("selected")
filteredList: ->
# @ruleDep.depend() # optional as long as we use depend on filter, because list will always get re-rendered
filter = @getFilter() # Reactively depend on the filter
return null if @matched is -1
rule = @rules[@matched]
# Don't display list unless we have a token or a filter (or both)
# Single field: nothing displayed until something is typed
return null unless rule.token or filter
[ selector, options ] = getFindParams(rule, filter, @limit)
Meteor.defer => @ensureSelection()
# if server collection, the server has already done the filtering work
return AutoCompleteRecords.find({}, options) if isServerSearch(rule)
# Otherwise, search on client
return rule.collection.find(selector, options)
isShowing: ->
rule = @matchedRule()
# Same rules as above
showing = rule? and (rule.token or @getFilter())
# Do this after the render
if showing
Meteor.defer =>
@positionContainer()
@ensureSelection()
return showing
# Replace text with currently selected item
select: ->
node = @tmplInst.find(".-autocomplete-item.selected")
return false unless node?
doc = Blaze.getData(node)
return false unless doc # Don't select if nothing matched
@processSelection(doc, @rules[@matched])
return true
processSelection: (doc, rule) ->
replacement = getField(doc, rule.field)
unless isWholeField(rule)
@replace(replacement, rule)
@hideList()
else
# Empty string or doesn't exist?
# Single-field replacement: replace whole field
@setText(replacement)
# Field retains focus, but list is hidden unless another key is pressed
# Must be deferred or onKeyUp will trigger and match again
# TODO this is a hack; see above
@onBlur()
@$element.trigger("autocompleteselect", doc)
return
# Replace the appropriate region
replace: (replacement) ->
startpos = @element.selectionStart
fullStuff = @getText()
val = fullStuff.substring(0, startpos)
val = val.replace(@expressions[@matched], "$1" + @rules[@matched].token + replacement)
posfix = fullStuff.substring(startpos, fullStuff.length)
separator = (if posfix.match(/^\s/) then "" else " ")
finalFight = val + separator + posfix
@setText finalFight
newPosition = val.length + 1
@element.setSelectionRange(newPosition, newPosition)
return
hideList: ->
@setMatchedRule(-1)
@setFilter(null)
getText: ->
return @$element.val() || @$element.text()
setText: (text) ->
if @$element.is("input,textarea")
@$element.val(text)
else
@$element.html(text)
###
Rendering functions
###
positionContainer: ->
# First render; Pick the first item and set css whenever list gets shown
position = @$element.position()
rule = @matchedRule()
offset = getCaretCoordinates(@element, @element.selectionStart)
# In whole-field positioning, we don't move the container and make it the
# full width of the field.
if rule? and isWholeField(rule)
pos =
left: position.left
width: @$element.outerWidth() # position.offsetWidth
else # Normal positioning, at token word
pos =
left: position.left + offset.left
# Position menu from top (above) or from bottom of caret (below, default)
if @position is "top"
pos.bottom = @$element.offsetParent().height() - position.top - offset.top
else
pos.top = position.top + offset.top + parseInt(@$element.css('font-size'))
@tmplInst.$(".-autocomplete-container").css(pos)
ensureSelection : ->
# Re-render; make sure selected item is something in the list or none if list empty
selectedItem = @tmplInst.$(".-autocomplete-item.selected")
unless selectedItem.length
# Select anything
@tmplInst.$(".-autocomplete-item:first-child").addClass("selected")
# Select next item in list
next: ->
currentItem = @tmplInst.$(".-autocomplete-item.selected")
return unless currentItem.length # Don't try to iterate an empty list
currentItem.removeClass("selected")
next = currentItem.next()
if next.length
next.addClass("selected")
else # End of list or lost selection; Go back to first item
@tmplInst.$(".-autocomplete-item:first-child").addClass("selected")
# Select previous item in list
prev: ->
currentItem = @tmplInst.$(".-autocomplete-item.selected")
return unless currentItem.length # Don't try to iterate an empty list
currentItem.removeClass("selected")
prev = currentItem.prev()
if prev.length
prev.addClass("selected")
else # Beginning of list or lost selection; Go to end of list
@tmplInst.$(".-autocomplete-item:last-child").addClass("selected")
# This doesn't need to be reactive because list already changes reactively
# and will cause all of the items to re-render anyway
currentTemplate: -> @rules[@matched].template
AutocompleteTest =
records: AutoCompleteRecords
getRegExp: getRegExp
getFindParams: getFindParams
class Autocomplete
@publishCursor: (cursor, sub) ->
# This also attaches an onStop callback to sub, so we don't need to worry about that.
# https://github.com/meteor/meteor/blob/devel/packages/mongo/collection.js
Mongo.Collection._publishCursor(cursor, sub, "autocompleteRecords")
Meteor.publish 'autocomplete-recordset', (selector, options, collName) ->
collection = global[collName]
unless collection
throw new Error(collName + ' is not defined on the global namespace of the server.')
# This is a semi-documented Meteor feature:
# https://github.com/meteor/meteor/blob/devel/packages/mongo-livedata/collection.js
unless collection._isInsecure()
Meteor._debug(collName + ' is a secure collection, therefore no data was returned because the client could compromise security by subscribing to arbitrary server collections via the browser console. Please write your own publish function.')
return [] # We need this for the subscription to be marked ready
# guard against client-side DOS: hard limit to 50
options.limit = Math.min(50, Math.abs(options.limit)) if options.limit
# Push this into our own collection on the client so they don't interfere with other publications of the named collection.
# This also stops the observer automatically when the subscription is stopped.
Autocomplete.publishCursor( collection.find(selector, options), this)
# Mark the subscription ready after the initial addition of documents.
this.ready()
.-autocomplete-container {
position: absolute;
background: white;
border: 1px solid #DDD;
border-radius: 3px;
box-shadow: 0 0 5px rgba(0, 0, 0, 0.1);
min-width: 180px;
z-index: 1000;
}
.-autocomplete-list {
list-style: none;
margin: 0;
padding: 0;
}
.-autocomplete-item {
display: block;
padding: 5px 10px;
border-bottom: 1px solid #DDD;
}
.-autocomplete-item.selected {
color: white;
background: #4183C4;
text-decoration: none;
}
<template name="inputAutocomplete">
<input type="text" {{attributes}}>
{{> autocompleteContainer}}
</template>
<template name="textareaAutocomplete">
<textarea {{attributes}}>{{> UI.contentBlock}}</textarea>
{{> autocompleteContainer}}
</template>
<template name="_autocompleteContainer">
{{#if isShowing}}
<div class='-autocomplete-container'>
{{#if isLoaded}}
{{#unless empty}}
<ul class='-autocomplete-list'>
{{#each filteredList}}
<li class="-autocomplete-item">
{{#with ../currentTemplate }}
{{#with ..}} {{! original 'data' context to itemTemplate}}
{{> ..}} {{! return value from itemTemplate }}
{{/with}}
{{/with}}
</li>
{{/each}}
</ul>
{{else}}
{{> noMatchTemplate }}
{{/unless}}
{{else}}
{{> loading}}
{{/if}}
</div>
{{/if}}
</template>
<template name="_noMatch">
(<i>no matches</i>)
</template>
Package.describe({
name: 'mizzao:autocomplete',
summary: 'Client/server autocompletion designed for Meteor\'s collections and reactivity',
version: '0.5.1',
git: 'https://github.com/mizzao/meteor-autocomplete.git'
});
Package.onUse(function(api) {
api.versionsFrom('1.0');
api.use(['blaze', 'templating', 'jquery'], 'client');
api.use(['coffeescript', 'underscore']); // both
api.use(['mongo', 'ddp']);
api.use('dandv:caret-position@2.1.0-3', 'client');
// Our files
api.addFiles([
'autocomplete.css',
'inputs.html',
'autocomplete-client.coffee',
'templates.coffee'
], 'client');
api.addFiles([
'autocomplete-server.coffee'
], 'server');
api.export('Autocomplete', 'server');
api.export('AutocompleteTest', {testOnly: true});
});
Package.onTest(function(api) {
api.use('mizzao:autocomplete');
api.use('coffeescript');
api.use('mongo');
api.use('tinytest');
api.addFiles('tests/rule_tests.coffee', 'client');
api.addFiles('tests/regex_tests.coffee', 'client');
api.addFiles('tests/param_tests.coffee', 'client');
api.addFiles('tests/security_tests.coffee');
});
# Events on template instances, sent to the autocomplete class
acEvents =
"keydown": (e, t) -> t.ac.onKeyDown(e)
"keyup": (e, t) -> t.ac.onKeyUp(e)
"focus": (e, t) -> t.ac.onFocus(e)
"blur": (e, t) -> t.ac.onBlur(e)
Template.inputAutocomplete.events(acEvents)
Template.textareaAutocomplete.events(acEvents)
attributes = -> _.omit(@, 'settings') # Render all but the settings parameter
autocompleteHelpers = {
attributes,
autocompleteContainer: new Template('AutocompleteContainer', ->
ac = new AutoComplete( Blaze.getData().settings )
# Set the autocomplete object on the parent template instance
this.parentView.templateInstance().ac = ac
# Set nodes on render in the autocomplete class
this.onViewReady ->
ac.element = this.parentView.firstNode()
ac.$element = $(ac.element)
return Blaze.With(ac, -> Template._autocompleteContainer)
)
}
Template.inputAutocomplete.helpers(autocompleteHelpers)
Template.textareaAutocomplete.helpers(autocompleteHelpers)
Template._autocompleteContainer.rendered = ->
@data.tmplInst = this
Template._autocompleteContainer.destroyed = ->
# Meteor._debug "autocomplete destroyed"
@data.teardown()
###
List rendering helpers
###
Template._autocompleteContainer.events
# t.data is the AutoComplete instance; `this` is the data item
"click .-autocomplete-item": (e, t) -> t.data.onItemClick(this, e)
"mouseenter .-autocomplete-item": (e, t) -> t.data.onItemHover(this, e)
Template._autocompleteContainer.helpers
empty: -> @filteredList().count() is 0
noMatchTemplate: -> @matchedRule().noMatchTemplate || Template._noMatch
...@@ -3,6 +3,10 @@ if (_.isUndefined(RocketChat.models.Subscriptions)) { ...@@ -3,6 +3,10 @@ if (_.isUndefined(RocketChat.models.Subscriptions)) {
} }
RocketChat.models.Subscriptions.isUserInRole = function(userId, roleName, roomId) { RocketChat.models.Subscriptions.isUserInRole = function(userId, roleName, roomId) {
if (roomId == null) {
return false;
}
var query = { var query = {
rid: roomId, rid: roomId,
roles: roleName roles: roleName
......
...@@ -4,7 +4,7 @@ RocketChat.authz.addUserRoles = (userId, roleNames, scope) -> ...@@ -4,7 +4,7 @@ RocketChat.authz.addUserRoles = (userId, roleNames, scope) ->
user = RocketChat.models.Users.findOneById(userId) user = RocketChat.models.Users.findOneById(userId)
if not user if not user
throw new Meteor.Error 'invalid-user' throw new Meteor.Error 'error-invalid-user', 'Invalid user', { function: 'RocketChat.authz.addUserRoles' }
roleNames = [].concat roleNames roleNames = [].concat roleNames
......
RocketChat.models._Base.prototype.roleBaseQuery = function(/*userId, scope*/) { RocketChat.models._Base.prototype.roleBaseQuery = function(/*userId, scope*/) {
return {}; return;
}; };
RocketChat.models._Base.prototype.findRolesByUserId = function(userId/*, options*/) { RocketChat.models._Base.prototype.findRolesByUserId = function(userId/*, options*/) {
...@@ -9,6 +9,11 @@ RocketChat.models._Base.prototype.findRolesByUserId = function(userId/*, options ...@@ -9,6 +9,11 @@ RocketChat.models._Base.prototype.findRolesByUserId = function(userId/*, options
RocketChat.models._Base.prototype.isUserInRole = function(userId, roleName, scope) { RocketChat.models._Base.prototype.isUserInRole = function(userId, roleName, scope) {
var query = this.roleBaseQuery(userId, scope); var query = this.roleBaseQuery(userId, scope);
if (query == null) {
return false;
}
query.roles = roleName; query.roles = roleName;
return !_.isUndefined(this.findOne(query)); return !_.isUndefined(this.findOne(query));
}; };
......
RocketChat.models.Subscriptions.roleBaseQuery = function(userId, scope) { RocketChat.models.Subscriptions.roleBaseQuery = function(userId, scope) {
if (scope == null) {
return;
}
var query = { 'u._id': userId }; var query = { 'u._id': userId };
if (!_.isUndefined(scope)) { if (!_.isUndefined(scope)) {
query.rid = scope; query.rid = scope;
......
...@@ -46,6 +46,7 @@ Meteor.startup -> ...@@ -46,6 +46,7 @@ Meteor.startup ->
{ _id: 'view-full-other-user-info', roles : ['admin'] } { _id: 'view-full-other-user-info', roles : ['admin'] }
{ _id: 'view-history', roles : ['admin', 'user'] } { _id: 'view-history', roles : ['admin', 'user'] }
{ _id: 'view-joined-room', roles : ['guest', 'bot'] } { _id: 'view-joined-room', roles : ['guest', 'bot'] }
{ _id: 'view-join-code', roles : ['admin'] }
{ _id: 'view-logs', roles : ['admin'] } { _id: 'view-logs', roles : ['admin'] }
{ _id: 'view-other-user-channels', roles : ['admin'] } { _id: 'view-other-user-channels', roles : ['admin'] }
{ _id: 'view-p-room', roles : ['admin', 'user'] } { _id: 'view-p-room', roles : ['admin', 'user'] }
...@@ -53,6 +54,7 @@ Meteor.startup -> ...@@ -53,6 +54,7 @@ Meteor.startup ->
{ _id: 'view-room-administration', roles : ['admin'] } { _id: 'view-room-administration', roles : ['admin'] }
{ _id: 'view-statistics', roles : ['admin'] } { _id: 'view-statistics', roles : ['admin'] }
{ _id: 'view-user-administration', roles : ['admin'] } { _id: 'view-user-administration', roles : ['admin'] }
{ _id: 'preview-c-room', roles : ['admin', 'user'] }
] ]
for permission in permissions for permission in permissions
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment