Skip to content

Commit 2b01069

Browse files
authored
Merge pull request #15 from Countly/next
Next
2 parents 4a07807 + 8613515 commit 2b01069

File tree

68 files changed

+1455
-902
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

68 files changed

+1455
-902
lines changed

CHANGELOG.md

+55
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,58 @@
1+
## Version 19.08.1
2+
3+
**Fixes**
4+
* [EChartMap] refactor library loading
5+
* [EchartMap] fixed date change refresh bug
6+
* [api] fixed i/tasks/name - calling correct function
7+
* [api] fixed total user correction for server side models
8+
* [api] increase user count in aggregated data for country when country changes
9+
* [crashes] fixed crash menu arrow
10+
* [crashes] update minidump
11+
* [events] fixed getting undefined _activeEvent in some situations
12+
* [events] fixed issue with hiding/showing event whan there is '.' in event name
13+
* [events] fixed issue with matching events in frontend if their keys have special symbols like "&" in them
14+
* [frontend] changed duration display format
15+
* [frontend] fixed check for admin apps (some sections that should be shown to admin, where not visible)
16+
* [frontend] fixed chrome autofill prevention
17+
* [frontend] fixed countries screen color problem
18+
* [frontend] fixed keeping filtered events after changing segments
19+
* [frontend] fixed localization for visits in top
20+
* [frontend] fixed total user correction in 0 cases
21+
* [frontend] fixed unknown country check
22+
* [frontend] fixed uploading app icon on first app screen
23+
* [frontend] fixes for 0 values having colors on the map
24+
* [frontend] improved Internet Explorer 11 support
25+
* [logger] let large texts scroll in table cell
26+
* [prelogin] fixed issue with showing messages in forgot page
27+
* [push] GCM Deprecation
28+
* [push] included in data migration
29+
* [push] show only for mobile type
30+
* [scripts] fixed db upgrade script running as separate script
31+
* [sdk] updated bundled Web SDK
32+
* [views] view deletion added in systemlogs
33+
34+
**Enterprise fixes**
35+
* [attribution] prevent user from creating campaign ID with " or '
36+
* [cohorts] cohort drawer ui bugs fixed
37+
* [concurrent_users] fixed alerts table
38+
* [crash_symbols] order the symbols when fetching to always use last symbol if multiple same symbols provided
39+
* [crashes-jira] fixed check for correct crashes view
40+
* [dashboards] fixed user estimation correction in custom dashboards
41+
* [drill] fixed country map bugs
42+
* [drill] fixed punchcard value formatting
43+
* [funnels] fixed delete multiple rows bug
44+
* [funnels] fixed dragging steps only by drag handler
45+
* [restrict] css fix for hiding menus
46+
* [users] also validate funnel step segmentation
47+
* [users] fix custom column selection bugs
48+
49+
**Enterprise Improvements**
50+
* [cohorts] added configuration to control minimal cohort regeneration time
51+
* [concurrent_users] added legacy live plugin endpoint support
52+
* [drill] return undefined values too in BY queries
53+
* [users] added cursor pointer on view message button
54+
* [users] updated to use long name and value transformations for custom selected fields
55+
156
## Version 19.08
257

358
**Fixes**

Gruntfile.js

