diff --git a/.meteor/packages b/.meteor/packages
index 4610b7e4b2e..1f8dcda4e2e 100644
--- a/.meteor/packages
+++ b/.meteor/packages
@@ -63,7 +63,7 @@ nimble:restivus
nooitaf:colors
pauli:accounts-linkedin
percolate:migrations
-percolatestudio:synced-cron
+percolate:synced-cron
raix:handlebar-helpers
raix:ui-dropped-event
tap:i18n
@@ -80,3 +80,4 @@ jalik:ufs-gridfs
monbro:mongodb-mapreduce-aggregation
rocketchat:custom-oauth
rocketchat:gitlab
+rocketchat:statistics
diff --git a/.meteor/versions b/.meteor/versions
index 2ac6859d26b..6d51817b864 100644
--- a/.meteor/versions
+++ b/.meteor/versions
@@ -44,8 +44,8 @@ jparker:crypto-md5@0.1.1
jparker:gravatar@0.4.1
jquery@1.11.3_2
json@1.0.3
-kadira:blaze-layout@2.0.0
-kadira:flow-router@2.3.0
+kadira:blaze-layout@2.0.1
+kadira:flow-router@2.4.0
kevohagan:sweetalert@1.0.0
konecty:autolinker@1.0.2
konecty:change-case@2.3.0
@@ -63,7 +63,7 @@ matb33:collection-hooks@0.7.14
meteor@1.1.6
meteor-developer@1.1.3
meteor-platform@1.2.2
-meteorhacks:kadira@2.23.0
+meteorhacks:kadira@2.23.1
meteorhacks:meteorx@1.3.1
meteorspark:util@0.2.0
minifiers@1.1.5
@@ -88,7 +88,7 @@ ordered-dict@1.0.3
pauli:accounts-linkedin@1.1.2
pauli:linkedin@1.1.2
percolate:migrations@0.7.6
-percolatestudio:synced-cron@1.1.0
+percolate:synced-cron@1.2.1
qnub:emojione@0.0.3
raix:eventemitter@0.1.3
raix:eventstate@0.0.2
@@ -115,6 +115,7 @@ rocketchat:markdown@0.0.1
rocketchat:me@0.0.1
rocketchat:mentions@0.0.1
rocketchat:oembed@0.0.1
+rocketchat:statistics@0.0.1
rocketchat:webrtc@0.0.1
routepolicy@1.0.5
service-configuration@1.0.4
diff --git a/client/views/admin/adminStatistics.coffee b/client/views/admin/adminStatistics.coffee
index 454a0e16711..f0d629a7f78 100644
--- a/client/views/admin/adminStatistics.coffee
+++ b/client/views/admin/adminStatistics.coffee
@@ -26,12 +26,30 @@ Template.adminStatistics.helpers
return out
numFormat: (number) ->
return _.numberFormat(number, 2)
+ optOut: ->
+ return RocketChat.settings.get 'Statistics_opt_out'
+
+Template.adminStatistics.events
+ 'click input[name=opt-out-statistics]': (e) ->
+ if $(e.currentTarget).prop('checked')
+ $('#opt-out-warning').show()
+ RocketChat.settings.set 'Statistics_opt_out', true, ->
+ toastr.success TAPi18next.t 'project:Settings_updated'
+ else
+ $('#opt-out-warning').hide()
+ RocketChat.settings.set 'Statistics_opt_out', false, ->
+ toastr.success TAPi18next.t 'project:Settings_updated'
Template.adminStatistics.onRendered ->
Tracker.afterFlush ->
SideNav.setFlex "adminFlex"
SideNav.openFlex()
+ if RocketChat.settings.get 'Statistics_opt_out'
+ $('#opt-out-warning').show()
+ else
+ $('#opt-out-warning').hide()
+
Template.adminStatistics.onCreated ->
instance = @
@statistics = new ReactiveVar {}
diff --git a/client/views/admin/adminStatistics.html b/client/views/admin/adminStatistics.html
index 24d29dc72df..fd02286c166 100644
--- a/client/views/admin/adminStatistics.html
+++ b/client/views/admin/adminStatistics.html
@@ -28,6 +28,10 @@
{{_ "Stats_Online_Users"}} |
{{statistics.onlineUsers}} |
+
+ | {{_ "Stats_Away_Users"}} |
+ {{statistics.awayUsers}} |
+
| {{_ "Stats_Offline_Users"}} |
{{statistics.offlineUsers}} |
@@ -105,6 +109,12 @@
{{_ "Please_wait_statistics"}}
{{/if}}
{{/unless}}
+
+
+
+ {{_ "Opt_out_statistics_warning"}}
+
+
\ No newline at end of file
diff --git a/i18n/en.i18n.json b/i18n/en.i18n.json
index 8fbf6ba1533..119d29f1d79 100644
--- a/i18n/en.i18n.json
+++ b/i18n/en.i18n.json
@@ -177,6 +177,8 @@
"Notify_all_in_this_room" : "Notify all in this room",
"Online" : "Online",
"Oops!" : "Oops",
+ "Opt_out_statistics": "Don't send my anonymous statistics to Rocket.Chat",
+ "Opt_out_statistics_warning": "By sending your anonymous statistics, you'll help us identify how many instances of Rocket.Chat are deployed, as well as how good the system is behaving, so we can further improve it. If you want to continue sending us your anonymous statistics, uncheck the above checkbox. Thank you.",
"others" : "others",
"Password" : "Password",
"Password_changed_successfully" : "Password changed successfully",
@@ -242,6 +244,7 @@
"Stats_Avg_Private_Group_Users": "Average Private Group Users",
"Stats_Max_Room_Users": "Max Rooms Users",
"Stats_Non_Active_Users": "Inactive Users",
+ "Stats_Away_Users": "Away Users",
"Stats_Offline_Users": "Offline Users",
"Stats_Online_Users": "Online Users",
"Stats_OS_Arch": "OS Arch",
diff --git a/packages/rocketchat-lib/package.js b/packages/rocketchat-lib/package.js
index fc6d85e3824..4aa1ae6af4d 100644
--- a/packages/rocketchat-lib/package.js
+++ b/packages/rocketchat-lib/package.js
@@ -20,14 +20,10 @@ Package.onUse(function(api) {
api.addFiles([
'server/functions/checkUsernameAvailability.coffee',
- 'server/functions/getAvgStatistics.coffee',
- 'server/functions/getStatistics.coffee',
- 'server/functions/saveStatistics.coffee',
'server/functions/setUsername.coffee'
], ['server']);
api.addFiles([
- 'server/methods/generateStatistics.coffee',
'server/methods/joinDefaultChannels.coffee',
'server/methods/setAdminStatus.coffee',
'server/methods/setUsername.coffee',
diff --git a/packages/rocketchat-lib/server/functions/checkUsernameAvailability.coffee b/packages/rocketchat-lib/server/functions/checkUsernameAvailability.coffee
index 1dd34b347b5..ea855eb7ecc 100644
--- a/packages/rocketchat-lib/server/functions/checkUsernameAvailability.coffee
+++ b/packages/rocketchat-lib/server/functions/checkUsernameAvailability.coffee
@@ -1,2 +1,2 @@
RocketChat.checkUsernameAvailability = (username) ->
- return not Meteor.users.findOne({ username: { $regex : new RegExp("^" + _.trim(username) + "$", "i") } })
\ No newline at end of file
+ return not Meteor.users.findOne({ username: { $regex : new RegExp("^" + s.trim(username) + "$", "i") } })
\ No newline at end of file
diff --git a/packages/rocketchat-lib/server/functions/saveStatistics.coffee b/packages/rocketchat-lib/server/functions/saveStatistics.coffee
deleted file mode 100644
index fca2fb1c808..00000000000
--- a/packages/rocketchat-lib/server/functions/saveStatistics.coffee
+++ /dev/null
@@ -1,5 +0,0 @@
-RocketChat.saveStatistics = ->
- statistics = RocketChat.getStatistics()
- statistics.createdAt = new Date
- Statistics.insert statistics
- return statistics
\ No newline at end of file
diff --git a/packages/rocketchat-lib/server/functions/setUsername.coffee b/packages/rocketchat-lib/server/functions/setUsername.coffee
index b16764b61fa..aed65e1e30a 100644
--- a/packages/rocketchat-lib/server/functions/setUsername.coffee
+++ b/packages/rocketchat-lib/server/functions/setUsername.coffee
@@ -1,5 +1,5 @@
RocketChat.setUsername = (user, username) ->
- username = _.trim username
+ username = s.trim username
if not user or not username
return false
diff --git a/packages/rocketchat-lib/server/methods/generateStatistics.coffee b/packages/rocketchat-lib/server/methods/generateStatistics.coffee
deleted file mode 100644
index 2f6174ea08a..00000000000
--- a/packages/rocketchat-lib/server/methods/generateStatistics.coffee
+++ /dev/null
@@ -1,11 +0,0 @@
-Meteor.methods
- generateStatistics: ->
- if not Meteor.userId()
- throw new Meteor.Error('invalid-user', "[methods] generateStatistics -> Invalid user")
-
- console.log '[methods] generateStatistics -> '.green, 'userId:', Meteor.userId(), 'arguments:', arguments
-
- unless Meteor.user()?.admin is true
- throw new Meteor.Error 'not-authorized', '[methods] setAdminStatus -> Not authorized'
-
- return RocketChat.getStatistics()
\ No newline at end of file
diff --git a/packages/rocketchat-lib/settings/server/startup.coffee b/packages/rocketchat-lib/settings/server/startup.coffee
index 1cb75e2b1bc..26625bc159c 100644
--- a/packages/rocketchat-lib/settings/server/startup.coffee
+++ b/packages/rocketchat-lib/settings/server/startup.coffee
@@ -80,6 +80,8 @@ Meteor.startup ->
RocketChat.settings.add 'Layout_Privacy_Policy', 'Privacy Policy
Go to APP SETTINGS -> Layout to customize this page.', { type: 'string', multiline: true, group: 'Layout', public: true }
RocketChat.settings.add 'Layout_Sidenav_Footer', '
Fork it on github ', { type: 'string', group: 'Layout', public: true, i18nDescription: 'Layout_Sidenav_Footer_description' }
+ RocketChat.settings.add 'Statistics_opt_out', false, { type: 'boolean', group: false }
+
if process?.env? and not process.env['MAIL_URL']? and RocketChat.settings.get('SMTP_Host') and RocketChat.settings.get('SMTP_Username') and RocketChat.settings.get('SMTP_Password')
process.env['MAIL_URL'] = "smtp://" + encodeURIComponent(RocketChat.settings.get('SMTP_Username')) + ':' + encodeURIComponent(RocketChat.settings.get('SMTP_Password')) + '@' + encodeURIComponent(RocketChat.settings.get('SMTP_Host'))
if RocketChat.settings.get('SMTP_Port')
diff --git a/packages/rocketchat-statistics/i18n/en.i18n.json b/packages/rocketchat-statistics/i18n/en.i18n.json
new file mode 100644
index 00000000000..4eb7c3f9836
--- /dev/null
+++ b/packages/rocketchat-statistics/i18n/en.i18n.json
@@ -0,0 +1,3 @@
+{
+
+}
\ No newline at end of file
diff --git a/packages/rocketchat-statistics/lib/rocketchat.coffee b/packages/rocketchat-statistics/lib/rocketchat.coffee
new file mode 100644
index 00000000000..3111fcc9479
--- /dev/null
+++ b/packages/rocketchat-statistics/lib/rocketchat.coffee
@@ -0,0 +1 @@
+RocketChat.statistics = {}
diff --git a/packages/rocketchat-statistics/package-tap.i18n b/packages/rocketchat-statistics/package-tap.i18n
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/packages/rocketchat-statistics/package.js b/packages/rocketchat-statistics/package.js
new file mode 100644
index 00000000000..541d7a82532
--- /dev/null
+++ b/packages/rocketchat-statistics/package.js
@@ -0,0 +1,36 @@
+Package.describe({
+ name: 'rocketchat:statistics',
+ version: '0.0.1',
+ summary: 'Statistics generator',
+ git: ''
+});
+
+Package.onUse(function(api) {
+ api.versionsFrom('1.0');
+
+ api.use([
+ 'templating',
+ 'coffeescript',
+ 'rocketchat:lib@0.0.1'
+ ]);
+
+ // TAPi18n
+ api.use(["tap:i18n@1.5.1"], ["client", "server"]);
+ api.addFiles("package-tap.i18n", ["client", "server"]);
+ api.addFiles([
+ "i18n/en.i18n.json",
+ ], ["client", "server"]);
+
+ // Statistics
+ api.addFiles('lib/rocketchat.coffee', [ 'client', 'server' ]);
+ api.addFiles([
+ 'server/collections/Statistics.coffee',
+ 'server/functions/get.coffee',
+ 'server/functions/save.coffee'
+ ], 'server');
+
+});
+
+Package.onTest(function(api) {
+
+});
diff --git a/packages/rocketchat-statistics/server/collections/MapReducedStatistics.coffee b/packages/rocketchat-statistics/server/collections/MapReducedStatistics.coffee
new file mode 100644
index 00000000000..be489dba370
--- /dev/null
+++ b/packages/rocketchat-statistics/server/collections/MapReducedStatistics.coffee
@@ -0,0 +1 @@
+@MapReducedStatistics = new Meteor.Collection 'rocketchat_mr_statistics'
diff --git a/packages/rocketchat-statistics/server/collections/Statistics.coffee b/packages/rocketchat-statistics/server/collections/Statistics.coffee
new file mode 100644
index 00000000000..3cf64d0319c
--- /dev/null
+++ b/packages/rocketchat-statistics/server/collections/Statistics.coffee
@@ -0,0 +1 @@
+@Statistics = new Meteor.Collection 'rocketchat_statistics'
diff --git a/packages/rocketchat-lib/server/functions/getStatistics.coffee b/packages/rocketchat-statistics/server/functions/get.coffee
similarity index 91%
rename from packages/rocketchat-lib/server/functions/getStatistics.coffee
rename to packages/rocketchat-statistics/server/functions/get.coffee
index cfb726ed408..e79c9c68118 100644
--- a/packages/rocketchat-lib/server/functions/getStatistics.coffee
+++ b/packages/rocketchat-statistics/server/functions/get.coffee
@@ -1,7 +1,8 @@
-RocketChat.getStatistics = ->
+RocketChat.statistics.get = ->
statistics = {}
# Version
+ statistics.uniqueId = Settings.findOne({ _id: "uniqueID" })?.value
statistics.version = BuildInfo?.commit?.hash
statistics.versionDate = BuildInfo?.commit?.date
@@ -10,7 +11,8 @@ RocketChat.getStatistics = ->
statistics.activeUsers = Meteor.users.find({ active: true }).count()
statistics.nonActiveUsers = statistics.totalUsers - statistics.activeUsers
statistics.onlineUsers = Meteor.users.find({ statusConnection: 'online' }).count()
- statistics.offlineUsers = statistics.totalUsers - statistics.onlineUsers
+ statistics.awayUsers = Meteor.users.find({ statusConnection: 'away' }).count()
+ statistics.offlineUsers = statistics.totalUsers - statistics.onlineUsers - statistics.awayUsers
# Room statistics
statistics.totalRooms = ChatRoom.find().count()
diff --git a/packages/rocketchat-lib/server/functions/getAvgStatistics.coffee b/packages/rocketchat-statistics/server/functions/getAverage.coffee
similarity index 93%
rename from packages/rocketchat-lib/server/functions/getAvgStatistics.coffee
rename to packages/rocketchat-statistics/server/functions/getAverage.coffee
index b76754d3f36..68098b50655 100644
--- a/packages/rocketchat-lib/server/functions/getAvgStatistics.coffee
+++ b/packages/rocketchat-statistics/server/functions/getAverage.coffee
@@ -1,4 +1,4 @@
-RocketChat.getAvgStatistics = ->
+RocketChat.statistics.getAverage = ->
statistics = {}
m = ->
@@ -14,6 +14,7 @@ RocketChat.getAvgStatistics = ->
a.activeUsers += b.activeUsers
a.nonActiveUsers += b.nonActiveUsers
a.onlineUsers += b.onlineUsers
+ a.awayUsers += b.awayUsers
a.offlineUsers += b.offlineUsers
a.totalRooms += b.totalRooms
a.totalChannels += b.totalChannels
@@ -36,6 +37,7 @@ RocketChat.getAvgStatistics = ->
out.activeUsers = v.activeUsers / v.count
out.nonActiveUsers = v.nonActiveUsers / v.count
out.onlineUsers = v.onlineUsers / v.count
+ out.awayUsers = v.awayUsers / v.count
out.offlineUsers = v.offlineUsers / v.count
out.totalRooms = v.totalRooms / v.count
out.totalChannels = v.totalChannels / v.count
diff --git a/packages/rocketchat-statistics/server/functions/save.coffee b/packages/rocketchat-statistics/server/functions/save.coffee
new file mode 100644
index 00000000000..ed19daf541e
--- /dev/null
+++ b/packages/rocketchat-statistics/server/functions/save.coffee
@@ -0,0 +1,6 @@
+RocketChat.statistics.save = ->
+ statistics = RocketChat.statistics.get()
+ statistics.createdAt = new Date
+ Statistics.insert statistics
+ return statistics
+
diff --git a/packages/rocketchat-statistics/server/methods/getStatistics.coffee b/packages/rocketchat-statistics/server/methods/getStatistics.coffee
new file mode 100644
index 00000000000..dde2b348a10
--- /dev/null
+++ b/packages/rocketchat-statistics/server/methods/getStatistics.coffee
@@ -0,0 +1,11 @@
+Meteor.methods
+ getStatistics: ->
+ if not Meteor.userId()
+ throw new Meteor.Error('invalid-user', "[methods] getStatistics -> Invalid user")
+
+ console.log '[methods] getStatistics -> '.green, 'userId:', Meteor.userId(), 'arguments:', arguments
+
+ unless Meteor.user()?.admin is true
+ throw new Meteor.Error 'not-authorized', '[methods] getStatistics -> Not authorized'
+
+ return RocketChat.statistics.get()
\ No newline at end of file
diff --git a/server/lib/collections.coffee b/server/lib/collections.coffee
index 4c22f223ad0..cf46a682a08 100644
--- a/server/lib/collections.coffee
+++ b/server/lib/collections.coffee
@@ -2,5 +2,4 @@
@ChatRoom = new Meteor.Collection 'rocketchat_room'
@ChatSubscription = new Meteor.Collection 'rocketchat_subscription'
@MapReducedStatistics = new Mongo.Collection 'rocketchat_mr_statistics'
-@Statistics = new Mongo.Collection 'rocketchat_statistics'
@ChatReports = new Meteor.Collection 'rocketchat_reports'
diff --git a/server/startup/cron.coffee b/server/startup/cron.coffee
new file mode 100644
index 00000000000..fb431b6bd39
--- /dev/null
+++ b/server/startup/cron.coffee
@@ -0,0 +1,22 @@
+# Config and Start SyncedCron
+SyncedCron.config
+ collectionName: 'rocketchat_cron_history'
+
+Meteor.startup ->
+ Meteor.defer ->
+
+ # Generate and save statistics every hour
+ SyncedCron.add
+ name: 'Generate and save statistics',
+ schedule: (parser) -># parser is a later.parse object
+ return parser.text 'every 1 hour'
+ job: ->
+ statistics = RocketChat.statistics.save()
+ unless RocketChat.settings.get 'Statistics_opt_out'
+ console.log 'Sending statistics data to Rocket.Chat'
+ HTTP.post 'https://rocket.chat/stats',
+ data: statistics
+
+ return
+
+ SyncedCron.start()
diff --git a/server/startup/initialData.coffee b/server/startup/initialData.coffee
index 9eb3d92c4fb..9af24b91b5b 100644
--- a/server/startup/initialData.coffee
+++ b/server/startup/initialData.coffee
@@ -1,5 +1,11 @@
Meteor.startup ->
Meteor.defer ->
+ # Insert server unique id if it doesn't exist
+ if not Settings.findOne { _id: 'uniqueID' }
+ Settings.insert
+ _id: 'uniqueID'
+ value: Random.id()
+
if not ChatRoom.findOne('name': 'general')?
ChatRoom.insert
_id: 'GENERAL'