Skip to content

Commit ab9d16a

Browse files
authored
Merge branch 'next' into validating-active-users
2 parents 438f95a + d95f275 commit ab9d16a

File tree

22 files changed

+474
-132
lines changed

22 files changed

+474
-132
lines changed

CHANGELOG.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,37 @@
22
Dependencies:
33
- Remove SQLite
44

5+
## Version 25.03.12
6+
Features:
7+
- [plugins] Add configuration warning tags to settings UI
8+
- [white-labeling] Add sidebar footer label setting to white labeling
9+
10+
Fixes:
11+
- [core] Use correct rights validation for loyality
12+
- [crashes] Fix free session for home widget
13+
- [crashes] Fix trend and change calculation for crash stats
14+
- [crashes] Use na for free session and free user when there's no data
15+
- [push] Show segmentation, geo and cohorts related components in push drawer on editing draft.
16+
17+
Enterprise Fixes:
18+
- [ldap] Error handling in ldap plugin on search error
19+
- [license] Display notification for non global admin user
20+
- [users] Load table data from report if user table calculation goes to report manager
21+
22+
Dependencies:
23+
- Bump mongodb from 6.17.0 to 6.18.0
24+
- Bump puppeteer from 24.14.0 to 24.15.0
25+
- Bump supertest from 7.1.3 to 7.1.4
26+
527
## Version 25.03.11
628
Fixes:
729
- [core] Fix mongo connection url parsing
830
- [core] Fix user analytics widget chart
931
- [crashes] Fix free session and free user calculation
1032
- [dashboards] Delete associated widgets and reports when a dashboard is removed
1133
- [star-rating] Fix widget close post message
34+
- [core] Adjust level and update content of app version log
35+
- [populator] Update getVersion to generate valid semantic version
1236

1337
Enterprise Fixes:
1438
- [crash_symbolication] Remove auto symbolication setting

LICENSE renamed to LICENSE.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ Countly Product Analytics - Countly Lite License
33

44
© Countly, https://count.ly
55

6-
Countly is provided under AGPL v3 with modified Section 7. In accordance
6+
Countly is provided under AGPL-3.0 with modified Section 7. In accordance
77
with Section 7 of the AGPL, the Works included in this package or repository
88
(excluding 3rd party Software), are subject to the following additional terms:
99

README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,3 +116,11 @@ If you like Countly, why not use one of our badges and give a link back to us?
116116
<a href="https://countly.com/?utm_source=badge" rel="nofollow"><img style="width:145px;height:60px" src="https://count.ly/badges/light.svg?v2" alt="Countly - Product Analytics" /></a>
117117

118118
<a href="https://countly.com/?utm_source=badge" rel="nofollow"><img style="width:145px;height:60px" src="https://count.ly/badges/light.svg" alt="Countly - Product Analytics" /></a>
119+
120+
121+
## License
122+
This project is licensed under **AGPL-3.0** with modified Section 7., see the [LICENSE](LICENSE) file for more details.
123+
124+
## 💚 Thanks
125+
126+
This project is tested with BrowserStack.

