From a91da86ae4f6c1f18964e07ca04269f55b642fae Mon Sep 17 00:00:00 2001 From: Yuri Kuznetsov Date: Mon, 9 Jan 2023 17:46:40 +0200 Subject: [PATCH 01/11] working calendar noJoin --- application/Espo/Resources/metadata/entityDefs/User.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/application/Espo/Resources/metadata/entityDefs/User.json b/application/Espo/Resources/metadata/entityDefs/User.json index f4f147b793..0aa2caf173 100644 --- a/application/Espo/Resources/metadata/entityDefs/User.json +++ b/application/Espo/Resources/metadata/entityDefs/User.json @@ -441,7 +441,8 @@ }, "workingTimeCalendar": { "type": "belongsTo", - "entity": "WorkingTimeCalendar" + "entity": "WorkingTimeCalendar", + "noJoin": true }, "workingTimeRanges": { "type": "hasMany", From 0c99f35c02fcd69e9fcb23663143fe49a150c5d9 Mon Sep 17 00:00:00 2001 From: Yuri Kuznetsov Date: Mon, 9 Jan 2023 18:11:26 +0200 Subject: [PATCH 02/11] fix working time calendar apperance --- application/Espo/Resources/metadata/app/acl.json | 5 ++++- .../Espo/Resources/metadata/entityAcl/User.json | 2 +- client/src/views/user/record/edit.js | 10 ++++++++++ 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/application/Espo/Resources/metadata/app/acl.json b/application/Espo/Resources/metadata/app/acl.json index 771e2162c5..ef01a14ee9 100644 --- a/application/Espo/Resources/metadata/app/acl.json +++ b/application/Espo/Resources/metadata/app/acl.json @@ -85,7 +85,10 @@ }, "User": { "dashboardTemplate": false, - "workingTimeCalendar": false, + "workingTimeCalendar": { + "read": "yes", + "edit": "no" + }, "password": false, "passwordConfirm": false, "auth2FA": false, diff --git a/application/Espo/Resources/metadata/entityAcl/User.json b/application/Espo/Resources/metadata/entityAcl/User.json index ed394972b3..ef29a9ae06 100644 --- a/application/Espo/Resources/metadata/entityAcl/User.json +++ b/application/Espo/Resources/metadata/entityAcl/User.json @@ -48,7 +48,7 @@ "nonAdminReadOnly": true }, "workingTimeCalendar": { - "onlyAdmin": true + "nonAdminReadOnly": true }, "accounts": { "nonAdminReadOnly": true diff --git a/client/src/views/user/record/edit.js b/client/src/views/user/record/edit.js index 8542801541..a2307dafe4 100644 --- a/client/src/views/user/record/edit.js +++ b/client/src/views/user/record/edit.js @@ -192,6 +192,16 @@ define('views/user/record/edit', ['views/record/edit', 'views/user/record/detail }); } + if (this.model.isAdmin() || this.model.isRegular()) { + layout.push({ + "label": "Misc", + "name": "misc", + "rows": [ + [{"name": "workingTimeCalendar"}, false] + ] + }); + } + if (this.type == 'edit' && this.getUser().isAdmin() && !this.model.isApi()) { layout.push({ label: 'Password', From 14705e2100ee1856fd300adf1ae47430fd22e8b5 Mon Sep 17 00:00:00 2001 From: Yuri Kuznetsov Date: Mon, 9 Jan 2023 18:12:05 +0200 Subject: [PATCH 03/11] v --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 6e935ab489..b8070a493d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "espocrm", - "version": "7.3.0", + "version": "7.3.1", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/package.json b/package.json index 1388e7ea7d..5141127d4a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "espocrm", - "version": "7.3.0", + "version": "7.3.1", "description": "Open-source CRM.", "repository": { "type": "git", From c0da4e1777f36caca3e97712232fd96a0c0817d5 Mon Sep 17 00:00:00 2001 From: Yuri Kuznetsov Date: Tue, 10 Jan 2023 09:28:30 +0200 Subject: [PATCH 04/11] setup system user attributes to select --- application/Espo/Core/ApplicationUser.php | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/application/Espo/Core/ApplicationUser.php b/application/Espo/Core/ApplicationUser.php index 3bd9ab6dc0..c2859aa090 100644 --- a/application/Espo/Core/ApplicationUser.php +++ b/application/Espo/Core/ApplicationUser.php @@ -55,7 +55,20 @@ class ApplicationUser */ public function setupSystemUser(): void { - $user = $this->entityManagerProxy->getEntityById(User::ENTITY_TYPE, self::SYSTEM_USER_ID); + $user = $this->entityManagerProxy + ->getRDBRepository(User::ENTITY_TYPE) + ->select([ + 'id', + 'name', + 'userName', + 'type', + 'isActive', + 'firstName', + 'lastName', + 'deleted', + ]) + ->where(['id' => self::SYSTEM_USER_ID]) + ->findOne(); if (!$user) { throw new RuntimeException("System user is not found."); From 08512304f10d0b1c1be585b97fd76779ab722992 Mon Sep 17 00:00:00 2001 From: Yuri Kuznetsov Date: Tue, 10 Jan 2023 09:44:04 +0200 Subject: [PATCH 05/11] class alias check existance --- .../Espo/Resources/defaults/class-aliases.php | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/application/Espo/Resources/defaults/class-aliases.php b/application/Espo/Resources/defaults/class-aliases.php index 2426cad869..1b3601ee82 100644 --- a/application/Espo/Resources/defaults/class-aliases.php +++ b/application/Espo/Resources/defaults/class-aliases.php @@ -29,6 +29,19 @@ /** @todo Remove in v8.0. */ -class_alias('Laminas\\Mail\\Protocol\\Smtp', 'Zend\\Mail\\Protocol\\Smtp'); -class_alias('Laminas\\Mail\\Protocol\\Imap', 'Zend\\Mail\\Protocol\\Imap'); -class_alias('Laminas\\Mail\\Message', 'Zend\\Mail\\Message'); +$map = [ + ['Laminas\\Mail\\Protocol\\Smtp', 'Zend\\Mail\\Protocol\\Smtp'], + ['Laminas\\Mail\\Protocol\\Imap', 'Zend\\Mail\\Protocol\\Imap'], + ['Laminas\\Mail\\Message', 'Zend\\Mail\\Message'], +]; + +foreach ($map as $item) { + $className = $item[0]; + $alias = $item[1]; + + if (!class_exists($className)) { + continue; + } + + class_alias($className, $alias); +} From 3ba9ea5085bd9a4a196a20ae98c5f6fd26f20edd Mon Sep 17 00:00:00 2001 From: Yuri Kuznetsov Date: Tue, 10 Jan 2023 11:31:26 +0200 Subject: [PATCH 06/11] email prevent drag by menu --- client/src/views/email/list.js | 1 + 1 file changed, 1 insertion(+) diff --git a/client/src/views/email/list.js b/client/src/views/email/list.js index db53b34f54..65aefd1b95 100644 --- a/client/src/views/email/list.js +++ b/client/src/views/email/list.js @@ -156,6 +156,7 @@ define('views/email/list', ['views/list'], function (Dep) { let $row = $container.find(`.list-row[data-id="${m.id}"]`).first(); $row.draggable({ + cancel: 'input,textarea,button,select,option,.dropdown-menu', helper: () => { let text = this.translate('Moving to Folder', 'labels', 'Email'); From 7f1bc0cf1d25001c3b70c13ca7f0f334baa090dc Mon Sep 17 00:00:00 2001 From: Taras Machyshyn Date: Tue, 10 Jan 2023 22:15:17 +0200 Subject: [PATCH 07/11] Upgrade script --- upgrades/7.3/scripts/BeforeUpgrade.php | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/upgrades/7.3/scripts/BeforeUpgrade.php b/upgrades/7.3/scripts/BeforeUpgrade.php index 3ead387369..73105d65ad 100644 --- a/upgrades/7.3/scripts/BeforeUpgrade.php +++ b/upgrades/7.3/scripts/BeforeUpgrade.php @@ -3,7 +3,7 @@ * This file is part of EspoCRM. * * EspoCRM - Open Source CRM application. - * Copyright (C) 2014-2021 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko + * Copyright (C) 2014-2023 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko * Website: https://www.espocrm.com * * EspoCRM is free software: you can redistribute it and/or modify @@ -32,11 +32,14 @@ use Espo\Entities\EmailAccount; use Espo\Entities\User; use Espo\Entities\Preferences; use Espo\ORM\EntityManager; +use Espo\Core\Utils\Config; +use Espo\Core\Utils\Database\Helper as DatabaseHelper; class BeforeUpgrade { public function run(Container $container): void { + $this->addUserColumn($container->get('config')); $this->migrateSmtp($container->get('entityManager')); } @@ -121,4 +124,21 @@ class BeforeUpgrade $entityManager->saveEntity($preferences); } + + private function addUserColumn(Config $config) + { + $databaseHelper = new DatabaseHelper($config); + + $pdo = $databaseHelper->createPdoConnection(); + + $query = " + ALTER TABLE `user` ADD `working_time_calendar_id` VARCHAR(24) + DEFAULT NULL COLLATE `utf8mb4_unicode_ci` + "; + + try { + $sth = $pdo->prepare($query); + $sth->execute(); + } catch (\Exception $e) {} + } } From ac2576b7606b911c52d4f1b15a8d74a77dd8b629 Mon Sep 17 00:00:00 2001 From: Yuri Kuznetsov Date: Wed, 11 Jan 2023 09:20:58 +0200 Subject: [PATCH 08/11] add idea/misc.xml --- .idea/.gitignore | 1 + .idea/misc.xml | 7 +++++++ 2 files changed, 8 insertions(+) create mode 100644 .idea/misc.xml diff --git a/.idea/.gitignore b/.idea/.gitignore index 5343c234d4..3ce7dcdfbe 100644 --- a/.idea/.gitignore +++ b/.idea/.gitignore @@ -3,3 +3,4 @@ !/codeStyles !/fileTemplates !/inspectionProfiles +!misc.xml diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000000..7053f4fa1a --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file From 967571f030857e6f0e1a1c33997ad47b7649fbe1 Mon Sep 17 00:00:00 2001 From: Yuri Kuznetsov Date: Wed, 11 Jan 2023 12:11:00 +0200 Subject: [PATCH 09/11] cs --- client/src/views/notification/badge.js | 45 +++++++++++++------------- 1 file changed, 22 insertions(+), 23 deletions(-) diff --git a/client/src/views/notification/badge.js b/client/src/views/notification/badge.js index 70c009e9fc..7d3086d979 100644 --- a/client/src/views/notification/badge.js +++ b/client/src/views/notification/badge.js @@ -80,10 +80,10 @@ define('views/notification/badge', ['view'], function (Dep) { window.addEventListener('storage', e => { if (e.key === 'messageClosePopupNotificationId') { - var id = localStorage.getItem('messageClosePopupNotificationId'); + let id = localStorage.getItem('messageClosePopupNotificationId'); if (id) { - var key = 'popup-' + id; + let key = 'popup-' + id; if (this.hasView(key)) { this.markPopupRemoved(id); @@ -105,7 +105,6 @@ define('views/notification/badge', ['view'], function (Dep) { afterRender: function () { this.$badge = this.$el.find('.notifications-button'); - this.$icon = this.$el.find('.notifications-button .icon'); this.$number = this.$el.find('.number-badge'); this.runCheckUpdates(true); @@ -119,10 +118,10 @@ define('views/notification/badge', ['view'], function (Dep) { .appendTo('body'); } - var popupNotificationsData = this.popupNotificationsData = + let popupNotificationsData = this.popupNotificationsData = this.getMetadata().get('app.popupNotifications') || {}; - for (var name in popupNotificationsData) { + for (let name in popupNotificationsData) { this.checkPopupNotifications(name); } @@ -197,7 +196,7 @@ define('views/notification/badge', ['view'], function (Dep) { .getRequest('Notification/action/notReadCount') .then(count => { if (!isFirstCheck && count > this.unreadCount) { - var messageBlockPlayNotificationSound = + let messageBlockPlayNotificationSound = localStorage.getItem('messageBlockPlayNotificationSound'); if (!messageBlockPlayNotificationSound) { @@ -241,10 +240,10 @@ define('views/notification/badge', ['view'], function (Dep) { }, checkGroupedPopupNotifications: function () { - var toCheck = false; + let toCheck = false; - for (var name in this.popupNotificationsData) { - var data = this.popupNotificationsData[name] || {}; + for (let name in this.popupNotificationsData) { + let data = this.popupNotificationsData[name] || {}; if (!data.grouped) { continue; @@ -263,8 +262,8 @@ define('views/notification/badge', ['view'], function (Dep) { Espo.Ajax.getRequest('PopupNotification/action/grouped') .then(result => { - for (const type in result) { - const list = result[type]; + for (let type in result) { + let list = result[type]; list.forEach(item => this.showPopupNotification(type, item)); } @@ -272,13 +271,13 @@ define('views/notification/badge', ['view'], function (Dep) { }, checkPopupNotifications: function (name, isNotFirstCheck) { - var data = this.popupNotificationsData[name] || {}; + let data = this.popupNotificationsData[name] || {}; - var url = data.url; - var interval = data.interval; - var disabled = data.disabled || false; + let url = data.url; + let interval = data.interval; + let disabled = data.disabled || false; - var isFirstCheck = !isNotFirstCheck; + let isFirstCheck = !isNotFirstCheck; if (disabled) { return; @@ -288,10 +287,10 @@ define('views/notification/badge', ['view'], function (Dep) { return; } - var useWebSocket = this.useWebSocket && data.useWebSocket; + let useWebSocket = this.useWebSocket && data.useWebSocket; if (useWebSocket) { - var category = 'popupNotifications.' + (data.webSocketCategory || name); + let category = 'popupNotifications.' + (data.webSocketCategory || name); this.getHelper().webSocketManager.subscribe(category, (c, response) => { if (!response.list) { @@ -356,19 +355,19 @@ define('views/notification/badge', ['view'], function (Dep) { }, showPopupNotification: function (name, data, isNotFirstCheck) { - var view = this.popupNotificationsData[name].view; + let view = this.popupNotificationsData[name].view; if (!view) { return; } - var id = data.id || null; + let id = data.id || null; if (id) { id = name + '_' + id; if (~this.shownNotificationIds.indexOf(id)) { - var notificationView = this.getView('popup-' + id); + let notificationView = this.getView('popup-' + id); if (notificationView) { notificationView.trigger('update-data', data.data); @@ -406,7 +405,7 @@ define('views/notification/badge', ['view'], function (Dep) { }, markPopupRemoved: function (id) { - var index = this.shownNotificationIds.indexOf(id); + let index = this.shownNotificationIds.indexOf(id); if (index > -1) { this.shownNotificationIds.splice(index, 1); @@ -437,7 +436,7 @@ define('views/notification/badge', ['view'], function (Dep) { showNotifications: function () { this.closeNotifications(); - var $container = $('
').attr('id', 'notifications-panel'); + let $container = $('
').attr('id', 'notifications-panel'); $container.appendTo(this.$el.find('.notifications-panel-container')); From 811ea3ad279a089926622dfcb22220e61865806b Mon Sep 17 00:00:00 2001 From: Yuri Kuznetsov Date: Wed, 11 Jan 2023 12:48:07 +0200 Subject: [PATCH 10/11] grouped popup notifications interval --- .../metadata/app/popupNotifications.json | 1 - .../Espo/Resources/defaults/config.php | 1 + client/src/views/notification/badge.js | 39 ++++++++++++++----- 3 files changed, 31 insertions(+), 10 deletions(-) diff --git a/application/Espo/Modules/Crm/Resources/metadata/app/popupNotifications.json b/application/Espo/Modules/Crm/Resources/metadata/app/popupNotifications.json index 25abf43ef5..7d2ad576ba 100644 --- a/application/Espo/Modules/Crm/Resources/metadata/app/popupNotifications.json +++ b/application/Espo/Modules/Crm/Resources/metadata/app/popupNotifications.json @@ -4,7 +4,6 @@ "providerClassName": "Espo\\Modules\\Crm\\Tools\\Activities\\PopupNotificationsProvider", "serviceName": "Activities", "methodName": "getPopupNotifications", - "interval": 15, "useWebSocket": true, "portalDisabled": true, "view": "crm:views/meeting/popup-notification" diff --git a/application/Espo/Resources/defaults/config.php b/application/Espo/Resources/defaults/config.php index 26859944c8..acad56120b 100644 --- a/application/Espo/Resources/defaults/config.php +++ b/application/Espo/Resources/defaults/config.php @@ -133,6 +133,7 @@ return [ 'emailNotificationsDelay' => 30, 'emailMessageMaxSize' => 10, 'notificationsCheckInterval' => 10, + 'popupNotificationsCheckInterval' => 15, 'maxEmailAccountCount' => 2, 'followCreatedEntities' => false, 'b2cMode' => false, diff --git a/client/src/views/notification/badge.js b/client/src/views/notification/badge.js index 7d3086d979..298732c00c 100644 --- a/client/src/views/notification/badge.js +++ b/client/src/views/notification/badge.js @@ -33,8 +33,10 @@ define('views/notification/badge', ['view'], function (Dep) { template: 'notification/badge', notificationsCheckInterval: 10, + groupedCheckInterval: 15, timeout: null, + groupedTimeout: null, popupNotificationsData: null, @@ -58,6 +60,10 @@ define('views/notification/badge', ['view'], function (Dep) { clearTimeout(this.timeout); } + if (this.groupedTimeout) { + clearTimeout(this.groupedTimeout); + } + for (let name in this.popupTimeouts) { clearTimeout(this.popupTimeouts[name]); } @@ -69,6 +75,9 @@ define('views/notification/badge', ['view'], function (Dep) { this.notificationsCheckInterval = this.getConfig().get('notificationsCheckInterval') || this.notificationsCheckInterval; + this.groupedCheckInterval = this.getConfig().get('popupNotificationsCheckInterval') || + this.groupedCheckInterval; + this.lastId = 0; this.shownNotificationIds = []; this.closedNotificationIds = []; @@ -125,7 +134,9 @@ define('views/notification/badge', ['view'], function (Dep) { this.checkPopupNotifications(name); } - this.checkGroupedPopupNotifications(); + if (this.hasGroupedPopupNotifications()) { + this.checkGroupedPopupNotifications(); + } }, playSound: function () { @@ -239,9 +250,11 @@ define('views/notification/badge', ['view'], function (Dep) { ); }, - checkGroupedPopupNotifications: function () { - let toCheck = false; - + /** + * @private + * @return {boolean} + */ + hasGroupedPopupNotifications: function () { for (let name in this.popupNotificationsData) { let data = this.popupNotificationsData[name] || {}; @@ -250,16 +263,19 @@ define('views/notification/badge', ['view'], function (Dep) { } if (data.portalDisabled && this.getUser().isPortal()) { - return; + continue; } - toCheck = true; + return true; } - if (!toCheck) { - return; - } + return false; + }, + /** + * @private + */ + checkGroupedPopupNotifications: function () { Espo.Ajax.getRequest('PopupNotification/action/grouped') .then(result => { for (let type in result) { @@ -268,6 +284,11 @@ define('views/notification/badge', ['view'], function (Dep) { list.forEach(item => this.showPopupNotification(type, item)); } }); + + this.groupedTimeout = setTimeout( + () => this.checkGroupedPopupNotifications(), + this.groupedCheckInterval * 1000 + ); }, checkPopupNotifications: function (name, isNotFirstCheck) { From 0612cd23991c8af61f9f6d47f573c9ed2450f18c Mon Sep 17 00:00:00 2001 From: Yuri Kuznetsov Date: Wed, 11 Jan 2023 13:05:32 +0200 Subject: [PATCH 11/11] fix --- client/src/views/notification/badge.js | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/client/src/views/notification/badge.js b/client/src/views/notification/badge.js index 298732c00c..75ccaa6e36 100644 --- a/client/src/views/notification/badge.js +++ b/client/src/views/notification/badge.js @@ -196,6 +196,8 @@ define('views/notification/badge', ['view'], function (Dep) { ) { return true; } + + return false; }, checkUpdates: function (isFirstCheck) { @@ -276,14 +278,16 @@ define('views/notification/badge', ['view'], function (Dep) { * @private */ checkGroupedPopupNotifications: function () { - Espo.Ajax.getRequest('PopupNotification/action/grouped') - .then(result => { - for (let type in result) { - let list = result[type]; + if (!this.checkBypass()) { + Espo.Ajax.getRequest('PopupNotification/action/grouped') + .then(result => { + for (let type in result) { + let list = result[type]; - list.forEach(item => this.showPopupNotification(type, item)); - } - }); + list.forEach(item => this.showPopupNotification(type, item)); + } + }); + } this.groupedTimeout = setTimeout( () => this.checkGroupedPopupNotifications(),