Compare commits

...

1397 Commits

Author SHA1 Message Date
Taras Machyshyn 0666880786 IIS web.config fixes 2022-10-19 13:18:45 +03:00
Yuri Kuznetsov e0113388d2 typo fix 2022-10-11 13:04:26 +03:00
Yuri Kuznetsov 384f28ecae cleanup 2022-10-11 13:02:25 +03:00
Yuri Kuznetsov d7596c208c fix 2022-10-05 09:35:07 +03:00
Yuri Kuznetsov 6d1ab5870f fix avatar 2022-10-04 08:52:40 +03:00
Yuri Kuznetsov 73dbfa38ec css fix 2022-09-29 14:46:37 +03:00
Yuri Kuznetsov 4adb068699 fix lead capture source 2022-09-29 09:02:55 +03:00
Yuri Kuznetsov 3e4c738ab1 fix link multiple cloning fetch 2022-09-28 15:08:12 +03:00
Yuri Kuznetsov cd92e4fcd8 fix util array to object to preserve lists 2022-09-27 09:49:14 +03:00
Yuri Kuznetsov fba191f22c global search label 2022-09-27 08:57:51 +03:00
Yuri Kuznetsov 8874c8827a fix 2022-09-24 13:07:22 +03:00
Yuri Kuznetsov 34529a8ed9 numpad enter 2022-09-23 10:40:05 +03:00
Yuri Kuznetsov 8fd44acae2 entry point 404 2022-09-21 14:40:28 +03:00
Yuri Kuznetsov 4dd540ffc7 cleanup 2022-09-21 11:27:36 +03:00
Yuri Kuznetsov 9c20116c9b v 2022-09-20 20:21:47 +03:00
Yuri Kuznetsov 926410d58f upgrade file 2022-09-20 20:21:11 +03:00
Yuri Kuznetsov c64a107ad9 dif docs 2022-09-20 20:20:54 +03:00
Yuri Kuznetsov 5dcd25946b diff mandatory files 2022-09-20 20:09:15 +03:00
Yuri Kuznetsov d12865bbcb lf command 2022-09-20 18:10:38 +03:00
Yuri Kuznetsov 3fed415437 clnup 2022-09-20 17:18:22 +03:00
Yuri Kuznetsov a03a13d3b9 fix auth token error 500 2022-09-20 17:05:11 +03:00
Yuri Kuznetsov e625951831 array link-list fix 2022-09-20 12:14:28 +03:00
Yuri Kuznetsov f533c68c9b fix enum empty value not selected 2022-09-19 15:37:52 +03:00
Yuri Kuznetsov 2420746f1b folders save fix 2022-09-16 21:46:42 +03:00
Yuri Kuznetsov 2dfd00dd2e cs fix 2022-09-16 21:46:09 +03:00
Yuri Kuznetsov e043bb48e9 css fix 2022-09-16 14:23:44 +03:00
Yuri Kuznetsov 9aaef9d957 v 2022-09-16 10:08:10 +03:00
Yuri Kuznetsov 87449aae67 cleanup 2022-09-16 10:06:48 +03:00
Yuri Kuznetsov 191d064fe1 exception log message interface 2022-09-16 10:01:22 +03:00
Yuri Kuznetsov dcb3e2feaf typo fix 2022-09-16 09:44:22 +03:00
Yuri Kuznetsov da9a423e59 css fix 2022-09-15 19:04:46 +03:00
Yuri Kuznetsov 3be4510e63 css fix 2022-09-15 18:55:02 +03:00
Yuri Kuznetsov a6bb5a239b style fix 2022-09-15 18:05:38 +03:00
Yuri Kuznetsov a5fb42609b calendar style fix 2022-09-15 16:30:09 +03:00
Yuri Kuznetsov 40c2c1718e calendar minor none 2022-09-15 16:19:09 +03:00
Yuri Kuznetsov aeecfd63da color fix 2022-09-15 15:53:55 +03:00
Yuri Kuznetsov 1da1e6da9b fix 2022-09-15 15:52:16 +03:00
Yuri Kuznetsov bf9f23ebdd dev 2022-09-15 15:45:16 +03:00
Yuri Kuznetsov 7beb4f8d83 fix 2022-09-15 15:33:36 +03:00
Yuri Kuznetsov 991b859643 calendar now circle 2022-09-15 15:31:40 +03:00
Yuri Kuznetsov 9bd74e08db disable email fields layout 2022-09-15 14:52:36 +03:00
Yuri Kuznetsov cfdf65025d fix stream post input event 2022-09-15 13:32:29 +03:00
Yuri Kuznetsov bf471e654c calendar now indicator 2022-09-15 12:42:53 +03:00
Yuri Kuznetsov 5feee1cf55 calendar today text color 2022-09-15 12:21:51 +03:00
Yuri Kuznetsov 6af6fc017b lead capture fix 2022-09-15 10:44:39 +03:00
Yuri Kuznetsov 8ee9a792fc lead industry acl fix 2022-09-15 10:09:37 +03:00
Yuri Kuznetsov 9ef1c5928f fix campaign bottom view 2022-09-14 16:50:50 +03:00
Yuri Kuznetsov 64c933e365 v 2022-09-14 12:52:15 +03:00
Yuri Kuznetsov 32055f3d6e fix upload in chunks check 2022-09-14 10:10:45 +03:00
Yuri Kuznetsov 804acae44b fix extension check 2022-09-13 20:51:34 +03:00
Yuri Kuznetsov 6b3f37c00e panel show hide ref 2022-09-13 20:32:24 +03:00
Yuri Kuznetsov 4bdc4878cd fix bottom tab layout 2022-09-13 17:41:33 +03:00
Yuri Kuznetsov 625d2bc128 fix tab race condition error 2022-09-13 17:27:44 +03:00
Yuri Kuznetsov 0af5bb1b4b fix bottom tabs fetch 2022-09-13 14:26:48 +03:00
Yuri Kuznetsov 7a0d59357c panel hide/show concurrency issue fix 2022-09-13 14:16:55 +03:00
Yuri Kuznetsov adbb46d02c system template inline attachments 2022-09-13 10:12:28 +03:00
Yuri Kuznetsov ac3884179e cs fix 2022-09-13 10:08:31 +03:00
Yuri Kuznetsov 7cf1af188d fix attachment replated validation 2022-09-13 09:36:47 +03:00
Yuri Kuznetsov 21b695e4ef style fix 2022-09-13 09:30:50 +03:00
Yuri Kuznetsov fbda66defc fix 2022-09-13 09:25:49 +03:00
Yuri Kuznetsov beb4435ee0 diff closest 2022-09-12 15:07:32 +03:00
Yuri Kuznetsov 6a4c78c1cb fix language settings 2022-09-12 14:51:05 +03:00
Yuri Kuznetsov dc7f5d8e66 fix grunt zip 2022-09-12 14:06:54 +03:00
Yuri Kuznetsov 872a4c2f2c fix typo 2022-09-12 12:35:27 +03:00
Yuri Kuznetsov 8a5204bc4c css fix 2022-09-12 12:03:26 +03:00
Yuri Kuznetsov acbf6a1742 v 2022-09-12 11:22:07 +03:00
Yuri Kuznetsov b2d3d56a85 attachment-multiple link 2022-09-12 11:20:48 +03:00
Yuri Kuznetsov 8b83fa640f note relate icon 2022-09-12 10:45:16 +03:00
Yuri Kuznetsov 5a95abed73 v 2022-09-12 09:05:25 +03:00
Yuri Kuznetsov ca1d689c3a it_IT 2022-09-12 09:04:35 +03:00
Yuri Kuznetsov 0c750789b1 auth cookie fallback 2022-09-11 16:01:04 +03:00
Yuri Kuznetsov ccb6c19c72 url field fix encode 2022-09-10 09:59:44 +03:00
Yuri Kuznetsov 011581f09d it_IT 2022-09-09 11:07:23 +03:00
Yuri Kuznetsov 110dd7b8b3 fix extension install 2022-09-09 10:45:14 +03:00
Yuri Kuznetsov f9bdd78df8 ref 2022-09-09 10:24:17 +03:00
Yuri Kuznetsov 241b582923 css fix 2022-09-08 15:01:59 +03:00
Yuri Kuznetsov 348a089e18 css fix 2022-09-08 13:22:01 +03:00
Yuri Kuznetsov 7528fb3871 email folder no actions + merge disabled 2022-09-07 14:11:17 +03:00
Yuri Kuznetsov e76f849bde cs fix 2022-09-06 19:54:07 +03:00
Yuri Kuznetsov 770658ffed cs fix 2022-09-06 19:50:31 +03:00
Yuri Kuznetsov 348bcd64ba popover viewport 2022-09-06 08:53:30 +03:00
Yuri Kuznetsov 23ec5a66a7 css fix 2022-09-05 18:20:18 +03:00
Yuri Kuznetsov 210da100ea user links entityAcl 2022-09-05 18:12:08 +03:00
Yuri Kuznetsov 91aec9fc1d fix typo 2022-09-05 09:59:49 +03:00
Yuri Kuznetsov 155565b946 cleanup 2022-09-03 21:14:36 +03:00
Yuri Kuznetsov 5157430104 email compose focus 2022-09-03 21:11:53 +03:00
Yuri Kuznetsov 2eb88617b3 fix email compose setup 2022-09-03 20:42:19 +03:00
Yuri Kuznetsov 5e4e0b8d3c button modal right position impr 2022-09-02 18:40:36 +03:00
Yuri Kuznetsov 5f1cb260ae reply btn style 2022-09-02 12:07:14 +03:00
Yuri Kuznetsov d854fc9fa6 css fix 2022-09-02 11:41:12 +03:00
Yuri Kuznetsov d455d0c73f css fix 2022-09-02 10:20:57 +03:00
Yuri Kuznetsov ae15b8fb5e fix table 2022-09-02 10:07:20 +03:00
Yuri Kuznetsov d686aee9d4 css fix 2022-09-02 10:02:12 +03:00
Yuri Kuznetsov 6e667a5024 merge fix 2022-09-01 20:24:18 +03:00
Yuri Kuznetsov 5610ac9b71 doc fix 2022-09-01 20:13:12 +03:00
Yuri Kuznetsov f9bb35ae68 fix 2022-08-31 18:48:04 +03:00
Yuri Kuznetsov 768f6d7c71 dashboard menu 2022-08-31 18:42:31 +03:00
Yuri Kuznetsov ea1455a50c css fix 2022-08-31 18:23:38 +03:00
Yuri Kuznetsov cd6a157f1d fix 2022-08-31 16:45:54 +03:00
Yuri Kuznetsov 447d54b61b cleanup 2022-08-31 16:06:20 +03:00
Yuri Kuznetsov f721220b1a css fix 2022-08-31 16:02:44 +03:00
Yuri Kuznetsov 95ed44d2ec css fix 2022-08-31 15:40:01 +03:00
Yuri Kuznetsov 5ad74492e1 css fix 2022-08-31 15:06:15 +03:00
Yuri Kuznetsov b191896fb1 cs fix 2022-08-31 15:06:09 +03:00
Yuri Kuznetsov 314636e298 update identicon 2022-08-31 13:45:12 +03:00
Yuri Kuznetsov 6e4d25fadd wysiwyg fallback fix 2022-08-31 12:40:15 +03:00
Yuri Kuznetsov d401cb4e4c filter join char 2022-08-31 12:28:57 +03:00
Yuri Kuznetsov 2f7c892f67 orm ref 2022-08-31 12:02:34 +03:00
Yuri Kuznetsov 1f33f41965 orm ref 2022-08-31 11:51:50 +03:00
dependabot[bot] 7e6c64364f Bump moment-timezone from 0.5.33 to 0.5.35 (#2418)
Bumps [moment-timezone](https://github.com/moment/moment-timezone) from 0.5.33 to 0.5.35.
- [Release notes](https://github.com/moment/moment-timezone/releases)
- [Changelog](https://github.com/moment/moment-timezone/blob/develop/changelog.md)
- [Commits](https://github.com/moment/moment-timezone/compare/0.5.33...0.5.35)

---
updated-dependencies:
- dependency-name: moment-timezone
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-08-31 09:36:00 +03:00
Yuri Kuznetsov 9916b95703 stream query impr 2022-08-30 12:49:02 +03:00
Yuri Kuznetsov cba0bf3d20 import entity bigint 2022-08-30 08:53:48 +03:00
Yuri Kuznetsov 82b043adf6 rename 2022-08-30 08:48:15 +03:00
Yuri Kuznetsov bfcf607468 import error integrity type constraint 2022-08-29 18:23:06 +03:00
Yuri Kuznetsov 6db74ec68f fix import error field 2022-08-29 17:59:25 +03:00
Yuri Kuznetsov 02cb7c1fd5 edit modal lost focus fix 2022-08-29 17:42:45 +03:00
Yuri Kuznetsov 8b06f2c02c dialog focus fix 2022-08-29 17:12:01 +03:00
Yuri Kuznetsov 9644729f68 dialog close on hide 2022-08-29 16:25:01 +03:00
Yuri Kuznetsov c0f3d5e765 fix 2022-08-29 16:10:41 +03:00
Yuri Kuznetsov e766625aa8 confirm fix 2022-08-29 16:01:34 +03:00
Yuri Kuznetsov ad97a71cb6 acceptance status focus 2022-08-29 15:00:45 +03:00
Yuri Kuznetsov d857231178 acceptance status fix modal 2022-08-29 14:42:50 +03:00
Yuri Kuznetsov 4976ebe89e glass fix 2022-08-29 14:10:53 +03:00
Yuri Kuznetsov 571ccc08d4 compose fix 2022-08-29 12:15:45 +03:00
Yuri Kuznetsov 63f73483a1 css fix 2022-08-29 10:52:59 +03:00
Yuri Kuznetsov 60e4d3e7c0 css fix 2022-08-29 10:49:05 +03:00
Yuri Kuznetsov 063b5e19a2 system requirements css 2022-08-29 10:35:16 +03:00
Yuri Kuznetsov 9899e5913f entity manager table css change 2022-08-29 09:59:46 +03:00
Yuri Kuznetsov 6730e8a48b reminder fix 2022-08-28 22:54:23 +03:00
Yuri Kuznetsov fe833862f3 theme fix 2022-08-28 22:42:13 +03:00
Yuri Kuznetsov f660c9aedf wysiwyg iframe fallback padding 2022-08-28 21:11:04 +03:00
Yuri Kuznetsov 58cd736ab9 wysiwyg fallback radius 2022-08-28 20:47:43 +03:00
Yuri Kuznetsov 693d952910 pt_BR 2022-08-28 19:15:20 +03:00
Yuri Kuznetsov 8ffb72df30 fix 2022-08-28 18:54:08 +03:00
Yuri Kuznetsov 2ae4094c64 theme fix 2022-08-28 18:49:45 +03:00
Yuri Kuznetsov 3c089c8628 quick view change 2022-08-27 16:43:08 +03:00
Yuri Kuznetsov a2f7bef58d email reply update 2022-08-27 16:39:44 +03:00
Yuri Kuznetsov cc2e64a4cd ref 2022-08-27 16:20:07 +03:00
Yuri Kuznetsov a2eb3d7b8c color fix 2022-08-26 19:39:06 +03:00
David 214f5cec96 fixed markdown mime type (#2416) 2022-08-26 15:24:16 +03:00
Arkadiy Asuratov d56b95d9f2 fix typo (#2414) 2022-08-26 11:49:28 +03:00
Yuri Kuznetsov 7f4595a374 color fixes 2022-08-26 10:34:29 +03:00
Yuri Kuznetsov c3b5191ca4 fix 2022-08-26 10:22:51 +03:00
Yuri Kuznetsov de8f9114ab color fix 2022-08-26 10:05:47 +03:00
Yuri Kuznetsov 960583c052 color fix 2022-08-26 10:03:21 +03:00
Yuri Kuznetsov 32baf94251 color fixes 2022-08-26 10:01:07 +03:00
Yuri Kuznetsov e290bc0b12 glass theme fixes 2022-08-26 09:49:30 +03:00
Yuri Kuznetsov 54bf5d98ae css fix 2022-08-26 09:42:32 +03:00
Yuri Kuznetsov d00b77137e css fix 2022-08-25 23:28:19 +03:00
Yuri Kuznetsov 293ca499cc color 2022-08-25 23:22:27 +03:00
Yuri Kuznetsov 5825f8a8f9 colors 2022-08-25 23:02:24 +03:00
Yuri Kuznetsov 70f8458d9c colors 2022-08-25 21:34:42 +03:00
Yuri Kuznetsov d8dc5d107e color fix 2022-08-25 21:17:25 +03:00
Yuri Kuznetsov 47d7064a72 fix 2022-08-25 20:12:01 +03:00
Yuri Kuznetsov 6874bee0ad ref and bypass app reload 2022-08-25 20:02:26 +03:00
Yuri Kuznetsov 18f285be91 js doc 2022-08-25 20:00:53 +03:00
Yuri Kuznetsov 9be5d5b102 jsdoc 2022-08-25 19:45:14 +03:00
Yuri Kuznetsov 25782c7696 ref 2022-08-25 19:45:05 +03:00
Yuri Kuznetsov 20ae316c63 page refresh on update 2022-08-25 19:18:08 +03:00
Yuri Kuznetsov 8fd166ef2b category flicker fix 2022-08-25 17:50:03 +03:00
Yuri Kuznetsov af8ce3a34e fix typo 2022-08-25 17:16:39 +03:00
Yuri Kuznetsov e5956abb85 fix 2022-08-25 17:15:40 +03:00
Yuri Kuznetsov 7024487528 bc fix 2022-08-25 16:51:34 +03:00
Yuri Kuznetsov a938905946 fix 2022-08-25 15:37:20 +03:00
Yuri Kuznetsov 0250bfcd12 fix 2022-08-25 14:25:21 +03:00
Yuri Kuznetsov 6d7e1f0952 enum default null 2022-08-25 11:16:27 +03:00
Yuri Kuznetsov a098b20221 idea 2022-08-25 11:14:50 +03:00
Yuri Kuznetsov 4ab21cb80b ref 2022-08-24 16:56:04 +03:00
Yuri Kuznetsov f9e3953f68 record modal refactor 2022-08-24 16:39:59 +03:00
Yuri Kuznetsov 313a2874cd quick detail 2022-08-24 15:53:44 +03:00
Yuri Kuznetsov 60ae130612 stream quick view 2022-08-24 12:52:26 +03:00
Yuri Kuznetsov 22084a3d77 link mul with primary fix 2022-08-24 11:43:21 +03:00
Yuri Kuznetsov ce35f68584 quick view middle click 2022-08-24 11:20:07 +03:00
Yuri Kuznetsov d0ff17ed40 user team no edit in modal 2022-08-24 11:09:00 +03:00
Yuri Kuznetsov 30cfea4ac4 team detail small 2022-08-24 11:07:32 +03:00
Yuri Kuznetsov f83d0a34c3 fix 2022-08-24 10:38:31 +03:00
Yuri Kuznetsov f2fd948c7b cleanup 2022-08-24 10:27:05 +03:00
Yuri Kuznetsov 1b0a20c001 ref 2022-08-24 10:25:18 +03:00
Yuri Kuznetsov 9fa4f40ccf cs fix 2022-08-24 10:18:21 +03:00
Yuri Kuznetsov 71e39fa4fe kanban quick view key 2022-08-24 10:14:54 +03:00
Yuri Kuznetsov d98dfecef3 file gray-box style fix 2022-08-23 21:16:12 +03:00
Yuri Kuznetsov adfd7efae6 comment 2022-08-23 13:26:29 +03:00
Yuri Kuznetsov 0d75fa4929 css fix 2022-08-23 12:51:03 +03:00
Yuri Kuznetsov 43c8613e94 fix label 2022-08-23 12:41:41 +03:00
Yuri Kuznetsov ee1e3bbaf9 fix webhook cache 2022-08-23 10:31:17 +03:00
Yuri Kuznetsov 1d03f557ce fix lead capture 2022-08-22 17:05:32 +03:00
Yuri Kuznetsov 939d482cfb css fix 2022-08-22 12:27:54 +03:00
Yuri Kuznetsov 044ffbff13 cs fix 2022-08-22 12:20:47 +03:00
Yuri Kuznetsov 37130f00a0 add image type inline 2022-08-22 11:37:34 +03:00
Yuri Kuznetsov c40070ceb0 download csp and fix 2022-08-22 11:37:26 +03:00
Yuri Kuznetsov 76d9b3cf9c audio video files inline 2022-08-22 11:30:15 +03:00
Yuri Kuznetsov 532321ddeb inlineMineTypeList 2022-08-22 11:23:43 +03:00
Yuri Kuznetsov 40b2613580 upload attachment same file fix 2022-08-22 11:14:52 +03:00
Yuri Kuznetsov e298ebec4b text field fetch trim empty 2022-08-22 11:03:21 +03:00
Yuri Kuznetsov 4073c60182 fix attachment upload 2022-08-22 10:53:03 +03:00
Yuri Kuznetsov 2553496ce6 stream post empty 2022-08-22 10:45:52 +03:00
Yuri Kuznetsov 67c0ac45bd fix typo 2022-08-22 10:45:35 +03:00
Yuri Kuznetsov 26f3c5630a dashbord layout add dashlet filter portal 2022-08-22 10:07:58 +03:00
Yuri Kuznetsov f92f21764c idea change 2022-08-22 10:07:38 +03:00
Yuri Kuznetsov a8631a6ce9 kanban fix 2022-08-21 19:36:36 +03:00
Yuri Kuznetsov 4045e33453 fix typo 2022-08-21 19:16:13 +03:00
Yuri Kuznetsov 8c79eb1ea4 fix dashboard layout focus 2022-08-21 19:14:50 +03:00
Yuri Kuznetsov 8885db0ae5 fix 2022-08-20 19:35:32 +03:00
Yuri Kuznetsov a8d210a22a cs fix 2022-08-20 18:32:57 +03:00
Yuri Kuznetsov 82912197eb cs fix 2022-08-20 18:07:33 +03:00
Yuri Kuznetsov 33cac3a551 cs fix 2022-08-20 18:03:27 +03:00
Yuri Kuznetsov ca89fd021d cs fix 2022-08-20 17:51:20 +03:00
Yuri Kuznetsov ad4e7c0beb cs fix 2022-08-20 17:44:08 +03:00
Yuri Kuznetsov 739e2b0ebc cleanup 2022-08-20 14:51:53 +03:00
Yuri Kuznetsov 738c6791a4 list select shift change 2022-08-20 14:51:36 +03:00
David 9bfa1a9225 Shift select implementation (#2409)
* Shift select implementation

* fixed checking for the first time

Co-authored-by: David Moškoř <david.moskor@apertia.cz>
2022-08-20 14:22:18 +03:00
Yuri Kuznetsov 7e3cc0480e fix edit focus 2022-08-20 12:56:53 +03:00
Yuri Kuznetsov 5365640ca8 ctrl+click quick view 2022-08-20 12:34:42 +03:00
Yuri Kuznetsov fe0570b0c4 color fix 2022-08-20 12:02:24 +03:00
Yuri Kuznetsov cca76ec4e0 fix popover hide on tab 2022-08-19 21:30:18 +03:00
Yuri Kuznetsov c226fd7936 popover fix 2022-08-19 21:22:34 +03:00
Yuri Kuznetsov f65143edf3 cleanup 2022-08-19 21:10:19 +03:00
Yuri Kuznetsov 483b717825 header fix 2022-08-19 18:44:32 +03:00
Yuri Kuznetsov b8a53f793a person name ref and fix 2022-08-19 17:45:32 +03:00
Yuri Kuznetsov 0210f9642a ref 2022-08-19 17:20:35 +03:00
Yuri Kuznetsov b6af13767d stream panel focus fix 2022-08-19 16:31:43 +03:00
Yuri Kuznetsov 73ec521f41 popover refactoring 2022-08-19 16:25:51 +03:00
Yuri Kuznetsov ea4ff23eee ref 2022-08-19 15:15:21 +03:00
Yuri Kuznetsov d4131bbcb0 fix orm 2022-08-18 14:49:05 +03:00
Yuri Kuznetsov 237ef71ad0 record dashlet fields filter forbidden 2022-08-18 12:23:59 +03:00
Yuri Kuznetsov 918f3fdd70 cleanup 2022-08-18 12:19:56 +03:00
Yuri Kuznetsov 98bbf8c1ba fix comment 2022-08-18 12:05:07 +03:00
Yuri Kuznetsov 613b82a85a ref 2022-08-18 12:01:46 +03:00
Yuri Kuznetsov f72cefeaef rebuild refactoring 2022-08-18 11:53:57 +03:00
Yuri Kuznetsov 316e7ec876 types 2022-08-18 11:22:46 +03:00
Yuri Kuznetsov e0ad00d857 query composer factory 2022-08-18 11:13:54 +03:00
Yuri Kuznetsov 108998be43 css fix 2022-08-17 20:43:31 +03:00
Yuri Kuznetsov 5b48d2d3eb cs fix 2022-08-17 20:37:10 +03:00
Yuri Kuznetsov 66c3aa2011 ref 2022-08-17 20:33:54 +03:00
Yuri Kuznetsov f7d0723516 fix email address 2022-08-17 19:56:28 +03:00
Yuri Kuznetsov 52588323a0 fix fullform draft 2022-08-17 19:46:23 +03:00
Yuri Kuznetsov 43451f99f1 fix icon 2022-08-17 19:19:22 +03:00
Yuri Kuznetsov 93139ee7d0 color fix 2022-08-17 19:09:46 +03:00
Yuri Kuznetsov 4ee86fccfa color fix 2022-08-17 19:06:32 +03:00
Yuri Kuznetsov cd1786c8ac fix naming 2022-08-17 18:47:24 +03:00
Yuri Kuznetsov 6a346bd01d fix last viewed 2022-08-17 15:37:45 +03:00
Yuri Kuznetsov b0b770eabe fix 2022-08-17 15:36:43 +03:00
Yuri Kuznetsov 21ef8c5fe6 jsdoc 2022-08-17 15:33:33 +03:00
Yuri Kuznetsov db6ecd6b68 fix typo 2022-08-17 14:48:28 +03:00
Yuri Kuznetsov cf54ede1ba var helper trim 2022-08-17 14:38:33 +03:00
Yuri Kuznetsov f8493d3300 fix test 2022-08-17 13:47:53 +03:00
Yuri Kuznetsov fad66fe813 fix 2022-08-17 13:30:50 +03:00
Yuri Kuznetsov 03c47cb625 sl_SI lang 2022-08-17 13:10:29 +03:00
Yuri Kuznetsov 21f2b22720 fix list view abort 2022-08-17 13:06:58 +03:00
Yuri Kuznetsov e4142bc0a1 language no santitize 2022-08-17 12:37:41 +03:00
Yuri Kuznetsov cba4d92f08 headerText usage 2022-08-17 12:13:24 +03:00
Yuri Kuznetsov 0836168771 clientSecurityHeadersDisabled 2022-08-17 10:36:19 +03:00
Yuri Kuznetsov 565f19f51b add google maps to csp 2022-08-17 10:31:23 +03:00
Yuri Kuznetsov b7de48a8e8 cleanup 2022-08-17 10:29:14 +03:00
Yuri Kuznetsov 402074a9b0 loader lib fix 2022-08-16 20:59:16 +03:00
Yuri Kuznetsov c5904fdda4 text usage 2022-08-16 20:01:18 +03:00
Yuri Kuznetsov 70fe7dfd62 text usage 2022-08-16 19:32:01 +03:00
Yuri Kuznetsov c3afa55f89 ref 2022-08-16 19:05:14 +03:00
Yuri Kuznetsov 3d9c5c01d2 fix loader exports to window 2022-08-16 18:56:22 +03:00
Yuri Kuznetsov 9422429df0 jsLibs exportVariable 2022-08-16 18:49:54 +03:00
Yuri Kuznetsov 4ecb8bf898 text usage 2022-08-16 18:32:29 +03:00
Yuri Kuznetsov 71dd03c914 text usage 2022-08-16 18:07:18 +03:00
Yuri Kuznetsov 9691f62ac3 fix formula set completer 2022-08-16 14:32:40 +03:00
Yuri Kuznetsov 374fcf2f9c autocomplete padding 2022-08-15 16:09:00 +03:00
Yuri Kuznetsov 940d407c77 css fix 2022-08-15 16:03:36 +03:00
Yuri Kuznetsov 9b2c988ddc hazyblue panel radius 2022-08-15 15:58:08 +03:00
Yuri Kuznetsov 6d25fb36a0 default theme 2022-08-15 15:53:46 +03:00
Yuri Kuznetsov 2fa287389b css fix 2022-08-15 15:51:57 +03:00
Yuri Kuznetsov 5b7010eebe default theme 2022-08-15 15:39:47 +03:00
Yuri Kuznetsov 32977a09f1 style fix 2022-08-15 15:37:00 +03:00
Yuri Kuznetsov b222a0996f ref 2022-08-15 13:18:26 +03:00
Yuri Kuznetsov 267dd01228 color fixes 2022-08-15 11:20:12 +03:00
Yuri Kuznetsov f07b129826 theme fix 2022-08-15 11:08:27 +03:00
Yuri Kuznetsov aae417a5d9 color fix 2022-08-15 10:56:35 +03:00
Yuri Kuznetsov 68aeaa54b9 loader path do not allow colon 2022-08-15 10:07:52 +03:00
Yuri Kuznetsov 8be18a2d11 cleanup 2022-08-15 10:07:08 +03:00
Yuri Kuznetsov cf35c20460 loader refactoring 2022-08-14 16:45:04 +03:00
Yuri Kuznetsov e51a8d74cb theme fix 2022-08-14 14:37:08 +03:00
Yuri Kuznetsov be6a872eda reminder border radius 2022-08-13 19:06:53 +03:00
Yuri Kuznetsov 34392267f1 fix link click 2022-08-13 19:02:41 +03:00
Yuri Kuznetsov b960228799 fix link key 2022-08-13 18:57:52 +03:00
Yuri Kuznetsov bb8f6c9941 key fix 2022-08-13 18:56:39 +03:00
Yuri Kuznetsov e6771aa5f6 fix click list 2022-08-13 18:50:59 +03:00
Yuri Kuznetsov 3f52538773 fix list view abort 2022-08-13 17:08:04 +03:00
Yuri Kuznetsov f66de9054d list view loading notification fix 2022-08-13 16:08:01 +03:00
Yuri Kuznetsov b109e8da68 shadow fix 2022-08-13 14:00:15 +03:00
Yuri Kuznetsov d14bb881bb theme shadows fixes 2022-08-13 13:55:12 +03:00
Yuri Kuznetsov eedfdd14e8 top bar shadow 2022-08-13 11:40:51 +03:00
Yuri Kuznetsov 14c28a5aab less ref 2022-08-13 11:02:52 +03:00
Yuri Kuznetsov 975ed83753 navbar shadow in variable 2022-08-13 10:17:35 +03:00
Yuri Kuznetsov df4e9c8ed0 renamane 2022-08-13 10:17:20 +03:00
Yuri Kuznetsov cd50307fdf stick-bar shadow 2022-08-13 09:57:37 +03:00
Yuri Kuznetsov feee057e2b ref 2022-08-12 15:59:58 +03:00
Yuri Kuznetsov 6581acd083 merge 2022-08-12 15:43:53 +03:00
Yuri Kuznetsov f3824117b3 v 2022-08-12 15:23:12 +03:00
Yuri Kuznetsov f3736da8a6 fix file field 2022-08-12 15:22:04 +03:00
Yuri Kuznetsov 198b746ce3 fix 2022-08-12 15:00:57 +03:00
Yuri Kuznetsov a9ded55e26 ref 2022-08-12 14:57:32 +03:00
Yuri Kuznetsov b50d56f884 fix attachment field focus 2022-08-12 13:45:34 +03:00
Yuri Kuznetsov 207794e168 tabindex 2022-08-12 13:33:42 +03:00
Yuri Kuznetsov 831e589b3d fix ics 2022-08-12 13:20:19 +03:00
Yuri Kuznetsov 61e6f5aeb5 select all a button fix 2022-08-12 13:10:35 +03:00
Yuri Kuznetsov a3477ac57d notification panel escape 2022-08-12 13:04:36 +03:00
Yuri Kuznetsov 9f08d3e9d6 a button click on enter 2022-08-12 12:58:57 +03:00
Yuri Kuznetsov d0f65e5fd9 wysiwyg validation message fix 2022-08-12 10:38:13 +03:00
Yuri Kuznetsov 5f20571958 jsdoc 2022-08-12 10:36:34 +03:00
Yuri Kuznetsov 4634597b7f tabindex 2022-08-12 10:17:46 +03:00
Yuri Kuznetsov ca44c8e9c7 tabindex 2022-08-12 10:05:38 +03:00
Yuri Kuznetsov 2551b024f0 css fixes 2022-08-11 21:09:21 +03:00
Yuri Kuznetsov e652320a3e modal panel shadow fix 2022-08-11 20:10:04 +03:00
Yuri Kuznetsov c1f13b146d file field focus 2022-08-11 17:58:00 +03:00
Yuri Kuznetsov 36f3d040dd inline edit disabled is html email tempalte 2022-08-11 17:53:23 +03:00
Yuri Kuznetsov 5ec3ac222b is-html disable inline edit 2022-08-11 17:52:17 +03:00
Yuri Kuznetsov 739f94a41b draft email attachment field fix 2022-08-11 17:50:34 +03:00
Yuri Kuznetsov 678ddfaf11 alert shadow 2022-08-11 16:46:33 +03:00
Yuri Kuznetsov 2cda441421 style shadow fix 2022-08-11 16:40:41 +03:00
Yuri Kuznetsov e518b8bef5 wysiwyg inline edit focus 2022-08-11 16:20:10 +03:00
Yuri Kuznetsov ae998c33bd wysiwyg fix shortcut 2022-08-11 16:15:38 +03:00
Yuri Kuznetsov dc2def1af7 hide smtp in preferences 2022-08-11 14:02:59 +03:00
Yuri Kuznetsov 9570857f8a selectize focus 2022-08-11 12:55:23 +03:00
Yuri Kuznetsov 6df100c2c2 fix inline edit focus 2022-08-11 12:49:59 +03:00
Yuri Kuznetsov 0b46188034 no username cookie 2022-08-11 12:44:38 +03:00
Yuri Kuznetsov eb8d958fa1 fix 2022-08-11 12:36:23 +03:00
Yuri Kuznetsov 7e74ddc54f phpdoc and type 2022-08-11 11:08:54 +03:00
Yuri Kuznetsov 90ac4bc071 throws tag 2022-08-11 11:05:16 +03:00
Yuri Kuznetsov c59b655200 add throw tags 2022-08-11 10:43:07 +03:00
Yuri Kuznetsov a1c7814f5a panel adjacent shadow fix 2022-08-11 10:11:54 +03:00
Yuri Kuznetsov db713e4c41 less blur 2022-08-11 09:56:04 +03:00
Yuri Kuznetsov 33d7f99c5c css fix 2022-08-11 09:53:27 +03:00
Yuri Kuznetsov 487fed21a1 radius change 2022-08-10 18:37:07 +03:00
Yuri Kuznetsov 8fed65f5be change radius 2022-08-10 18:35:41 +03:00
Yuri Kuznetsov d2f57cb558 radius change 2022-08-10 18:32:31 +03:00
Yuri Kuznetsov 8b905b6cd3 more radius 2022-08-10 18:16:19 +03:00
Yuri Kuznetsov e5389af306 fix phpdoc 2022-08-10 17:52:55 +03:00
Yuri Kuznetsov d524178d3b fix phpdoc 2022-08-10 17:41:20 +03:00
Yuri Kuznetsov 6f7198690d fix phpdoc 2022-08-10 17:36:13 +03:00
Yuri Kuznetsov d52b73f738 fix phpdoc 2022-08-10 17:28:36 +03:00
Yuri Kuznetsov 785aadc288 fix phpdoc 2022-08-10 17:19:31 +03:00
Yuri Kuznetsov 42175f98e4 fix phpdoc 2022-08-10 17:13:31 +03:00
Yuri Kuznetsov 62975b2957 fix phpdoc 2022-08-10 17:08:39 +03:00
Yuri Kuznetsov dc433f5a82 fix phpdoc 2022-08-10 17:05:49 +03:00
Yuri Kuznetsov 1af9256658 ref 2022-08-10 16:43:47 +03:00
Yuri Kuznetsov a3696af196 fix 2022-08-10 14:57:30 +03:00
Yuri Kuznetsov 20913c283e add slashes for text filter 2022-08-10 14:46:19 +03:00
Yuri Kuznetsov f9df70e710 Merge branch 'fix' 2022-08-10 12:57:14 +03:00
Yuri Kuznetsov c0c878c20b fix mass action 2022-08-10 12:56:43 +03:00
Yuri Kuznetsov abc19ee574 fix mass action 2022-08-10 12:55:47 +03:00
Yuri Kuznetsov 3b97d17aed color fix 2022-08-10 12:22:36 +03:00
Yuri Kuznetsov 06ceb371e3 fix 2022-08-10 12:12:42 +03:00
Yuri Kuznetsov d78666e0d2 fix login 2022-08-10 11:53:58 +03:00
Yuri Kuznetsov cde19542a6 dropdown fix 2022-08-10 11:36:37 +03:00
Yuri Kuznetsov 263e553a0f source mapping fixes 2022-08-10 11:26:29 +03:00
Yuri Kuznetsov a129d54e06 window.Espo 2022-08-10 09:55:39 +03:00
Yuri Kuznetsov 09f9cbf3df sourceUrl 2022-08-10 09:54:08 +03:00
Yuri Kuznetsov 14cb5730b5 fix validation message no element 2022-08-10 09:31:27 +03:00
Yuri Kuznetsov 64a0064f3c fix 2022-08-10 09:28:50 +03:00
Yuri Kuznetsov 7c9a605450 fix user auth method empty 2022-08-10 09:26:30 +03:00
Yuri Kuznetsov 7a9d0af314 after-update kb 2022-08-10 09:14:53 +03:00
Yuri Kuznetsov e2e3bf9a71 fix kanban remove from detail 2022-08-10 09:08:29 +03:00
Yuri Kuznetsov 7752c85304 fix 2022-08-10 09:03:07 +03:00
Yuri Kuznetsov 51fcd5aca9 ref 2022-08-10 08:43:45 +03:00
Yuri Kuznetsov ad9391ad9e cs fix 2022-08-10 08:37:37 +03:00
Yuri Kuznetsov 0a97096c61 fix ctrl click 2022-08-10 08:27:25 +03:00
Yuri Kuznetsov fd5b77d919 email filder folder mass action fix 2022-08-09 23:24:33 +03:00
Yuri Kuznetsov f118f572bc default article 2022-08-09 23:15:46 +03:00
Yuri Kuznetsov c08b43991a ref 2022-08-09 20:35:36 +03:00
Yuri Kuznetsov f58f2071ed fix style 2022-08-09 20:34:22 +03:00
Yuri Kuznetsov 879df57fa0 fix 2022-08-09 19:35:58 +03:00
Yuri Kuznetsov 46d0fa2c15 role button 2022-08-09 19:31:36 +03:00
Yuri Kuznetsov f242cd1454 role button 2022-08-09 19:05:11 +03:00
Yuri Kuznetsov ba04f1c7f1 fix 2022-08-09 19:01:29 +03:00
Yuri Kuznetsov df45635fd5 fixes 2022-08-09 19:00:01 +03:00
Yuri Kuznetsov 260f13bf3f a role button 2022-08-09 18:57:02 +03:00
Yuri Kuznetsov bdd0ced5d2 loader fix 2022-08-09 17:43:30 +03:00
Yuri Kuznetsov f3a729e283 update flowchart 2022-08-09 17:39:31 +03:00
Yuri Kuznetsov af345e8721 fix phone email selectable 2022-08-09 16:57:11 +03:00
Yuri Kuznetsov 99670c7b3d strict mode js 2022-08-09 16:50:24 +03:00
Yuri Kuznetsov 01dffd98f2 fixes 2022-08-09 16:48:44 +03:00
Yuri Kuznetsov 137b989064 fix 2022-08-09 16:34:41 +03:00
Yuri Kuznetsov 19eaca9bf4 fix 2022-08-09 15:53:55 +03:00
Yuri Kuznetsov 83c82767c3 cs fix 2022-08-09 14:39:07 +03:00
Yuri Kuznetsov 838be733ff fix 2022-08-09 14:38:08 +03:00
Yuri Kuznetsov 6a49e812fb revert param 2022-08-09 14:37:10 +03:00
Yuri Kuznetsov 95ce8645f2 client headers 2022-08-09 14:30:01 +03:00
Yuri Kuznetsov b707bf70b8 fix 2022-08-09 13:13:14 +03:00
Yuri Kuznetsov bc7de312c1 style fixes 2022-08-09 11:51:20 +03:00
Yuri Kuznetsov 9c851a1662 fix style 2022-08-09 11:40:49 +03:00
Yuri Kuznetsov 41cc7efe5b preint to pdf focus 2022-08-08 17:34:23 +03:00
Yuri Kuznetsov 22d141cd20 export ctrl+enter 2022-08-08 17:28:41 +03:00
Yuri Kuznetsov 768c8f6247 more radius 2022-08-08 13:53:43 +03:00
Yuri Kuznetsov 478079ce19 kanban no draggin btn 2022-08-08 13:16:53 +03:00
Yuri Kuznetsov d472809175 color fix 2022-08-08 13:11:31 +03:00
Yuri Kuznetsov 37ed7f3527 imap fetch since without quotes 2022-08-08 12:46:39 +03:00
Yuri Kuznetsov 1d9b011a68 layout manager grid cancel fix 2022-08-08 12:39:32 +03:00
Yuri Kuznetsov c6e8ce5ad4 kanban css change 2022-08-08 11:26:14 +03:00
Yuri Kuznetsov 63223a1150 style fixes 2022-08-07 23:12:22 +03:00
Yuri Kuznetsov 580e2858a5 css fixes 2022-08-07 23:03:34 +03:00
Yuri Kuznetsov f298a5d0b2 focus on reset fitler 2022-08-07 19:09:31 +03:00
Yuri Kuznetsov 1e9e60640c css fix 2022-08-07 19:00:58 +03:00
Yuri Kuznetsov c36096cb45 date-start date-end dep 2022-08-07 16:45:35 +03:00
Yuri Kuznetsov cf6bf2ad28 color fix 2022-08-07 16:39:09 +03:00
Yuri Kuznetsov 09b62d5112 color fix 2022-08-07 16:15:36 +03:00
Yuri Kuznetsov c12eacc4c1 theme color changes 2022-08-07 16:11:49 +03:00
Yuri Kuznetsov b5289212b1 see-more style fix 2022-08-07 15:38:57 +03:00
Yuri Kuznetsov 856e68f9ee dark theme more radius 2022-08-07 15:30:54 +03:00
Yuri Kuznetsov 9eb7e2ce0f theme fix 2022-08-07 15:26:28 +03:00
Yuri Kuznetsov a0777dc452 dashlet dropdwon issue fix 2022-08-07 12:05:07 +03:00
Yuri Kuznetsov 7ecf2ea11d see more icon 2022-08-07 11:05:42 +03:00
Yuri Kuznetsov 4af7fde960 commented 2022-08-07 11:00:52 +03:00
Yuri Kuznetsov 838a80fa55 dashlet no dragging over menu 2022-08-07 10:49:51 +03:00
Yuri Kuznetsov 36a72d0cb9 link mult search filter style fix 2022-08-07 10:41:13 +03:00
Yuri Kuznetsov ea00d2d869 theme fixes 2022-08-07 10:37:06 +03:00
Yuri Kuznetsov 1b2f96fe5b theme fixes 2022-08-06 21:35:49 +03:00
Yuri Kuznetsov 8323627e9a fix radius 2022-08-06 21:23:46 +03:00
Yuri Kuznetsov 73a2ece6a1 strip source maps 2022-08-06 21:14:11 +03:00
Yuri Kuznetsov 3fc84a8453 cs fix 2022-08-06 20:39:18 +03:00
Yuri Kuznetsov d2480e9637 ref 2022-08-06 20:33:30 +03:00
Yuri Kuznetsov fb95614171 tpl fix 2022-08-06 20:28:16 +03:00
Yuri Kuznetsov 97e497cced style changes 2022-08-06 20:15:44 +03:00
Yuri Kuznetsov 0857e28931 ref 2022-08-06 17:53:05 +03:00
Yuri Kuznetsov b1226fb091 fix popover 2022-08-06 17:43:34 +03:00
Yuri Kuznetsov a68c797cb6 ref 2022-08-06 17:33:39 +03:00
Yuri Kuznetsov f29a1a1e80 fix 2022-08-06 17:30:01 +03:00
Yuri Kuznetsov bea7442ecf stream tpl fix 2022-08-06 17:17:18 +03:00
Yuri Kuznetsov fc09c29769 style fix 2022-08-06 17:09:44 +03:00
Yuri Kuznetsov 5c997b04c3 css fixes 2022-08-06 17:05:25 +03:00
Yuri Kuznetsov 9a7e0e8b2a navbar fix 2022-08-06 17:05:18 +03:00
Yuri Kuznetsov ff519194c9 css fixes 2022-08-06 14:44:46 +03:00
Yuri Kuznetsov b89d9bd6b5 fix tab index 2022-08-06 13:56:56 +03:00
Yuri Kuznetsov 183ab82f5a css fix 2022-08-06 13:55:10 +03:00
Yuri Kuznetsov 8be9e1ec55 btn fixes 2022-08-06 13:49:56 +03:00
Yuri Kuznetsov a4ae94f5e9 fix less 2022-08-06 12:37:11 +03:00
Yuri Kuznetsov be981741ce fix 2022-08-06 12:29:04 +03:00
Yuri Kuznetsov 9491895991 ref 2022-08-06 12:27:25 +03:00
Yuri Kuznetsov 038c374dcd detail record buttons impr 2022-08-06 11:59:15 +03:00
Yuri Kuznetsov ccc2ed09b8 select record ctrl enter 2022-08-06 11:22:32 +03:00
Yuri Kuznetsov 4a570c6699 formula sandbox run title 2022-08-06 10:57:43 +03:00
Yuri Kuznetsov 8bccde26d2 css type fixes 2022-08-06 10:56:35 +03:00
Yuri Kuznetsov f924f60de3 fix user teams 2022-08-06 10:44:19 +03:00
Yuri Kuznetsov 3d646039be css fix 2022-08-05 21:54:18 +03:00
Yuri Kuznetsov da064ff76f css fix 2022-08-05 17:53:26 +03:00
Yuri Kuznetsov 4f17ebabee link text muted hover 2022-08-05 17:52:05 +03:00
Yuri Kuznetsov 0cd88f2ec9 colorpicker style fix 2022-08-05 17:47:00 +03:00
Yuri Kuznetsov 77d5e1997d bc fix 2022-08-05 17:08:54 +03:00
Yuri Kuznetsov 2e688d7671 dashlet options buttons small 2022-08-05 16:27:36 +03:00
Yuri Kuznetsov 3a5e514691 cs fix 2022-08-05 16:24:05 +03:00
Yuri Kuznetsov 5e7e920687 focus fix 2022-08-05 13:28:20 +03:00
Yuri Kuznetsov adefd7d3c5 router fix 2022-08-05 13:15:19 +03:00
Yuri Kuznetsov e2a1e497fc focus fix 2022-08-05 13:15:13 +03:00
Yuri Kuznetsov bd9dc3dfe3 ref 2022-08-05 12:37:39 +03:00
Yuri Kuznetsov 58a0be2cef fix 2022-08-05 12:13:04 +03:00
Yuri Kuznetsov fea97768d9 fetch main 2022-08-05 12:11:36 +03:00
Yuri Kuznetsov 41865d8cde note acl 2022-08-05 12:05:52 +03:00
Yuri Kuznetsov b2df17cf93 cleanup 2022-08-05 11:30:47 +03:00
Yuri Kuznetsov ec0649f65a note view 2022-08-05 11:17:10 +03:00
Yuri Kuznetsov 01cc896972 fix validate tab list unique 2022-08-05 09:17:36 +03:00
Yuri Kuznetsov 522e9be624 css fix 2022-08-04 21:23:54 +03:00
Yuri Kuznetsov 856f4687e7 css fixes 2022-08-04 21:09:23 +03:00
Yuri Kuznetsov ca4f787131 theme fix 2022-08-04 19:47:50 +03:00
Yuri Kuznetsov d518d3671b fix tab switch 2022-08-04 18:35:57 +03:00
Yuri Kuznetsov 9bcdef9107 cs fix 2022-08-04 16:30:23 +03:00
Yuri Kuznetsov 8854f60f4a fix notify not closed 2022-08-04 15:01:49 +03:00
Yuri Kuznetsov 760f51352b preset filters shortcuts 2022-08-04 13:52:14 +03:00
Yuri Kuznetsov 11860495c6 modal on backdrop click 2022-08-04 12:35:34 +03:00
Yuri Kuznetsov 320420ef29 ref 2022-08-04 12:00:30 +03:00
Yuri Kuznetsov 157b2a60bb ref 2022-08-04 10:57:49 +03:00
Yuri Kuznetsov 1299b20fe1 ref 2022-08-04 10:49:44 +03:00
Yuri Kuznetsov b69109a5d4 fix icon 2022-08-04 10:44:10 +03:00
Yuri Kuznetsov 3d6e83a8cf email draft layout 2022-08-04 10:41:09 +03:00
Yuri Kuznetsov abe076dc68 fix wywiwyg 2022-08-04 10:02:37 +03:00
Yuri Kuznetsov cf5b6a6b62 pt_BR 2022-08-04 09:59:04 +03:00
Yuri Kuznetsov ac115642cd ctrl click 2022-08-04 09:55:42 +03:00
Yuri Kuznetsov 41af71ebf3 fix dashlets 2022-08-04 09:47:55 +03:00
Yuri Kuznetsov 85ed285c79 dashlet btn header style 2022-08-03 18:07:51 +03:00
Yuri Kuznetsov 906f6e5717 fix test 2022-08-03 16:58:49 +03:00
Yuri Kuznetsov 4d2933797d email importer refactor 2022-08-03 16:48:07 +03:00
Yuri Kuznetsov e237084ec4 last viewed loading 2022-08-03 16:27:24 +03:00
Yuri Kuznetsov 6e5148c48a global search escape 2022-08-03 16:06:10 +03:00
Yuri Kuznetsov 9dd9d4e33a css fix 2022-08-03 16:02:13 +03:00
Yuri Kuznetsov 306de81763 css fix 2022-08-03 15:11:48 +03:00
Yuri Kuznetsov 533b51aed4 fix theme field 2022-08-03 14:58:27 +03:00
Yuri Kuznetsov 7576428284 css panel buttons 2022-08-03 14:50:22 +03:00
Yuri Kuznetsov ab24606bdf activities create buttons 2022-08-03 13:37:56 +03:00
Yuri Kuznetsov 204046620c cs fix 2022-08-03 12:47:44 +03:00
Yuri Kuznetsov f3d234877b admin disabled scope as acl false 2022-08-03 12:45:23 +03:00
Yuri Kuznetsov 81f346fcc9 cs fix 2022-08-03 12:17:01 +03:00
Yuri Kuznetsov 3033d3f09b cs fixes 2022-08-03 11:45:04 +03:00
Yuri Kuznetsov 5c7b674b60 ref 2022-08-03 11:27:14 +03:00
Yuri Kuznetsov 89ccbbb2ec theme fixes 2022-08-03 11:04:48 +03:00
Yuri Kuznetsov 899c9dcb74 focus color 2022-08-03 11:00:30 +03:00
Yuri Kuznetsov 74d00c8b89 css fix 2022-08-03 10:27:42 +03:00
Yuri Kuznetsov f8bfd6869c draft email shortcuts 2022-08-03 10:15:16 +03:00
Yuri Kuznetsov 2aaafe850f no keyCode usage 2022-08-03 09:53:18 +03:00
Yuri Kuznetsov 4d7f86b5da cleanup 2022-08-03 09:34:06 +03:00
Yuri Kuznetsov 9eaab5176a after upgrade event fix 2022-08-03 09:30:10 +03:00
Yuri Kuznetsov 74661e676c fix theme 2022-08-02 20:23:27 +03:00
Yuri Kuznetsov 7f3db957a4 reminder ui fix 2022-08-02 20:06:59 +03:00
Yuri Kuznetsov 0f7ed3d4c7 ctrl space from input 2022-08-02 19:04:41 +03:00
Yuri Kuznetsov 5f2a2728fc create from modals shortcuts 2022-08-02 18:59:02 +03:00
Yuri Kuznetsov 429faf405c focus on searcn bar shortcut 2022-08-02 18:35:14 +03:00
Yuri Kuznetsov 6a5f15bbc5 shortcut next/prev 2022-08-02 17:49:35 +03:00
Yuri Kuznetsov 375454fc5d fix 2022-08-02 16:24:09 +03:00
Yuri Kuznetsov 41b92c9ee1 past calls in my calls dashlet 2022-08-02 16:19:46 +03:00
Yuri Kuznetsov 6293fb98d6 fix layout css 2022-08-02 15:41:14 +03:00
Yuri Kuznetsov 306321258d update jquery-ui 2022-08-02 15:16:08 +03:00
Yuri Kuznetsov 03e85ea32e update moment 2022-08-02 15:05:12 +03:00
Yuri Kuznetsov 551e2a6b47 update selectize 2022-08-02 15:03:50 +03:00
Yuri Kuznetsov 0d4214fe46 update selectize 2022-08-02 14:48:07 +03:00
Yuri Kuznetsov c8d0fb019e revert package 2022-08-02 14:30:52 +03:00
Yuri Kuznetsov 9867eab381 update moment 2022-08-02 13:43:45 +03:00
Yuri Kuznetsov 252c5a34b3 fix lock 2022-08-02 13:42:40 +03:00
Yuri Kuznetsov ce5e7d79e8 update package lock 2022-08-02 13:14:18 +03:00
Yuri Kuznetsov e8946bb4fe fix 2022-08-02 13:14:04 +03:00
Yuri Kuznetsov d79a5cadd4 Merge branch 'stable' 2022-08-02 12:58:54 +03:00
Yuri Kuznetsov 4e3cee4dc8 fix 2022-08-02 11:07:48 +03:00
Yuri Kuznetsov 61922e0558 acceptance status list view 2022-08-02 10:56:30 +03:00
Yuri Kuznetsov 300cb53f3a more metadata forceAppendPathList 2022-08-02 10:39:23 +03:00
Yuri Kuznetsov 0a43c781e0 filter aux 2022-08-01 22:01:06 +03:00
Yuri Kuznetsov 87c6b9e5d5 stream post do not hide on tab 2022-08-01 16:46:12 +03:00
Yuri Kuznetsov 3f6a8eea5a file upload input tab outline 2022-08-01 16:41:16 +03:00
Yuri Kuznetsov 1b8142d4c3 fix 2022-08-01 16:33:20 +03:00
Yuri Kuznetsov 28113d50ca cs fix 2022-08-01 16:26:37 +03:00
Yuri Kuznetsov 0851d2ce7a theme fix 2022-08-01 16:16:05 +03:00
Yuri Kuznetsov b32f627bda modal record view shortcut switch tabs 2022-08-01 16:08:35 +03:00
Yuri Kuznetsov e88153b917 fix tabs switch 2022-08-01 16:02:50 +03:00
Yuri Kuznetsov 893a260ed8 fix text cut 2022-08-01 15:59:31 +03:00
Yuri Kuznetsov e93ff18b4c shortcut switch tabs 2022-08-01 15:41:14 +03:00
Yuri Kuznetsov 048a75ff67 calendar create event shortcut 2022-08-01 15:06:42 +03:00
Yuri Kuznetsov 911bdb3a51 timeline create shortcut 2022-08-01 14:41:56 +03:00
Yuri Kuznetsov b87019e200 calendar mode shortcuts 2022-08-01 13:14:01 +03:00
Yuri Kuznetsov e413ca6643 shift key 2022-08-01 12:58:02 +03:00
Yuri Kuznetsov 642ddf5a6c calendar shortcuts 2022-08-01 12:53:21 +03:00
Yuri Kuznetsov 42c62ea59b cs fix 2022-08-01 12:01:16 +03:00
Yuri Kuznetsov d19b7ef657 search with ctrl+enter 2022-08-01 11:26:13 +03:00
Yuri Kuznetsov 6fbc892acf cleanup 2022-08-01 11:12:29 +03:00
Yuri Kuznetsov 38d62d3a8f multi-select prevent ctrl enter 2022-08-01 11:10:37 +03:00
Yuri Kuznetsov a44d3aa4a3 selectize wrapper 2022-08-01 11:01:28 +03:00
Yuri Kuznetsov 248d288803 fix assigned user tab 2022-08-01 08:47:04 +03:00
Yuri Kuznetsov 564956d151 fix 2022-08-01 08:17:54 +03:00
Yuri Kuznetsov 75888d2f38 css fix 2022-07-31 21:53:57 +03:00
Yuri Kuznetsov 3b9f705a53 fix 2022-07-31 21:51:11 +03:00
Yuri Kuznetsov e24d695eb3 theme fixes 2022-07-31 21:35:36 +03:00
Yuri Kuznetsov 55d68f1143 disable tabindex footer 2022-07-31 20:02:19 +03:00
Yuri Kuznetsov 58e006cdde focus for create modal 2022-07-31 19:56:45 +03:00
Yuri Kuznetsov 48811450a8 show validation message if field is in hidden panel 2022-07-31 19:45:33 +03:00
Yuri Kuznetsov 8aa2f12a8b email focus 2022-07-31 17:47:37 +03:00
Yuri Kuznetsov a13c7d9465 focus on create 2022-07-31 17:44:03 +03:00
Yuri Kuznetsov 3dd82aa61e shortcuts 2022-07-31 17:31:40 +03:00
Yuri Kuznetsov e67a1b67ed save and new shortcut 2022-07-31 16:57:10 +03:00
Yuri Kuznetsov 463649ffa8 modal prevent scroll 2022-07-31 16:28:08 +03:00
Yuri Kuznetsov e037f2fddb css fix 2022-07-31 16:20:07 +03:00
Yuri Kuznetsov 6aef2939f9 shortcuts 2022-07-31 14:58:53 +03:00
Yuri Kuznetsov 0b6486a03b shorctuts 2022-07-31 13:36:21 +03:00
Yuri Kuznetsov fb39f0915f fix inline edit link 2022-07-31 13:24:05 +03:00
Yuri Kuznetsov a80178494a fixes 2022-07-31 12:23:20 +03:00
Yuri Kuznetsov 7bdb61c05e alt shortcut support 2022-07-31 11:58:25 +03:00
Yuri Kuznetsov 47403e2e35 title 2022-07-31 11:54:29 +03:00
Yuri Kuznetsov a7d86dff4b shortcuts and titles 2022-07-31 11:51:44 +03:00
Yuri Kuznetsov a1ef2886dc fix 2022-07-31 11:33:59 +03:00
Yuri Kuznetsov 2f8ec94169 shortcut keys 2022-07-31 11:21:07 +03:00
Yuri Kuznetsov 1c6c445a59 shortcut keys 2022-07-31 10:54:59 +03:00
Yuri Kuznetsov f1cfb01451 shortcut keys 2022-07-31 10:10:02 +03:00
Yuri Kuznetsov 8921ed42ad modal edit shortcut 2022-07-30 18:07:42 +03:00
Yuri Kuznetsov 0836d52065 modal shortcut keys 2022-07-30 17:51:33 +03:00
Yuri Kuznetsov 4f4b362c05 cleanup 2022-07-30 17:31:51 +03:00
Yuri Kuznetsov 63db221a2a cs fix 2022-07-30 17:31:15 +03:00
Yuri Kuznetsov 31c4206b2e login key 2022-07-30 17:27:07 +03:00
Yuri Kuznetsov 13e0f0f06a isAllDay not readonly 2022-07-30 13:16:04 +03:00
Yuri Kuznetsov 153dd65a0d ctrl+s fixes 2022-07-30 13:04:58 +03:00
Yuri Kuznetsov 306fca8291 ctrl+s fixes 2022-07-30 12:44:44 +03:00
Yuri Kuznetsov ef253690d6 ctrl+s modal fix 2022-07-30 12:31:34 +03:00
Yuri Kuznetsov f5377fe12b ctrl+s inline edit 2022-07-30 12:14:36 +03:00
Yuri Kuznetsov c35cb98b32 fix doc 2022-07-30 11:42:32 +03:00
Yuri Kuznetsov 009f090061 ctrl+s 2022-07-30 11:38:54 +03:00
Yuri Kuznetsov 61c586c31d email compose shortcut 2022-07-30 11:00:54 +03:00
Yuri Kuznetsov a90cf7b8e3 modal edit escape fetch 2022-07-30 10:53:13 +03:00
Yuri Kuznetsov db4e5c001b escape key fetch 2022-07-30 10:45:28 +03:00
Yuri Kuznetsov ead76d78a3 ref 2022-07-30 10:40:28 +03:00
Yuri Kuznetsov 02cf4f4bb8 fix 2022-07-30 10:37:44 +03:00
Yuri Kuznetsov f9040b3afb wysiwyg disable ctrl enter 2022-07-30 10:26:29 +03:00
Yuri Kuznetsov e47155006b cs fix 2022-07-30 10:16:17 +03:00
Yuri Kuznetsov a07213fa5f bypass shorcut enter action 2022-07-30 10:07:58 +03:00
Yuri Kuznetsov d74d89e9d4 restore focus on modal close 2022-07-29 17:30:17 +03:00
Yuri Kuznetsov fc2c3ce644 shortcuts 2022-07-29 17:08:23 +03:00
Yuri Kuznetsov 7745a92490 shortcuts 2022-07-29 16:58:29 +03:00
Yuri Kuznetsov 92c0cf297c cleanup 2022-07-29 16:14:52 +03:00
Yuri Kuznetsov c6accac161 theme fixes 2022-07-29 14:30:39 +03:00
Yuri Kuznetsov 58b4a9b965 theme fix 2022-07-29 14:07:26 +03:00
Yuri Kuznetsov d77022a167 fix 2022-07-29 13:49:49 +03:00
Yuri Kuznetsov 1c77a197ab inline edit save close shortcuts 2022-07-29 13:48:52 +03:00
Yuri Kuznetsov fa1354a3b9 key save 2022-07-29 13:05:10 +03:00
Yuri Kuznetsov fc5c159d00 ref 2022-07-29 12:38:42 +03:00
Yuri Kuznetsov d4aa273661 stream post ctrl enter 2022-07-29 12:25:55 +03:00
Yuri Kuznetsov ff1c5e9e20 theme fix 2022-07-29 12:00:10 +03:00
Yuri Kuznetsov eff9ae7225 add theme to install 2022-07-29 11:14:42 +03:00
Yuri Kuznetsov 146bdf01f1 style fixes 2022-07-29 10:52:17 +03:00
Yuri Kuznetsov 487201d8e7 glass theme 2022-07-29 10:41:17 +03:00
Yuri Kuznetsov 788d72afc4 summernote tooltip radius 2022-07-29 10:11:54 +03:00
Yuri Kuznetsov f5da342192 css fix 2022-07-29 10:07:05 +03:00
Yuri Kuznetsov 2ce82fe605 panel title -1px 2022-07-29 10:01:13 +03:00
Yuri Kuznetsov 291a54f070 login middle 2022-07-29 09:50:28 +03:00
Yuri Kuznetsov 14750fa131 cleanup 2022-07-29 08:56:22 +03:00
Yuri Kuznetsov 26fde45450 dashlet dragging cursor 2022-07-28 20:20:30 +03:00
Yuri Kuznetsov cef116b4d6 hover color 2022-07-28 20:09:37 +03:00
Yuri Kuznetsov 04324bd03b update flotr 2022-07-28 19:44:06 +03:00
Yuri Kuznetsov 4a302e637b ref 2022-07-28 17:51:30 +03:00
Yuri Kuznetsov da13823dbc calendar new event color 2022-07-28 17:18:59 +03:00
Yuri Kuznetsov 9b6d1455a8 css fix 2022-07-28 16:59:19 +03:00
Yuri Kuznetsov 10845e1432 calendar color fix 2022-07-28 16:59:19 +03:00
Yuri Kuznetsov 88dacb6a02 cs fix 2022-07-28 16:59:19 +03:00
Taras Machyshyn d9cc84b50a Tests: added utf8mb3_unicode_ci 2022-07-28 16:34:51 +03:00
Yuri Kuznetsov 9505fe8222 calendar shade color aplha 2022-07-28 16:05:52 +03:00
Yuri Kuznetsov 70eca7a7b2 kanban padding fix 2022-07-28 11:37:23 +03:00
Yuri Kuznetsov e9636e7e6f less fix 2022-07-28 11:33:57 +03:00
Yuri Kuznetsov 84dc7f796e less ref 2022-07-28 11:28:54 +03:00
Yuri Kuznetsov 16ccab49a5 theme ref 2022-07-28 11:04:33 +03:00
Yuri Kuznetsov 3dfd6b6c57 fix text info 2022-07-28 09:59:25 +03:00
Yuri Kuznetsov 5ac84f20d3 css fix 2022-07-28 09:21:55 +03:00
Yuri Kuznetsov 590b32c7ae css fix 2022-07-28 09:21:55 +03:00
Yuri Kuznetsov 9530c378d5 kanban gray color fix 2022-07-28 09:21:54 +03:00
Taras Machyshyn 8fc2f85c37 Tests: added utf8mb3_unicode_ci 2022-07-27 18:40:09 +03:00
Taras Machyshyn 88063f5ec7 Tests: added utf8mb3_unicode_ci 2022-07-27 18:31:36 +03:00
Yuri Kuznetsov 9e6e8f580c css fix 2022-07-27 14:42:14 +03:00
Yuri Kuznetsov 586baeea14 css fix 2022-07-27 14:40:51 +03:00
Yuri Kuznetsov 3bf44c44a1 style fix 2022-07-27 13:26:03 +03:00
Yuri Kuznetsov f05cc3f045 dep lang meeting 2022-07-27 13:18:08 +03:00
Yuri Kuznetsov 0aced49759 tab fix 2022-07-27 13:13:28 +03:00
Yuri Kuznetsov 45d87e5462 invintation email message 2022-07-27 13:10:06 +03:00
Yuri Kuznetsov 5b10212354 fix diff 2022-07-27 12:47:57 +03:00
Yuri Kuznetsov 5324c4f681 v 2022-07-27 12:31:45 +03:00
Yuri Kuznetsov e315063e3f fix install 2022-07-27 12:14:36 +03:00
Yuri Kuznetsov ba35a08cca cleanup 2022-07-27 10:49:56 +03:00
Yuri Kuznetsov e4a2fafb90 thmes ref 2022-07-27 10:47:08 +03:00
Yuri Kuznetsov 2c89644149 cleanup 2022-07-27 10:08:48 +03:00
Yuri Kuznetsov 07d2a1e897 css fix 2022-07-27 10:07:31 +03:00
Yuri Kuznetsov 89f832bf22 style fixes 2022-07-27 09:46:29 +03:00
Yuri Kuznetsov 6161cd8ea2 dark theme more radius 2022-07-27 09:38:18 +03:00
Yuri Kuznetsov 7b708ace1e theme fixes 2022-07-27 09:36:18 +03:00
Yuri Kuznetsov e0b542fac6 global search scope text change 2022-07-26 17:37:18 +03:00
Yuri Kuznetsov 17f871b424 style fix 2022-07-26 17:35:18 +03:00
Yuri Kuznetsov 4b565d9dd0 css fix 2022-07-26 16:32:16 +03:00
Yuri Kuznetsov d1c2678cb1 style fix 2022-07-26 16:17:18 +03:00
Yuri Kuznetsov 1fbe13bb03 style fixes 2022-07-26 16:12:53 +03:00
Yuri Kuznetsov 38b0dbd4d0 style fix 2022-07-26 15:44:16 +03:00
Yuri Kuznetsov 6d9bab52bd preferences tabs 2022-07-26 15:41:04 +03:00
Yuri Kuznetsov 11aac106cd color fix 2022-07-26 15:26:53 +03:00
Yuri Kuznetsov 9a043cc95e lang fix 2022-07-26 13:42:07 +03:00
Yuri Kuznetsov c527144719 default theme params 2022-07-26 13:38:21 +03:00
Yuri Kuznetsov 73905a789e after upgrade 2022-07-26 13:36:24 +03:00
Yuri Kuznetsov d115005a14 navbar fix 2022-07-26 13:13:00 +03:00
Yuri Kuznetsov df2c381946 css fix 2022-07-26 12:56:57 +03:00
Yuri Kuznetsov 3b4539858c style fix 2022-07-26 12:47:27 +03:00
Yuri Kuznetsov 0403af91fa Merge branch 'fix' 2022-07-26 12:37:58 +03:00
Yuri Kuznetsov b98bbd8d4c v 2022-07-26 12:26:24 +03:00
Yuri Kuznetsov 1722e43fa8 dashboard layout settings refactor 2022-07-26 12:25:23 +03:00
Yuri Kuznetsov 05af5fa0f6 dashboard layout settings refactor 2022-07-26 12:24:49 +03:00
Yuri Kuznetsov fdab5c42a6 join themes 2022-07-26 11:49:34 +03:00
Yuri Kuznetsov d62fce6b32 fix 2022-07-26 11:23:48 +03:00
Yuri Kuznetsov 33b737296e grid autofit 2022-07-26 11:02:05 +03:00
Yuri Kuznetsov dfcb7f1445 ref 2022-07-26 09:55:16 +03:00
Yuri Kuznetsov 14511dce70 fix text color 2022-07-25 15:52:00 +03:00
Yuri Kuznetsov f0dc216994 navbar fixes 2022-07-25 15:44:36 +03:00
Yuri Kuznetsov 023b5a977a css fix 2022-07-25 15:07:48 +03:00
Yuri Kuznetsov 2c08b5d80a login footer fix 2022-07-25 14:40:30 +03:00
Yuri Kuznetsov 890b9d8db0 footer fixes 2022-07-25 14:33:12 +03:00
Yuri Kuznetsov 7240f13c12 fix 2022-07-25 13:42:18 +03:00
Yuri Kuznetsov 910fe5546c navbar tabs no select 2022-07-25 12:54:54 +03:00
Yuri Kuznetsov 4d633853a8 kanban no select 2022-07-25 12:51:09 +03:00
Yuri Kuznetsov 1b0c9f77ab error tpl changes 2022-07-25 12:46:11 +03:00
Yuri Kuznetsov d262c4bc51 sticky top small screen fix 2022-07-25 11:57:03 +03:00
Yuri Kuznetsov 625ee91bc0 role ui fix 2022-07-25 11:50:26 +03:00
Yuri Kuznetsov 05e296879f role sticky head detail 2022-07-25 11:44:54 +03:00
Yuri Kuznetsov 33140d4928 fix sticky head 2022-07-25 11:37:44 +03:00
Yuri Kuznetsov f178d92273 cs fix 2022-07-25 11:33:19 +03:00
Yuri Kuznetsov ffae105b38 rtl fixes 2022-07-25 11:16:23 +03:00
Yuri Kuznetsov 7a65acc8ee top bar css fix 2022-07-25 10:27:44 +03:00
Yuri Kuznetsov 5de892d3b3 notification li open 2022-07-25 10:18:34 +03:00
Yuri Kuznetsov 180e4143a1 top-bar imp 2022-07-25 10:13:54 +03:00
Yuri Kuznetsov f92b1168ac buttons style ref 2022-07-24 21:31:53 +03:00
Yuri Kuznetsov c343ee4c5f css fix 2022-07-24 20:54:44 +03:00
Yuri Kuznetsov ee33e560e7 ref 2022-07-24 20:49:07 +03:00
Yuri Kuznetsov 6589c8ed17 tabindex 2022-07-24 18:11:46 +03:00
Yuri Kuznetsov 7a93eb59a1 css fixes 2022-07-24 18:05:37 +03:00
Yuri Kuznetsov a18bd4d184 css fixes 2022-07-24 17:41:01 +03:00
Yuri Kuznetsov 34821f2b4b fix 2022-07-24 14:18:21 +03:00
Yuri Kuznetsov 16d46abb1a css fixes 2022-07-24 14:09:13 +03:00
Yuri Kuznetsov 10259a9c99 attachment field clearfix 2022-07-24 13:53:02 +03:00
Yuri Kuznetsov 937971e3dd fix sticked 2022-07-24 13:49:01 +03:00
Yuri Kuznetsov dd732d3044 install themes 2022-07-24 13:23:57 +03:00
Yuri Kuznetsov 77129b9f81 themes grand refactoring 2022-07-24 13:21:11 +03:00
Yuri Kuznetsov 00bf9561da readme 2022-07-24 10:35:54 +03:00
Yuri Kuznetsov 1fb820c930 readme 2022-07-24 10:03:50 +03:00
Yuri Kuznetsov 76c1fc1669 theme parent 2022-07-24 08:40:48 +03:00
Yuri Kuznetsov 64be5837f6 fix sticked buttons 2022-07-23 21:01:41 +03:00
Yuri Kuznetsov 2b2af83ddc css colors fix 2022-07-23 20:43:17 +03:00
Yuri Kuznetsov 1817ec2aaa breadcrump selection fix 2022-07-23 20:37:05 +03:00
Yuri Kuznetsov 738a10f970 css fix 2022-07-23 20:30:35 +03:00
Yuri Kuznetsov 85a44b6657 css fix 2022-07-23 14:15:30 +03:00
Yuri Kuznetsov 869ebbe44e css fix 2022-07-23 14:12:05 +03:00
Yuri Kuznetsov b648cf730f btn-text active line 2022-07-23 14:02:31 +03:00
Yuri Kuznetsov 046a901c40 less ref 2022-07-23 13:18:24 +03:00
Yuri Kuznetsov df478027d8 less cleanup 2022-07-22 17:17:03 +03:00
Yuri Kuznetsov 3eca6024cd less ref 2022-07-22 17:10:41 +03:00
Yuri Kuznetsov fc3f517a1b avatar ref 2022-07-22 14:40:15 +03:00
Yuri Kuznetsov f2e82acd4a enum options style 2022-07-22 14:33:38 +03:00
Yuri Kuznetsov f9122b7450 less ref 2022-07-22 14:19:53 +03:00
Yuri Kuznetsov c6fdff53c3 modal backdrop blur 2022-07-22 13:37:29 +03:00
Yuri Kuznetsov dfcb63c18a less ref 2022-07-22 12:43:07 +03:00
Yuri Kuznetsov 9afcdbf65d sticked bar fixes 2022-07-22 12:18:53 +03:00
Yuri Kuznetsov 6c7e7b7df2 kanban dd fix 2022-07-22 11:29:21 +03:00
Yuri Kuznetsov 6fd0f10ee7 ref 2022-07-22 11:26:10 +03:00
Yuri Kuznetsov 7889d562b4 preferences validation 2022-07-22 11:19:00 +03:00
Yuri Kuznetsov 5731e7e408 cleanup 2022-07-22 11:08:03 +03:00
Yuri Kuznetsov e333503954 theme fixes 2022-07-22 10:55:29 +03:00
Yuri Kuznetsov fd85bb95d2 button rounding 2022-07-22 10:47:53 +03:00
Yuri Kuznetsov b48660118b fix 2022-07-22 09:54:58 +03:00
Yuri Kuznetsov e9de116400 2fa password check for admin 2022-07-22 09:52:11 +03:00
Yuri Kuznetsov 6127ef9c72 list stick top fix 2022-07-21 20:54:28 +03:00
Yuri Kuznetsov 21842d159d theme fixes 2022-07-21 19:26:30 +03:00
Yuri Kuznetsov 21e0598159 less ref 2022-07-21 17:21:21 +03:00
Yuri Kuznetsov 8e4f83f7fe record panel radius adjustment 2022-07-21 15:50:46 +03:00
Yuri Kuznetsov b394089a79 style fix 2022-07-21 12:59:10 +03:00
Yuri Kuznetsov 2faf1e65c3 css fix 2022-07-21 12:38:17 +03:00
Yuri Kuznetsov 5eafec9f03 theme impr 2022-07-21 12:29:43 +03:00
Yuri Kuznetsov 668bc38d70 fix 2022-07-21 12:14:46 +03:00
Yuri Kuznetsov 20108478b5 fix css 2022-07-21 11:51:07 +03:00
Yuri Kuznetsov fbd1a9d02f theme impr 2022-07-21 11:30:03 +03:00
Yuri Kuznetsov 5b466f7979 btn radius 2 2022-07-21 10:38:09 +03:00
Yuri Kuznetsov c1d7854eb7 theme impr 2022-07-21 10:24:57 +03:00
Yuri Kuznetsov b642f091c3 fix 2022-07-21 09:10:59 +03:00
Yuri Kuznetsov 19a2523089 css fix 2022-07-20 20:40:26 +03:00
Yuri Kuznetsov 1bc1d23934 theme impr 2022-07-20 20:34:16 +03:00
Yuri Kuznetsov b1a0f979c9 css fix 2022-07-20 17:36:11 +03:00
Yuri Kuznetsov fa67206416 theme impr 2022-07-20 17:33:31 +03:00
Yuri Kuznetsov ae891362c7 theme impr 2022-07-20 16:30:24 +03:00
Yuri Kuznetsov 8d94a7d7bd show-more changes 2022-07-20 16:21:11 +03:00
Yuri Kuznetsov 4d450111e9 theme impr 2022-07-20 15:01:30 +03:00
Yuri Kuznetsov 5d2bb9bea9 theme impr 2022-07-20 11:56:11 +03:00
Yuri Kuznetsov dc04e6c5f8 less ref 2022-07-20 11:28:54 +03:00
Yuri Kuznetsov 7551032364 less ref 2022-07-20 10:20:06 +03:00
Yuri Kuznetsov 1a935d0758 css fix 2022-07-19 18:08:41 +03:00
Yuri Kuznetsov a696a1b0b4 style impr 2022-07-19 17:46:30 +03:00
Yuri Kuznetsov 25a553c8f4 less fix 2022-07-19 15:41:27 +03:00
Yuri Kuznetsov 9c4a472ac1 less ref 2022-07-19 15:28:18 +03:00
Yuri Kuznetsov fb7202ad34 theme changes 2022-07-19 14:27:08 +03:00
Yuri Kuznetsov 1de62f442e css fix 2022-07-19 14:18:56 +03:00
Yuri Kuznetsov 257bf317d0 css fix 2022-07-19 13:51:18 +03:00
Yuri Kuznetsov 0fbfde71fa form element radius in variable 2022-07-19 13:19:30 +03:00
Yuri Kuznetsov 10ba796288 fix tpl 2022-07-19 13:03:53 +03:00
Yuri Kuznetsov 0249c6fdce layout manager edit dynamic logic 2022-07-19 11:05:47 +03:00
Yuri Kuznetsov 7cabbf1ee7 ref 2022-07-19 10:31:09 +03:00
Yuri Kuznetsov bcbf15df13 bottom tabs fix 2022-07-19 10:28:30 +03:00
Yuri Kuznetsov 578e77b5c3 idea gitignore 2022-07-19 09:53:34 +03:00
Yuri Kuznetsov 68ccfde3f3 max-size 0 fix 2022-07-18 16:23:36 +03:00
Yuri Kuznetsov 9edc8eeab5 css fix 2022-07-18 15:54:26 +03:00
Yuri Kuznetsov b3d570527e radius 1px 2022-07-18 15:35:01 +03:00
Yuri Kuznetsov c1e5af36c4 bottom tabs 2022-07-18 15:31:43 +03:00
Yuri Kuznetsov 1404698c5e fix 2022-07-18 15:29:34 +03:00
Yuri Kuznetsov 7ece2673cc cs fix 2022-07-18 12:10:33 +03:00
Yuri Kuznetsov 95ceb279a2 cs fix 2022-07-18 11:58:57 +03:00
Yuri Kuznetsov 3a6611602f cleanup 2022-07-18 10:10:16 +03:00
Yuri Kuznetsov 468cdebb38 cs fix 2022-07-18 10:09:31 +03:00
Yuri Kuznetsov 9faca0fefe fix trim 2022-07-18 09:36:20 +03:00
Yuri Kuznetsov 58c5eb8118 radius 2022-07-17 21:44:27 +03:00
Yuri Kuznetsov ac2eca2529 css fix 2022-07-17 21:13:50 +03:00
Yuri Kuznetsov f1054af1f1 layout manager css fix 2022-07-17 16:54:00 +03:00
Yuri Kuznetsov 6864405e55 css fix 2022-07-17 16:48:21 +03:00
Yuri Kuznetsov 39027acc91 fix dashlet options 2022-07-17 16:17:26 +03:00
Yuri Kuznetsov 6e65991341 css fix 2022-07-17 16:10:26 +03:00
Yuri Kuznetsov 760bf67c32 css fix 2022-07-17 16:08:24 +03:00
Yuri Kuznetsov 9ece367ff1 layout panel no name by default 2022-07-17 11:17:01 +03:00
Yuri Kuznetsov 8ff9c94936 tabs improvements 2022-07-17 11:08:51 +03:00
Yuri Kuznetsov d401655b45 jsdoc 2022-07-16 18:59:29 +03:00
Yuri Kuznetsov 85b266cb79 message ref 2022-07-16 18:50:53 +03:00
Yuri Kuznetsov 39be4cf2be stream note message ref 2022-07-16 18:13:39 +03:00
Yuri Kuznetsov 68477319e2 tabs and hidden panel compatiblity 2022-07-16 17:07:51 +03:00
Yuri Kuznetsov 08d3cc11d9 tab rename param and overview label by default 2022-07-16 16:59:59 +03:00
Yuri Kuznetsov 082e67287a jsdoc fix 2022-07-16 14:10:04 +03:00
Yuri Kuznetsov 518fe07ea1 detail tabs 2022-07-16 14:05:26 +03:00
Yuri Kuznetsov d2e36316ea ref 2022-07-15 16:34:01 +03:00
Yuri Kuznetsov 5362e98500 ref 2022-07-15 16:21:57 +03:00
Yuri Kuznetsov 2839f0457a ref 2022-07-15 16:10:48 +03:00
Yuri Kuznetsov a77f70f457 Merge branch 'fix' 2022-07-15 15:55:07 +03:00
Yuri Kuznetsov 73036c7347 fix stream 2022-07-15 15:52:53 +03:00
Yuri Kuznetsov b9dbac65cb fix stream 2022-07-15 15:52:22 +03:00
Yuri Kuznetsov 8f49fe7adf cs fix 2022-07-15 15:50:39 +03:00
Yuri Kuznetsov 52e968809e ref 2022-07-15 15:40:44 +03:00
Yuri Kuznetsov 7192d5d5f5 fix panel filter height 2022-07-15 13:53:55 +03:00
Yuri Kuznetsov dc7d4714d9 fix test 2022-07-15 13:32:13 +03:00
Yuri Kuznetsov 9a714b92fb link-multiple-with-columns-with-primary. 2022-07-15 13:20:15 +03:00
Yuri Kuznetsov ed57338919 mail duplicate finder 2022-07-15 12:40:41 +03:00
Yuri Kuznetsov d696f7347f ref 2022-07-15 12:28:08 +03:00
Yuri Kuznetsov d7aa35bf7c ref 2022-07-15 11:52:23 +03:00
Yuri Kuznetsov d67eba2684 ref 2022-07-15 11:50:39 +03:00
Yuri Kuznetsov 6779684834 password validation 2022-07-15 11:21:25 +03:00
Yuri Kuznetsov 9f594f8259 fix tpl 2022-07-15 11:21:08 +03:00
Yuri Kuznetsov 46bab9cd8f tpl fix 2022-07-15 11:20:17 +03:00
Yuri Kuznetsov c533eea6e3 validation wysiwyg 2022-07-15 10:29:01 +03:00
Yuri Kuznetsov 6c187a0cc2 tpl fix 2022-07-15 10:21:40 +03:00
Yuri Kuznetsov 4a14f91c54 login fixes 2022-07-15 10:09:46 +03:00
Yuri Kuznetsov bcadc1e1d0 fix forgot password modal 2022-07-14 23:23:37 +03:00
Yuri Kuznetsov ceb4979949 metadata force __APPEND__ 2022-07-14 16:42:15 +03:00
Yuri Kuznetsov a913eb600d base test methods 2022-07-14 16:41:26 +03:00
Yuri Kuznetsov bbdf8edce8 enum max length validation 2022-07-14 12:04:14 +03:00
Yuri Kuznetsov 9abb977844 link parent validation 2022-07-14 11:57:10 +03:00
Yuri Kuznetsov b2d143c931 cleanup 2022-07-14 10:44:17 +03:00
Yuri Kuznetsov 486b14e818 notification and stream pages fixes 2022-07-14 10:22:00 +03:00
Yuri Kuznetsov 92a9280e8e portal preferences fix 2022-07-14 09:54:04 +03:00
Yuri Kuznetsov 8741baeb52 style fix 2022-07-13 23:30:09 +03:00
Yuri Kuznetsov db39305290 style fix 2022-07-13 23:22:47 +03:00
Yuri Kuznetsov c1152d7fe7 category link url 2022-07-13 22:16:00 +03:00
Yuri Kuznetsov dddf3f6e7c ref 2022-07-13 21:57:26 +03:00
Yuri Kuznetsov e2510113d4 categories path action 2022-07-13 19:25:53 +03:00
Yuri Kuznetsov 23fbea2b9b js doc 2022-07-13 19:25:43 +03:00
Yuri Kuznetsov 1fcef2654c getHeaderView 2022-07-13 19:21:16 +03:00
Yuri Kuznetsov b2f98d25bc category up style 2022-07-13 19:08:22 +03:00
Yuri Kuznetsov 9bae43d200 category link 2022-07-13 19:02:49 +03:00
Yuri Kuznetsov 2b4c690886 fix categories flicker 2022-07-13 18:48:35 +03:00
Yuri Kuznetsov 132264864c cs fix ref 2022-07-13 18:23:32 +03:00
Yuri Kuznetsov 7f1297e6ae fix 2022-07-13 18:20:01 +03:00
Yuri Kuznetsov 69a5ab9b83 no data margin 2022-07-13 18:13:14 +03:00
Yuri Kuznetsov a49eb15cbe post button wide 2022-07-13 17:09:06 +03:00
Yuri Kuznetsov ca5b56266d fix 2022-07-13 15:20:31 +03:00
Yuri Kuznetsov 822ef61a0c stream audited style fix 2022-07-13 15:17:29 +03:00
Yuri Kuznetsov 363184e7fa text field audited w/o values 2022-07-13 14:31:57 +03:00
Yuri Kuznetsov 5e877a8a8b readOnly take into account entityDefs links 2022-07-13 12:42:57 +03:00
Yuri Kuznetsov 4715350d7c list reset custom order 2022-07-13 12:22:46 +03:00
Yuri Kuznetsov ce32d6dcec fix rtl 2022-07-13 12:05:07 +03:00
Yuri Kuznetsov d801c10e62 tpl fix 2022-07-13 10:55:17 +03:00
Yuri Kuznetsov 3b143de0ca tpl fix 2022-07-13 10:50:24 +03:00
Yuri Kuznetsov 0ac1d6bbce ref 2022-07-13 10:39:24 +03:00
Yuri Kuznetsov 66761665da import error line number 2022-07-13 10:01:35 +03:00
Yuri Kuznetsov 8236902325 css fix 2022-07-12 21:07:07 +03:00
Yuri Kuznetsov f4a2d21ca6 tpl fixes 2022-07-12 21:06:56 +03:00
Yuri Kuznetsov 4fea9b5ef3 tpl integration style fixes 2022-07-12 20:57:19 +03:00
Yuri Kuznetsov 5a4c151cff style fixes 2022-07-12 18:08:36 +03:00
Yuri Kuznetsov 6e067a091e rename 2022-07-12 17:52:12 +03:00
Yuri Kuznetsov 40562ef5b2 barcode validation max length 2022-07-12 17:52:06 +03:00
Yuri Kuznetsov 1a04a17d0c less ref 2022-07-12 17:40:27 +03:00
Yuri Kuznetsov 6e89eaaa4e style fix 2022-07-12 17:27:36 +03:00
Yuri Kuznetsov 0ecdf6b037 fix 2022-07-12 17:21:27 +03:00
Yuri Kuznetsov 62eb882d31 translate field validations 2022-07-12 17:14:58 +03:00
Yuri Kuznetsov 971779b408 import error fixes 2022-07-12 15:12:29 +03:00
Yuri Kuznetsov 726c21ee47 field translation 2022-07-12 14:24:54 +03:00
Yuri Kuznetsov 92ef1620b2 import error export 2022-07-12 14:00:17 +03:00
Yuri Kuznetsov e5589d8b7d file storage fixes 2022-07-12 14:00:09 +03:00
Yuri Kuznetsov 14d4647b5a ref 2022-07-12 12:22:27 +03:00
Yuri Kuznetsov b46717e8f0 impot file accept 2022-07-12 11:20:01 +03:00
Yuri Kuznetsov edfaf8993c style fix 2022-07-12 11:17:40 +03:00
Yuri Kuznetsov 95b4dd3d71 ref 2022-07-12 11:15:38 +03:00
Yuri Kuznetsov 53ff803f38 fix 2022-07-12 10:47:49 +03:00
Yuri Kuznetsov 6d5bd92f1b import error changes 2022-07-12 10:27:34 +03:00
Yuri Kuznetsov 63ae29af74 props 2022-07-12 10:15:41 +03:00
Yuri Kuznetsov 7acc0b2021 fix 2022-07-12 10:01:29 +03:00
Yuri Kuznetsov b5f998eb0e rename 2022-07-11 16:25:45 +03:00
Yuri Kuznetsov dbe35eb3c4 image no original link 2022-07-11 16:16:55 +03:00
Yuri Kuznetsov cf9d446035 rename 2022-07-11 16:13:13 +03:00
Yuri Kuznetsov 6e768dda5d ref 2022-07-11 15:57:28 +03:00
Yuri Kuznetsov 22d70ad987 fix header 2022-07-11 15:38:54 +03:00
Yuri Kuznetsov d0f0042073 no duplicate if create disabled 2022-07-11 15:36:59 +03:00
Yuri Kuznetsov 31685d57b7 textFilterDisabled 2022-07-11 15:28:57 +03:00
Yuri Kuznetsov f054a3fb0d filter 2022-07-11 15:28:45 +03:00
Yuri Kuznetsov 935253d79d import error and import validation 2022-07-11 14:58:55 +03:00
Yuri Kuznetsov 30feae269c modal headerText 2022-07-11 08:56:02 +03:00
Yuri Kuznetsov bb86f2b01e disable spellcheck 2022-07-10 20:05:16 +03:00
Yuri Kuznetsov 8247429780 fix 2022-07-10 19:58:12 +03:00
Yuri Kuznetsov f8134aa08c fix 2022-07-10 19:55:41 +03:00
Yuri Kuznetsov 863ea657ed modal/main supporting elements 2022-07-10 18:57:09 +03:00
Yuri Kuznetsov 77c5471656 css 2022-07-10 18:01:46 +03:00
Yuri Kuznetsov ba33994252 Merge branch 'fix' 2022-07-10 14:24:01 +03:00
Yuri Kuznetsov c08cb0a8f3 fix array at 2022-07-10 14:23:54 +03:00
Yuri Kuznetsov e25465ba5b css fixes btn hover 2022-07-10 13:48:06 +03:00
Yuri Kuznetsov 1276f4c286 btn variables 2022-07-10 13:32:58 +03:00
Yuri Kuznetsov b66d09634b css fixes 2022-07-10 11:32:46 +03:00
Yuri Kuznetsov 3c127293bc css vertical gap 2022-07-10 11:24:58 +03:00
Yuri Kuznetsov f79e64883f css panel ref 2022-07-10 10:52:25 +03:00
Yuri Kuznetsov 479a26b5c9 border width var 2022-07-09 19:18:19 +03:00
Yuri Kuznetsov 662a6cd2dd revert 2022-07-09 15:32:59 +03:00
Yuri Kuznetsov 1ee63dec26 css fix 2022-07-09 15:20:59 +03:00
Yuri Kuznetsov 7f1186c316 dark theme changes 2022-07-09 15:14:30 +03:00
Yuri Kuznetsov a35fd6759a css shadow var 2022-07-09 14:46:54 +03:00
Yuri Kuznetsov 8466c44a9a has-navbar class 2022-07-09 12:07:50 +03:00
Yuri Kuznetsov 5750ae3a6b cs fixes 2022-07-09 12:01:27 +03:00
Yuri Kuznetsov ccab48d31e cs fix 2022-07-09 11:55:28 +03:00
Yuri Kuznetsov eba68da269 cs fix 2022-07-09 11:46:22 +03:00
Yuri Kuznetsov 83e56e4279 css fix 2022-07-09 11:31:42 +03:00
Yuri Kuznetsov cbb2267164 css fix 2022-07-09 11:10:51 +03:00
Yuri Kuznetsov 526317f142 logo 39 2022-07-09 11:03:43 +03:00
Yuri Kuznetsov 3e68adf77a css fix 2022-07-09 09:51:42 +03:00
Yuri Kuznetsov bd58259a43 css fixes 2022-07-08 17:35:37 +03:00
Yuri Kuznetsov df75d4c989 idea change 2022-07-08 16:03:59 +03:00
Yuri Kuznetsov 7e88d16197 css fix 2022-07-08 16:02:22 +03:00
Yuri Kuznetsov 3b7a884f4c dark navbar border 2022-07-08 15:50:55 +03:00
Yuri Kuznetsov fc910fec05 image mime type check for chunk upload 2022-07-08 14:49:54 +03:00
Yuri Kuznetsov 2f797719fa backdrop opacity in var 2022-07-08 14:23:09 +03:00
Yuri Kuznetsov e30af4f4c6 css fix 2022-07-08 13:21:46 +03:00
Yuri Kuznetsov 1a85f9a047 css fix 2022-07-08 13:13:49 +03:00
Yuri Kuznetsov 7737128b6e ignore ics if event originated from espo 2022-07-08 13:10:45 +03:00
Yuri Kuznetsov bd2a318618 clear email created event on meeting removal 2022-07-08 12:59:51 +03:00
Yuri Kuznetsov 441551f8d8 menu button class name 2022-07-08 12:21:19 +03:00
Yuri Kuznetsov 4b679732b1 fix 2022-07-08 12:15:57 +03:00
Yuri Kuznetsov 676a9d42cd draft reply arrow color 2022-07-08 12:11:43 +03:00
Yuri Kuznetsov b24a45e4bf style fix 2022-07-08 11:57:42 +03:00
Yuri Kuznetsov b2ae958992 email fix 2022-07-08 11:52:36 +03:00
Yuri Kuznetsov f2c8942b84 css fix 2022-07-08 11:52:29 +03:00
Yuri Kuznetsov 9523ff2db0 fix dark css 2022-07-08 10:05:34 +03:00
Yuri Kuznetsov 00a921e0e7 fix tabindex 2022-07-08 09:51:49 +03:00
Yuri Kuznetsov d960bd978c fields search on enter 2022-07-08 09:42:41 +03:00
Yuri Kuznetsov 83263717fd cs fix 2022-07-08 08:45:57 +03:00
Yuri Kuznetsov bc2389968d no need to clone events excessive 2022-07-08 08:21:11 +03:00
Yuri Kuznetsov 2b49625106 create/modified fields customization restrictions 2022-07-08 08:14:39 +03:00
Yuri Kuznetsov d375986c36 css fix 2022-07-07 23:18:44 +03:00
Yuri Kuznetsov ddd4c1e427 restrict email field customization 2022-07-07 22:02:12 +03:00
Yuri Kuznetsov 9848e18caf field manager restriction 2022-07-07 21:02:26 +03:00
Yuri Kuznetsov d490107bce admin notification css fix 2022-07-07 17:41:49 +03:00
Yuri Kuznetsov 55c72a5570 fix tag 2022-07-07 17:23:41 +03:00
Yuri Kuznetsov ef892af56f email sending error exception 2022-07-07 17:05:49 +03:00
Yuri Kuznetsov 46ea00aef8 ref 2022-07-07 16:43:32 +03:00
Yuri Kuznetsov bac2a36472 fix record save error handling 2022-07-07 16:28:54 +03:00
Yuri Kuznetsov 3156909cf6 fix 2022-07-07 16:10:11 +03:00
Yuri Kuznetsov 956beb3850 fix 2022-07-07 16:04:01 +03:00
Yuri Kuznetsov 7869268e2d email send error do not close 2022-07-07 15:56:37 +03:00
Yuri Kuznetsov 8e94164976 email customizable 2022-07-07 15:41:51 +03:00
Yuri Kuznetsov 9ef06d7a08 note clean parent id 2022-07-07 15:40:48 +03:00
Yuri Kuznetsov fd7960643d fix note 2022-07-07 15:39:26 +03:00
Yuri Kuznetsov 72ee2c4939 clnup 2022-07-07 15:39:02 +03:00
Yuri Kuznetsov 01e5350060 ref 2022-07-07 15:34:55 +03:00
Yuri Kuznetsov 478fc69ca5 ref 2022-07-07 15:29:19 +03:00
Yuri Kuznetsov 98a5c584e0 cs fix 2022-07-07 14:53:46 +03:00
Yuri Kuznetsov c34211ae59 fix 2022-07-07 14:42:57 +03:00
Yuri Kuznetsov 30fbc5b50a ref 2022-07-07 14:17:59 +03:00
Yuri Kuznetsov a2356ce977 ui ref 2022-07-07 12:46:26 +03:00
Yuri Kuznetsov 34df179c4c alert fix 2022-07-07 11:25:30 +03:00
Yuri Kuznetsov 20f3c21368 cleanup 2022-07-07 10:57:47 +03:00
Yuri Kuznetsov 0c1c10a5f1 alert notification markdown 2022-07-07 10:40:40 +03:00
Yuri Kuznetsov 4c0eea3dae clnp 2022-07-07 10:37:50 +03:00
Yuri Kuznetsov 72c9ec3453 confirm wide btn 2022-07-07 10:14:01 +03:00
Yuri Kuznetsov 2602e970e1 wide btn 2022-07-07 10:11:39 +03:00
Yuri Kuznetsov fecf9b43aa tpl fix 2022-07-06 17:31:48 +03:00
Yuri Kuznetsov 70e25d17ea css fix 2022-07-06 16:34:02 +03:00
Yuri Kuznetsov 2943f46829 fix css 2022-07-06 16:30:31 +03:00
Yuri Kuznetsov dfb667f0de button hover color fix 2022-07-06 14:41:20 +03:00
Yuri Kuznetsov 90f7a9ec81 fix 2022-07-06 14:37:45 +03:00
Yuri Kuznetsov 26a43e76a4 fix 2022-07-06 14:33:49 +03:00
Yuri Kuznetsov d28e2513ee cleanup 2022-07-06 14:31:27 +03:00
Yuri Kuznetsov 2ad0336a53 dark navbar lighter 2022-07-06 14:26:03 +03:00
Yuri Kuznetsov d9405ea058 fix login style 2022-07-06 14:18:20 +03:00
Yuri Kuznetsov f46132b95c line height 1.5 2022-07-06 14:13:14 +03:00
Yuri Kuznetsov 1bfad8ba22 login page dark color fix 2022-07-06 14:11:06 +03:00
Yuri Kuznetsov 7893eae4ca login page style changes 2022-07-06 14:03:09 +03:00
Yuri Kuznetsov 06a3edc446 css fixes 2022-07-06 13:47:16 +03:00
Yuri Kuznetsov 237bd8e6b7 fa 16px 2022-07-06 13:36:19 +03:00
Yuri Kuznetsov e15c200541 cs fix 2022-07-06 13:35:27 +03:00
Yuri Kuznetsov 405c5d145b fix 2022-07-06 13:26:59 +03:00
Yuri Kuznetsov 848a05beb8 global restriction read only from entityDefs 2022-07-06 12:49:35 +03:00
Yuri Kuznetsov 96917675a0 ref 2022-07-06 12:06:16 +03:00
Yuri Kuznetsov 751b3f2fc7 fix typo 2022-07-06 11:27:02 +03:00
Yuri Kuznetsov c12807d040 cleanup 2022-07-06 11:24:09 +03:00
Yuri Kuznetsov d8c42dcee6 cleanup 2022-07-06 11:16:14 +03:00
Yuri Kuznetsov 2f073dc8eb fix typo 2022-07-06 11:08:56 +03:00
Yuri Kuznetsov cfb50440d7 no trim 2022-07-06 10:51:14 +03:00
Yuri Kuznetsov 216e945c06 array trim 2022-07-06 10:47:39 +03:00
Yuri Kuznetsov 4174864b8c validations 2022-07-06 10:30:00 +03:00
Yuri Kuznetsov 9e7cc42c78 fix 2022-07-06 09:24:21 +03:00
Yuri Kuznetsov d6846adacf array max item length ui 2022-07-06 06:30:05 +03:00
Yuri Kuznetsov a30900aebe field man limitation 2022-07-06 06:23:59 +03:00
Yuri Kuznetsov 9c0e0122c5 user field manager limitations 2022-07-06 06:19:49 +03:00
Yuri Kuznetsov 2dd59f36a0 global restriction ui 2022-07-06 05:55:52 +03:00
Yuri Kuznetsov d5d4c5874b fix 2022-07-05 17:16:35 +03:00
Yuri Kuznetsov d2107eccde fix 2022-07-05 17:12:42 +03:00
Yuri Kuznetsov 377268359b min node version 2022-07-05 16:12:03 +03:00
Yuri Kuznetsov a726f7f1d5 fix lead capture 2022-07-05 16:08:18 +03:00
Yuri Kuznetsov e252e554c4 person entity methods 2022-07-05 16:08:06 +03:00
Yuri Kuznetsov 85b3d698b2 lead capture headers example 2022-07-05 15:11:43 +03:00
Yuri Kuznetsov abfcae48e0 lead capture validation 2022-07-05 15:05:06 +03:00
Yuri Kuznetsov 4ab88c19c9 rename 2022-07-05 14:57:47 +03:00
Yuri Kuznetsov 7615857663 field validation error message 2022-07-05 14:50:46 +03:00
Yuri Kuznetsov 00045f4837 bad request with body 2022-07-05 14:50:16 +03:00
Yuri Kuznetsov 348c3ef725 docs 2022-07-05 14:50:06 +03:00
Yuri Kuznetsov 7d3d4de51c doc 2022-07-05 13:58:43 +03:00
Yuri Kuznetsov 0c5c7953b6 ref docs 2022-07-05 13:37:09 +03:00
Yuri Kuznetsov 58049dd449 ref 2022-07-05 13:34:26 +03:00
Yuri Kuznetsov 8413b5c9d5 ref 2022-07-05 13:28:43 +03:00
Yuri Kuznetsov 101a9084b8 ref 2022-07-05 13:26:03 +03:00
Yuri Kuznetsov 79c51379b8 ref 2022-07-05 13:07:16 +03:00
Yuri Kuznetsov 7db116cb89 ref 2022-07-05 13:01:32 +03:00
Yuri Kuznetsov 219168a378 tooltips 2022-07-05 10:48:07 +03:00
Yuri Kuznetsov 958cfd7eba fix tooltip 2022-07-05 10:26:34 +03:00
Yuri Kuznetsov 3fbd65ceca no special chars regexp 2022-07-05 10:20:23 +03:00
Yuri Kuznetsov 4bef98e3f4 fetch empty column as null 2022-07-05 10:13:54 +03:00
Yuri Kuznetsov 610a0c3c92 link multiple column client side validation 2022-07-05 09:59:23 +03:00
Yuri Kuznetsov 9ae755af2a ref 2022-07-05 09:34:32 +03:00
Yuri Kuznetsov c5900911d7 fix 2022-07-04 16:03:19 +03:00
Yuri Kuznetsov 926787fffb link multiple columns server side validation 2022-07-04 15:47:45 +03:00
Yuri Kuznetsov 1b5bd3f5d8 fieldManagerParamList 2022-07-04 14:06:20 +03:00
Yuri Kuznetsov 562fb14cea Merge branch 'fix' 2022-07-04 12:19:16 +03:00
Yuri Kuznetsov b5104cef3c diff fix 2022-07-04 12:17:02 +03:00
Yuri Kuznetsov a067eb2070 v 2022-07-04 11:55:24 +03:00
Yuri Kuznetsov d5b6120d45 ref 2022-07-04 11:54:35 +03:00
Yuri Kuznetsov bba15ab759 ref 2022-07-04 11:54:25 +03:00
Yuri Kuznetsov afb42e3589 fix url 2022-07-04 11:49:50 +03:00
Yuri Kuznetsov 59bf7d5a7a ref 2022-07-04 11:45:21 +03:00
Yuri Kuznetsov 8e297fda48 ref 2022-07-04 11:38:25 +03:00
Yuri Kuznetsov 1093143773 bc fix 2022-07-04 11:12:46 +03:00
Yuri Kuznetsov 2ccc796b6f cs fixes 2022-07-04 11:03:30 +03:00
Yuri Kuznetsov 634002c471 ref and fix 2022-07-04 10:59:48 +03:00
Yuri Kuznetsov 5d41085dd4 refactor and fixes 2022-07-04 10:23:33 +03:00
Yuri Kuznetsov 5f2bc52e6b fix 2022-07-04 09:02:09 +03:00
Yuri Kuznetsov 2d1cbaae73 validation list for specific field 2022-07-03 23:02:11 +03:00
Yuri Kuznetsov 2d4f11717c throws tags 2022-07-03 22:54:13 +03:00
Yuri Kuznetsov be6cfefbec ref 2022-07-03 21:04:44 +03:00
Yuri Kuznetsov 2e1aa59250 link multiple field view ref 2022-07-03 20:20:27 +03:00
Yuri Kuznetsov 04398052d8 noSpellCheck param 2022-07-03 16:30:29 +03:00
Yuri Kuznetsov 88db4a5020 default varchar max length validation 2022-07-03 16:11:08 +03:00
Yuri Kuznetsov 6338fe68c9 url encoding email phone validation 2022-07-03 15:38:47 +03:00
Yuri Kuznetsov bf11678e20 tooltip 2022-07-03 13:46:38 +03:00
Yuri Kuznetsov 4d0bda7238 note post customizable 2022-07-03 13:33:17 +03:00
Yuri Kuznetsov 2c4d5ab31d text validate 2022-07-03 13:14:14 +03:00
Yuri Kuznetsov 13629079a8 fix reg exp uri 2022-07-03 13:07:31 +03:00
Yuri Kuznetsov 4d2cd9eff4 varchar trim if not set 2022-07-03 13:04:37 +03:00
Yuri Kuznetsov 4a358f12f4 typo 2022-07-03 12:59:28 +03:00
Yuri Kuznetsov a182ad7a0f install defaults null 2022-07-03 12:56:07 +03:00
Yuri Kuznetsov dda1b4e547 fix translate option 2022-07-03 12:54:11 +03:00
Yuri Kuznetsov 572aceb107 rename 2022-07-03 12:46:59 +03:00
Yuri Kuznetsov 97440a776c fix typo 2022-07-03 12:45:39 +03:00
Yuri Kuznetsov 8b23c591ff validations 2022-07-03 12:29:07 +03:00
Yuri Kuznetsov 1d776ea766 cleanup 2022-07-03 10:19:47 +03:00
Yuri Kuznetsov 3b34c6746e system reg exp patterns 2022-07-03 09:48:00 +03:00
Yuri Kuznetsov 73aaba15a9 array validation 2022-07-02 20:02:23 +03:00
Yuri Kuznetsov dd7220a62b cleanup 2022-07-02 18:41:56 +03:00
Yuri Kuznetsov 4a774d387c optionPath for array 2022-07-02 18:37:30 +03:00
Yuri Kuznetsov 1a46723abb formatting fixes 2022-07-02 18:28:06 +03:00
Yuri Kuznetsov 2052bc7353 fix 2022-07-02 17:37:32 +03:00
Yuri Kuznetsov 6e9136d4ea enum validation 2022-07-02 16:10:15 +03:00
Yuri Kuznetsov 08e5f3cf40 note only post check 2 2022-07-02 14:37:05 +03:00
Yuri Kuznetsov 9af6b6a8bd note only post check 2022-07-02 14:35:11 +03:00
Yuri Kuznetsov 9bc31c1111 fix 2022-07-02 14:25:06 +03:00
Yuri Kuznetsov 6b887f26c5 fix 2022-07-02 14:23:13 +03:00
Yuri Kuznetsov 97a33bde9a note type enum 2022-07-02 14:18:06 +03:00
Yuri Kuznetsov 10597931be varchar pattern 2022-07-02 13:12:32 +03:00
Yuri Kuznetsov 452f7a5d98 noSpellCheck 2022-07-02 12:49:28 +03:00
Yuri Kuznetsov 3b6e690046 field invalid message html 2022-07-02 12:33:40 +03:00
Yuri Kuznetsov 233b7163a2 fix 2022-07-02 11:45:10 +03:00
Yuri Kuznetsov 821a4dd5b4 field entityType prop 2022-07-02 10:43:22 +03:00
Yuri Kuznetsov 2116b7fc7d format fix 2022-07-02 09:44:05 +03:00
Yuri Kuznetsov aed9191584 format fix 2022-07-02 09:41:38 +03:00
Yuri Kuznetsov 75633fc216 login button wide 2022-07-01 20:53:24 +03:00
Yuri Kuznetsov efe41212b0 fix 2022-07-01 20:16:11 +03:00
Yuri Kuznetsov cfca519c5a image mime type detection 2022-07-01 19:52:50 +03:00
Yuri Kuznetsov 544ddbbddd fix 2022-07-01 19:02:33 +03:00
Yuri Kuznetsov 3cbc2e07bb fix jsdoc 2022-07-01 18:30:08 +03:00
Yuri Kuznetsov cae82e9866 cs fixes 2022-07-01 18:27:40 +03:00
Yuri Kuznetsov c3ea3ac6cc fix typo 2022-07-01 18:10:49 +03:00
Yuri Kuznetsov 0641cf4d62 authTokenMaxIdleTime default 48 2022-07-01 17:38:35 +03:00
Yuri Kuznetsov ebd859e4d2 list noDataDisabled option 2022-07-01 16:24:39 +03:00
Yuri Kuznetsov 2fe09b5f40 ref 2022-07-01 16:24:23 +03:00
Yuri Kuznetsov 29d6ea3668 ref 2022-07-01 14:36:42 +03:00
Yuri Kuznetsov d901459581 fix jsdoc 2022-07-01 13:22:41 +03:00
Yuri Kuznetsov 38897f7f3b list layout noLabel and widthPx 2022-07-01 11:51:11 +03:00
Yuri Kuznetsov d13a82fc9f fix jsdoc 2022-07-01 11:45:03 +03:00
Yuri Kuznetsov b0d3d19694 fix field reset to default 2022-07-01 11:12:17 +03:00
Yuri Kuznetsov 512fb35fb4 note attachments customizable 2022-07-01 10:18:33 +03:00
Yuri Kuznetsov f61714db17 fix 2022-07-01 09:16:38 +03:00
Yuri Kuznetsov 2a08678635 ref 2022-06-30 19:53:46 +03:00
Yuri Kuznetsov 4e3ff06d7c ref 2022-06-30 19:49:59 +03:00
Yuri Kuznetsov d9d7567f06 ref 2022-06-30 19:40:02 +03:00
Yuri Kuznetsov ce84389980 cleanup 2022-06-30 19:35:05 +03:00
Yuri Kuznetsov c78feffc41 ref 2022-06-30 19:33:37 +03:00
Yuri Kuznetsov d3f490ee89 ref 2022-06-30 19:31:57 +03:00
Yuri Kuznetsov 5195656bac add legacy office extensions 2022-06-30 18:41:41 +03:00
Yuri Kuznetsov dfce7eeac7 document accept extensions 2022-06-30 18:34:22 +03:00
Yuri Kuznetsov e8060925a3 file upload server side check 2022-06-30 15:25:49 +03:00
Yuri Kuznetsov 0fe2ea1d44 fix test 2022-06-30 14:33:12 +03:00
Yuri Kuznetsov f461a02b1f attachment name max length param 2022-06-30 13:51:47 +03:00
Yuri Kuznetsov 6c9196fdb2 validate image file type 2022-06-30 13:45:49 +03:00
Yuri Kuznetsov e76ee0b093 idea no phpstan 2022-06-30 12:47:31 +03:00
Yuri Kuznetsov 498f1bf2dc extensionMimeType map 2022-06-30 12:45:12 +03:00
Yuri Kuznetsov e3cd5edaf7 ref 2022-06-30 11:44:31 +03:00
Yuri Kuznetsov b8d1d954b6 ref 2022-06-30 11:20:36 +03:00
Yuri Kuznetsov 7c2687b753 add throws tags 2022-06-30 11:08:59 +03:00
Yuri Kuznetsov 96b3a522c3 merge 2022-06-30 10:43:32 +03:00
Yuri Kuznetsov 8ec6fa0f52 v 2022-06-29 19:41:06 +03:00
Yuri Kuznetsov 0fb683bbac secure cookie 2022-06-29 19:33:40 +03:00
Yuri Kuznetsov bc9490a7e4 sanitize csv export 2022-06-29 18:50:08 +03:00
Yuri Kuznetsov a0642e85b4 sanitize import preview 2022-06-29 17:45:55 +03:00
Yuri Kuznetsov e6e76df8c2 fix test 2022-06-29 12:25:51 +03:00
Yuri Kuznetsov 9b506f12e3 acl fixes 2022-06-29 11:43:12 +03:00
Yuri Kuznetsov c176f72fd8 boolean filter resolver 2022-06-29 11:30:47 +03:00
Yuri Kuznetsov 678ce4f139 fix 2022-06-28 22:54:26 +03:00
Yuri Kuznetsov 1d2818ef61 AppParam interface 2022-06-28 17:17:50 +03:00
Yuri Kuznetsov 6f56fffcbd cleanup 2022-06-28 16:01:04 +03:00
Yuri Kuznetsov c25d0e1304 fix bundle config 2022-06-28 12:11:46 +03:00
Yuri Kuznetsov 45dce17fc6 Merge branch 'fix' 2022-06-28 09:44:03 +03:00
Yuri Kuznetsov e61f9eb242 cut long email subject 2022-06-28 09:36:07 +03:00
Yuri Kuznetsov 8a37c9ec83 fix prepare lib original script 2022-06-28 09:08:19 +03:00
Yuri Kuznetsov 49bc91117a cleanup 2022-06-27 23:01:06 +03:00
Yuri Kuznetsov 9f7177d847 cleanup 2022-06-27 22:59:19 +03:00
Yuri Kuznetsov 4bd330ff59 cleanup 2022-06-27 22:58:33 +03:00
Yuri Kuznetsov 4895066215 massUpdateDisabled param in clientDefs 2022-06-27 19:13:49 +03:00
Yuri Kuznetsov f9e30b778d detail modal actions in clientDefs 2022-06-27 18:54:44 +03:00
Yuri Kuznetsov 72cf250afe ref 2022-06-27 17:11:40 +03:00
Yuri Kuznetsov efb99f090d ref 2022-06-27 17:07:32 +03:00
Yuri Kuznetsov 661402d052 cs fix 2022-06-27 17:00:39 +03:00
Yuri Kuznetsov 45a1c38418 action-item-setup helper and visibility check function 2022-06-27 14:47:44 +03:00
Yuri Kuznetsov 7d0851a785 ref and jsdoc 2022-06-27 12:27:25 +03:00
Yuri Kuznetsov 7f8c935a82 fix lib dev paths 2022-06-27 11:45:46 +03:00
Yuri Kuznetsov 169bb09576 strip source mapping url 2022-06-27 11:40:17 +03:00
Yuri Kuznetsov b14a989141 original js libs 2022-06-27 10:54:59 +03:00
Yuri Kuznetsov 74844260c8 cleanup 2022-06-26 22:10:23 +03:00
Yuri Kuznetsov 970b4ce104 exceptions in module 2022-06-26 20:36:23 +03:00
Yuri Kuznetsov 74abbe9cc0 bundled 2022-06-26 20:16:29 +03:00
Yuri Kuznetsov 7ac7fea803 espo bundled 2022-06-26 18:24:18 +03:00
Yuri Kuznetsov c28aec44b6 ref grunt 2022-06-26 18:08:55 +03:00
Yuri Kuznetsov da8b117055 Merge branch 'fix' 2022-06-26 12:19:20 +03:00
Yuri Kuznetsov 2c87d47507 fix 2022-06-26 12:11:55 +03:00
Yuri Kuznetsov 92c9f2ca2d fix 2022-06-26 11:50:01 +03:00
Yuri Kuznetsov 8d34a5bd1a jsdoc 2022-06-26 11:24:09 +03:00
Yuri Kuznetsov 27b9035fd8 fix 2022-06-26 11:20:57 +03:00
Yuri Kuznetsov 59b20dabbb idea change 2022-06-26 11:07:56 +03:00
Yuri Kuznetsov 1e6dcd8fa7 bc fix 2022-06-26 10:48:53 +03:00
Yuri Kuznetsov e08bf1b18d getRepositoryByClass and refactoring 2022-06-25 16:17:30 +03:00
Yuri Kuznetsov 645b7f4482 idea gitignore 2022-06-25 15:50:03 +03:00
Yuri Kuznetsov 37a71f050f cleanup 2022-06-25 15:49:51 +03:00
Yuri Kuznetsov 8c5b24689a getRDBRepositoryByClass 2022-06-25 15:41:40 +03:00
Yuri Kuznetsov 37777f0d92 cleanup idea 2022-06-25 15:40:49 +03:00
Yuri Kuznetsov c5191deb40 idea ignore 2022-06-25 15:39:42 +03:00
Yuri Kuznetsov 78afd40955 merge 2022-06-24 20:58:18 +03:00
Yuri Kuznetsov 2c7249f12d translations 2022-06-24 20:57:10 +03:00
Yuri Kuznetsov d9806e8ea9 lang ignore language name translations 2022-06-24 20:52:22 +03:00
Yuri Kuznetsov 373de97515 refactoring 2022-06-24 20:36:10 +03:00
Yuri Kuznetsov 52a96ae480 refactoring 2022-06-24 20:35:33 +03:00
Yuri Kuznetsov d2398400e2 rename 2022-06-24 19:58:25 +03:00
Yuri Kuznetsov 50de79e2f7 jasmine-browser-runner 2022-06-24 19:57:08 +03:00
Yuri Kuznetsov 8af367d67e update bullbone 2022-06-24 15:29:45 +03:00
Yuri Kuznetsov 31a94da8ff throws tags 2022-06-24 12:22:27 +03:00
Yuri Kuznetsov a9d5bc5d60 fix 2022-06-24 12:14:47 +03:00
Yuri Kuznetsov 344a0f7ff0 throws tags 2022-06-24 12:09:03 +03:00
Yuri Kuznetsov 1bd6c1c451 throws tags 2022-06-24 11:44:24 +03:00
Yuri Kuznetsov 1d2f862d98 phpdoc fixes 2022-06-24 11:13:02 +03:00
Yuri Kuznetsov 2ecfdc2d4b exceptions 2022-06-24 11:06:01 +03:00
Yuri Kuznetsov 503e1f6eff idea cs 2022-06-24 10:26:24 +03:00
Yuri Kuznetsov 6516616bf4 exception fixes 2022-06-23 17:28:35 +03:00
Yuri Kuznetsov bcd063b8f3 fix 2022-06-23 14:51:23 +03:00
Yuri Kuznetsov 459de7be6f exception changes 2022-06-23 14:03:04 +03:00
Yuri Kuznetsov 2d12868849 idea 2022-06-23 13:03:22 +03:00
Yuri Kuznetsov dfe921c231 exception change 2022-06-23 13:03:17 +03:00
Yuri Kuznetsov 17dddfd5d9 phpunit default 2022-06-23 13:02:11 +03:00
Yuri Kuznetsov 21cb6f505e Merge branch 'fix' 2022-06-23 12:55:43 +03:00
Yuri Kuznetsov e5f0ca77a9 fix request wrapper 2022-06-23 12:55:03 +03:00
Yuri Kuznetsov 6e5c85be7e exceptions changes 2022-06-23 12:40:11 +03:00
Yuri Kuznetsov 43d5f93c4e fix test 2022-06-23 11:50:13 +03:00
Yuri Kuznetsov 955a489e21 exception changes 2022-06-23 11:45:50 +03:00
Yuri Kuznetsov 05ef2099d4 exceptions changes 2022-06-23 10:31:45 +03:00
Yuri Kuznetsov af717a53b0 fix 2022-06-23 10:16:04 +03:00
Yuri Kuznetsov 59c53a191d exception changes 2022-06-22 20:36:03 +03:00
Yuri Kuznetsov 9ec7c897df add throws 2022-06-22 16:24:25 +03:00
Yuri Kuznetsov cfe7212a80 add throws 2022-06-22 16:15:16 +03:00
Yuri Kuznetsov c0dd692263 throw tags 2022-06-22 15:54:51 +03:00
Yuri Kuznetsov 71dc18afdb add ext-pdo 2022-06-22 15:35:07 +03:00
Yuri Kuznetsov ebf0fbd5d5 fix 2022-06-22 13:50:53 +03:00
Yuri Kuznetsov 7a75c3c961 stream dashlet skip own false 2022-06-22 13:43:38 +03:00
Yuri Kuznetsov b7ebc55c17 print trace job errors 2022-06-22 13:36:52 +03:00
Yuri Kuznetsov 3c97db414b idea cs 2022-06-22 13:34:12 +03:00
Yuri Kuznetsov 988fc94710 php version in composer change 2022-06-22 13:22:23 +03:00
Yuri Kuznetsov d33355d561 support unnamed amd modules in bundle 2022-06-22 13:01:24 +03:00
Yuri Kuznetsov bbc3aa5dd2 fix 2022-06-22 12:52:16 +03:00
Yuri Kuznetsov 4afc962f79 idea fix 2022-06-22 12:34:05 +03:00
Yuri Kuznetsov f22fe29b26 fix typo 2022-06-21 19:13:13 +03:00
Yuri Kuznetsov 0f7b28a6cd jsdoc 2022-06-21 17:06:53 +03:00
Yuri Kuznetsov 1539574fba jsdoc 2022-06-20 21:59:42 +03:00
Yuri Kuznetsov 3e5f193964 jsdoc 2022-06-20 17:34:49 +03:00
Yuri Kuznetsov 40e1e8b559 idea 2022-06-20 15:11:30 +03:00
Yuri Kuznetsov 4871811385 jsdoc 2022-06-20 15:11:15 +03:00
Yuri Kuznetsov bf46534d4d fix 2022-06-20 12:29:52 +03:00
Yuri Kuznetsov 471486ce01 idea files 2022-06-20 12:20:08 +03:00
Yuri Kuznetsov 9590a39d4b jsdoc 2022-06-20 12:16:30 +03:00
Yuri Kuznetsov 51a4dd2a05 Merge branch 'fix' 2022-06-20 11:50:04 +03:00
Eymen Elkum c85f28f8ea fix merge conflict (#2349) 2022-06-20 11:49:39 +03:00
Yuri Kuznetsov 973e8cc77a fix case email address order 2022-06-20 10:29:38 +03:00
Yuri Kuznetsov e852253f93 jsdoc 2022-06-19 21:30:39 +03:00
Yuri Kuznetsov 4e5c041a81 cleanup 2022-06-19 19:55:03 +03:00
Yuri Kuznetsov 250f6f0670 jsdoc 2022-06-19 19:53:50 +03:00
Yuri Kuznetsov 313d52ad14 jsdoc 2022-06-19 18:03:04 +03:00
Yuri Kuznetsov e1af7b22f0 jsdoc 2022-06-19 17:57:40 +03:00
Yuri Kuznetsov db2303bc16 jsdoc 2022-06-19 16:36:58 +03:00
Yuri Kuznetsov 6010aa1336 jsdoc 2022-06-19 15:00:48 +03:00
Yuri Kuznetsov f7e9dbf292 jsdoc 2022-06-19 14:29:02 +03:00
Yuri Kuznetsov d13222bfe3 cleanup 2022-06-18 23:56:45 +03:00
Yuri Kuznetsov e8daec0d0d cleanup 2022-06-18 23:56:33 +03:00
Yuri Kuznetsov 2bc4c3c1c7 cs fix 2022-06-18 19:15:03 +03:00
Yuri Kuznetsov cb43e60ae9 jsdoc 2022-06-18 19:14:09 +03:00
Yuri Kuznetsov b69d55d114 fixes 2022-06-18 17:45:45 +03:00
Yuri Kuznetsov c4ce4d6258 jsdoc 2022-06-18 16:13:05 +03:00
Yuri Kuznetsov 01081ab9a7 jsdoc 2022-06-18 14:31:25 +03:00
Yuri Kuznetsov d2a400d08a jsdoc 2022-06-18 13:54:40 +03:00
Yuri Kuznetsov 5c1fe910b6 jsdoc 2022-06-18 10:10:02 +03:00
Yuri Kuznetsov 97a2f644f0 jsdoc 2022-06-17 17:15:11 +03:00
Yuri Kuznetsov 35e1caf817 jsdoc 2022-06-17 16:29:28 +03:00
Yuri Kuznetsov bccf6a4e05 jsdoc and fixes 2022-06-17 16:01:55 +03:00
Yuri Kuznetsov 7ef9b5be4e jsdoc 2022-06-17 15:31:14 +03:00
Yuri Kuznetsov 075509f97e jsdoc 2022-06-17 13:58:07 +03:00
Yuri Kuznetsov 56dfe8d88a jsdoc 2022-06-17 12:35:55 +03:00
Yuri Kuznetsov f2c5fdf9b5 fix doc 2022-06-16 22:11:35 +03:00
Yuri Kuznetsov 48020c771e jsdoc 2022-06-16 20:59:26 +03:00
Yuri Kuznetsov cd7f060feb jsdoc 2022-06-16 18:20:22 +03:00
Yuri Kuznetsov c0bb2148c6 jsdoc 2022-06-16 16:54:55 +03:00
Yuri Kuznetsov 966025c04f js docs 2022-06-16 16:28:08 +03:00
Yuri Kuznetsov e9fdd976ce jsdoc fixes 2022-06-16 15:06:10 +03:00
Yuri Kuznetsov d1b5847f4c jsdoc fix 2022-06-16 15:01:50 +03:00
Yuri Kuznetsov ad58272a21 jsdoc 2022-06-16 14:56:09 +03:00
Yuri Kuznetsov ec5f66d6f1 fix 2022-06-16 14:28:50 +03:00
Yuri Kuznetsov 5e59538865 jsdoc 2022-06-16 14:26:30 +03:00
Yuri Kuznetsov 4f884b3321 jsdoc 2022-06-16 13:23:37 +03:00
Yuri Kuznetsov cc55430837 Merge branch 'fix' 2022-06-16 10:54:17 +03:00
Yuri Kuznetsov 9e2fbbe494 fix stream fetch-new race condition 2022-06-16 10:48:00 +03:00
Yuri Kuznetsov 322091248e fix 2022-06-16 10:40:38 +03:00
Yuri Kuznetsov 6f241d8803 Arabic lang 2022-06-16 09:22:25 +03:00
Yuri Kuznetsov 720c318186 jsdoc 2022-06-15 16:28:41 +03:00
Yuri Kuznetsov caeed2ab43 jsdoc 2022-06-15 16:22:04 +03:00
Yuri Kuznetsov 4a5ef3c1b2 jsdoc 2022-06-15 16:13:03 +03:00
Yuri Kuznetsov 2da413754e jsdoc 2022-06-15 15:37:22 +03:00
Yuri Kuznetsov 7375149ada jsdoc 2022-06-15 15:15:17 +03:00
Yuri Kuznetsov 2cfd1b76d5 jsdoc 2022-06-15 12:33:54 +03:00
Yuri Kuznetsov 7de4a806a2 jsdoc 2022-06-14 22:53:06 +03:00
Yuri Kuznetsov 9e21dc43e9 jsdocs 2022-06-13 17:16:07 +03:00
Yuri Kuznetsov b711f263eb jsdoc 2022-06-13 16:42:19 +03:00
Yuri Kuznetsov d61cb52afe ide template 2022-06-13 11:43:19 +03:00
Yuri Kuznetsov f0f20dc39f add idea 2022-06-13 11:12:18 +03:00
Yuri Kuznetsov 3beebe0a87 update jscondif 2022-06-13 10:52:36 +03:00
Yuri Kuznetsov 3de40cd5cf type fixes 2022-06-13 10:52:28 +03:00
Yuri Kuznetsov 680ddad042 ide ignoring 2022-06-13 10:12:19 +03:00
Yuri Kuznetsov d65234508f fix 2022-06-12 22:19:03 +03:00
Yuri Kuznetsov 8c7bce4244 jsconfig 2022-06-12 22:16:11 +03:00
Yuri Kuznetsov 58b9b5a775 amd require usage change 2022-06-12 21:37:42 +03:00
Yuri Kuznetsov 3c2b7a757c doc fix 2022-06-12 19:37:43 +03:00
Yuri Kuznetsov 69fc1c199d updage bullbone 2022-06-11 15:00:09 +03:00
Yuri Kuznetsov 8c5e597f43 fix editor config 2022-06-11 12:59:54 +03:00
Yuri Kuznetsov 551427b5a1 dics fixes 2022-06-11 12:56:42 +03:00
Yuri Kuznetsov 9905cfa01b jsdocs 2022-06-11 12:22:53 +03:00
Yuri Kuznetsov 981693093c update bullbone 2022-06-10 21:33:45 +03:00
Yuri Kuznetsov 69ef1bf275 docs 2022-06-10 20:29:29 +03:00
Yuri Kuznetsov cd953b47fe idea gitignore 2022-06-10 20:27:42 +03:00
Yuri Kuznetsov 0578268f2d update bullbone and cs fixes 2022-06-10 11:52:24 +03:00
Yuri Kuznetsov 2422940477 fix docs 2022-06-09 21:08:44 +03:00
Yuri Kuznetsov c551828192 fix docs 2022-06-09 21:05:33 +03:00
Yuri Kuznetsov 077dd51e8a docs 2022-06-09 19:06:04 +03:00
Yuri Kuznetsov 1a06f66555 update bullbone 2022-06-09 18:16:13 +03:00
Yuri Kuznetsov 345c6226b8 frontend controller fixes and docs 2022-06-09 17:36:09 +03:00
Yuri Kuznetsov d2052bf8b1 Merge branch 'fix' 2022-06-09 11:03:25 +03:00
Yuri Kuznetsov 2f90205a16 update bullbone 2022-06-09 11:03:23 +03:00
Yuri Kuznetsov e267c88043 fix label manager empty array 2022-06-09 09:39:48 +03:00
Yuri Kuznetsov 11c9bf7704 update bullbone 2022-06-08 17:28:42 +03:00
Yuri Kuznetsov fb4730942f datetime time format map 2022-06-08 15:23:18 +03:00
Yuri Kuznetsov 68b3fcaa43 docs fix 2022-06-08 10:58:20 +03:00
Yuri Kuznetsov 45d8cc9e8e ref 2022-06-08 10:54:07 +03:00
Yuri Kuznetsov e501eaf238 refactor field view 2022-06-08 10:30:13 +03:00
Yuri Kuznetsov c8395c6558 onListModeSet 2022-06-07 18:33:42 +03:00
Yuri Kuznetsov 00b69bfbab set mode changes 2022-06-07 18:18:13 +03:00
Yuri Kuznetsov 4d5fe65988 Merge branch 'fix' 2022-06-07 14:46:01 +03:00
Yuri Kuznetsov 9441825b8f fix not read-only race condition 2022-06-07 14:45:38 +03:00
Yuri Kuznetsov 48047cfea7 refactoring 2022-06-07 13:18:54 +03:00
Yuri Kuznetsov b26ea22041 fix lang 2022-06-07 10:42:45 +03:00
Yuri Kuznetsov 2bdde353b1 fix acl user frontend 2022-06-07 10:20:52 +03:00
Yuri Kuznetsov cb18485c3a stream fetch new no rebuild fix 2022-06-06 10:34:48 +03:00
Yuri Kuznetsov 0e267ca2a6 cs fix 2022-06-06 10:01:21 +03:00
Yuri Kuznetsov 085f93ba39 hard bounce statuses 2022-06-05 13:44:32 +03:00
Yuri Kuznetsov afea99ae6f bounced test 2022-06-04 20:49:53 +03:00
Yuri Kuznetsov 3cd9e4919a Inline edit change (#2333) 2022-06-03 12:30:57 +03:00
Yuri Kuznetsov e6b7914ccd binder inContextd 2022-06-03 11:50:53 +03:00
Yuri Kuznetsov c7e74fe209 request wrapper fix 2022-06-02 10:36:13 +03:00
Yuri Kuznetsov b3f69c0c70 stream post button fixes 2022-06-02 10:31:28 +03:00
Yuri Kuznetsov 11848b1ded fix post button 2022-06-02 10:07:40 +03:00
Yuri Kuznetsov d6b48962ce attachments fixes 2022-05-31 12:30:53 +03:00
Yuri Kuznetsov 521f407714 upload by chunk 2022-05-31 11:56:36 +03:00
Yuri Kuznetsov fd50868899 Merge branch 'fix' 2022-05-30 09:47:17 +03:00
Yuri Kuznetsov 4f00a18599 type fixes 2022-05-29 10:34:48 +03:00
Yuri Kuznetsov 767e775f31 fix mass convert currency 2022-05-27 11:50:51 +03:00
Yuri Kuznetsov bedb66fdd5 datetime comparison methods 2022-05-26 14:06:45 +03:00
dependabot[bot] e3319a3bd4 Bump grunt from 1.5.2 to 1.5.3 (#2326)
Bumps [grunt](https://github.com/gruntjs/grunt) from 1.5.2 to 1.5.3.
- [Release notes](https://github.com/gruntjs/grunt/releases)
- [Changelog](https://github.com/gruntjs/grunt/blob/main/CHANGELOG)
- [Commits](https://github.com/gruntjs/grunt/compare/v1.5.2...v1.5.3)

---
updated-dependencies:
- dependency-name: grunt
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-05-26 09:23:27 +03:00
Yuri Kuznetsov c74111e29b Merge branch 'fix' 2022-05-24 16:47:47 +03:00
Yuri Kuznetsov bd2204156e v 2022-05-24 16:14:19 +03:00
Yuri Kuznetsov fe15eeeac7 wysiwyg fix paste upload duplicate 2022-05-24 16:08:48 +03:00
Yuri Kuznetsov 280a1a9313 cs fix 2022-05-24 14:37:47 +03:00
Yuri Kuznetsov 27105910df date time withTime 2022-05-24 14:15:59 +03:00
Eymen Elkum f3d2616afb code style improve (#2324) 2022-05-24 13:20:25 +03:00
Yuri Kuznetsov 99cd5ddf3c Merge branch 'master' of github.com:espocrm/espocrm 2022-05-23 17:30:01 +03:00
Yuri Kuznetsov 02ebef1db8 Merge branch 'fix' 2022-05-23 17:29:48 +03:00
Yuri Kuznetsov 28227ddc2e v 2022-05-23 15:30:21 +03:00
Yuri Kuznetsov b848261468 category tree fix 2022-05-23 13:49:33 +03:00
Yuri Kuznetsov f1ff31829c related list modal sync model change 2022-05-23 12:36:24 +03:00
Yuri Kuznetsov 0736285e24 fix range fields 2022-05-23 11:47:32 +03:00
Yuri Kuznetsov 79bc345307 Merge branch 'fix' 2022-05-20 09:54:44 +03:00
Yuri Kuznetsov a737711652 move config params 2022-05-19 11:47:59 +03:00
Yuri Kuznetsov b5e8b89ed5 formula attribute id 2022-05-19 11:14:36 +03:00
Yuri Kuznetsov a51b45df90 move config params 2022-05-19 10:35:08 +03:00
Yuri Kuznetsov 5612055b85 move config params to default 2022-05-19 10:26:09 +03:00
Yuri Kuznetsov e3aff78e89 link manager hook assigned users 2022-05-19 10:19:08 +03:00
Yuri Kuznetsov 84a7576772 cs fix 2022-05-18 16:04:44 +03:00
Yuri Kuznetsov 69cd14f471 fix filter bool any 2022-05-18 16:03:34 +03:00
Yuri Kuznetsov 1a2d223740 callable usage 2022-05-17 17:10:24 +03:00
Yuri Kuznetsov 4ccbf86589 exit status usage 2022-05-17 16:51:34 +03:00
Yuri Kuznetsov 502b6586f1 console io error status code 2022-05-17 16:39:16 +03:00
Yuri Kuznetsov 4f57677e03 Merge branch 'fix' 2022-05-17 15:44:47 +03:00
Yuri Kuznetsov 8c1494e17e notify 2022-05-17 15:43:36 +03:00
Yuri Kuznetsov 178d06650a entity renamer 2022-05-17 12:55:27 +03:00
Yuri Kuznetsov 926ee94aae Merge branch 'master' of https://github.com/espocrm/espocrm 2022-05-16 09:43:29 +03:00
Yuri Kuznetsov 50a6209f11 php version required 2022-05-13 16:33:52 +03:00
Yuri Kuznetsov e82a467f42 Merge branch 'fix' 2022-05-13 15:25:51 +03:00
Yuri Kuznetsov f0d508be6f v 2022-05-13 15:15:33 +03:00
Yuri Kuznetsov 82a3c606c7 Merge branch 'fix' 2022-05-13 15:12:31 +03:00
Yuri Kuznetsov e2b0aa65f8 fix convert lead 2022-05-13 09:40:43 +03:00
Yuri Kuznetsov 271344cfc1 fix convert lead 2022-05-13 09:40:07 +03:00
Yuri Kuznetsov 72cafdb27a Merge branch 'fix' 2022-05-12 17:17:35 +03:00
Yuri Kuznetsov 930675672b duration fixes 2022-05-12 17:07:04 +03:00
Yuri Kuznetsov 73f8731014 fix 2022-05-12 15:22:24 +03:00
Yuri Kuznetsov 33addcd90c Merge branch 'fix' 2022-05-12 15:02:00 +03:00
Yuri Kuznetsov b5ee03b142 fix all-day event duration 2022-05-12 14:33:08 +03:00
Yuri Kuznetsov a0d4fa5b82 cs fix 2022-05-12 14:08:14 +03:00
Yuri Kuznetsov 317bbba8c9 cs fix 2022-05-12 13:54:57 +03:00
Yuri Kuznetsov cd58ccbb52 cleanup 2022-05-12 13:44:09 +03:00
Yuri Kuznetsov da032f3518 preserve duration 2022-05-12 13:39:53 +03:00
Yuri Kuznetsov 487da37bea Merge branch 'fix' 2022-05-12 11:16:15 +03:00
Yuri Kuznetsov 3da95cce76 cs fix 2022-05-12 10:53:05 +03:00
Yuri Kuznetsov 9a7cf5c35e duration 3h 2022-05-12 10:04:28 +03:00
Yuri Kuznetsov a022a4e8e8 fix test 2022-05-11 14:19:27 +03:00
Yuri Kuznetsov b96ae74c14 cs fix 2022-05-10 17:57:54 +03:00
Yuri Kuznetsov 7a420fb6f9 fix clearAllStoredMainViews 2022-05-10 17:51:45 +03:00
Yuri Kuznetsov 58d161210d image silent exceptions 2022-05-10 17:42:14 +03:00
Yuri Kuznetsov 41c7cf115d clear stored main views on logout 2022-05-10 17:41:02 +03:00
Yuri Kuznetsov f511553ff1 mass update actions 2022-05-10 14:53:21 +03:00
Yuri Kuznetsov 9e047650e8 Merge branch 'fix' 2022-05-09 18:18:25 +03:00
Yuri Kuznetsov ba3bbfc097 fix autocomplete search no full-text 2022-05-09 16:03:54 +03:00
Yuri Kuznetsov dcc2d1ee70 Merge branch 'master' of github.com:espocrm/espocrm 2022-05-09 13:02:16 +03:00
Yuri Kuznetsov f167f0a0f9 Merge branch 'stable' 2022-05-09 13:01:46 +03:00
Yuri Kuznetsov 7758e20224 v 2022-05-09 12:52:28 +03:00
Yuri Kuznetsov a1cc2ee8c6 fix duplicate link multiple 2022-05-09 12:39:47 +03:00
Yuri Kuznetsov bdf397ad0f lt lv lang 2022-05-09 10:13:57 +03:00
Yuri Kuznetsov 4dcae92ab7 fix label 2022-05-09 10:11:26 +03:00
Yuri Kuznetsov a7f5192057 Merge branch 'fix' 2022-05-08 16:24:26 +03:00
Yuri Kuznetsov 1d4fedf7c7 fix mass delete 2022-05-08 16:23:17 +03:00
Yuri Kuznetsov 7496d0c347 Merge branch 'fix' 2022-05-08 15:50:25 +03:00
Yuri Kuznetsov 24e6f2469f record service factory 2022-05-08 15:50:14 +03:00
Yuri Kuznetsov fdad7065f7 Merge branch 'fix' 2022-05-07 14:59:37 +03:00
Arkadiy Asuratov 7b16d3541d fix entity type (#2308) 2022-05-06 22:05:31 +03:00
Yuri Kuznetsov 3c9c3c9c8c fix 2022-05-05 18:04:48 +03:00
Yuri Kuznetsov f0413b7037 message partList and bounced recognizer 2022-05-05 17:39:36 +03:00
Yuri Kuznetsov 749f1a6a70 fix modal backdrop close on drag 2022-05-05 11:05:53 +03:00
Yuri Kuznetsov 512a23ef64 bg lang fixes 2022-05-05 10:19:47 +03:00
Yuri Kuznetsov 23729c5b89 scan barcode white background 2022-05-05 10:10:17 +03:00
Yuri Kuznetsov f31ee10cf7 second step login autocomplete off 2022-05-05 10:09:58 +03:00
Yuri Kuznetsov 21d34b091b css label fix 2022-05-04 15:04:36 +03:00
Yuri Kuznetsov fd19aa8b2e multi-enum max length 2022-05-04 14:36:24 +03:00
Arkadiy Asuratov 5af0b62e8e fix undeclared variable (#2304) 2022-05-04 14:00:51 +03:00
Yuri Kuznetsov 965cb33e34 layout set removal 2022-04-30 18:25:33 +03:00
Yuri Kuznetsov aca19b3427 send button position 2022-04-30 11:08:02 +03:00
Yuri Kuznetsov 238baf89be spin color 2022-04-29 13:08:35 +03:00
Yuri Kuznetsov 50218a1146 fetch model on followers change 2022-04-29 12:21:35 +03:00
Yuri Kuznetsov d1280a8797 cs fix 2022-04-29 12:16:57 +03:00
Yuri Kuznetsov ca9358cf1a cs fix 2022-04-29 11:59:41 +03:00
Yuri Kuznetsov c3fa8f3131 after relete link event 2022-04-29 11:51:10 +03:00
Yuri Kuznetsov 4af110cd69 followers fix 2022-04-29 11:34:55 +03:00
Yuri Kuznetsov 45d2d4306b cs fix 2022-04-29 11:31:00 +03:00
Yuri Kuznetsov 385f01da8b css text-gray 2022-04-29 10:23:34 +03:00
Yuri Kuznetsov 85da160957 menu button re-render fix 2022-04-28 22:17:36 +03:00
Yuri Kuznetsov ff15f318c4 cs fix 2022-04-28 21:55:24 +03:00
Yuri Kuznetsov 5769e1b58f email address css fix 2022-04-28 14:43:37 +03:00
Yuri Kuznetsov 028a7c728a fix list extended empty cell 2022-04-28 13:37:16 +03:00
Yuri Kuznetsov 9ec3889f96 date-time none 2022-04-28 13:07:28 +03:00
Yuri Kuznetsov 3aa6502996 phone number css fix 2022-04-28 12:47:49 +03:00
Yuri Kuznetsov 847f8713c9 timeline gray axis color 2022-04-28 12:42:46 +03:00
Yuri Kuznetsov 7405476f61 none value gray 2022-04-28 12:34:23 +03:00
Yuri Kuznetsov 985903e99b loading-value class 2022-04-28 12:34:23 +03:00
Pavel Martínek 347f4e5566 Update Admin.json (#2302) 2022-04-28 11:55:52 +03:00
Pavel Martínek 15d72afbd5 Update Account.json (#2300) 2022-04-28 11:10:31 +03:00
Yuri Kuznetsov 085964dc58 none-value class 2022-04-28 11:08:12 +03:00
Yuri Kuznetsov 6c0a1265ab global search, notifications panel spinner 2022-04-28 10:31:02 +03:00
dependabot[bot] 6ee0de8f62 Bump grunt from 1.4.1 to 1.5.2 (#2298)
Bumps [grunt](https://github.com/gruntjs/grunt) from 1.4.1 to 1.5.2.
- [Release notes](https://github.com/gruntjs/grunt/releases)
- [Changelog](https://github.com/gruntjs/grunt/blob/main/CHANGELOG)
- [Commits](https://github.com/gruntjs/grunt/compare/v1.4.1...v1.5.2)

---
updated-dependencies:
- dependency-name: grunt
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-04-27 16:32:04 +03:00
Yuri Kuznetsov bc2613d6a3 Merge branch 'master' of github.com:espocrm/espocrm 2022-04-27 15:52:32 +03:00
Yuri Kuznetsov 655a499caa v 2022-04-27 15:45:28 +03:00
Yuri Kuznetsov bcf6f33965 no data gray 2022-04-27 13:33:34 +03:00
Yuri Kuznetsov 7c04febb3b fix invitation error 2022-04-27 10:59:45 +03:00
Yuri Kuznetsov de39bcf197 Update SECURITY.md 2022-04-27 10:26:11 +03:00
Yuri Kuznetsov 4e7779ee48 fix entity manager edit 2022-04-27 10:14:09 +03:00
Yuri Kuznetsov 4b17896ff1 ref 2022-04-25 16:42:08 +03:00
Yuri Kuznetsov caf20a5e61 global search fix 2022-04-25 13:14:31 +03:00
Yuri Kuznetsov 896d42df43 v 2022-04-25 11:44:48 +03:00
Yuri Kuznetsov 1905be823d upgrade change 2022-04-25 11:40:05 +03:00
Yuri Kuznetsov 6347992b6e before upgrade fix 2022-04-23 22:53:19 +03:00
Yuri Kuznetsov b695d7c148 upgrade fix 2022-04-23 22:17:04 +03:00
Yuri Kuznetsov 9b75cdc2ac lang updates 2022-04-22 15:51:30 +03:00
Yuri Kuznetsov da80ed0130 formula parser only comment fix 2022-04-21 18:42:00 +03:00
Yuri Kuznetsov e8a23de848 refactoring 2022-04-21 18:02:02 +03:00
Yuri Kuznetsov cb431a03df fix diff 2022-04-21 12:29:01 +03:00
Yuri Kuznetsov 4979fb7326 fix workflow 2022-04-21 12:27:59 +03:00
Yuri Kuznetsov aaae51bb37 version tag empty 2022-04-21 12:20:29 +03:00
Yuri Kuznetsov 4d860e3764 v 2022-04-21 12:14:34 +03:00
Yuri Kuznetsov e73ba5c734 email fetch bc fix 2022-04-21 12:14:07 +03:00
2128 changed files with 71161 additions and 31112 deletions
+1 -2
View File
@@ -4,8 +4,7 @@ root = true
[*]
indent_style = space
indent_size = 4
end_of_line = lf
end_of_line = crlf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
+5
View File
@@ -8,4 +8,9 @@
*.tpl text eol=crlf
*.html text eol=crlf
bin/command text eol=lf
.gitattributes text eol=crlf
.gitignore text eol=crlf
*.png binary
+1 -1
View File
@@ -6,4 +6,4 @@ If you believe you have discovered a vulnerability in EspoCRM please contacts us
## Supported versions
For severe vulnerabilities we provide fixes for 2 minor versions (the second number in the version string) back from the current stable version. Separate patches or manual fix guidelines will be provided for more old versions.
For severe vulnerabilities we provide fixes for 2 minor versions (the second number in the version string) back from the current stable version.
+1 -1
View File
@@ -4,7 +4,7 @@ on:
push:
branches:
- 'master'
- 'hotfix/**'
- 'fix'
paths:
- '**.php'
- '**.json'
-5
View File
@@ -24,11 +24,6 @@ Options -Indexes
# Skip redirect for `client` dir.
RewriteRule ^client/ - [L]
# {#dev}
# Skip redirect for `node_modules` dir. Actual only for dev environment.
RewriteRule ^node_modules/ - [L]
# {/dev}
# Store base path.
RewriteCond %{REQUEST_URI}::$1 ^(.*?/)(.*)::\2$
RewriteRule ^(.*)$ - [E=BASE:%1]
+5
View File
@@ -0,0 +1,5 @@
*
!.gitignore
!/codeStyles
!/fileTemplates
!/inspectionProfiles
+11
View File
@@ -0,0 +1,11 @@
<component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173">
<PHPCodeStyleSettings>
<option name="GROUP_USE_WRAP" value="2" />
</PHPCodeStyleSettings>
<codeStyleSettings language="PHP">
<option name="ALIGN_MULTILINE_PARAMETERS" value="false" />
<option name="ALIGN_MULTILINE_FOR" value="false" />
</codeStyleSettings>
</code_scheme>
</component>
+5
View File
@@ -0,0 +1,5 @@
<component name="ProjectCodeStyleConfiguration">
<state>
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
</state>
</component>
+27
View File
@@ -0,0 +1,27 @@
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM - Open Source CRM application.
* Copyright (C) 2014-${YEAR} Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
* Website: https://www.espocrm.com
*
* EspoCRM is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* EspoCRM is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU General Public License version 3.
*
* In accordance with Section 7(b) of the GNU General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
+1
View File
@@ -0,0 +1 @@
#parse("PHP File Header.php")
+19
View File
@@ -0,0 +1,19 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="DuplicatedCode" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="ES6ConvertVarToLetConst" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="JSIgnoredPromiseFromCall" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="PhpDocMissingThrowsInspection" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="PhpDocSignatureIsNotCompleteInspection" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="PhpMissingFieldTypeInspection" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="PhpMissingParamTypeInspection" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="PhpMissingReturnTypeInspection" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="PhpStanGlobal" enabled="false" level="WEAK WARNING" enabled_by_default="false">
<option name="config" value="$PROJECT_DIR$/phpstan.neon" />
</inspection_tool>
<inspection_tool class="PhpSwitchStatementWitSingleBranchInspection" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="PsalmAdvanceCallableParamsInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="TrivialIfJS" enabled="false" level="WARNING" enabled_by_default="false" />
</profile>
</component>
+1
View File
@@ -0,0 +1 @@
tag-version-prefix = ""
+49 -98
View File
@@ -32,36 +32,41 @@
const fs = require('fs');
const cp = require('child_process');
const path = require('path');
const buildUtils = require('./js/build-utils');
module.exports = grunt => {
const pkg = grunt.file.readJSON('package.json');
const bundleConfig = require('./frontend/bundle-config.json');
const libs = require('./frontend/libs.json');
let jsFilesToBundle = getBundleLibList().concat(bundleConfig.jsFiles);
let jsFilesToCopy = getCopyLibDataList();
const originalLibDir = 'client/lib/original';
let libFilesToMinify = jsFilesToCopy
let bundleJsFileList = buildUtils.getPreparedBundleLibList(libs).concat(originalLibDir + '/espo.js');
let copyJsFileList = buildUtils.getCopyLibDataList(libs);
let minifyLibFileList = copyJsFileList
.filter(item => item.minify)
.reduce((map, item) => (
map[item.dest] = item.dest,
map
), {});
.map(item => {
return {
dest: item.dest,
src: item.originalDest,
};
});
let currentPath = path.dirname(fs.realpathSync(__filename));
let themeList = [];
fs.readdirSync('application/Espo/Resources/metadata/themes').forEach(file => {
themeList.push(file.substr(0, file.length - 5));
themeList.push(file.substring(0, file.length - 5));
});
let cssminFilesData = {};
let lessData = {};
themeList.forEach(theme => {
let name = camelCaseToHyphen(theme);
let name = buildUtils.camelCaseToHyphen(theme);
let files = {};
@@ -71,14 +76,12 @@ module.exports = grunt => {
cssminFilesData['client/css/espo/'+name+'.css'] = 'client/css/espo/'+name+'.css';
cssminFilesData['client/css/espo/'+name+'-iframe.css'] = 'client/css/espo/'+name+'-iframe.css';
let o = {
lessData[theme] = {
options: {
yuicompress: true,
},
files: files,
};
lessData[theme] = o;
});
grunt.initConfig({
@@ -118,7 +121,7 @@ module.exports = grunt => {
'build/tmp/client/custom/modules/*',
'!build/tmp/client/custom/modules/dummy.txt',
]
}
},
},
less: lessData,
@@ -142,11 +145,11 @@ module.exports = grunt => {
banner: '/*! <%= pkg.name %> <%= grunt.template.today("yyyy-mm-dd") %> */\n',
},
files: {
'client/lib/espo.min.js': jsFilesToBundle,
'client/lib/espo.min.js': bundleJsFileList,
},
},
lib: {
files: libFilesToMinify,
files: minifyLibFileList,
},
},
@@ -172,7 +175,7 @@ module.exports = grunt => {
dest: 'build/tmp/client',
},
frontendLib: {
files: jsFilesToCopy,
files: copyJsFileList,
},
backend: {
expand: true,
@@ -246,15 +249,33 @@ module.exports = grunt => {
],
},
},
});
grunt.registerTask('espo-bundle', () => {
const Bundler = require('./js/bundler');
let contents = (new Bundler()).bundle(bundleConfig.jsFiles);
if (!fs.existsSync(originalLibDir)) {
fs.mkdirSync(originalLibDir);
}
fs.writeFileSync(originalLibDir + '/espo.js', contents, 'utf8');
});
grunt.registerTask('prepare-lib-original', () => {
// Even though `npm ci` runs the same script, 'clean:start' deletes files.
cp.execSync("node js/scripts/prepare-lib-original");
});
grunt.registerTask('prepare-lib', () => {
cp.execSync("node js/scripts/prepare-lib");
});
grunt.registerTask('chmod-folders', () => {
cp.execSync(
"find . -type d -exec chmod 755 {} +",
{
cwd: 'build/EspoCRM-' + pkg.version,
}
{cwd: 'build/EspoCRM-' + pkg.version}
);
});
@@ -360,7 +381,7 @@ module.exports = grunt => {
});
grunt.registerTask('upgrade', () => {
cp.execSync("node diff --all --vendor", {stdio: 'inherit'});
cp.execSync("node diff --closest", {stdio: 'inherit'});
});
grunt.registerTask('unit-tests-run', () => {
@@ -375,7 +396,7 @@ module.exports = grunt => {
cp.execSync("composer run-script setConfigParams", {stdio: 'ignore'});
});
grunt.registerTask('zip', () => {
grunt.registerTask('zip', function () { // Don't change to arrow-function.
const archiver = require('archiver');
let resolve = this.async();
@@ -404,8 +425,9 @@ module.exports = grunt => {
archive
.directory(currentPath + '/build/' + folder, folder)
.pipe(zipOutput)
.finalize();
.pipe(zipOutput);
archive.finalize();
});
grunt.registerTask('npm-install', () => {
@@ -423,8 +445,11 @@ module.exports = grunt => {
grunt.registerTask('internal', [
'less',
'cssmin',
'espo-bundle',
'prepare-lib-original',
'uglify:bundle',
'copy:frontendLib',
'prepare-lib',
'uglify:lib',
]);
@@ -487,77 +512,3 @@ module.exports = grunt => {
'offline',
]);
};
function getBundleLibList() {
const libs = require('./frontend/libs.json');
let list = [];
libs.forEach(item => {
if (!item.bundle) {
return;
}
if (item.files) {
item.files.forEach(item => {
list.push(item.src);
});
return;
}
if (!item.src) {
throw new Error("No lib src.");
}
list.push(item.src);
});
return list;
}
function getCopyLibDataList() {
const libs = require('./frontend/libs.json');
let list = [];
libs.forEach(item => {
if (item.bundle) {
return;
}
let minify = item.minify;
if (item.files) {
item.files.forEach(item => {
list.push({
src: item.src,
dest: item.dest || 'client/lib/' + item.src.split('/').pop(),
minify: minify,
});
});
return;
}
if (!item.src) {
throw new Error("No lib src.");
}
list.push({
src: item.src,
dest: item.dest || 'client/lib/' + item.src.split('/').pop(),
minify: minify,
});
});
return list;
}
function camelCaseToHyphen(string){
if (string === null) {
return string;
}
return string.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();
}
@@ -104,7 +104,7 @@ class AccessChecker implements AccessEntityCREDSChecker
return true;
}
/** @var string[] */
/** @var string[] $assignedUserIdList */
$assignedUserIdList = $entity->getLinkMultipleIdList('assignedUsers');
if (
@@ -29,6 +29,7 @@
namespace Espo\Classes\Acl\Note;
use Espo\Entities\Note;
use Espo\Entities\User;
use Espo\ORM\Entity;
@@ -77,6 +78,9 @@ class AccessChecker implements AccessEntityCREDChecker
$this->config = $config;
}
/**
* @param Note $entity
*/
public function checkEntityCreate(User $user, Entity $entity, ScopeData $data): bool
{
$parentId = $entity->get('parentId');
@@ -95,6 +99,62 @@ class AccessChecker implements AccessEntityCREDChecker
return false;
}
/**
* @param Note $entity
*/
public function checkEntityRead(User $user, Entity $entity, ScopeData $data): bool
{
if ($user->isAdmin()) {
return true;
}
$parentId = $entity->getParentId();
$parentType = $entity->getParentType();
if ($parentId && $parentType) {
$parent = $this->entityManager->getEntityById($parentType, $parentId);
if (!$parent) {
return false;
}
return $this->aclManager->checkEntityStream($user, $parent);
}
if ($entity->getType() !== Note::TYPE_POST) {
return false;
}
if ($entity->getCreatedById() === $user->getId()) {
return true;
}
if ($entity->getTargetType() === Note::TARGET_ALL) {
return true;
}
if ($entity->getTargetType() === Note::TARGET_TEAMS) {
$targetTeamIdList = $entity->getLinkMultipleIdList('teams') ?? [];
foreach ($user->getTeamIdList() as $teamId) {
if (in_array($teamId, $targetTeamIdList)) {
return true;
}
}
return false;
}
if ($entity->getTargetType() === Note::TARGET_USERS) {
return in_array($user->getId(), $entity->getLinkMultipleIdList('users') ?? []);
}
return false;
}
/**
* @param Note $entity
*/
public function checkEntityEdit(User $user, Entity $entity, ScopeData $data): bool
{
if ($user->isAdmin()) {
@@ -106,7 +166,7 @@ class AccessChecker implements AccessEntityCREDChecker
}
if (!$this->aclManager->checkOwnershipOwn($user, $entity)) {
return true;
return false;
}
$createdAt = $entity->get('createdAt');
@@ -134,6 +194,9 @@ class AccessChecker implements AccessEntityCREDChecker
return true;
}
/**
* @param Note $entity
*/
public function checkEntityDelete(User $user, Entity $entity, ScopeData $data): bool
{
if ($user->isAdmin()) {
@@ -145,7 +208,7 @@ class AccessChecker implements AccessEntityCREDChecker
}
if (!$this->aclManager->checkOwnershipOwn($user, $entity)) {
return true;
return false;
}
$createdAt = $entity->get('createdAt');
@@ -29,22 +29,24 @@
namespace Espo\Classes\Acl\Note;
use Espo\Entities\Note;
use Espo\Entities\User;
use Espo\ORM\Entity;
use Espo\Core\{
Acl\OwnershipOwnChecker,
};
use Espo\Core\Acl\OwnershipOwnChecker;
/**
* @implements OwnershipOwnChecker<\Espo\Entities\Note>
*/
class OwnershipChecker implements OwnershipOwnChecker
{
/**
* @param Note $entity
*/
public function checkOwn(User $user, Entity $entity): bool
{
if ($entity->get('type') === 'Post' && $user->getId() === $entity->get('createdById')) {
if ($entity->getType() === Note::TYPE_POST && $user->getId() === $entity->getCreatedById()) {
return true;
}
@@ -44,7 +44,7 @@ class OwnershipChecker implements OwnershipOwnChecker
{
public function checkOwn(User $user, Entity $entity): bool
{
/** @var string[] */
/** @var string[] $userTeamIdList */
$userTeamIdList = $user->getLinkMultipleIdList('teams');
return in_array($entity->getId(), $userTeamIdList);
@@ -29,6 +29,7 @@
namespace Espo\Classes\AclPortal\Note;
use Espo\Entities\Note;
use Espo\Entities\User;
use Espo\ORM\Entity;
@@ -77,16 +78,19 @@ class AccessChecker implements AccessEntityCREDChecker
$this->config = $config;
}
/**
* @param Note $entity
*/
public function checkEntityCreate(User $user, Entity $entity, ScopeData $data): bool
{
$parentId = $entity->get('parentId');
$parentType = $entity->get('parentType');
$parentId = $entity->getParentId();
$parentType = $entity->getParentType();
if (!$parentId || !$parentType) {
return $this->defaultAccessChecker->checkEntityCreate($user, $entity, $data);
}
$parent = $this->entityManager->getEntity($parentType, $parentId);
$parent = $this->entityManager->getEntityById($parentType, $parentId);
if ($parent && $this->aclManager->checkEntityStream($user, $parent)) {
return true;
@@ -95,31 +99,42 @@ class AccessChecker implements AccessEntityCREDChecker
return $this->defaultAccessChecker->checkEntityCreate($user, $entity, $data);
}
/**
* @param Note $entity
*/
public function checkEntityRead(User $user, Entity $entity, ScopeData $data): bool
{
if ($entity->get('type') !== 'Post') {
return false;
}
$parentId = $entity->getParentId();
$parentType = $entity->getParentType();
if ($entity->get('type') === 'Post' && $entity->get('targetType')) {
return false;
}
if ($parentId && $parentType) {
$parent = $this->entityManager->getEntityById($parentType, $parentId);
if (!$entity->get('parentId') || !$entity->get('parentType')) {
return false;
}
$parent = $this->entityManager->getEntity($entity->get('parentType'), $entity->get('parentId'));
if ($parent) {
if ($this->aclManager->checkEntityStream($user, $parent)) {
return true;
if (!$parent) {
return false;
}
return $this->aclManager->checkEntityStream($user, $parent);
}
if ($entity->getType() !== Note::TYPE_POST) {
return false;
}
if ($entity->getCreatedById() === $user->getId()) {
return true;
}
if ($entity->getTargetType() === Note::TARGET_PORTALS) {
return in_array($user->getPortalId(), $entity->getLinkMultipleIdList('portals') ?? []);
}
return false;
}
/**
* @param Note $entity
*/
public function checkEntityEdit(User $user, Entity $entity, ScopeData $data): bool
{
if (!$this->defaultAccessChecker->checkEntityEdit($user, $entity, $data)) {
@@ -127,7 +142,7 @@ class AccessChecker implements AccessEntityCREDChecker
}
if (!$this->aclManager->checkOwnershipOwn($user, $entity)) {
return true;
return false;
}
$createdAt = $entity->get('createdAt');
@@ -155,6 +170,9 @@ class AccessChecker implements AccessEntityCREDChecker
return true;
}
/**
* @param Note $entity
*/
public function checkEntityDelete(User $user, Entity $entity, ScopeData $data): bool
{
if (!$this->defaultAccessChecker->checkEntityDelete($user, $entity, $data)) {
@@ -162,7 +180,7 @@ class AccessChecker implements AccessEntityCREDChecker
}
if (!$this->aclManager->checkOwnershipOwn($user, $entity)) {
return true;
return false;
}
$createdAt = $entity->get('createdAt');
@@ -29,22 +29,24 @@
namespace Espo\Classes\AclPortal\Note;
use Espo\Entities\Note;
use Espo\Entities\User;
use Espo\ORM\Entity;
use Espo\Core\{
Acl\OwnershipOwnChecker,
};
use Espo\Core\Acl\OwnershipOwnChecker;
/**
* @implements OwnershipOwnChecker<\Espo\Entities\Note>
*/
class OwnershipChecker implements OwnershipOwnChecker
{
/**
* @param Note $entity
*/
public function checkOwn(User $user, Entity $entity): bool
{
if ($entity->get('type') === 'Post' && $user->getId() === $entity->get('createdById')) {
if ($entity->getType() === Note::TYPE_POST && $user->getId() === $entity->getCreatedById()) {
return true;
}
@@ -63,7 +63,7 @@ class Container
'user',
];
/** @var string[] */
/** @var string[] $fileList */
$fileList = scandir('application/Espo/Core/Loaders');
if (file_exists('custom/Espo/Custom/Core/Loaders')) {
@@ -34,20 +34,24 @@ use Espo\Core\{
Select\SelectBuilderFactory,
ORM\EntityManager,
};
use Espo\Tools\App\AppParam;
/**
* Returns a list of entity types for which a PDF template exists.
*/
class TemplateEntityTypeList
class TemplateEntityTypeList implements AppParam
{
private $acl;
private Acl $acl;
private $selectBuilderFactory;
private SelectBuilderFactory $selectBuilderFactory;
private $entityManager;
private EntityManager $entityManager;
public function __construct(Acl $acl, SelectBuilderFactory $selectBuilderFactory, EntityManager $entityManager)
{
public function __construct(
Acl $acl,
SelectBuilderFactory $selectBuilderFactory,
EntityManager $entityManager
) {
$this->acl = $acl;
$this->selectBuilderFactory = $selectBuilderFactory;
$this->entityManager = $entityManager;
@@ -83,14 +83,21 @@ class Import implements Command
$resultId = $result->getId();
$countCreated = $result->getCountCreated();
$countUpdated = $result->getCountUpdated();
$countError = $result->getCountError();
$countDuplicate = $result->getCountDuplicate();
}
catch (Throwable $e) {
$io->writeLine("Error occurred: ". $e->getMessage() . "");
$io->writeLine("Error occurred: " . $e->getMessage());
return;
}
$io->writeLine("Finished. Import ID: {$resultId}. Created: {$countCreated}. Updated: {$countUpdated}.");
$io->writeLine("Finished.");
$io->writeLine(" Import ID: {$resultId}");
$io->writeLine(" Created: {$countCreated}");
$io->writeLine(" Updated: {$countUpdated}");
$io->writeLine(" Duplicates: {$countDuplicate}");
$io->writeLine(" Errors: {$countError}");
return;
}
@@ -102,7 +109,7 @@ class Import implements Command
$this->service->revert($id);
}
catch (Throwable $e) {
$io->writeLine("Error occurred: " . $e->getMessage() . "");
$io->writeLine("Error occurred: " . $e->getMessage());
return;
}
@@ -119,15 +126,21 @@ class Import implements Command
$result = $this->service->importById($id, true, $forceResume);
}
catch (Throwable $e) {
$io->writeLine("Error occurred: " . $e->getMessage() . "");
$io->writeLine("Error occurred: " . $e->getMessage());
return;
}
$countCreated = $result->getCountCreated();
$countUpdated = $result->getCountUpdated();
$countError = $result->getCountError();
$countDuplicate = $result->getCountDuplicate();
$io->writeLine("Finished. Created: {$countCreated}. Updated: {$countUpdated}.");
$io->writeLine("Finished.");
$io->writeLine(" Created: {$countCreated}");
$io->writeLine(" Updated: {$countUpdated}");
$io->writeLine(" Duplicates: {$countDuplicate}");
$io->writeLine(" Errors: {$countError}");
return;
}
@@ -71,7 +71,7 @@ class LinkMultiple implements FieldDuplicator
->getRelation($relationDefs->getForeignRelationName())
->getType();
if ($foreignRelationType !== Entity::HAS_MANY) {
if ($foreignRelationType !== Entity::MANY_MANY) {
$valueMap->{$field . 'Ids'} = [];
$valueMap->{$field . 'Names'} = (object) [];
$valueMap->{$field . 'Columns'} = (object) [];
@@ -29,6 +29,8 @@
namespace Espo\Classes\FieldProcessing\Email;
use Espo\Modules\Crm\Entities\Call;
use Espo\Modules\Crm\Entities\Meeting;
use Espo\ORM\Entity;
use Espo\ORM\EntityManager;
use Espo\Repositories\EmailAddress as EmailAddressRepository;
@@ -44,7 +46,6 @@ use Espo\Core\{
};
use ICal\ICal;
use ICal\Event;
use Throwable;
use stdClass;
@@ -85,7 +86,7 @@ class IcsDataLoader implements Loader
$ical->initString($icsContents);
/* @var $event Event */
/* @var \ICal\Event|null $event */
$event = $ical->events()[0] ?? null;
if ($event === null) {
@@ -121,11 +122,14 @@ class IcsDataLoader implements Loader
return;
}
if ($this->eventAlreadyExists($espoEvent)) {
return;
}
/** @var EmailAddressRepository $emailAddressRepository */
$emailAddressRepository = $this->entityManager->getRepository(EmailAddress::ENTITY_TYPE);
$attendeeEmailAddressList = $espoEvent->getAttendeeEmailAddressList();
$organizerEmailAddress = $espoEvent->getOrganizerEmailAddress();
if ($organizerEmailAddress) {
@@ -167,7 +171,6 @@ class IcsDataLoader implements Loader
$this->loadCreatedEvent($entity, $espoEvent, $eventData);
$entity->set('icsEventData', $eventData);
$entity->set('icsEventDateStart', $espoEvent->getDateStart());
if ($espoEvent->isAllDay()) {
@@ -209,4 +212,35 @@ class IcsDataLoader implements Loader
'name' => $createdEvent->get('name'),
];
}
private function eventAlreadyExists(EspoEvent $espoEvent): bool
{
$id = $espoEvent->getUid();
if (!$id) {
return false;
}
$found1 = $this->entityManager
->getRDBRepository(Meeting::ENTITY_TYPE)
->select(['id'])
->where(['id' => $id])
->findOne();
if ($found1) {
return true;
}
$found2 = $this->entityManager
->getRDBRepository(Call::ENTITY_TYPE)
->select(['id'])
->where(['id' => $id])
->findOne();
if ($found2) {
return true;
}
return false;
}
}
@@ -104,7 +104,7 @@ class StringDataLoader implements Loader
return;
}
/** @var ?string */
/** @var ?string $fromEmailAddressId */
$fromEmailAddressId = $entity->get('fromEmailAddressId');
if (!$fromEmailAddressId) {
@@ -29,12 +29,27 @@
namespace Espo\Classes\FieldValidators;
use Espo\Core\Utils\Metadata;
use Espo\ORM\Defs;
use Espo\ORM\Entity;
use StdClass;
use stdClass;
class ArrayType
{
private Metadata $metadata;
private Defs $defs;
private const DEFAULT_MAX_LENGTH = 100;
public function __construct(Metadata $metadata, Defs $defs)
{
$this->metadata = $metadata;
$this->defs = $defs;
}
public function checkRequired(Entity $entity, string $field): bool
{
return $this->isNotEmpty($entity, $field);
@@ -55,9 +70,92 @@ class ArrayType
return true;
}
public function rawCheckArray(StdClass $data, string $field): bool
public function checkArrayOfString(Entity $entity, string $field): bool
{
if (isset($data->$field) && $data->$field !== null && !is_array($data->$field)) {
/** @var ?mixed[] $list */
$list = $entity->get($field);
if ($list === null) {
return true;
}
foreach ($list as $item) {
if (!is_string($item)) {
return false;
}
}
return true;
}
public function checkValid(Entity $entity, string $field): bool
{
if (!$entity->has($field)) {
return true;
}
/** @var ?string[] $value */
$value = $entity->get($field);
if ($value === null || $value === []) {
return true;
}
$fieldDefs = $this->defs
->getEntity($entity->getEntityType())
->getField($field);
if ($fieldDefs->getParam('allowCustomOptions')) {
return true;
}
$optionList = $this->getOptionList($entity->getEntityType(), $field);
if ($optionList === null) {
return true;
}
foreach ($value as $item) {
if (!in_array($item, $optionList)) {
return false;
}
}
return true;
}
/**
* @return ?string[]
*/
private function getOptionList(string $entityType, string $field): ?array
{
$fieldDefs = $this->defs
->getEntity($entityType)
->getField($field);
/** @var ?string $path */
$path = $fieldDefs->getParam('optionsPath');
/** @var string[]|null|false $optionList */
$optionList = $path ?
$this->metadata->get($path) :
$fieldDefs->getParam('options');
if ($optionList === null) {
return null;
}
// For bc.
if ($optionList === false) {
return null;
}
return $optionList;
}
public function rawCheckArray(stdClass $data, string $field): bool
{
if (isset($data->$field) && !is_array($data->$field)) {
return false;
}
@@ -82,4 +180,73 @@ class ArrayType
return false;
}
public function checkMaxLength(Entity $entity, string $field, ?int $validationValue): bool
{
$maxLength = $validationValue ?? self::DEFAULT_MAX_LENGTH;
/** @var string[] $value */
$value = $entity->get($field) ?? [];
foreach ($value as $item) {
if (mb_strlen($item) > $maxLength) {
return false;
}
}
return true;
}
public function checkPattern(Entity $entity, string $field, ?string $validationValue): bool
{
if (!$validationValue) {
return true;
}
$pattern = $validationValue;
if ($validationValue[0] === '$') {
$patternName = substr($validationValue, 1);
$pattern = $this->metadata->get(['app', 'regExpPatterns', $patternName, 'pattern']) ??
$pattern;
}
$preparedPattern = '/^' . $pattern . '$/';
/** @var string[] $value */
$value = $entity->get($field) ?? [];
foreach ($value as $item) {
if ($item === '') {
continue;
}
if (!preg_match($preparedPattern, $item)) {
return false;
}
}
return true;
}
public function checkNoEmptyString(Entity $entity, string $field, ?bool $validationValue): bool
{
if (!$validationValue) {
return true;
}
/** @var string[] $value */
$value = $entity->get($field) ?? [];
$optionList = $this->getOptionList($entity->getEntityType(), $field) ?? [];
foreach ($value as $item) {
if ($item === '' && !in_array($item, $optionList)) {
return false;
}
}
return true;
}
}
@@ -0,0 +1,47 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM - Open Source CRM application.
* Copyright (C) 2014-2022 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
* Website: https://www.espocrm.com
*
* EspoCRM is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* EspoCRM is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU General Public License version 3.
*
* In accordance with Section 7(b) of the GNU General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
namespace Espo\Classes\FieldValidators\Attachment;
use Espo\Classes\FieldValidators\LinkParentType;
use Espo\ORM\Entity;
class Related extends LinkParentType
{
public function checkValid(Entity $entity, string $field): bool
{
$typeValue = $entity->get($field . 'Type');
if ($typeValue === 'TemplateManager') {
return true;
}
return parent::checkValid($entity, $field);
}
}
@@ -29,7 +29,4 @@
namespace Espo\Classes\FieldValidators;
class ChecklistType extends ArrayType
{
}
class ChecklistType extends ArrayType {}
@@ -29,10 +29,21 @@
namespace Espo\Classes\FieldValidators;
use Espo\Core\Utils\Metadata;
use Espo\ORM\Entity;
use stdClass;
class EmailType
{
private Metadata $metadata;
private const DEFAULT_MAX_LENGTH = 255;
public function __construct(Metadata $metadata)
{
$this->metadata = $metadata;
}
public function checkRequired(Entity $entity, string $field): bool
{
if ($this->isNotEmpty($entity, $field)) {
@@ -71,6 +82,10 @@ class EmailType
}
foreach ($dataList as $item) {
if (!$item instanceof stdClass) {
return false;
}
if (empty($item->emailAddress)) {
continue;
}
@@ -85,6 +100,36 @@ class EmailType
return true;
}
public function checkMaxLength(Entity $entity, string $field): bool
{
/** @var ?string $value */
$value = $entity->get($field);
/** @var int $maxLength */
$maxLength = $this->metadata->get(['entityDefs', 'EmailAddress', 'fields', 'name', 'maxLength']) ??
self::DEFAULT_MAX_LENGTH;
if ($value && mb_strlen($value) > $maxLength) {
return false;
}
$dataList = $entity->get($field . 'Data');
if (!is_array($dataList)) {
return true;
}
foreach ($dataList as $item) {
$value = $item->emailAddress;
if ($value && mb_strlen($value) > $maxLength) {
return false;
}
}
return true;
}
protected function isNotEmpty(Entity $entity, string $field): bool
{
return $entity->has($field) && $entity->get($field) !== '' && $entity->get($field) !== null;
@@ -29,15 +29,90 @@
namespace Espo\Classes\FieldValidators;
use Espo\Core\Utils\Metadata;
use Espo\ORM\Defs;
use Espo\ORM\Entity;
class EnumType
{
private Metadata $metadata;
private Defs $defs;
private const DEFAULT_MAX_LENGTH = 255;
public function __construct(Metadata $metadata, Defs $defs)
{
$this->metadata = $metadata;
$this->defs = $defs;
}
public function checkRequired(Entity $entity, string $field): bool
{
return $this->isNotEmpty($entity, $field);
}
public function checkValid(Entity $entity, string $field): bool
{
if (!$entity->has($field)) {
return true;
}
$fieldDefs = $this->defs
->getEntity($entity->getEntityType())
->getField($field);
/** @var ?string $path */
$path = $fieldDefs->getParam('optionsPath');
/** @var string[]|null|false $optionList */
$optionList = $path ?
$this->metadata->get($path) :
$fieldDefs->getParam('options');
if ($optionList === null) {
return true;
}
// For bc.
if ($optionList === false) {
return true;
}
$optionList = array_map(
fn ($item) => $item === '' ? null : $item,
$optionList
);
$value = $entity->get($field);
// For bc.
// @todo Remove in v8.0.
if ($value === '') {
$value = null;
}
return in_array($value, $optionList);
}
public function checkMaxLength(Entity $entity, string $field, ?int $validationValue): bool
{
if (!$this->isNotEmpty($entity, $field)) {
return true;
}
$value = $entity->get($field);
$maxLength = $validationValue ?? self::DEFAULT_MAX_LENGTH;
if (mb_strlen($value) > $maxLength) {
return false;
}
return true;
}
protected function isNotEmpty(Entity $entity, string $field): bool
{
return $entity->has($field) && $entity->get($field) !== null;
@@ -37,7 +37,7 @@ class JsonArrayType
{
public function rawCheckArray(StdClass $data, string $field): bool
{
if (isset($data->$field) && $data->$field !== null && !is_array($data->$field)) {
if (isset($data->$field) && !is_array($data->$field)) {
return false;
}
@@ -29,20 +29,240 @@
namespace Espo\Classes\FieldValidators;
use Espo\Core\Utils\Metadata;
use Espo\ORM\Defs;
use Espo\ORM\Entity;
use Espo\Core\ORM\Entity as CoreEntity;
use stdClass;
class LinkMultipleType
{
private Metadata $metadata;
private Defs $defs;
private const COLUMN_TYPE_ENUM = 'enum';
private const COLUMN_TYPE_VARCHAR = 'varchar';
private const COLUMN_TYPE_BOOL = 'bool';
public function __construct(Metadata $metadata, Defs $defs)
{
$this->metadata = $metadata;
$this->defs = $defs;
}
public function checkRequired(Entity $entity, string $field): bool
{
if (!$entity instanceof CoreEntity) {
return false;
}
/** @var string[] */
/** @var string[] $idList */
$idList = $entity->getLinkMultipleIdList($field);
return count($idList) > 0;
}
public function checkPattern(Entity $entity, string $field): bool
{
/** @var ?mixed[] $idList */
$idList = $entity->get($field . 'Ids');
if ($idList === null || $idList === []) {
return true;
}
$pattern = $this->metadata->get(['app', 'regExpPatterns', 'id', 'pattern']);
if (!$pattern) {
return true;
}
$preparedPattern = '/^' . $pattern . '$/';
foreach ($idList as $id) {
if (!is_string($id)) {
return false;
}
if (!preg_match($preparedPattern, $id)) {
return false;
}
}
return true;
}
public function checkColumnsValid(Entity $entity, string $field): bool
{
if (!$entity instanceof CoreEntity) {
return true;
}
if (!$entity->has($field . 'Columns')) {
return true;
}
/** @var ?stdClass $columnsData */
$columnsData = $entity->get($field . 'Columns');
if ($columnsData === null) {
return true;
}
$entityDefs = $this->defs->getEntity($entity->getEntityType());
$fieldDefs = $entityDefs->getField($field);
if ($fieldDefs->isNotStorable()) {
return true;
}
/** @var ?array<string,string> $columnsMap */
$columnsMap = $fieldDefs->getParam('columns');
if ($columnsMap === null || $columnsMap === []) {
return true;
}
if (!$entityDefs->hasRelation($field)) {
return true;
}
$relationDefs = $entityDefs->getRelation($field);
if (!$relationDefs->hasForeignEntityType()) {
return true;
}
$foreignEntityType = $relationDefs->getForeignEntityType();
foreach (array_keys(get_object_vars($columnsData)) as $id) {
$itemData = $columnsData->$id;
if (!$itemData instanceof stdClass) {
return false;
}
foreach ($columnsMap as $column => $foreignField) {
if (!property_exists($itemData, $column)) {
continue;
}
$value = $itemData->$column;
$result = $this->checkColumnValue($foreignEntityType, $foreignField, $value);
if (!$result) {
return false;
}
}
}
return true;
}
/**
* @param mixed $value
*/
private function checkColumnValue(string $entityType, string $field, $value): bool
{
$fieldDefs = $this->defs
->getEntity($entityType)
->getField($field);
$type = $fieldDefs->getType();
if ($type === self::COLUMN_TYPE_VARCHAR) {
return $this->checkColumnValueVarchar($fieldDefs, $value);
}
if ($type === self::COLUMN_TYPE_ENUM) {
return $this->checkColumnValueEnum($fieldDefs, $value);
}
if ($type === self::COLUMN_TYPE_BOOL) {
return is_bool($value);
}
return true;
}
/**
* @param mixed $value
*/
private function checkColumnValueVarchar(Defs\FieldDefs $fieldDefs, $value): bool
{
if ($value === null) {
return true;
}
if (!is_string($value)) {
return false;
}
$maxLength = $fieldDefs->getParam('maxLength');
$pattern = $fieldDefs->getParam('pattern');
if ($maxLength && mb_strlen($value) > $maxLength) {
return false;
}
if ($pattern) {
if ($pattern[0] === '$') {
$patternName = substr($pattern, 1);
$pattern = $this->metadata
->get(['app', 'regExpPatterns', $patternName, 'pattern']) ??
$pattern;
}
$preparedPattern = '/^' . $pattern . '$/';
if (!preg_match($preparedPattern, $value)) {
return false;
}
}
return true;
}
/**
* @param mixed $value
*/
private function checkColumnValueEnum(Defs\FieldDefs $fieldDefs, $value): bool
{
if (!is_string($value) && $value !== null) {
return false;
}
/** @var ?string $path */
$path = $fieldDefs->getParam('optionsPath');
/** @var string[]|null|false $optionList */
$optionList = $path ?
$this->metadata->get($path) :
$fieldDefs->getParam('options');
if ($optionList === null) {
return true;
}
// For bc.
if ($optionList === false) {
return true;
}
$optionList = array_map(
fn ($item) => $item === '' ? null : $item,
$optionList
);
// For bc.
// @todo Remove in v8.0.
if ($value === '') {
$value = null;
}
return in_array($value, $optionList);
}
}
@@ -29,10 +29,21 @@
namespace Espo\Classes\FieldValidators;
use Espo\Core\Utils\Metadata;
use Espo\ORM\Defs;
use Espo\ORM\Entity;
class LinkParentType
{
private Metadata $metadata;
private Defs $defs;
public function __construct(Metadata $metadata, Defs $defs)
{
$this->metadata = $metadata;
$this->defs = $defs;
}
public function checkRequired(Entity $entity, string $field): bool
{
$idAttribute = $field . 'Id';
@@ -52,4 +63,46 @@ class LinkParentType
return true;
}
public function checkPattern(Entity $entity, string $field): bool
{
/** @var ?string $idValue */
$idValue = $entity->get($field . 'Id');
if ($idValue === null) {
return true;
}
$pattern = $this->metadata->get(['app', 'regExpPatterns', 'id', 'pattern']);
if (!$pattern) {
return true;
}
$preparedPattern = '/^' . $pattern . '$/';
return (bool) preg_match($preparedPattern, $idValue);
}
public function checkValid(Entity $entity, string $field): bool
{
/** @var ?string $typeValue */
$typeValue = $entity->get($field . 'Type');
if ($typeValue === null) {
return true;
}
/** @var ?string[] $entityTypeList */
$entityTypeList = $this->defs
->getEntity($entity->getEntityType())
->getField($field)
->getParam('entityList');
if ($entityTypeList !== null) {
return in_array($typeValue, $entityTypeList);
}
return (bool) $this->metadata->get(['entityDefs', $typeValue]);
}
}
@@ -29,10 +29,18 @@
namespace Espo\Classes\FieldValidators;
use Espo\Core\Utils\Metadata;
use Espo\ORM\Entity;
class LinkType
{
private Metadata $metadata;
public function __construct(Metadata $metadata)
{
$this->metadata = $metadata;
}
public function checkRequired(Entity $entity, string $field): bool
{
$idAttribute = $field . 'Id';
@@ -43,4 +51,23 @@ class LinkType
return $entity->get($idAttribute) !== null && $entity->get($idAttribute) !== '';
}
public function checkPattern(Entity $entity, string $field): bool
{
$idValue = $entity->get($field . 'Id');
if ($idValue === null) {
return true;
}
$pattern = $this->metadata->get(['app', 'regExpPatterns', 'id', 'pattern']);
if (!$pattern) {
return true;
}
$preparedPattern = '/^' . $pattern . '$/';
return (bool) preg_match($preparedPattern, $idValue);
}
}
@@ -29,7 +29,12 @@
namespace Espo\Classes\FieldValidators;
use Espo\ORM\Entity;
class MultiEnumType extends ArrayType
{
public function checkNoEmptyString(Entity $entity, string $field, ?bool $validationValue): bool
{
return parent::checkNoEmptyString($entity, $field, true);
}
}
@@ -0,0 +1,65 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM - Open Source CRM application.
* Copyright (C) 2014-2022 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
* Website: https://www.espocrm.com
*
* EspoCRM is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* EspoCRM is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU General Public License version 3.
*
* In accordance with Section 7(b) of the GNU General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
namespace Espo\Classes\FieldValidators;
use stdClass;
class PasswordType
{
private const DEFAULT_MAX_LENGTH = 255;
public function rawCheckValid(stdClass $data, string $field): bool
{
$value = $data->$field ?? null;
if ($value === null) {
return true;
}
return is_string($value);
}
public function rawCheckMaxLength(stdClass $data, string $field, ?int $validationValue): bool
{
$value = $data->$field ?? null;
if (!is_string($value)) {
return true;
}
$maxLength = $validationValue ?? self::DEFAULT_MAX_LENGTH;
if (mb_strlen($value) > $maxLength) {
return false;
}
return true;
}
}
@@ -29,10 +29,26 @@
namespace Espo\Classes\FieldValidators;
use Espo\Core\Utils\Metadata;
use Espo\ORM\Defs;
use Espo\ORM\Entity;
use stdClass;
class PhoneType
{
private Metadata $metadata;
private Defs $defs;
private const DEFAULT_MAX_LENGTH = 36;
public function __construct(Metadata $metadata, Defs $defs)
{
$this->metadata = $metadata;
$this->defs = $defs;
}
public function checkRequired(Entity $entity, string $field): bool
{
if ($this->isNotEmpty($entity, $field)) {
@@ -54,6 +70,132 @@ class PhoneType
return false;
}
public function checkValid(Entity $entity, string $field): bool
{
if ($this->isNotEmpty($entity, $field)) {
$number = $entity->get($field);
if (!$this->isValidNumber($number)) {
return false;
}
}
$dataList = $entity->get($field . 'Data');
if (!is_array($dataList)) {
return true;
}
foreach ($dataList as $item) {
if (!$item instanceof stdClass) {
return false;
}
$number = $item->phoneNumber ?? null;
$type = $item->type ?? null;
if (!$number) {
return false;
}
if (!$this->isValidNumber($number)) {
return false;
}
if (!$this->isValidType($entity->getEntityType(), $field, $type)) {
return false;
}
}
return true;
}
public function checkMaxLength(Entity $entity, string $field): bool
{
/** @var ?string $value */
$value = $entity->get($field);
/** @var int $maxLength */
$maxLength = $this->metadata->get(['entityDefs', 'PhoneNumber', 'fields', 'name', 'maxLength']) ??
self::DEFAULT_MAX_LENGTH;
if ($value && mb_strlen($value) > $maxLength) {
return false;
}
$dataList = $entity->get($field . 'Data');
if (!is_array($dataList)) {
return true;
}
foreach ($dataList as $item) {
$value = $item->phoneNumber;
if ($value && mb_strlen($value) > $maxLength) {
return false;
}
}
return true;
}
/**
* @param mixed $type
*/
private function isValidType(string $entityType, string $field, $type): bool
{
if ($type === null) {
// Will be stored with a default type.
return true;
}
if (!is_string($type)) {
return false;
}
/** @var string[]|null|false $typeList */
$typeList = $this->defs
->getEntity($entityType)
->getField($field)
->getParam('typeList');
if ($typeList === null) {
return true;
}
// For bc.
if ($typeList === false) {
return true;
}
return in_array($type, $typeList);
}
/**
* @param mixed $number
*/
private function isValidNumber($number): bool
{
if (!is_string($number)) {
return false;
}
if ($number === '') {
return false;
}
$pattern = $this->metadata->get(['app', 'regExpPatterns', 'phoneNumberLoose', 'pattern']);
if (!$pattern) {
return true;
}
$preparedPattern = '/^' . $pattern . '$/';
return (bool) preg_match($preparedPattern, $number);
}
protected function isNotEmpty(Entity $entity, string $field): bool
{
return $entity->has($field) && $entity->get($field) !== '' && $entity->get($field) !== null;
@@ -0,0 +1,63 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM - Open Source CRM application.
* Copyright (C) 2014-2022 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
* Website: https://www.espocrm.com
*
* EspoCRM is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* EspoCRM is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU General Public License version 3.
*
* In accordance with Section 7(b) of the GNU General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
namespace Espo\Classes\FieldValidators;
use Espo\ORM\Entity;
class TextType
{
public function checkRequired(Entity $entity, string $field): bool
{
return $this->isNotEmpty($entity, $field);
}
public function checkMaxLength(Entity $entity, string $field, int $validationValue): bool
{
if (!$this->isNotEmpty($entity, $field)) {
return true;
}
$value = $entity->get($field);
if (mb_strlen($value) > $validationValue) {
return false;
}
return true;
}
protected function isNotEmpty(Entity $entity, string $field): bool
{
return
$entity->has($field) &&
$entity->get($field) !== '' &&
$entity->get($field) !== null;
}
}
@@ -0,0 +1,72 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM - Open Source CRM application.
* Copyright (C) 2014-2022 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
* Website: https://www.espocrm.com
*
* EspoCRM is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* EspoCRM is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU General Public License version 3.
*
* In accordance with Section 7(b) of the GNU General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
namespace Espo\Classes\FieldValidators;
use Espo\Core\Utils\Metadata;
use Espo\ORM\Entity;
class UrlType
{
private Metadata $metadata;
private VarcharType $varcharType;
public function __construct(Metadata $metadata, VarcharType $varcharType)
{
$this->metadata = $metadata;
$this->varcharType = $varcharType;
}
public function checkRequired(Entity $entity, string $field): bool
{
return $this->varcharType->checkRequired($entity, $field);
}
public function checkMaxLength(Entity $entity, string $field, ?int $validationValue): bool
{
return $this->varcharType->checkMaxLength($entity, $field, $validationValue);
}
public function checkValid(Entity $entity, string $field): bool
{
$value = $entity->get($field);
if ($value === null) {
return true;
}
/** @var string $pattern */
$pattern = $this->metadata->get(['app', 'regExpPatterns', 'uriOptionalProtocol', 'pattern']);
$preparedPattern = '/^' . $pattern . '$/';
return (bool) preg_match($preparedPattern, $value);
}
}
@@ -29,30 +29,68 @@
namespace Espo\Classes\FieldValidators;
use Espo\Core\Utils\Metadata;
use Espo\ORM\Entity;
class VarcharType
{
private Metadata $metadata;
private const DEFAULT_MAX_LENGTH = 255;
public function __construct(Metadata $metadata)
{
$this->metadata = $metadata;
}
public function checkRequired(Entity $entity, string $field): bool
{
return $this->isNotEmpty($entity, $field);
}
public function checkMaxLength(Entity $entity, string $field, int $validationValue): bool
public function checkMaxLength(Entity $entity, string $field, ?int $validationValue): bool
{
if ($this->isNotEmpty($entity, $field)) {
$value = $entity->get($field);
if (!$this->isNotEmpty($entity, $field)) {
return true;
}
if (mb_strlen($value) > $validationValue) {
return false;
}
$value = $entity->get($field);
$maxLength = $validationValue ?? self::DEFAULT_MAX_LENGTH;
if (mb_strlen($value) > $maxLength) {
return false;
}
return true;
}
public function checkPattern(Entity $entity, string $field, ?string $validationValue): bool
{
if (!$this->isNotEmpty($entity, $field) || !$validationValue) {
return true;
}
$value = $entity->get($field);
$pattern = $validationValue;
if ($validationValue[0] === '$') {
$patternName = substr($validationValue, 1);
$pattern = $this->metadata->get(['app', 'regExpPatterns', $patternName, 'pattern']) ??
$pattern;
}
$preparedPattern = '/^' . $pattern . '$/';
return (bool) preg_match($preparedPattern, $value);
}
protected function isNotEmpty(Entity $entity, string $field): bool
{
return $entity->has($field) && $entity->get($field) !== '' && $entity->get($field) !== null;
return
$entity->has($field) &&
$entity->get($field) !== '' &&
$entity->get($field) !== null;
}
}
+24 -6
View File
@@ -30,6 +30,7 @@
namespace Espo\Classes\Jobs;
use Espo\Core\Record\ServiceContainer;
use Espo\Entities\Attachment;
use Espo\ORM\Repository\RDBRepository;
use Espo\Core\ORM\Entity as CoreEntity;
@@ -134,7 +135,7 @@ class Cleanup implements JobDataLess
foreach ($items as $name => $item) {
try {
/** @var class-string<\Espo\Core\Cleanup\Cleanup> */
/** @var class-string<\Espo\Core\Cleanup\Cleanup> $className */
$className = $item['className'];
$obj = $injectableFactory->create($className);
@@ -312,11 +313,15 @@ class Cleanup implements JobDataLess
$datetime->modify($period);
$collection = $this->entityManager
->getRDBRepository('Attachment')
->getRDBRepository(Attachment::ENTITY_TYPE)
->where([
'OR' => [
[
'role' => ['Export File', 'Mail Merge', 'Mass Pdf']
'role' => [
Attachment::ROLE_EXPORT_FILE,
'Mail Merge',
'Mass Pdf',
]
]
],
'createdAt<' => $datetime->format('Y-m-d H:i:s'),
@@ -357,7 +362,7 @@ class Cleanup implements JobDataLess
$datetimeFrom->modify($fromPeriod);
/** @var string[] */
/** @var string[] $scopeList */
$scopeList = array_keys($this->metadata->get(['scopes']));
foreach ($scopeList as $scope) {
@@ -450,6 +455,19 @@ class Cleanup implements JobDataLess
}
}
$isBeingUploadedCollection = $this->entityManager
->getRDBRepository('Attachment')
->sth()
->where([
'isBeingUploaded' => true,
'createdAt<' => $datetime->format('Y-m-d H:i:s'),
])
->find();
foreach ($isBeingUploadedCollection as $e) {
$this->entityManager->removeEntity($e);
}
$delete = $this->entityManager
->getQueryBuilder()
->delete()
@@ -554,7 +572,7 @@ class Cleanup implements JobDataLess
$fileManager = $this->fileManager;
if ($fileManager->exists($path)) {
/** @var string[] */
/** @var string[] $fileList */
$fileList = $fileManager->getFileList($path, false, '', false);
foreach ($fileList as $dirName) {
@@ -713,7 +731,7 @@ class Cleanup implements JobDataLess
$datetime = new DateTime($period);
/** @var string[] */
/** @var string[] $scopeList */
$scopeList = array_keys($this->metadata->get(['scopes']));
foreach ($scopeList as $scope) {
@@ -29,40 +29,54 @@
namespace Espo\Classes\MassAction\User;
use Espo\Core\{
MassAction\Actions\MassUpdate as MassUpdateOriginal,
MassAction\QueryBuilder,
MassAction\Params,
MassAction\Result,
MassAction\Data,
MassAction\MassAction,
Utils\File\Manager as FileManager,
DataManager,
Acl,
ORM\EntityManager,
Exceptions\Forbidden,
};
use Espo\Core\MassAction\Actions\MassUpdate as MassUpdateOriginal;
use Espo\Core\MassAction\QueryBuilder;
use Espo\Core\MassAction\Params;
use Espo\Core\MassAction\Result;
use Espo\Core\MassAction\Data;
use Espo\Core\MassAction\MassAction;
use Espo\Core\Utils\File\Manager as FileManager;
use Espo\Core\DataManager;
use Espo\Core\Acl;
use Espo\Core\Acl\Table;
use Espo\{
Entities\User,
ORM\Entity,
};
use Espo\Core\Exceptions\Forbidden;
use Espo\Entities\User;
use Espo\ORM\Entity;
use Espo\ORM\EntityManager;
use Espo\Tools\MassUpdate\Data as MassUpdateData;
class MassUpdate implements MassAction
{
private $massUpdateOriginal;
private MassUpdateOriginal $massUpdateOriginal;
private $queryBuilder;
private QueryBuilder $queryBuilder;
private $entityManager;
private EntityManager $entityManager;
private $acl;
private Acl $acl;
private $user;
private User $user;
private $fileManager;
private FileManager $fileManager;
private $dataManager;
private DataManager $dataManager;
private const PERMISSION = 'massUpdatePermission';
private const SYSTEM_USER_ID = 'system';
/** @var string[] */
private array $notAllowedAttributeList = [
'type',
'password',
'emailAddress',
'isAdmin',
'isSuperAdmin',
'isPortalUser',
];
public function __construct(
MassUpdateOriginal $massUpdateOriginal,
@@ -86,48 +100,54 @@ class MassUpdate implements MassAction
{
$entityType = $params->getEntityType();
if (!$this->acl->check($entityType, 'edit')) {
if (!$this->user->isAdmin()) {
throw new Forbidden("Only admin can mass-update users.");
}
if (!$this->acl->check($entityType, Table::ACTION_EDIT)) {
throw new Forbidden("No edit access for '{$entityType}'.");
}
if ($this->acl->get('massUpdatePermission') !== 'yes') {
if ($this->acl->get(self::PERMISSION) !== Table::LEVEL_YES) {
throw new Forbidden("No mass-update permission.");
}
if (
$data->has('type') ||
$data->has('password') ||
$data->has('emailAddress') ||
$data->has('isAdmin') ||
$data->has('isSuperAdmin') ||
$data->has('isPortalUser')
) {
throw new Forbidden("Not allowed fields.");
}
$massUpdateData = MassUpdateData::fromMassActionData($data);
$this->checkAccess($massUpdateData);
$query = $this->queryBuilder->build($params);
$collection = $this->entityManager
->getRDBRepository('User')
->getRDBRepository(User::ENTITY_TYPE)
->clone($query)
->sth()
->select(['id'])
->find();
foreach ($collection as $entity) {
$this->checkEntity($entity, $data);
$this->checkEntity($entity, $massUpdateData);
}
$result = $this->massUpdateOriginal->process($params, $data);
$this->afterProcess($result, $data);
$this->afterProcess($result, $massUpdateData);
return $result;
}
protected function checkEntity(Entity $entity, Data $data): void
private function checkAccess(MassUpdateData $data): void
{
if ($entity->getId() === 'system') {
foreach ($this->notAllowedAttributeList as $attribute) {
if ($data->has($attribute)) {
throw new Forbidden("Attribute '{$attribute}' not allowed for mass-update.");
}
}
}
private function checkEntity(Entity $entity, MassUpdateData $data): void
{
if ($entity->getId() === self::SYSTEM_USER_ID) {
throw new Forbidden("Can't update 'system' user.");
}
@@ -138,9 +158,9 @@ class MassUpdate implements MassAction
}
}
protected function afterProcess(Result $result, Data $dataWrapped): void
private function afterProcess(Result $result, MassUpdateData $dataWrapped): void
{
$data = $dataWrapped->getRaw();
$data = $dataWrapped->getValues();
if (
property_exists($data, 'rolesIds') ||
@@ -168,12 +188,12 @@ class MassUpdate implements MassAction
}
}
protected function clearRoleCache(string $id): void
private function clearRoleCache(string $id): void
{
$this->fileManager->removeFile('data/cache/application/acl/' . $id . '.php');
}
protected function clearPortalRolesCache(): void
private function clearPortalRolesCache(): void
{
$this->fileManager->removeInDir('data/cache/application/aclPortal');
}
@@ -0,0 +1,119 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM - Open Source CRM application.
* Copyright (C) 2014-2022 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
* Website: https://www.espocrm.com
*
* EspoCRM is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* EspoCRM is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU General Public License version 3.
*
* In accordance with Section 7(b) of the GNU General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
namespace Espo\Classes\RecordHooks\Event;
use Espo\Core\Record\Hook\UpdateHook;
use Espo\Core\Record\UpdateParams;
use Espo\Core\ORM\Entity as CoreEntity;
use Espo\Core\Field\DateTime;
use Espo\Core\Field\Date;
use Espo\ORM\Entity;
use Espo\ORM\Defs as OrmDefs;
/**
* @implements UpdateHook<CoreEntity>
*/
class BeforeUpdatePreserveDuration implements UpdateHook
{
private OrmDefs $ormDefs;
public function __construct(OrmDefs $ormDefs)
{
$this->ormDefs = $ormDefs;
}
public function process(Entity $entity, UpdateParams $params): void
{
/** @var CoreEntity $entity */
if (!$entity->isAttributeChanged('dateStart') && !$entity->isAttributeChanged('dateStartDate')) {
return;
}
if ($entity->isAttributeWritten('dateEnd') || $entity->isAttributeWritten('dateEndDate')) {
return;
}
$preserveDurationDisabled = $this->ormDefs
->getEntity($entity->getEntityType())
->getField('dateEnd')
->getParam('preserveDurationDisabled');
if ($preserveDurationDisabled) {
return;
}
$this->processDateTime($entity);
$this->processDate($entity);
}
private function processDateTime(Entity $entity): void
{
$dateStartFetchedString = $entity->getFetched('dateStart');
$dateStartString = $entity->get('dateStart');
$dateEndString = $entity->get('dateEnd');
if (!$dateStartFetchedString || !$dateStartString || !$dateEndString) {
return;
}
$dateStartFetched = DateTime::fromString($dateStartFetchedString);
$dateStart = DateTime::fromString($dateStartString);
$dateEnd = DateTime::fromString($dateEndString);
$diff = $dateStartFetched->diff($dateEnd);
$dateEndModified = $dateStart->add($diff);
$entity->set('dateEnd', $dateEndModified->getString());
}
private function processDate(Entity $entity): void
{
$dateStartFetchedString = $entity->getFetched('dateStartDate');
$dateStartString = $entity->get('dateStartDate');
$dateEndString = $entity->get('dateEndDate');
if (!$dateStartFetchedString || !$dateStartString || !$dateEndString) {
return;
}
$dateStartFetched = Date::fromString($dateStartFetchedString);
$dateStart = Date::fromString($dateStartString);
$dateEnd = Date::fromString($dateEndString);
$diff = $dateStartFetched->diff($dateEnd);
$dateEndModified = $dateStart->add($diff);
$entity->set('dateEndDate', $dateEndModified->getString());
}
}
@@ -61,7 +61,7 @@ class PortalOnlyAccount implements Filter
'emailUser.userId' => $this->user->getId(),
];
/** @var string[] */
/** @var string[] $accountIdList */
$accountIdList = $this->user->getLinkMultipleIdList('accounts');
if (count($accountIdList)) {
@@ -0,0 +1,49 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM - Open Source CRM application.
* Copyright (C) 2014-2022 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
* Website: https://www.espocrm.com
*
* EspoCRM is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* EspoCRM is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU General Public License version 3.
*
* In accordance with Section 7(b) of the GNU General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
namespace Espo\Classes\Select\ImportError\OrderItemConverters;
use Espo\Core\Select\Order\ItemConverter;
use Espo\Core\Select\Order\Item;
use Espo\ORM\Query\Part\OrderList;
use Espo\ORM\Query\Part\Order;
use Espo\ORM\Query\Part\Expression as Expr;
class ExportLineNumber implements ItemConverter
{
public function convert(Item $item): OrderList
{
return OrderList::create([
Order
::create(Expr::column('exportRowIndex'))
->withDirection($item->getOrder())
]);
}
}
@@ -0,0 +1,49 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM - Open Source CRM application.
* Copyright (C) 2014-2022 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
* Website: https://www.espocrm.com
*
* EspoCRM is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* EspoCRM is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU General Public License version 3.
*
* In accordance with Section 7(b) of the GNU General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
namespace Espo\Classes\Select\ImportError\OrderItemConverters;
use Espo\Core\Select\Order\ItemConverter;
use Espo\Core\Select\Order\Item;
use Espo\ORM\Query\Part\OrderList;
use Espo\ORM\Query\Part\Order;
use Espo\ORM\Query\Part\Expression as Expr;
class LineNumber implements ItemConverter
{
public function convert(Item $item): OrderList
{
return OrderList::create([
Order
::create(Expr::column('rowIndex'))
->withDirection($item->getOrder())
]);
}
}
@@ -50,7 +50,7 @@ class OnlyMyTeam implements Filter
public function apply(SelectBuilder $queryBuilder, OrGroupBuilder $orGroupBuilder): void
{
/** @var string[] */
/** @var string[] $teamIdList */
$teamIdList = $this->user->getLinkMultipleIdList('teams');
if (count($teamIdList) === 0) {
+8 -2
View File
@@ -106,14 +106,16 @@ class Admin
/**
* @todo Use Request.
*
* @param array<string,mixed> $params
* @param string $data
* @return array{
* id: string,
* version: string,
* }
* @throws Forbidden
* @throws \Espo\Core\Exceptions\Error
* @todo Use Request.
*
*/
public function postActionUploadUpgradePackage($params, $data): array
{
@@ -134,6 +136,10 @@ class Admin
];
}
/**
* @throws Forbidden
* @throws \Espo\Core\Exceptions\Error
*/
public function postActionRunUpgrade(Request $request): bool
{
$data = $request->getParsedBody();
@@ -52,6 +52,11 @@ class Attachment extends RecordBase
return parent::getActionList($request, $response);
}
/**
* @throws BadRequest
* @throws Forbidden
* @throws \Espo\Core\Exceptions\Error
*/
public function postActionGetAttachmentFromImageUrl(Request $request): stdClass
{
$data = $request->getParsedBody();
@@ -69,6 +74,12 @@ class Attachment extends RecordBase
->getValueMap();
}
/**
* @throws BadRequest
* @throws Forbidden
* @throws \Espo\Core\Exceptions\Error
* @throws \Espo\Core\Exceptions\NotFound
*/
public function postActionGetCopiedAttachment(Request $request): stdClass
{
$data = $request->getParsedBody();
@@ -86,6 +97,11 @@ class Attachment extends RecordBase
->getValueMap();
}
/**
* @throws BadRequest
* @throws Forbidden
* @throws \Espo\Core\Exceptions\NotFound
*/
public function getActionFile(Request $request, Response $response): void
{
$id = $request->getRouteParam('id');
@@ -103,6 +119,26 @@ class Attachment extends RecordBase
->setBody($fileData->stream);
}
/**
* @throws BadRequest
* @throws Forbidden
* @throws \Espo\Core\Exceptions\Error
* @throws \Espo\Core\Exceptions\NotFound
*/
public function postActionChunk(Request $request, Response $response): void
{
$id = $request->getRouteParam('id');
$body = $request->getBodyContents();
if (!$id || !$body) {
throw new BadRequest();
}
$this->getAttachmentService()->uploadChunk($id, $body);
$response->writeBody('true');
}
private function getAttachmentService(): Service
{
/** @var Service */
+82 -77
View File
@@ -29,22 +29,28 @@
namespace Espo\Controllers;
use Espo\Core\Acl\Table;
use Espo\Core\Exceptions\BadRequest;
use Espo\Core\Exceptions\Error;
use Espo\Core\Exceptions\Forbidden;
use Espo\Core\Exceptions\NotFound;
use Espo\Core\Controllers\Record;
use Espo\Core\Api\Request;
use Espo\Entities\Email as EmailEntity;
use Espo\Services\Email as Service;
use Espo\Services\EmailTemplate as EmailTemplateService;
use Espo\Core\Utils\Crypt;
use stdClass;
class Email extends Record
{
/**
* @throws BadRequest
* @throws Forbidden
* @throws NotFound
*/
public function postActionGetCopiedAttachments(Request $request): stdClass
{
$data = $request->getParsedBody();
@@ -61,89 +67,73 @@ class Email extends Record
}
/**
* @todo Move to service.
* @throws Forbidden
* @throws NotFound
* @throws Error
* @throws BadRequest
*/
public function postActionSendTestEmail(Request $request): bool
{
$data = $request->getParsedBody();
if (!$this->acl->checkScope('Email')) {
if (!$this->acl->checkScope(EmailEntity::ENTITY_TYPE)) {
throw new Forbidden();
}
if (is_null($data->password)) {
if ($data->type == 'preferences') {
if (!$this->user->isAdmin() && $data->id !== $this->user->id) {
throw new Forbidden();
}
$data = get_object_vars($request->getParsedBody());
$preferences = $this->getEntityManager()->getEntity('Preferences', $data->id);
$allowedParamList = [
'type',
'id',
'username',
'password',
'auth',
'authMechanism',
'userId',
'fromAddress',
'fromName',
'server',
'port',
'security',
'emailAddress',
];
if (!$preferences) {
throw new NotFound();
}
if (is_null($data->password)) {
$data->password = $this->getCrypt()->decrypt($preferences->get('smtpPassword'));
}
}
else if ($data->type == 'emailAccount') {
if (!$this->acl->checkScope('EmailAccount')) {
throw new Forbidden();
}
if (!empty($data->id)) {
$emailAccount = $this->getEntityManager()
->getEntity('EmailAccount', $data->id);
if (!$emailAccount) {
throw new NotFound();
}
if (!$this->user->isAdmin()) {
if ($emailAccount->get('assignedUserId') !== $this->user->id) {
throw new Forbidden();
}
}
if (is_null($data->password)) {
$data->password = $this->getCrypt()->decrypt($emailAccount->get('smtpPassword'));
}
}
}
else if ($data->type == 'inboundEmail') {
if (!$this->user->isAdmin()) {
throw new Forbidden();
}
if (!empty($data->id)) {
$emailAccount = $this->getEntityManager()->getEntity('InboundEmail', $data->id);
if (!$emailAccount) {
throw new NotFound();
}
if (is_null($data->password)) {
$data->password = $this->getCrypt()->decrypt($emailAccount->get('smtpPassword'));
}
}
}
else {
if (!$this->user->isAdmin()) {
throw new Forbidden();
}
if (is_null($data->password)) {
$data->password = $this->getConfig()->get('smtpPassword');
}
foreach (array_keys($data) as $key) {
if (!in_array($key, $allowedParamList)) {
throw new BadRequest("Not allowed parameter `{$key}`.");
}
}
$this->getEmailService()->sendTestEmail(get_object_vars($data));
$emailAddress = $data['emailAddress'] ?? null;
if (!is_string($emailAddress)) {
throw new BadRequest("No email address.");
}
/**
* @var array{
* type?: ?string,
* id?: ?string,
* username?: ?string,
* password?: ?string,
* auth?: bool,
* authMechanism?: ?string,
* userId?: ?string,
* fromAddress?: ?string,
* fromName?: ?string,
* server: string,
* port: int,
* security: string,
* emailAddress: string,
* } $data
*/
$this->getEmailService()->sendTestEmail($data);
return true;
}
/**
* @throws BadRequest
*/
public function postActionMarkAsRead(Request $request): bool
{
$data = $request->getParsedBody();
@@ -165,6 +155,9 @@ class Email extends Record
return true;
}
/**
* @throws BadRequest
*/
public function postActionMarkAsNotRead(Request $request): bool
{
$data = $request->getParsedBody();
@@ -193,6 +186,9 @@ class Email extends Record
return true;
}
/**
* @throws BadRequest
*/
public function postActionMarkAsImportant(Request $request): bool
{
$data = $request->getParsedBody();
@@ -214,6 +210,9 @@ class Email extends Record
return true;
}
/**
* @throws BadRequest
*/
public function postActionMarkAsNotImportant(Request $request): bool
{
$data = $request->getParsedBody();
@@ -235,6 +234,9 @@ class Email extends Record
return true;
}
/**
* @throws BadRequest
*/
public function postActionMoveToTrash(Request $request): bool
{
$data = $request->getParsedBody();
@@ -256,6 +258,9 @@ class Email extends Record
return true;
}
/**
* @throws BadRequest
*/
public function postActionRetrieveFromTrash(Request $request): bool
{
$data = $request->getParsedBody();
@@ -282,6 +287,9 @@ class Email extends Record
return $this->getEmailService()->getFoldersNotReadCounts();
}
/**
* @throws BadRequest
*/
public function postActionMoveToFolder(Request $request): bool
{
$data = $request->getParsedBody();
@@ -305,9 +313,12 @@ class Email extends Record
return true;
}
/**
* @throws Forbidden
*/
public function getActionGetInsertFieldData(Request $request): stdClass
{
if (!$this->acl->checkScope('Email', 'create')) {
if (!$this->acl->checkScope(EmailEntity::ENTITY_TYPE, Table::ACTION_CREATE)) {
throw new Forbidden();
}
@@ -329,10 +340,4 @@ class Email extends Record
/** @var EmailTemplateService */
return $this->getServiceFactory()->create('EmailTemplate');
}
private function getCrypt(): Crypt
{
/** @var Crypt */
return $this->getContainer()->get('crypt');
}
}
@@ -29,6 +29,8 @@
namespace Espo\Controllers;
use Espo\Core\Exceptions\Error;
use Espo\Core\Exceptions\Forbidden;
use Espo\Core\Mail\Account\PersonalAccount\Service;
use Espo\Core\Mail\Account\Storage\Params as StorageParams;
@@ -44,6 +46,8 @@ class EmailAccount extends Record
/**
* @return string[]
* @throws Forbidden
* @throws Error
*/
public function postActionGetFolders(Request $request): array
{
@@ -63,6 +67,10 @@ class EmailAccount extends Record
return $this->getEmailAccountService()->getFolderList($params);
}
/**
* @throws Error
* @throws Forbidden
*/
public function postActionTestConnection(Request $request): bool
{
$data = $request->getParsedBody();
@@ -45,6 +45,8 @@ class EmailAddress extends RecordBase
/**
* @return array<int,array<string,mixed>>
* @throws Forbidden
* @throws BadRequest
*/
public function actionSearchInAddressBook(Request $request): array
{
@@ -66,7 +66,7 @@ class ExternalAccount extends RecordBase
$entity->get('enabled') &&
$this->metadata->get('integrations.' . $entity->getId() .'.allowUserAccounts')
) {
/** @var string */
/** @var string $id */
$id = $entity->getId();
$userAccountAclScope = $this->metadata
@@ -118,9 +118,13 @@ class ExternalAccount extends RecordBase
public function getActionRead(Request $request, Response $response): stdClass
{
/** @var string */
/** @var string $id */
$id = $request->getRouteParam('id');
if ($id === '') {
throw new BadRequest();
}
return $this->getRecordService()
->read($id, ReadParams::create())
->getValueMap();
@@ -128,7 +132,7 @@ class ExternalAccount extends RecordBase
public function putActionUpdate(Request $request, Response $response): stdClass
{
/** @var string */
/** @var string $id */
$id = $request->getRouteParam('id');
$data = $request->getParsedBody();
+25 -3
View File
@@ -35,12 +35,12 @@ use Espo\{
};
use Espo\Core\{
Exceptions\Conflict,
Exceptions\Error,
Exceptions\Forbidden,
Exceptions\BadRequest,
Api\Request,
DataManager,
};
DataManager};
class FieldManager
{
@@ -50,6 +50,9 @@ class FieldManager
private $fieldManagerTool;
/**
* @throws Forbidden
*/
public function __construct(User $user, DataManager $dataManager, FieldManagerTool $fieldManagerTool)
{
$this->user = $user;
@@ -59,6 +62,9 @@ class FieldManager
$this->checkControllerAccess();
}
/**
* @throws Forbidden
*/
protected function checkControllerAccess(): void
{
if (!$this->user->isAdmin()) {
@@ -68,6 +74,8 @@ class FieldManager
/**
* @return array<string,mixed>
* @throws BadRequest
* @throws Error
*/
public function getActionRead(Request $request): array
{
@@ -83,6 +91,9 @@ class FieldManager
/**
* @return array<string,mixed>
* @throws BadRequest
* @throws Conflict
* @throws Error
*/
public function postActionCreate(Request $request): array
{
@@ -113,6 +124,8 @@ class FieldManager
/**
* @return array<string,mixed>
* @throws BadRequest
* @throws Error
*/
public function patchActionUpdate(Request $request): array
{
@@ -121,6 +134,8 @@ class FieldManager
/**
* @return array<string,mixed>
* @throws BadRequest
* @throws Error
*/
public function putActionUpdate(Request $request): array
{
@@ -146,6 +161,10 @@ class FieldManager
return $fieldManagerTool->read($scope, $name);
}
/**
* @throws BadRequest
* @throws Error
*/
public function deleteActionDelete(Request $request): bool
{
$scope = $request->getRouteParam('scope');
@@ -162,6 +181,10 @@ class FieldManager
return $result;
}
/**
* @throws BadRequest
* @throws Error
*/
public function postActionResetToDefault(Request $request): bool
{
$data = $request->getParsedBody();
@@ -173,7 +196,6 @@ class FieldManager
$this->fieldManagerTool->resetToDefault($data->scope, $data->name);
$this->dataManager->clearCache();
$this->dataManager->rebuildMetadata();
return true;
+19
View File
@@ -144,6 +144,25 @@ class Import extends Record
return true;
}
/**
* @throws BadRequest
* @throws \Espo\Core\Exceptions\NotFound
*/
public function postActionExportErrors(Request $request): stdClass
{
$id = $request->getParsedBody()->id ?? null;
if (!$id) {
throw new BadRequest("No `id`.");
}
$attachmentId = $this->getImportService()->exportErrors($id);
return (object) [
'attachmentId' => $attachmentId,
];
}
public function putActionUpdate(Request $request, Response $response): stdClass
{
throw new Forbidden();
@@ -0,0 +1,36 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM - Open Source CRM application.
* Copyright (C) 2014-2022 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
* Website: https://www.espocrm.com
*
* EspoCRM is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* EspoCRM is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU General Public License version 3.
*
* In accordance with Section 7(b) of the GNU General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
namespace Espo\Controllers;
use Espo\Core\Controllers\Record;
class ImportError extends Record
{
}
@@ -44,6 +44,7 @@ class InboundEmail extends Record
/**
* @return string[]
* @throws \Espo\Core\Exceptions\Error
*/
public function postActionGetFolders(Request $request): array
{
+2 -2
View File
@@ -56,7 +56,7 @@ class Integration
public function getActionRead(Request $request): stdClass
{
/** @var string */
/** @var string $id */
$id = $request->getRouteParam('id');
$entity = $this->service->read($id);
@@ -66,7 +66,7 @@ class Integration
public function putActionUpdate(Request $request): stdClass
{
/** @var string */
/** @var string $id */
$id = $request->getRouteParam('id');
$data = $request->getParsedBody();
+1 -1
View File
@@ -53,7 +53,7 @@ class Kanban
public function getActionGetData(Request $request): stdClass
{
/** @var string */
/** @var string $entityType */
$entityType = $request->getRouteParam('entityType');
$searchParams = $this->searchParamsFetcher->fetch($request);
+18
View File
@@ -29,10 +29,12 @@
namespace Espo\Controllers;
use Espo\Core\Exceptions\Error;
use Espo\Core\Exceptions\Forbidden;
use Espo\Core\Exceptions\BadRequest;
use Espo\Core\Api\Request;
use Espo\Core\Exceptions\NotFound;
use Espo\Services\Layout as Service;
use Espo\Entities\User;
@@ -50,6 +52,10 @@ class Layout
/**
* @return mixed
* @throws Forbidden
* @throws NotFound
* @throws Error
* @throws BadRequest
*/
public function getActionRead(Request $request)
{
@@ -67,6 +73,10 @@ class Layout
/**
* @return mixed
* @throws Forbidden
* @throws BadRequest
* @throws NotFound
* @throws Error
*/
public function putActionUpdate(Request $request)
{
@@ -95,6 +105,10 @@ class Layout
/**
* @return mixed
* @throws Forbidden
* @throws BadRequest
* @throws NotFound
* @throws Error
*/
public function postActionResetToDefault(Request $request)
{
@@ -113,6 +127,10 @@ class Layout
/**
* @return mixed
* @throws BadRequest
* @throws Forbidden
* @throws NotFound
* @throws Error
*/
public function getActionGetOriginal(Request $request)
{
@@ -99,6 +99,7 @@ class LeadCapture extends Record
/**
* @return stdClass[]
* @throws Forbidden
*/
public function getActionSmtpAccountDataList(): array
{
@@ -119,6 +119,7 @@ class MassAction
/**
* @return array<string,mixed>
* @throws BadRequest
*/
private function prepareMassActionParams(stdClass $data): array
{
@@ -149,6 +150,9 @@ class MassAction
throw new BadRequest("Bad search params for mass action.");
}
/**
* @throws Error
*/
private function convertResult(ServiceResult $serviceResult): stdClass
{
if (!$serviceResult->hasResult()) {
@@ -49,6 +49,7 @@ class Metadata extends Base
/**
* @return mixed
* @throws Forbidden
*/
public function getActionGet(Request $request)
{
+5 -5
View File
@@ -30,9 +30,9 @@
namespace Espo\Core;
use Espo\Core\{
Acl\GlobalRestriction,
Acl\Table,
Acl\Exceptions\NotImplemented,
};
Acl\Exceptions\NotImplemented};
use Espo\ORM\Entity;
use Espo\Entities\User;
@@ -283,7 +283,7 @@ class Acl
/**
* Get a restricted field list for a specific scope by a restriction type.
*
* @param string|string[] $type
* @param GlobalRestriction::TYPE_*|array<int,GlobalRestriction::TYPE_*> $type
* @return string[]
*/
public function getScopeRestrictedFieldList(string $scope, $type): array
@@ -294,7 +294,7 @@ class Acl
/**
* Get a restricted attribute list for a specific scope by a restriction type.
*
* @param string|string[] $type
* @param GlobalRestriction::TYPE_*|array<int,GlobalRestriction::TYPE_*> $type
* @return string[]
*/
public function getScopeRestrictedAttributeList(string $scope, $type): array
@@ -305,7 +305,7 @@ class Acl
/**
* Get a restricted link list for a specific scope by a restriction type.
*
* @param string|string[] $type
* @param GlobalRestriction::TYPE_*|array<int,GlobalRestriction::TYPE_*> $type
* @return string[]
*/
public function getScopeRestrictedLinkList(string $scope, $type): array
@@ -85,7 +85,7 @@ class AccessCheckerFactory
*/
private function getClassName(string $scope): string
{
/** @var ?class-string<AccessChecker> */
/** @var ?class-string<AccessChecker> $className1 */
$className1 = $this->metadata->get(['aclDefs', $scope, 'accessCheckerClassName']);
if ($className1) {
@@ -0,0 +1,170 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM - Open Source CRM application.
* Copyright (C) 2014-2022 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
* Website: https://www.espocrm.com
*
* EspoCRM is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* EspoCRM is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU General Public License version 3.
*
* In accordance with Section 7(b) of the GNU General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
namespace Espo\Core\Acl\AccessChecker\AccessCheckers;
use Espo\Entities\User;
use Espo\ORM\Entity;
use Espo\Core\Utils\Metadata;
use Espo\Core\Acl\DefaultAccessChecker;
use Espo\Core\Acl\Traits\DefaultAccessCheckerDependency;
use Espo\Core\Acl\AccessEntityCreateChecker;
use Espo\Core\Acl\AccessEntityReadChecker;
use Espo\Core\Acl\AccessEntityEditChecker;
use Espo\Core\Acl\AccessEntityDeleteChecker;
use Espo\Core\Acl\AccessEntityStreamChecker;
use Espo\Core\Acl\ScopeData;
use Espo\ORM\EntityManager;
use LogicException;
/**
* Access is determined by access to a foreign entity.
*
* @implements AccessEntityCreateChecker<Entity>
* @implements AccessEntityReadChecker<Entity>
* @implements AccessEntityEditChecker<Entity>
* @implements AccessEntityDeleteChecker<Entity>
* @implements AccessEntityStreamChecker<Entity>
*/
class Foreign implements
AccessEntityCreateChecker,
AccessEntityReadChecker,
AccessEntityEditChecker,
AccessEntityDeleteChecker,
AccessEntityStreamChecker
{
use DefaultAccessCheckerDependency;
private Metadata $metadata;
private EntityManager $entityManager;
public function __construct(
Metadata $metadata,
DefaultAccessChecker $defaultAccessChecker,
EntityManager $entityManager
) {
$this->metadata = $metadata;
$this->defaultAccessChecker = $defaultAccessChecker;
$this->entityManager = $entityManager;
}
private function getForeignEntity(Entity $entity): ?Entity
{
$entityType = $entity->getEntityType();
$link = $this->metadata->get(['aclDefs', $entityType, 'link']);
if (!$link) {
throw new LogicException("No `link` in aclDefs for {$entityType}.");
}
if ($entity->isNew()) {
$foreignEntityType = $this->entityManager
->getDefs()
->getEntity($entityType)
->getRelation($link)
->getForeignEntityType();
/** @var ?string $id */
$id = $entity->get($link . 'Id');
if (!$id) {
return null;
}
return $this->entityManager->getEntityById($foreignEntityType, $id);
}
return $this->entityManager
->getRDBRepository($entityType)
->getRelation($entity, $link)
->findOne();
}
public function checkEntityCreate(User $user, Entity $entity, ScopeData $data): bool
{
$foreign = $this->getForeignEntity($entity);
if (!$foreign) {
return false;
}
return $this->defaultAccessChecker->checkEntityCreate($user, $entity, $data);
}
public function checkEntityRead(User $user, Entity $entity, ScopeData $data): bool
{
$foreign = $this->getForeignEntity($entity);
if (!$foreign) {
return false;
}
return $this->defaultAccessChecker->checkEntityRead($user, $entity, $data);
}
public function checkEntityEdit(User $user, Entity $entity, ScopeData $data): bool
{
$foreign = $this->getForeignEntity($entity);
if (!$foreign) {
return false;
}
return $this->defaultAccessChecker->checkEntityEdit($user, $entity, $data);
}
public function checkEntityDelete(User $user, Entity $entity, ScopeData $data): bool
{
$foreign = $this->getForeignEntity($entity);
if (!$foreign) {
return false;
}
return $this->defaultAccessChecker->checkEntityDelete($user, $entity, $data);
}
public function checkEntityStream(User $user, Entity $entity, ScopeData $data): bool
{
$foreign = $this->getForeignEntity($entity);
if (!$foreign) {
return false;
}
return $this->defaultAccessChecker->checkEntityStream($user, $entity, $data);
}
}
@@ -75,7 +75,7 @@ class AssignmentCheckerFactory
*/
private function getClassName(string $scope): string
{
/** @var ?class-string<AssignmentChecker<\Espo\ORM\Entity>> */
/** @var ?class-string<AssignmentChecker<\Espo\ORM\Entity>> $className */
$className = $this->metadata->get(['aclDefs', $scope, 'assignmentCheckerClassName']);
if ($className) {
@@ -228,7 +228,7 @@ class DefaultAssignmentChecker implements AssignmentChecker
return true;
}
/** @var string[] */
/** @var string[] $userTeamIdList */
$userTeamIdList = $user->getLinkMultipleIdList(self::FIELD_TEAMS);
foreach ($newIdList as $id) {
@@ -331,7 +331,7 @@ class DefaultAssignmentChecker implements AssignmentChecker
private function isPermittedAssignedUsersLevelNo(User $user, CoreEntity $entity): bool
{
/** @var string[] */
/** @var string[] $userIdList */
$userIdList = $entity->getLinkMultipleIdList(self::FIELD_ASSIGNED_USERS);
$fetchedAssignedUserIdList = $entity->getFetched(self::ATTR_ASSIGNED_USERS_IDS);
@@ -351,12 +351,12 @@ class DefaultAssignmentChecker implements AssignmentChecker
private function isPermittedAssignedUsersLevelTeam(User $user, CoreEntity $entity): bool
{
/** @var string[] */
/** @var string[] $userIdList */
$userIdList = $entity->getLinkMultipleIdList(self::FIELD_ASSIGNED_USERS);
$fetchedAssignedUserIdList = $entity->getFetched(self::ATTR_ASSIGNED_USERS_IDS);
/** @var string[] */
/** @var string[] $teamIdList */
$teamIdList = $user->getLinkMultipleIdList(self::FIELD_TEAMS);
foreach ($userIdList as $userId) {
@@ -85,7 +85,7 @@ class DefaultOwnershipChecker implements OwnershipOwnChecker, OwnershipTeamCheck
return false;
}
/** @var string[] */
/** @var string[] $userTeamIdList */
$userTeamIdList = $user->getLinkMultipleIdList(self::FIELD_TEAMS);
if (
@@ -33,7 +33,6 @@ use Espo\Core\{
Utils\Metadata,
Utils\DataCache,
Utils\FieldUtil,
Utils\Log,
Utils\Config,
};
@@ -43,68 +42,75 @@ use stdClass;
* Lists of restricted fields can be obtained from here. Restricted fields
* are specified in metadata > entityAcl.
*/
class GlobalRestricton
class GlobalRestriction
{
/** Totally forbidden. */
public const TYPE_FORBIDDEN = 'forbidden';
/** Reading forbidden, writing allowed. */
public const TYPE_INTERNAL = 'internal';
/** Forbidden for non-admin users. */
public const TYPE_ONLY_ADMIN = 'onlyAdmin';
/** Read-only for all users. */
public const TYPE_READ_ONLY = 'readOnly';
/** Read-only for non-admin users. */
public const TYPE_NON_ADMIN_READ_ONLY = 'nonAdminReadOnly';
/**
* @var string[]
* @var array<int,self::TYPE_*>
*/
protected $fieldTypeList = [
'forbidden', // totally forbidden
'internal', // reading forbidden, writing allowed
'onlyAdmin', // forbidden for non admin users
'readOnly', // read-only for all users
'nonAdminReadOnly' // read-only for non-admin users
private $fieldTypeList = [
self::TYPE_FORBIDDEN,
self::TYPE_INTERNAL,
self::TYPE_ONLY_ADMIN,
self::TYPE_READ_ONLY,
self::TYPE_NON_ADMIN_READ_ONLY,
];
/**
* @var string[]
* @var array<int,self::TYPE_*>
*/
protected $linkTypeList = [
'forbidden', // totally forbidden
'internal', // reading forbidden, writing allowed
'onlyAdmin', // forbidden for non admin users
'readOnly', // read-only for all users
'nonAdminReadOnly' // read-only for non-admin users
private $linkTypeList = [
self::TYPE_FORBIDDEN,
self::TYPE_INTERNAL,
self::TYPE_ONLY_ADMIN,
self::TYPE_READ_ONLY,
self::TYPE_NON_ADMIN_READ_ONLY,
];
/**
* Types that should also be taken from entityDefs.
* @var array<int,self::TYPE_*>
*/
private array $entityDefsTypeList = [
self::TYPE_READ_ONLY,
];
private ?stdClass $data = null;
protected string $cacheKey = 'entityAcl';
private string $cacheKey = 'entityAcl';
private Metadata $metadata;
private DataCache $dataCache;
private FieldUtil $fieldUtil;
private Log $log;
public function __construct(
Metadata $metadata,
DataCache $dataCache,
FieldUtil $fieldUtil,
Log $log,
Config $config
) {
$this->metadata = $metadata;
$this->dataCache = $dataCache;
$this->fieldUtil = $fieldUtil;
$this->log = $log;
$useCache = $config->get('useCache');
if ($useCache && $this->dataCache->has($this->cacheKey)) {
/** @var stdClass */
/** @var stdClass $cachedData */
$cachedData = $this->dataCache->get($this->cacheKey);
$this->data = $cachedData;
@@ -121,25 +127,25 @@ class GlobalRestricton
}
}
protected function storeCacheFile(): void
private function storeCacheFile(): void
{
assert($this->data !== null);
$this->dataCache->store($this->cacheKey, $this->data);
}
protected function buildData(): void
private function buildData(): void
{
/** @var string[] */
$scopeList = array_keys($this->metadata->get(['entityDefs'], []));
/** @var string[] $scopeList */
$scopeList = array_keys($this->metadata->get(['entityDefs']) ?? []);
$data = (object) [];
foreach ($scopeList as $scope) {
/** @var string[] */
$fieldList = array_keys($this->metadata->get(['entityDefs', $scope, 'fields'], []));
/** @var string[] */
$linkList = array_keys($this->metadata->get(['entityDefs', $scope, 'links'], []));
/** @var string[] $fieldList */
$fieldList = array_keys($this->metadata->get(['entityDefs', $scope, 'fields']) ?? []);
/** @var string[] $linkList */
$linkList = array_keys($this->metadata->get(['entityDefs', $scope, 'links']) ?? []);
$isNotEmpty = false;
@@ -154,16 +160,24 @@ class GlobalRestricton
$resultAttributeList = [];
foreach ($fieldList as $field) {
if ($this->metadata->get(['entityAcl', $scope, 'fields', $field, $type])) {
$isNotEmpty = true;
$value = $this->metadata->get(['entityAcl', $scope, 'fields', $field, $type]);
$resultFieldList[] = $field;
if (!$value && in_array($type, $this->entityDefsTypeList)) {
$value = $this->metadata->get(['entityDefs', $scope, 'fields', $field, $type]);
}
$fieldAttributeList = $this->fieldUtil->getAttributeList($scope, $field);
if (!$value) {
continue;
}
foreach ($fieldAttributeList as $attribute) {
$resultAttributeList[] = $attribute;
}
$isNotEmpty = true;
$resultFieldList[] = $field;
$fieldAttributeList = $this->fieldUtil->getAttributeList($scope, $field);
foreach ($fieldAttributeList as $attribute) {
$resultAttributeList[] = $attribute;
}
}
@@ -175,12 +189,21 @@ class GlobalRestricton
$resultLinkList = [];
foreach ($linkList as $link) {
if ($this->metadata->get(['entityAcl', $scope, 'links', $link, $type])) {
$isNotEmpty = true;
$value = $this->metadata->get(['entityAcl', $scope, 'links', $link, $type]);
$resultLinkList[] = $link;
if (!$value && in_array($type, $this->entityDefsTypeList)) {
$value = $this->metadata->get(['entityDefs', $scope, 'links', $link, $type]);
}
if (!$value) {
continue;
}
$isNotEmpty = true;
$resultLinkList[] = $link;
}
$scopeData->links->$type = $resultLinkList;
}
@@ -193,6 +216,7 @@ class GlobalRestricton
}
/**
* @param self::TYPE_* $type
* @return string[]
*/
public function getScopeRestrictedFieldList(string $scope, string $type): array
@@ -215,6 +239,7 @@ class GlobalRestricton
}
/**
* @param self::TYPE_* $type
* @return string[]
*/
public function getScopeRestrictedAttributeList(string $scope, string $type): array
@@ -237,6 +262,7 @@ class GlobalRestricton
}
/**
* @param self::TYPE_* $type
* @return string[]
*/
public function getScopeRestrictedLinkList(string $scope, string $type): array
+1 -1
View File
@@ -95,7 +95,7 @@ class Map
$this->cacheKey = $cacheKeyProvider->get();
if ($this->config->get('useCache') && $this->dataCache->has($this->cacheKey)) {
/** @var stdClass */
/** @var stdClass $cachedData */
$cachedData = $this->dataCache->get($this->cacheKey);
$this->data = $cachedData;
@@ -80,7 +80,7 @@ class OwnershipCheckerFactory
*/
private function getClassName(string $scope): string
{
/** @var ?class-string<OwnershipChecker> */
/** @var ?class-string<OwnershipChecker> $className */
$className = $this->metadata->get(['aclDefs', $scope, 'ownershipCheckerClassName']);
if ($className) {
@@ -53,7 +53,7 @@ class DefaultRoleListProvider implements RoleListProvider
{
$roleList = [];
/** @var iterable<RoleEntity> */
/** @var iterable<RoleEntity> $userRoleList */
$userRoleList = $this->entityManager
->getRDBRepository('User')
->getRelation($this->user, 'roles')
@@ -63,14 +63,14 @@ class DefaultRoleListProvider implements RoleListProvider
$roleList[] = $role;
}
/** @var iterable<\Espo\Entities\Team> */
/** @var iterable<\Espo\Entities\Team> $teamList */
$teamList = $this->entityManager
->getRDBRepository('User')
->getRelation($this->user, 'teams')
->find();
foreach ($teamList as $team) {
/** @var iterable<RoleEntity> */
/** @var iterable<RoleEntity> $teamRoleList */
$teamRoleList = $this->entityManager
->getRDBRepository('Team')
->getRelation($team, 'roles')
@@ -146,7 +146,7 @@ class DefaultTable implements Table
$this->cacheKey = $cacheKeyProvider->get();
if ($config->get('useCache') && $dataCache->has($this->cacheKey)) {
/** @var stdClass */
/** @var stdClass $cachedData */
$cachedData = $dataCache->get($this->cacheKey);
$this->data = $cachedData;
@@ -248,6 +248,7 @@ class DefaultTable implements Table
$fieldTable = (object) [];
$this->applyHighest($aclTable, $fieldTable);
$this->applyDisabled($aclTable, $fieldTable);
$this->applyAdminMandatory($aclTable, $fieldTable);
}
@@ -526,10 +527,6 @@ class DefaultTable implements Table
protected function applyDisabled(stdClass &$table, stdClass &$fieldTable): void
{
if ($this->user->isAdmin()) {
return;
}
foreach ($this->getScopeList() as $scope) {
if ($this->metadata->get(['scopes', $scope, 'disabled'])) {
$table->$scope = false;
+24 -24
View File
@@ -37,7 +37,7 @@ use Espo\ORM\EntityManager;
use Espo\Core\{
Acl,
Acl\GlobalRestricton,
Acl\GlobalRestriction,
Acl\OwnerUserFieldProvider,
Acl\Table\TableFactory,
Acl\Table,
@@ -140,9 +140,9 @@ class AclManager
private $mapFactory;
/**
* @var GlobalRestricton
* @var GlobalRestriction
*/
protected $globalRestricton;
protected $globalRestriction;
/**
* @var OwnerUserFieldProvider
@@ -159,7 +159,7 @@ class AclManager
OwnershipCheckerFactory $ownershipCheckerFactory,
TableFactory $tableFactory,
MapFactory $mapFactory,
GlobalRestricton $globalRestricton,
GlobalRestriction $globalRestriction,
OwnerUserFieldProvider $ownerUserFieldProvider,
EntityManager $entityManager
) {
@@ -167,7 +167,7 @@ class AclManager
$this->ownershipCheckerFactory = $ownershipCheckerFactory;
$this->tableFactory = $tableFactory;
$this->mapFactory = $mapFactory;
$this->globalRestricton = $globalRestricton;
$this->globalRestriction = $globalRestriction;
$this->ownerUserFieldProvider = $ownerUserFieldProvider;
$this->entityManager = $entityManager;
}
@@ -487,27 +487,27 @@ class AclManager
}
/**
* @return string[]
* @return array<int,GlobalRestriction::TYPE_*>
*/
protected function getGlobalRestrictionTypeList(User $user, string $action = Table::ACTION_READ): array
{
$typeList = [
GlobalRestricton::TYPE_FORBIDDEN,
GlobalRestriction::TYPE_FORBIDDEN,
];
if ($action === Table::ACTION_READ) {
$typeList[] = GlobalRestricton::TYPE_INTERNAL;
$typeList[] = GlobalRestriction::TYPE_INTERNAL;
}
if (!$user->isAdmin()) {
$typeList[] = GlobalRestricton::TYPE_ONLY_ADMIN;
$typeList[] = GlobalRestriction::TYPE_ONLY_ADMIN;
}
if ($action === Table::ACTION_EDIT) {
$typeList[] = GlobalRestricton::TYPE_READ_ONLY;
$typeList[] = GlobalRestriction::TYPE_READ_ONLY;
if (!$user->isAdmin()) {
$typeList[] = GlobalRestricton::TYPE_NON_ADMIN_READ_ONLY;
$typeList[] = GlobalRestriction::TYPE_NON_ADMIN_READ_ONLY;
}
}
@@ -596,7 +596,7 @@ class AclManager
*/
public function checkUserPermission(User $user, $target, string $permissionType = 'user'): bool
{
$permission = $this->get($user, $permissionType);
$permission = $this->getPermissionLevel($user, $permissionType);
if (is_object($target)) {
$userId = $target->getId();
@@ -618,11 +618,11 @@ class AclManager
}
if ($permission === Table::LEVEL_TEAM) {
/** @var string[] */
/** @var string[] $teamIdList */
$teamIdList = $user->getLinkMultipleIdList('teams');
/** @var \Espo\Repositories\User $userRepository */
$userRepository = $this->entityManager->getRepository('User');
$userRepository = $this->entityManager->getRepository(User::ENTITY_TYPE);
if (!$userRepository->checkBelongsToAnyOfTeams($userId, $teamIdList)) {
return false;
@@ -658,7 +658,7 @@ class AclManager
/**
* Get a restricted field list for a specific scope by a restriction type.
*
* @param string|string[] $type
* @param GlobalRestriction::TYPE_*|array<int,GlobalRestriction::TYPE_*> $type
* @return string[]
*/
public function getScopeRestrictedFieldList(string $scope, $type): array
@@ -671,20 +671,20 @@ class AclManager
foreach ($typeList as $type) {
$list = array_merge(
$list,
$this->globalRestricton->getScopeRestrictedFieldList($scope, $type)
$this->globalRestriction->getScopeRestrictedFieldList($scope, $type)
);
}
return array_unique($list);
}
return $this->globalRestricton->getScopeRestrictedFieldList($scope, $type);
return $this->globalRestriction->getScopeRestrictedFieldList($scope, $type);
}
/**
* Get a restricted attribute list for a specific scope by a restriction type.
*
* @param string|string[] $type
* @param GlobalRestriction::TYPE_*|array<int,GlobalRestriction::TYPE_*> $type
* @return string[]
*/
public function getScopeRestrictedAttributeList(string $scope, $type): array
@@ -697,20 +697,20 @@ class AclManager
foreach ($typeList as $type) {
$list = array_merge(
$list,
$this->globalRestricton->getScopeRestrictedAttributeList($scope, $type)
$this->globalRestriction->getScopeRestrictedAttributeList($scope, $type)
);
}
return array_unique($list);
}
return $this->globalRestricton->getScopeRestrictedAttributeList($scope, $type);
return $this->globalRestriction->getScopeRestrictedAttributeList($scope, $type);
}
/**
* Get a restricted link list for a specific scope by a restriction type.
*
* @param string|string[] $type
* @param GlobalRestriction::TYPE_*|array<int,GlobalRestriction::TYPE_*> $type
* @return string[]
*/
public function getScopeRestrictedLinkList(string $scope, $type): array
@@ -723,19 +723,19 @@ class AclManager
foreach ($typeList as $type) {
$list = array_merge(
$list,
$this->globalRestricton->getScopeRestrictedLinkList($scope, $type)
$this->globalRestriction->getScopeRestrictedLinkList($scope, $type)
);
}
return array_unique($list);
}
return $this->globalRestricton->getScopeRestrictedLinkList($scope, $type);
return $this->globalRestriction->getScopeRestrictedLinkList($scope, $type);
}
/**
* Get an entity field that stores an owner-user (or multiple users).
* Must be link or linkMulitple field. NULL means no owner.
* Must be a link or linkMultiple field. NULL means no owner.
*/
public function getReadOwnerUserField(string $entityType): ?string
{
@@ -65,8 +65,10 @@ class ActionFactory
/**
* @param array<string,object> $with
* @deprecated
* @throws NotFound
* @throws Forbidden
* @todo Remove.
* @deprecated
*/
public function createWith(string $action, ?string $entityType, array $with): Action
{
@@ -209,7 +209,7 @@ class Merger
{
$list = [];
/** @var iterable<PhoneNumber> */
/** @var iterable<PhoneNumber> $collection */
$collection = $this->entityManager
->getRDBRepository($entity->getEntityType())
->getRelation($entity, 'phoneNumbers')
@@ -229,7 +229,7 @@ class Merger
{
$list = [];
/** @var iterable<EmailAddress> */
/** @var iterable<EmailAddress> $collection */
$collection = $this->entityManager
->getRDBRepository($entity->getEntityType())
->getRelation($entity, 'emailAddresses')
@@ -135,6 +135,7 @@ class ActionProcessor
/**
* @param mixed $result
* @throws \JsonException
*/
private function handleResult(Response $response, $result): void
{
@@ -177,7 +178,7 @@ class ActionProcessor
return false;
}
/** @var class-string */
/** @var class-string $className */
$className = $type->getName();
$firstParamClass = new ReflectionClass($className);
+32 -14
View File
@@ -33,8 +33,6 @@ use Espo\Core\Exceptions\BadRequest;
use Espo\Core\Exceptions\ServiceUnavailable;
use Espo\Core\Exceptions\Forbidden;
use Espo\Core\Api\Request;
use Espo\Core\Api\Response;
use Espo\Core\Authentication\Authentication;
use Espo\Core\Authentication\AuthenticationData;
use Espo\Core\Authentication\Result;
@@ -69,6 +67,10 @@ class Auth
$this->isEntryPoint = $isEntryPoint;
}
/**
* @throws BadRequest
* @throws Exception
*/
public function process(Request $request, Response $response): AuthResult
{
$username = null;
@@ -87,6 +89,18 @@ class Auth
$hasAuthData = (bool) ($username || $authenticationMethod);
if (!$hasAuthData) {
$password = $this->obtainTokenFromCookies($request);
if ($password) {
$authenticationData = AuthenticationData::create()
->withPassword($password)
->withByTokenOnly(true);
$hasAuthData = true;
}
}
if (!$this->authRequired && !$this->isEntryPoint && $hasAuthData) {
$authResult = $this->processAuthNotRequired(
$authenticationData,
@@ -118,6 +132,9 @@ class Auth
return AuthResult::createNotResolved();
}
/**
* @throws Exception
*/
private function processAuthNotRequired(
AuthenticationData $data,
Request $request,
@@ -140,6 +157,9 @@ class Auth
return null;
}
/**
* @throws Exception
*/
private function processWithAuthData(
AuthenticationData $data,
Request $request,
@@ -178,7 +198,7 @@ class Auth
*/
protected function decodeAuthorizationString(string $string): array
{
/** @var string */
/** @var string $stringDecoded */
$stringDecoded = base64_decode($string);
if (strpos($stringDecoded, ':') === false) {
@@ -205,6 +225,9 @@ class Auth
$response->writeBody(Json::encode($bodyData));
}
/**
* @throws Exception
*/
protected function handleException(Response $response, Exception $e): void
{
if (
@@ -269,6 +292,7 @@ class Auth
/**
* @return array{?string,?string}
* @throws BadRequest
*/
protected function obtainUsernamePasswordFromRequest(Request $request): array
{
@@ -290,17 +314,6 @@ class Auth
return [$username, $password];
}
if (
$request->getCookieParam('auth-username') &&
$request->getCookieParam('auth-token')
) {
$username = $request->getCookieParam('auth-username');
$password = $request->getCookieParam('auth-token');
return [$username, $password];
}
$cgiAuthString = $request->getHeader('Http-Espo-Cgi-Auth') ??
$request->getHeader('Redirect-Http-Espo-Cgi-Auth');
@@ -312,4 +325,9 @@ class Auth
return [null, null];
}
private function obtainTokenFromCookies(Request $request): ?string
{
return $request->getCookieParam('auth-token');
}
}
+3 -2
View File
@@ -30,11 +30,12 @@
namespace Espo\Core\Api;
use Espo\Core\{
Exceptions\Error,
Authentication\Authentication,
Utils\Log,
};
use RuntimeException;
/**
* Builds Auth instance.
*/
@@ -77,7 +78,7 @@ class AuthBuilder
public function build(): Auth
{
if (!$this->authentication) {
throw new Error("Authentication is not set.");
throw new RuntimeException("Authentication is not set.");
}
return new Auth($this->log, $this->authentication, $this->authRequired, $this->isEntryPoint);
+8 -5
View File
@@ -34,6 +34,7 @@ use Espo\Core\Exceptions\HasBody;
use Espo\Core\{
Api\Request,
Api\Response,
Exceptions\HasLogMessage,
Utils\Log,
Utils\Config,
};
@@ -121,14 +122,16 @@ class ErrorOutput
$message = $exception->getMessage();
$statusCode = $exception->getCode();
if ($exception instanceof HasLogMessage) {
$message = $exception->getLogMessage();
}
if ($route) {
$this->processRoute($route, $request, $exception);
}
$logLevel = 'error';
$messageLineFile = null;
$messageLineFile =
'line: ' . $exception->getLine() . ', ' .
'file: ' . $exception->getFile();
@@ -176,10 +179,10 @@ class ErrorOutput
}
if ($toPrintBody) {
$codeDesription = $this->getCodeDescription($statusCode);
$codeDescription = $this->getCodeDescription($statusCode);
$statusText = isset($codeDesription) ?
$statusCode . ' '. $codeDesription :
$statusText = isset($codeDescription) ?
$statusCode . ' '. $codeDescription :
'HTTP ' . $statusCode;
if ($message) {
@@ -29,7 +29,7 @@
namespace Espo\Core\Api;
use Espo\Core\Exceptions\Error;
use Espo\Core\Exceptions\BadRequest;
use Espo\Core\Authentication\AuthenticationFactory;
use Espo\Core\Utils\Config;
use Espo\Core\Utils\Log;
@@ -37,6 +37,7 @@ use Espo\Core\ApplicationUser;
use Exception;
use Throwable;
use LogicException;
/**
* Processes requests. Handles authentication. Obtains a controller name, action, body from a request.
@@ -113,6 +114,10 @@ class RequestProcessor
ob_clean();
}
/**
* @throws \Espo\Core\Exceptions\NotFound
* @throws BadRequest
*/
private function proceed(Request $request, Response $response): void
{
$this->beforeProceed($response);
@@ -129,7 +134,7 @@ class RequestProcessor
$actionName = $crudList[$httpMethod] ?? null;
if (!$actionName) {
throw new Error("No action for method {$httpMethod}.");
throw new BadRequest("No action for method {$httpMethod}.");
}
}
@@ -143,7 +148,7 @@ class RequestProcessor
$controllerName = $request->getRouteParam('controller');
if (!$controllerName) {
throw new Error("Route doesn't have specified controller.");
throw new LogicException("Route doesn't have specified controller.");
}
return ucfirst($controllerName);
@@ -174,6 +179,7 @@ class RequestProcessor
private function afterProceed(Response $response): void
{
$response
->setHeader('X-App-Timestamp', (string) ($this->config->get('appTimestamp') ?? '0'))
->setHeader('Expires', '0')
->setHeader('Last-Modified', gmdate('D, d M Y H:i:s') . ' GMT')
->setHeader('Cache-Control', 'no-store, no-cache, must-revalidate, post-check=0, pre-check=0')
+22 -5
View File
@@ -29,6 +29,9 @@
namespace Espo\Core\Api;
use Espo\Core\Utils\Json;
use Espo\Core\Exceptions\BadRequest;
use Psr\Http\Message\{
ServerRequestInterface as Psr7Request,
UriInterface,
@@ -181,30 +184,44 @@ class RequestWrapper implements ApiRequest
return $contents;
}
/**
* @throws BadRequest
*/
public function getParsedBody(): stdClass
{
if ($this->parsedBody === null) {
$this->initParsedBody();
}
assert($this->parsedBody !== null);
if ($this->parsedBody === null) {
throw new BadRequest();
}
return Util::cloneObject($this->parsedBody);
}
/**
* @throws BadRequest
*/
private function initParsedBody(): void
{
$contents = $this->getBodyContents();
if ($this->getContentType() === 'application/json' && $contents) {
$this->parsedBody = json_decode($contents);
$parsedBody = Json::decode($contents);
if (is_array($this->parsedBody)) {
$this->parsedBody = (object) [
'list' => $this->parsedBody,
if (is_array($parsedBody)) {
$parsedBody = (object) [
'list' => $parsedBody,
];
}
if (!$parsedBody instanceof stdClass) {
throw new BadRequest("Body is not a JSON object.");
}
$this->parsedBody = $parsedBody;
return;
}
+1 -1
View File
@@ -61,7 +61,7 @@ class Application
protected function initContainer(): void
{
/** @var Container */
/** @var Container $container */
$container = (new ContainerBuilder())->build();
$this->container = $container;
@@ -32,6 +32,7 @@ namespace Espo\Core\ApplicationRunners;
use Espo\Core\{
Application\Runner,
DataManager,
Exceptions\Error,
};
/**
@@ -48,6 +49,9 @@ class ClearCache implements Runner
$this->dataManager = $dataManager;
}
/**
* @throws Error
*/
public function run(): void
{
$this->dataManager->clearCache();
@@ -42,7 +42,7 @@ class Command implements Runner
use Cli;
use SetupSystemUser;
private $commandManager;
private ConsoleCommandManager $commandManager;
public function __construct(ConsoleCommandManager $commandManager)
{
@@ -52,10 +52,14 @@ class Command implements Runner
public function run(): void
{
try {
$this->commandManager->run($_SERVER['argv']);
$exitStatus = $this->commandManager->run($_SERVER['argv']);
}
catch (Exception $e) {
echo "Error: " . $e->getMessage() . "\n";
exit(1);
}
exit($exitStatus);
}
}
@@ -29,11 +29,10 @@
namespace Espo\Core\ApplicationRunners;
use Espo\Core\Exceptions\Error;
use Espo\Core\{
Application\RunnerParameterized,
Application\Runner\Params,
Exceptions\BadRequest,
Exceptions\NotFound,
Utils\ClientManager,
Utils\Config,
@@ -42,8 +41,7 @@ use Espo\Core\{
Portal\Utils\Url,
Api\ErrorOutput,
Api\RequestWrapper,
Api\ResponseWrapper,
};
Api\ResponseWrapper};
use Slim\{
ResponseEmitter,
@@ -89,7 +87,7 @@ class PortalClient implements RunnerParameterized
$responseWrapped = new ResponseWrapper(new Response());
if ($requestWrapped->getMethod() !== 'GET') {
throw new Error("Only GET request is allowed.");
throw new BadRequest("Only GET request is allowed.");
}
try {
+6 -4
View File
@@ -33,12 +33,14 @@ use Espo\Core\Exceptions\Error;
use Espo\Entities\Portal as PortalEntity;
use Espo\Entities\User as UserEntity;
use LogicException;
/**
* Provides information about an application, current user, portal.
*/
class ApplicationState
{
private $container;
private Container $container;
public function __construct(Container $container)
{
@@ -59,7 +61,7 @@ class ApplicationState
public function getPortalId(): string
{
if (!$this->isPortal()) {
throw new Error("Can't get portal ID for non-portal application.");
throw new LogicException("Can't get portal ID for non-portal application.");
}
return $this->getPortal()->getId();
@@ -71,7 +73,7 @@ class ApplicationState
public function getPortal(): PortalEntity
{
if (!$this->isPortal()) {
throw new Error("Can't get portal for non-portal application.");
throw new LogicException("Can't get portal for non-portal application.");
}
/** @var PortalEntity */
@@ -92,7 +94,7 @@ class ApplicationState
public function getUser(): UserEntity
{
if (!$this->hasUser()) {
throw new Error("User is not yet available.");
throw new LogicException("User is not yet available.");
}
/** @var UserEntity */
+4 -6
View File
@@ -29,10 +29,6 @@
namespace Espo\Core;
use Espo\Core\Exceptions\{
Error,
};
use Espo\Entities\{
User,
};
@@ -41,6 +37,8 @@ use Espo\Core\{
ORM\EntityManagerProxy,
};
use RuntimeException;
/**
* Setting a current user for the application.
*/
@@ -57,14 +55,14 @@ class ApplicationUser
}
/**
* Setup the system user as a current user. The system user is used when no user is logged in.
* Set up the system user as a current user. The system user is used when no user is logged in.
*/
public function setupSystemUser(): void
{
$user = $this->entityManagerProxy->getEntity('User', 'system');
if (!$user) {
throw new Error("System user is not found.");
throw new RuntimeException("System user is not found.");
}
$user->set('ipAddress', $_SERVER['REMOTE_ADDR'] ?? null);
@@ -50,7 +50,7 @@ class EspoManager implements Manager
{
$this->entityManager = $entityManager;
/** @var RDBRepository<AuthTokenEntity> */
/** @var RDBRepository<AuthTokenEntity> $repository */
$repository = $entityManager->getRDBRepository(AuthTokenEntity::ENTITY_TYPE);
$this->repository = $repository;
@@ -58,7 +58,7 @@ class EspoManager implements Manager
public function get(string $token): ?AuthToken
{
/** @var ?AuthTokenEntity */
/** @var ?AuthTokenEntity $authToken */
$authToken = $this->entityManager
->getRDBRepository(AuthTokenEntity::ENTITY_TYPE)
->select([
@@ -83,7 +83,7 @@ class EspoManager implements Manager
public function create(Data $data): AuthToken
{
/** @var AuthTokenEntity */
/** @var AuthTokenEntity $authToken */
$authToken = $this->repository->getNew();
$authToken->set([
@@ -169,7 +169,7 @@ class EspoManager implements Manager
}
if (function_exists('openssl_random_pseudo_bytes')) {
/** @var string */
/** @var string $randomValue */
$randomValue = openssl_random_pseudo_bytes($length);
return bin2hex($randomValue);
@@ -29,7 +29,6 @@
namespace Espo\Core\Authentication;
use Espo\Core\Exceptions\Forbidden;
use Espo\Core\Exceptions\ServiceUnavailable;
use Espo\Repositories\UserData as UserDataRepository;
@@ -43,9 +42,7 @@ use Espo\Entities\{
};
use Espo\Core\Authentication\{
Result,
Result\FailReason,
LoginFactory,
TwoFactor\LoginFactory as TwoFactorLoginFactory,
AuthToken\Manager as AuthTokenManager,
AuthToken\Data as AuthTokenData,
@@ -124,7 +121,6 @@ class Authentication
*
* Warning: This method can change the state of the object (by setting the `portal` prop.).
*
* @throws Forbidden
* @throws ServiceUnavailable
*/
public function login(AuthenticationData $data, Request $request, Response $response): Result
@@ -132,14 +128,14 @@ class Authentication
$username = $data->getUsername();
$password = $data->getPassword();
$authenticationMethod = $data->getMethod();
$byTokenOnly = $data->byTokenOnly();
if (
$authenticationMethod &&
!$this->configDataProvider->authenticationMethodIsApi($authenticationMethod)
) {
$this->log->warning(
"AUTH: Trying to use not allowed authentication method '{$authenticationMethod}'."
);
$this->log
->warning("AUTH: Trying to use not allowed authentication method '{$authenticationMethod}'.");
return $this->processFail(
Result::fail(FailReason::METHOD_NOT_ALLOWED),
@@ -184,9 +180,13 @@ class Authentication
}
}
$isByTokenOnly = !$authenticationMethod && $request->getHeader('Espo-Authorization-By-Token') === 'true';
$byTokenAndUsername = $request->getHeader('Espo-Authorization-By-Token') === 'true';
if ($isByTokenOnly && !$authToken) {
if ($authenticationMethod && $byTokenAndUsername) {
return Result::fail(FailReason::DISCREPANT_DATA);
}
if (($byTokenAndUsername || $byTokenOnly) && !$authToken) {
if ($username) {
$this->log->info(
"AUTH: Trying to login as user '{$username}' by token but token is not found."
@@ -200,6 +200,20 @@ class Authentication
);
}
if ($byTokenOnly) {
assert($authToken !== null);
$username = $this->getUsernameByAuthToken($authToken);
if (!$username) {
return $this->processFail(
Result::fail(FailReason::USER_NOT_FOUND),
$data,
$request
);
}
}
if (!$authenticationMethod) {
$authenticationMethod = $this->configDataProvider->getDefaultAuthenticationMethod();
}
@@ -354,7 +368,7 @@ class Authentication
private function processAuthTokenCheck(AuthToken $authToken): bool
{
if ($this->allowAnyAccess && $authToken->getPortalId() && !$this->isPortal()) {
/** @var ?Portal */
/** @var ?Portal $portal */
$portal = $this->entityManager->getEntity('Portal', $authToken->getPortalId());
if ($portal) {
@@ -504,7 +518,7 @@ class Authentication
);
if ($createSecret) {
$this->setSecretInCookie($authToken->getSecret(), $response);
$this->setSecretInCookie($authToken->getSecret(), $response, $request);
}
if (
@@ -563,7 +577,7 @@ class Authentication
return null;
}
/** @var AuthLogRecord */
/** @var AuthLogRecord $authLogRecord */
$authLogRecord = $this->entityManager->getNewEntity('AuthLogRecord');
$requestUrl =
@@ -612,7 +626,7 @@ class Authentication
$this->entityManager->saveEntity($authLogRecord);
}
private function setSecretInCookie(?string $secret, Response $response): void
private function setSecretInCookie(?string $secret, Response $response, ?Request $request = null): void
{
$time = $secret ? strtotime('+1000 days') : 1;
@@ -625,9 +639,36 @@ class Authentication
'; HttpOnly' .
'; SameSite=Lax';
if ($request && self::isSecureRequest($request)) {
$headerValue .= "; Secure";
}
$response->addHeader('Set-Cookie', $headerValue);
}
private static function isSecureRequest(Request $request): bool
{
$https = $request->getServerParam('HTTPS');
if ($https === 'on') {
return true;
}
$scheme = $request->getServerParam('REQUEST_SCHEME');
if ($scheme === 'https') {
return true;
}
$forwardedProto = $request->getServerParam('HTTP_X_FORWARDED_PROTO');
if ($forwardedProto === 'https') {
return true;
}
return false;
}
private function processFail(Result $result, AuthenticationData $data, Request $request): Result
{
$this->hookManager->processOnFail($result, $data, $request);
@@ -669,4 +710,20 @@ class Authentication
/** @var UserDataRepository */
return $this->entityManager->getRepository(UserData::ENTITY_TYPE);
}
private function getUsernameByAuthToken(AuthToken $authToken): ?string
{
/** @var ?User $user */
$user = $this->entityManager
->getRDBRepository(User::ENTITY_TYPE)
->select(['userName'])
->where(['id' => $authToken->getUserId()])
->findOne();
if (!$user) {
return null;
}
return $user->getUserName();
}
}
@@ -31,11 +31,10 @@ namespace Espo\Core\Authentication;
class AuthenticationData
{
private $username;
private $password;
private $method;
private ?string $username;
private ?string $password;
private ?string $method;
private bool $byTokenOnly = false;
public function __construct(
?string $username = null,
@@ -52,21 +51,38 @@ class AuthenticationData
return new self();
}
/**
* A username.
*/
public function getUsername(): ?string
{
return $this->username;
}
/**
* A password or auth-token.
*/
public function getPassword(): ?string
{
return $this->password;
}
/**
* A method.
*/
public function getMethod(): ?string
{
return $this->method;
}
/**
* Authenticate by auth-token only. No username check.
*/
public function byTokenOnly(): bool
{
return $this->byTokenOnly;
}
public function withUsername(?string $username): self
{
$obj = clone $this;
@@ -90,4 +106,12 @@ class AuthenticationData
return $obj;
}
public function withByTokenOnly(bool $byTokenOnly): self
{
$obj = clone $this;
$obj->byTokenOnly = $byTokenOnly;
return $obj;
}
}
@@ -44,7 +44,7 @@ class UserFinder
public function find(string $username, string $hash): ?User
{
/** @var ?User */
/** @var ?User $user */
$user = $this->entityManager
->getRDBRepository(User::ENTITY_TYPE)
->where([
@@ -59,7 +59,7 @@ class UserFinder
public function findApiHmac(string $apiKey): ?User
{
/** @var ?User */
/** @var ?User $user */
$user = $this->entityManager
->getRDBRepository(User::ENTITY_TYPE)
->where([
@@ -74,7 +74,7 @@ class UserFinder
public function findApiApiKey(string $apiKey): ?User
{
/** @var ?User */
/** @var ?User $user */
$user = $this->entityManager
->getRDBRepository(User::ENTITY_TYPE)
->where([
@@ -33,6 +33,7 @@ class ClientFactory
{
/**
* @param array<string,mixed> $options
* @throws \Laminas\Ldap\Exception\LdapException
*/
public function create(array $options): Client
{
@@ -52,14 +52,14 @@ class LoginFactory
public function create(string $method, bool $isPortal = false): Login
{
/** @var class-string<Login> */
/** @var class-string<Login> $className */
$className = $this->metadata->get(['authenticationMethods', $method, 'implementationClassName']);
if (!$className) {
$sanitizedName = preg_replace('/[^a-zA-Z0-9]+/', '', $method);
if (!class_exists($className)) {
/** @var class-string<Login> */
/** @var class-string<Login> $className */
$className = "Espo\\Core\\Authentication\\Logins\\" . $sanitizedName;
}
}
@@ -43,9 +43,10 @@ use RuntimeException;
class Espo implements Login
{
private $userFinder;
public const NAME = 'Espo';
private $passwordHash;
private UserFinder $userFinder;
private PasswordHash $passwordHash;
public function __construct(UserFinder $userFinder, PasswordHash $passwordHash)
{
@@ -81,7 +82,7 @@ class Espo implements Login
return Result::fail(FailReason::WRONG_CREDENTIALS);
}
if ($authToken && $user->id !== $authToken->getUserId()) {
if ($authToken && $user->getId() !== $authToken->getUserId()) {
return Result::fail(FailReason::USER_TOKEN_MISMATCH);
}
@@ -36,10 +36,11 @@ use Espo\Core\{
Authentication\Login\Data,
Authentication\Result,
Authentication\Helper\UserFinder,
Exceptions\Error,
Authentication\Result\FailReason,
};
use RuntimeException;
class Hmac implements Login
{
private $userFinder;
@@ -71,7 +72,7 @@ class Hmac implements Login
$secretKey = $this->apiKeyUtil->getSecretKeyForUserId($user->getId());
if (!$secretKey) {
throw new Error("No secret key for API user '" . $user->getId() . "'.");
throw new RuntimeException("No secret key for API user '" . $user->getId() . "'.");
}
$string = $request->getMethod() . ' ' . $request->getResourcePath();
@@ -408,6 +408,8 @@ class LDAP implements Login
/**
* Find LDAP user DN by his username.
*
* @throws \Laminas\Ldap\Exception\LdapException
*/
private function findLdapUserDnByUsername(string $username): ?string
{
@@ -426,7 +428,7 @@ class LDAP implements Login
'(' . $options['userNameAttribute'] . '=' . $username . ')' .
$loginFilterString . ')';
/** @var array<int,array{dn: string}> */
/** @var array<int,array{dn: string}> $result */
$result = $ldapClient->search($searchString, null, Client::SEARCH_SCOPE_SUB);
$this->log->debug('LDAP: user search string: "' . $searchString . '"');
@@ -37,7 +37,7 @@ class Data
{
private ?string $message = null;
private ?string$token = null;
private ?string $token = null;
private ?string $view = null;
@@ -50,4 +50,6 @@ class FailReason
public const HASH_NOT_MATCHED = 'Hash not matched';
public const METHOD_NOT_ALLOWED = 'Not allowed authentication method';
public const DISCREPANT_DATA = 'Discrepant authentication data';
}

Some files were not shown because too many files have changed in this diff Show More