api/parts/data/usage.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1121,7 +1121,7 @@ plugins.register("/sdk/user_properties", async function(ob) {
11211121
userProps.av_build = versionComponents.build;
11221122
}
11231123
else {
1124-
log.w("Invalid app version format: %s", params.qstring.metrics._app_version);
1124+
log.d("App version %s is not a valid semantic version. It cannot be separated into semantic version parts", params.qstring.metrics._app_version);
11251125
userProps.av_major = null;
11261126
userProps.av_minor = null;
11271127
userProps.av_patch = null;

api/utils/requestProcessor.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1567,7 +1567,7 @@ const processRequest = (params) => {
15671567
common.returnMessage(params, 400, 'Missing parameter "app_id"');
15681568
return false;
15691569
}
1570-
validateUserForMgmtReadAPI(countlyApi.mgmt.appUsers.loyalty, params);
1570+
validateUserForRead(params, countlyApi.mgmt.appUsers.loyalty);
15711571
break;
15721572
}
15731573
/**

bin/scripts/export-data/setting_limits_and_real_values.js

Lines changed: 12 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
* Path: $(countly dir)/bin/scripts/export-data
55
* Command: node setting_limits_and_real_values.js
66
*/
7-
87
const fs = require('fs');
98
const crypto = require('crypto');
109
const common = require('../../../api/utils/common.js');
@@ -20,10 +19,10 @@ const DEFAULT_LIMITS = {
2019
view_name_limit: 128,
2120
view_segment_limit: 100,
2221
view_segment_value_limit: 10,
23-
custom_prop_limit: 20,
22+
//custom_prop_limit: 20,
23+
custom_property_limit: 20,
2424
custom_prop_value_limit: 50,
2525
};
26-
2726
Promise.all([pluginManager.dbConnection("countly"), pluginManager.dbConnection("countly_drill")]).then(async function([countlyDb, drillDb]) {
2827
console.log("Connected to databases...");
2928
common.db = countlyDb;
@@ -36,21 +35,17 @@ Promise.all([pluginManager.dbConnection("countly"), pluginManager.dbConnection("
3635
else {
3736
// WRITE START OF ARRAY
3837
WriteStream.write('[\n', 'utf8');
39-
4038
// GETTING DATA FOR SET LIMITS FOR EVENTS, VIEWS, AND CUSTOM PROPERTIES
4139
var pluginsCollectionPlugins = await countlyDb.collection("plugins").findOne({"_id": 'plugins'});
42-
4340
// LOOP APPS FOR EACH REQUIREMENT
4441
for (let i = 0; i < apps.length; i++) {
4542
var app = apps[i];
4643
console.log(i + 1, ") Processing app:", app.name);
47-
4844
try {
4945
var app_results = { "App Name": app.name },
5046
defaultVal,
5147
realVal,
5248
currentVal;
53-
5449
// SETTING UP CURRENT SET LIMITS PER APP
5550
var appsCollectionPerApp = await countlyDb.collection("apps").findOne({"_id": common.db.ObjectID(app._id)});
5651
var CURRENT_LIMITS = {
@@ -61,10 +56,10 @@ Promise.all([pluginManager.dbConnection("countly"), pluginManager.dbConnection("
6156
view_name_limit: pluginsCollectionPlugins?.views?.view_name_limit || DEFAULT_LIMITS.view_name_limit,
6257
view_segment_limit: pluginsCollectionPlugins?.views?.segment_limit || DEFAULT_LIMITS.view_segment_limit,
6358
view_segment_value_limit: pluginsCollectionPlugins?.views?.segment_value_limit || DEFAULT_LIMITS.view_segment_value_limit,
64-
custom_prop_limit: pluginsCollectionPlugins?.users?.custom_prop_limit || DEFAULT_LIMITS.custom_prop_limit,
59+
//custom_prop_limit: pluginsCollectionPlugins?.users?.custom_prop_limit || DEFAULT_LIMITS.custom_prop_limit,
60+
custom_property_limit: pluginsCollectionPlugins?.drill?.custom_property_limit || DEFAULT_LIMITS.custom_property_limit,
6561
custom_prop_value_limit: pluginsCollectionPlugins?.users?.custom_set_limit || DEFAULT_LIMITS.custom_prop_value_limit,
6662
};
67-
6863
// GETTING REAL DATA PER APP
6964
var eventsCollectionPerApp = await countlyDb.collection("events").findOne({"_id": common.db.ObjectID(app._id)});
7065
var viewsCountsPerApp = await countlyDb.collection("app_viewsmeta" + app._id).countDocuments();
@@ -140,46 +135,34 @@ Promise.all([pluginManager.dbConnection("countly"), pluginManager.dbConnection("
140135
catch (err) {
141136
console.log("Mongodb operation failed for app: ", app.name, err);
142137
}
143-
144138
// EVENT KEYS
145139
defaultVal = DEFAULT_LIMITS.event_limit;
146-
147140
currentVal = CURRENT_LIMITS.event_limit;
148-
149141
let realEvents = eventsCollectionPerApp?.list || [];
150142
realVal = realEvents.length;
151-
152143
app_results['Event Keys'] = {"default": defaultVal, "set": currentVal, "real": realVal};
153-
154144
// SEGMENTS IN ONE EVENT
155145
defaultVal = DEFAULT_LIMITS.event_segment_limit;
156-
157146
currentVal = CURRENT_LIMITS.event_segment_limit;
158-
159147
let eventSegments = eventsCollectionPerApp && eventsCollectionPerApp.segments || {};
160148
realVal = Object.entries(eventSegments)
161149
.sort((a, b) => b[1].length - a[1].length)
162150
.reduce((acc, [key, value]) => {
163151
acc[key] = value.length;
164152
return acc;
165153
}, {});
166-
167154
app_results['Event Segments'] = {"default": defaultVal, "set": currentVal, "real": realVal};
168-
169155
// UNIQUE EVENT SEGMENT VALUES FOR 1 SEGMENT
170156
defaultVal = DEFAULT_LIMITS.event_segment_value_limit;
171-
172157
currentVal = CURRENT_LIMITS.event_segment_value_limit;
173-
174158
realVal = {};
175159
await Promise.all(realEvents.map(async(event) => {
176160
var shortEventName = common.fixEventKey(event);
177-
var eventCollectionName = "events" + crypto.createHash('sha1').update(shortEventName + app._id).digest('hex');
178-
161+
var hash = crypto.createHash('sha1').update(shortEventName + app._id).digest('hex');
162+
var eventCollectionName = "events_data";
179163
try {
180164
var regexes = [
181-
"^no-segment_2023:0.*",
182-
"^no-segment_2024:0.*"
165+
"^" + app._id + "_" + hash + "_no-segment_2025:0.*"
183166
];
184167
var eventsSegmentsValues = await countlyDb.collection(eventCollectionName).aggregate([
185168
{
@@ -218,7 +201,6 @@ Promise.all([pluginManager.dbConnection("countly"), pluginManager.dbConnection("
218201
}
219202
}
220203
]).toArray();
221-
222204
// Use reduce to transform array
223205
eventsSegmentsValues = eventsSegmentsValues.reduce((acc, item) => {
224206
const key = item.meta_v2.k;
@@ -228,7 +210,6 @@ Promise.all([pluginManager.dbConnection("countly"), pluginManager.dbConnection("
228210
acc[key] += item.meta_v2.v;
229211
return acc;
230212
}, {});
231-
232213
if (Object.keys(eventsSegmentsValues).length > 0) {
233214
realVal[event] = eventsSegmentsValues;
234215
}
@@ -238,65 +219,45 @@ Promise.all([pluginManager.dbConnection("countly"), pluginManager.dbConnection("
238219
}
239220
}));
240221
app_results['Unique Event Segment Values'] = {"default": defaultVal, "set": currentVal, "real": realVal};
241-
242222
// UNIQUE VIEV NAMES
243223
defaultVal = DEFAULT_LIMITS.view_limit;
244-
245224
currentVal = CURRENT_LIMITS.view_limit;
246-
247225
realVal = viewsCountsPerApp;
248-
249226
app_results['Unique View Names'] = {"default": defaultVal, "set": currentVal, "real": realVal};
250-
251227
// VIEW NAME LENGTH LIMIT
252228
defaultVal = DEFAULT_LIMITS.view_name_limit;
253-
254229
currentVal = CURRENT_LIMITS.view_name_limit;
255-
256230
realVal = {longestViewName: "", longestViewLength: 0};
257231
realVal.longestViewName = viewsCollectionPerApp && viewsCollectionPerApp[0] && viewsCollectionPerApp[0]?.view;
258232
realVal.longestViewLength = viewsCollectionPerApp && viewsCollectionPerApp[0] && viewsCollectionPerApp[0]?.max_length;
259-
260233
app_results['View Name Length Limit'] = {"default": defaultVal, "set": currentVal, "real": realVal};
261-
262234
// SEGMENTS IN ONE VIEW
263235
defaultVal = DEFAULT_LIMITS.view_segment_limit;
264-
265236
currentVal = CURRENT_LIMITS.view_segment_limit;
266-
267237
realVal = viewsSegmentsPerApp && viewsSegmentsPerApp[0]?.numberOfSegments || 0;
268-
269238
app_results['View Segments'] = {"default": defaultVal, "set": currentVal, "real": realVal};
270-
271239
// VIEW SEGMENT'S UNIQUE VALUES
272240
defaultVal = DEFAULT_LIMITS.view_segment_value_limit;
273-
274241
currentVal = CURRENT_LIMITS.view_segment_value_limit;
275-
276242
realVal = viewsSegmentsPerApp && viewsSegmentsPerApp[0]?.segments || 0;
277243
Object.keys(realVal).forEach(key => {
278244
if (realVal[key] === 0) {
279245
delete realVal[key];
280246
}
281247
});
282248
app_results['View Segments Unique Values'] = {"default": defaultVal, "set": currentVal, "real": realVal};
283-
284249
// USER PROPERTIES
285-
defaultVal = DEFAULT_LIMITS.custom_prop_limit;
286-
287-
currentVal = CURRENT_LIMITS.custom_prop_limit;
288-
250+
//defaultVal = DEFAULT_LIMITS.custom_prop_limit;
251+
//currentVal = CURRENT_LIMITS.custom_prop_limit;
252+
defaultVal = DEFAULT_LIMITS.custom_property_limit;
253+
currentVal = CURRENT_LIMITS.custom_property_limit;
289254
realVal = customPropsPerApp && customPropsPerApp[0]?.customPropertiesCount || 0;
290-
app_results['Custom User Properties'] = {"default": defaultVal, "set": currentVal, "real": realVal};
291-
255+
app_results['Max user custom properties'] = {"default": defaultVal, "set": currentVal, "real": realVal};
292256
// VALUES IN AN ARRAY FOR ONE USER PROPERTY
293257
defaultVal = DEFAULT_LIMITS.custom_prop_value_limit;
294-
295258
currentVal = CURRENT_LIMITS.custom_prop_value_limit;
296-
297259
realVal = valueFieldCounts || undefined;
298260
app_results['Values In Array For One User Property'] = {"default": defaultVal, "set": currentVal, "real": realVal};
299-
300261
// WRITE RESULTS PER APP TO FILE
301262
WriteStream.write(JSON.stringify(app_results, null, 2), 'utf8');
302263
if (i + 1 < apps.length) {
@@ -315,7 +276,6 @@ Promise.all([pluginManager.dbConnection("countly"), pluginManager.dbConnection("
315276
finally {
316277
close();
317278
}
318-
319279
async function getAppList(options) {
320280
var query = {};
321281
if (app_list && app_list.length > 0) {
@@ -325,7 +285,6 @@ Promise.all([pluginManager.dbConnection("countly"), pluginManager.dbConnection("
325285
}
326286
query = {_id: {$in: listed}};
327287
}
328-
329288
try {
330289
let apps = await options.db.collection("apps").find(query).toArray();
331290
return apps;
@@ -334,9 +293,7 @@ Promise.all([pluginManager.dbConnection("countly"), pluginManager.dbConnection("
334293
console.log("Error getting apps: ", err);
335294
return [];
336295
}
337-
338296
}
339-
340297
function close(err) {
341298
if (err) {
342299
console.log("Error: ", err);

frontend/express/app.js

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -422,6 +422,14 @@ Promise.all([plugins.dbConnection(countlyConfig), plugins.dbConnection("countly_
422422
app.loadThemeFiles(curTheme);
423423
app.dashboard_headers = plugins.getConfig("security").dashboard_additional_headers;
424424

425+
var overriddenCountlyNamedType = COUNTLY_NAMED_TYPE;
426+
var whiteLabelingConfig = plugins.getConfig("white-labeling");
427+
if (whiteLabelingConfig && whiteLabelingConfig.footerLabel && whiteLabelingConfig.footerLabel.length) {
428+
overriddenCountlyNamedType = whiteLabelingConfig.footerLabel;
429+
}
430+
431+
COUNTLY_NAMED_TYPE = overriddenCountlyNamedType;
432+
425433
if (typeof plugins.getConfig('frontend').countly_tracking !== 'boolean' && plugins.isPluginEnabled('tracker')) {
426434
plugins.updateConfigs(countlyDb, 'frontend', { countly_tracking: true });
427435
}
@@ -929,6 +937,12 @@ Promise.all([plugins.dbConnection(countlyConfig), plugins.dbConnection("countly_
929937
licenseNotification, licenseError;
930938
var isLocked = false;
931939
configs.export_limit = plugins.getConfig("api").export_limit;
940+
941+
var currentWhiteLabelingConfig = plugins.getConfig("white-labeling");
942+
var overriddenCountlyNamedType = COUNTLY_NAMED_TYPE;
943+
if (currentWhiteLabelingConfig && currentWhiteLabelingConfig.footerLabel && currentWhiteLabelingConfig.footerLabel.length) {
944+
overriddenCountlyNamedType = currentWhiteLabelingConfig.footerLabel;
945+
}
932946
app.loadThemeFiles(configs.theme, async function(theme) {
933947
if (configs._user.theme) {
934948
res.cookie("theme", configs.theme);
@@ -1004,7 +1018,7 @@ Promise.all([plugins.dbConnection(countlyConfig), plugins.dbConnection("countly_
10041018
licenseError,
10051019
ssr: serverSideRendering,
10061020
timezones: timezones,
1007-
countlyTypeName: COUNTLY_NAMED_TYPE,
1021+
countlyTypeName: overriddenCountlyNamedType,
10081022
countlyTypeTrack: COUNTLY_TRACK_TYPE,
10091023
countlyTypeCE: COUNTLY_TYPE_CE,
10101024
countly_tracking,
@@ -1037,7 +1051,7 @@ Promise.all([plugins.dbConnection(countlyConfig), plugins.dbConnection("countly_
10371051
countlyVersion: req.countly.version,
10381052
countlyType: COUNTLY_TYPE_CE,
10391053
countlyTrial: COUNTLY_TRIAL,
1040-
countlyTypeName: COUNTLY_NAMED_TYPE,
1054+
countlyTypeName: overriddenCountlyNamedType,
10411055
feedbackLink: COUNTLY_FEEDBACK_LINK,
10421056
documentationLink: COUNTLY_DOCUMENTATION_LINK,
10431057
helpCenterLink: COUNTLY_HELPCENTER_LINK,

frontend/express/public/javascripts/countly/vue/data/vuex.js

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -274,14 +274,23 @@
274274
options.onOverrideResponse(context, res);
275275
}
276276
var convertedResponse = _dataTableAdapters.toStandardResponse(res, requestOptions);
277-
if (!Object.prototype.hasOwnProperty.call(convertedResponse, "echo") ||
278-
convertedResponse.echo >= context.state[echoField]) {
279-
if (typeof options.onReady === 'function') {
280-
convertedResponse.rows = options.onReady(context, convertedResponse.rows);
277+
if (res.task_id) {
278+
if (typeof options.onTask === 'function') {
279+
options.onTask(context, res.task_id);
281280
}
282281
context.commit(_capitalized("set", resourceName), convertedResponse);
283282
context.commit(_capitalized("set", lastSuccessfulRequestKey), requestOptions);
284283
}
284+
else {
285+
if (!Object.prototype.hasOwnProperty.call(convertedResponse, "echo") ||
286+
convertedResponse.echo >= context.state[echoField]) {
287+
if (typeof options.onReady === 'function') {
288+
convertedResponse.rows = options.onReady(context, convertedResponse.rows);
289+
}
290+
context.commit(_capitalized("set", resourceName), convertedResponse);
291+
context.commit(_capitalized("set", lastSuccessfulRequestKey), requestOptions);
292+
}
293+
}
285294
})
286295
.catch(function(err) {
287296
if (typeof options.onError === 'function') {

0 commit comments

Comments
 (0)