+1
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ module.exports = function(grunt) {
3333
},
3434
utils: {
3535
src: [
36+
'frontend/express/public/javascripts/utils/polyfills.js',
3637
'frontend/express/public/javascripts/utils/underscore-min.js',
3738
'frontend/express/public/javascripts/utils/prefixfree.min.js',
3839
'frontend/express/public/javascripts/utils/moment/moment-with-locales.min.js',

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ Countly is a privacy-focused and 360-degree analytics platform with several, uni
9898
* Configurable and extensible via open source [plugins](https://count.ly/plugins).
9999
* Modern and easy to use web based dashboard with a focus on user experience, which makes getting complex insights a breeze.
100100
* Tracking more than 2500 web sites and 16000 mobile applications.
101-
* Collecting more than 60 billion datapoints worlwide.
101+
* Collecting more than 60 billion datapoints worldwide.
102102

103103
## Differences between Community Edition & Enterprise Edition
104104

api/jobs/topEvents.js

+9-3
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,12 @@ const crypto = require("crypto");
44
const countlyApi = {
55
data: {
66
fetch: require("../parts/data/fetch.js"),
7-
lib: require("../lib/countly.model.js")
7+
lib: require("../lib/countly.model.js"),
8+
common: require("../lib/countly.common.js")
89
}
910
};
1011
const countlyEvents = countlyApi.data.lib.load("event");
11-
12+
const countlyCommon = countlyApi.data.common;
1213
/** Class for job of top events widget **/
1314
class TopEventsJob extends job.Job {
1415
/**
@@ -62,9 +63,14 @@ class TopEventsJob extends job.Job {
6263
doneEventMap();
6364
});
6465
}, () => {
66+
const _data = {};
67+
Object.keys(data).forEach(key => {
68+
const encodeKey = countlyCommon.encode(key);
69+
_data[encodeKey] = data[key];
70+
});
6571
const ts = Math.round(new Date().getTime() / 1000);
6672
const period = givenPeriod === "hour" ? "today" : givenPeriod;
67-
db.collection(collectionName).insert({app_id: _id, ts, period, data}, (error, records) => {
73+
db.collection(collectionName).insert({app_id: _id, ts, period, data: _data}, (error, records) => {
6874
if (!error && records) {
6975
eachCallback();
7076
}

api/lib/countly.common.js

+33-4
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,17 @@ function getPeriodObject() {
9898
};
9999

100100
endTimestamp = _currMoment.clone().utc().endOf("day");
101+
102+
if (_period && _period.indexOf(",") !== -1) {
103+
try {
104+
_period = JSON.parse(_period);
105+
}
106+
catch (SyntaxError) {
107+
console.log("period JSON parse failed");
108+
_period = "month";
109+
}
110+
}
111+
101112
if (Array.isArray(_period)) {
102113
var fromDate, toDate;
103114

@@ -192,6 +203,17 @@ function getPeriodObject() {
192203
isSpecialPeriod: true
193204
});
194205
}
206+
//incorrect period, defaulting to 30 days
207+
else {
208+
let nDays = 30;
209+
210+
startTimestamp = _currMoment.clone().utc().startOf("day").subtract(nDays - 1, "days");
211+
cycleDuration = moment.duration(nDays, "days");
212+
Object.assign(periodObject, {
213+
dateString: "D MMM",
214+
isSpecialPeriod: true
215+
});
216+
}
195217

196218
Object.assign(periodObject, {
197219
start: startTimestamp.valueOf(),
@@ -831,7 +853,7 @@ countlyCommon.extractTwoLevelData = function(db, rangeArray, clearFunction, data
831853
}
832854

833855
if (propertyNames.indexOf("u") !== -1 && Object.keys(tmpPropertyObj).length) {
834-
if (totalUserOverrideObj && totalUserOverrideObj[rangeArray[j]]) {
856+
if (totalUserOverrideObj && typeof totalUserOverrideObj[rangeArray[j]] !== "undefined") {
835857

836858
tmpPropertyObj.u = totalUserOverrideObj[rangeArray[j]];
837859

@@ -953,7 +975,14 @@ countlyCommon.extractBarData = function(db, rangeArray, clearFunction, fetchFunc
953975
dataProps.push({name: "n"});
954976
dataProps.push({name: "t"});
955977
}
978+
if (metric === "n") {
979+
dataProps.push({name: "u"});
980+
}
956981
var rangeData = countlyCommon.extractTwoLevelData(db, rangeArray, clearFunction, dataProps, totalUserOverrideObj);
982+
rangeData.chartData = countlyCommon.mergeMetricsByName(rangeData.chartData, "range");
983+
rangeData.chartData = underscore.sortBy(rangeData.chartData, function(obj) {
984+
return -obj[metric];
985+
});
957986
var rangeNames = underscore.pluck(rangeData.chartData, 'range'),
958987
rangeTotal = underscore.pluck(rangeData.chartData, metric),
959988
barData = [],
@@ -1330,7 +1359,7 @@ countlyCommon.extractMetric = function(db, rangeArray, clearFunction, dataProper
13301359
}
13311360

13321361
if (propertyNames.indexOf("u") !== -1 && Object.keys(tmpPropertyObj).length) {
1333-
if (totalUserOverrideObj && totalUserOverrideObj[rangeArray[j]]) {
1362+
if (totalUserOverrideObj && typeof totalUserOverrideObj[rangeArray[j]] !== "undefined") {
13341363

13351364
tmpPropertyObj.u = totalUserOverrideObj[rangeArray[j]];
13361365

@@ -1607,7 +1636,7 @@ countlyCommon.getDashboardData = function(data, properties, unique, totalUserOve
16071636

16081637
// Total users can't be less than new users
16091638
if (typeof current.u !== "undefined" && typeof current.n !== "undefined" && current.u < current.n) {
1610-
if (totalUserOverrideObj && typeof totalUserOverrideObj.u !== "undefined" && totalUserOverrideObj.u) {
1639+
if (totalUserOverrideObj && typeof totalUserOverrideObj.u !== "undefined") {
16111640
current.n = current.u;
16121641
}
16131642
else {
@@ -1636,7 +1665,7 @@ countlyCommon.getDashboardData = function(data, properties, unique, totalUserOve
16361665
//check if we can correct data using total users correction
16371666
if (totalUserOverrideObj) {
16381667
for (let i = 0; i < unique.length; i++) {
1639-
if (dataArr[unique[i]] && typeof totalUserOverrideObj[unique[i]] !== "undefined" && totalUserOverrideObj[unique[i]]) {
1668+
if (dataArr[unique[i]] && typeof totalUserOverrideObj[unique[i]] !== "undefined") {
16401669
dataArr[unique[i]].is_estimate = false;
16411670
}
16421671
}

api/lib/countly.model.js

+15-4
Original file line numberDiff line numberDiff line change
@@ -488,7 +488,16 @@ countlyModel.create = function(fetchValue) {
488488
*/
489489
countlyMetric.getNumber = function(metric) {
490490
metric = metric || _metrics[0];
491-
var data = countlyCommon.getDashboardData(this.getDb(), [metric], _uniques, {u: this.getTotalUsersObj().users}, {u: this.getTotalUsersObj(true).users});
491+
var metrics = [metric];
492+
//include other default metrics for data correction
493+
if (metric === "u") {
494+
metrics.push("n");
495+
metrics.push("t");
496+
}
497+
if (metric === "n") {
498+
metrics.push("u");
499+
}
500+
var data = countlyCommon.getDashboardData(this.getDb(), metrics, _uniques, {u: this.getTotalUsersObj().users}, {u: this.getTotalUsersObj(true).users});
492501
var ob = {};
493502
ob[metric] = metric;
494503
var sparkLines = countlyCommon.getSparklineData(this.getDb(), ob, function(obj) {
@@ -505,9 +514,11 @@ countlyModel.create = function(fetchValue) {
505514
return obj;
506515
});
507516
for (let i in data) {
508-
data[i].sparkline = sparkLines[i].split(",").map(function(item) {
509-
return parseInt(item);
510-
});
517+
if (sparkLines[i]) {
518+
data[i].sparkline = sparkLines[i].split(",").map(function(item) {
519+
return parseInt(item);
520+
});
521+
}
511522
}
512523
return data[metric];
513524
};

api/parts/data/fetch.js

+4-1
Original file line numberDiff line numberDiff line change
@@ -1108,8 +1108,9 @@ fetch.fetchDataTopEvents = function(params) {
11081108
// eslint-disable-next-line no-shadow
11091109
const { app_id, data, _id, ts, period } = result;
11101110
let _data = Object.keys(data).map(function(key) {
1111+
const decodeKey = countlyCommon.decode(key);
11111112
const { sparkline, total, change } = data[key].data.count;
1112-
return { name: key, data: sparkline, count: total, trend: change };
1113+
return { name: decodeKey, data: sparkline, count: total, trend: change };
11131114
});
11141115
const sortByCount = _data.sort((a, b) => b.count - a.count).slice(0, limit);
11151116
common.returnOutput(params, { _id, app_id, ts, period, data: sortByCount });
@@ -1277,7 +1278,9 @@ fetch.getTotalUsersObjWithOptions = function(metric, params, options, callback)
12771278
var shortcodesForMetrics = {
12781279
"devices": "d",
12791280
"app_versions": "av",
1281+
"os": "p",
12801282
"platforms": "p",
1283+
"os_versions": "pv",
12811284
"platform_versions": "pv",
12821285
"resolutions": "r",
12831286
"countries": "cc",

api/parts/data/usage.js

+14-3
Original file line numberDiff line numberDiff line change
@@ -789,6 +789,11 @@ function processUserSession(dbAppUser, params, done) {
789789
return true;
790790
}
791791

792+
if (dbAppUser.cc !== params.user.country) {
793+
monthObjUpdate.push(params.user.country + '.' + common.dbMap.unique);
794+
zeroObjUpdate.push(params.user.country + '.' + common.dbMap.unique);
795+
}
796+
792797
// Calculate the frequency range of the user
793798

794799
if ((params.time.timestamp - userLastSeenTimestamp) >= (sessionFrequencyMax * 60 * 60)) {
@@ -842,17 +847,23 @@ function processUserSession(dbAppUser, params, done) {
842847
for (let k = 0; k < uniqueLevelsZero.length; k++) {
843848
if (uniqueLevelsZero[k] === "Y") {
844849
updateUsersZero['d.' + common.dbMap.unique] = 1;
845-
updateUsersZero['d.' + params.user.country + '.' + common.dbMap.unique] = 1;
850+
if (dbAppUser.cc === params.user.country) {
851+
updateUsersZero['d.' + params.user.country + '.' + common.dbMap.unique] = 1;
852+
}
846853
}
847854
else {
848855
updateUsersZero['d.' + uniqueLevelsZero[k] + '.' + common.dbMap.unique] = 1;
849-
updateUsersZero['d.' + uniqueLevelsZero[k] + '.' + params.user.country + '.' + common.dbMap.unique] = 1;
856+
if (dbAppUser.cc === params.user.country) {
857+
updateUsersZero['d.' + uniqueLevelsZero[k] + '.' + params.user.country + '.' + common.dbMap.unique] = 1;
858+
}
850859
}
851860
}
852861

853862
for (let l = 0; l < uniqueLevelsMonth.length; l++) {
854863
updateUsersMonth['d.' + uniqueLevelsMonth[l] + '.' + common.dbMap.unique] = 1;
855-
updateUsersMonth['d.' + uniqueLevelsMonth[l] + '.' + params.user.country + '.' + common.dbMap.unique] = 1;
864+
if (dbAppUser.cc === params.user.country) {
865+
updateUsersMonth['d.' + uniqueLevelsMonth[l] + '.' + params.user.country + '.' + common.dbMap.unique] = 1;
866+
}
856867
}
857868

858869
plugins.dispatch("/session/begin", {

api/parts/mgmt/users.js

+12
Original file line numberDiff line numberDiff line change
@@ -918,4 +918,16 @@ usersApi.fetchNotes = async function(params) {
918918
return true;
919919
};
920920

921+
usersApi.ackNotification = function(params) {
922+
common.db.collection('members').updateOne({"_id": common.db.ObjectID(params.member._id)}, {$set: {['notes.' + params.qstring.path]: true}}, err => {
923+
if (err) {
924+
log.e('Error while acking member notification', err);
925+
return common.returnMessage(params, 500, 'Unknown error');
926+
}
927+
else {
928+
common.returnOutput(params, {['notes.' + params.qstring.path]: true});
929+
}
930+
});
931+
};
932+
921933
module.exports = usersApi;

api/utils/requestProcessor.js

+10-4
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,9 @@ const processRequest = (params) => {
236236
case 'deleteOwnAccount':
237237
validateUserForWriteAPI(countlyApi.mgmt.users.deleteOwnAccount, params);
238238
break;
239+
case 'ack':
240+
validateUserForWriteAPI(countlyApi.mgmt.users.ackNotification, params);
241+
break;
239242
default:
240243
if (!plugins.dispatch(apiPath, {
241244
params: params,
@@ -594,7 +597,7 @@ const processRequest = (params) => {
594597
break;
595598
case 'name':
596599
validateUserForWrite(params, () => {
597-
taskmanager.deleteResult({
600+
taskmanager.nameResult({
598601
db: common.db,
599602
id: params.qstring.task_id,
600603
name: params.qstring.name
@@ -925,12 +928,14 @@ const processRequest = (params) => {
925928
for (let i = 0; i < idss.length; i++) {
926929

927930
if (idss[i].indexOf('.') !== -1) {
928-
updateThese.$unset["map." + idss[i].replace(/\./g, ':')] = 1;
929-
updateThese.$unset["segments." + idss[i].replace(/\./g, ':')] = 1;
931+
updateThese.$unset["map." + idss[i].replace(/\./g, '\\u002e')] = 1;
932+
updateThese.$unset["segments." + idss[i].replace(/\./g, '\\u002e')] = 1;
933+
updateThese.$unset["omitted_segments." + idss[i].replace(/\./g, '\\u002e')] = 1;
930934
}
931935
else {
932936
updateThese.$unset["map." + idss[i]] = 1;
933937
updateThese.$unset["segments." + idss[i]] = 1;
938+
updateThese.$unset["omitted_segments." + idss[i]] = 1;
934939
}
935940
}
936941

@@ -1053,6 +1058,7 @@ const processRequest = (params) => {
10531058

10541059
for (let i = 0; i < idss.length; i++) {
10551060

1061+
var baseID = idss[i].replace(/\\u002e/g, ".");
10561062
if (!update_array.map[idss[i]]) {
10571063
update_array.map[idss[i]] = {};
10581064
}
@@ -1074,7 +1080,7 @@ const processRequest = (params) => {
10741080

10751081
if (params.qstring.set_visibility === 'hide' && event && event.overview && Array.isArray(event.overview)) {
10761082
for (let j = 0; j < event.overview.length; j++) {
1077-
if (event.overview[j].eventKey === idss[i]) {
1083+
if (event.overview[j].eventKey === baseID) {
10781084
event.overview.splice(j, 1);
10791085
j = j - 1;
10801086
}

bin/scripts/checking_versions.js

+18-1
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,24 @@ function compareVersions(a, b) {
7575
}
7676
}
7777

78-
return Math.sign(a.length - b.length);
78+
if (aParts.length === bParts.length) {
79+
return 0;
80+
}
81+
82+
let longestArray = aParts;
83+
if (bParts.length > longestArray.length) {
84+
longestArray = bParts;
85+
}
86+
87+
const continueIndex = Math.min(aParts.length, bParts.length);
88+
89+
for (let i = continueIndex; i < longestArray.length; i += 1) {
90+
if (parseInt(longestArray[i], 10) > 0) {
91+
return longestArray === bParts ? -1 : +1;
92+
}
93+
}
94+
95+
return 0;
7996
}
8097

8198
versions = versions.sort(compareVersions);

0 commit comments

Comments
 (0)