Compare commits

...

136 Commits
2.7.2 ... 2.8.0

Author SHA1 Message Date
Yuri Kuznetsov
25b81357c7 de_DE 2014-12-17 11:42:32 +02:00
Yuri Kuznetsov
f7f380ff04 Merge remote-tracking branch 'origin/master' 2014-12-17 10:36:20 +02:00
Yuri Kuznetsov
a09d99fe53 fix date field 2014-12-17 10:35:20 +02:00
Taras Machyshyn
bdd6538984 exclude custom Loaders from .gitignore 2014-12-16 12:34:45 +02:00
Yuri Kuznetsov
2787d3bc72 cleanup 2014-12-15 18:15:58 +02:00
Yuri Kuznetsov
72e53d096f replace tabs 2014-12-15 18:15:35 +02:00
Yuri Kuznetsov
acd48513f7 po.js and lang.js 2014-12-15 16:55:42 +02:00
Yuri Kuznetsov
6ffa5654ea fix calendar 2014-12-15 15:38:02 +02:00
Yuri Kuznetsov
b2dfdb3446 fix cache 2014-12-15 12:29:41 +02:00
Yuri Kuznetsov
dc5934a0ad change reminder interval 2014-12-15 11:41:11 +02:00
Yuri Kuznetsov
ec586874a6 change version 2014-12-15 11:39:32 +02:00
Yuri Kuznetsov
eb99d643b1 remove outline css 2014-12-12 16:37:56 +02:00
Yuri Kuznetsov
f6b37f346f search button 2014-12-12 15:38:11 +02:00
Yuri Kuznetsov
180b31f6fa less changes 2014-12-10 17:11:12 +02:00
Yuri Kuznetsov
b16de8591e cleanup 2014-12-10 16:17:35 +02:00
Yuri Kuznetsov
f87f418c1b globar search refactor 2014-12-10 16:04:05 +02:00
Yuri Kuznetsov
f79fd38478 sendEmailReminders record 2014-12-10 14:59:45 +02:00
Yuri Kuznetsov
2e13f73b34 email reminder 2014-12-10 13:14:26 +02:00
Yuri Kuznetsov
300f442b22 invitation email changes 2014-12-10 11:52:55 +02:00
Yuri Kuznetsov
0f1c31e55a some changes in lang 2014-12-09 18:11:05 +02:00
Yuri Kuznetsov
86633b31d3 change css 2014-12-09 17:20:04 +02:00
Yuri Kuznetsov
f60aa78dde popup norifications dev 2014-12-09 17:18:27 +02:00
Yuri Kuznetsov
ae77cb1d77 reminder css fix 2014-12-09 15:22:35 +02:00
Yuri Kuznetsov
74d99d8cda upgrade bootstrap 2014-12-09 15:13:20 +02:00
Yuri Kuznetsov
a90e0874bd reminders dev 2014-12-09 13:09:11 +02:00
Yuri Kuznetsov
141d20b472 reminders 2 2014-12-08 17:44:25 +02:00
Yuri Kuznetsov
6f08ecc1a7 reminders 1 2014-12-08 15:51:02 +02:00
Yuri Kuznetsov
3e4d6edac8 popup notifications 2 2014-12-05 18:12:18 +02:00
Yuri Kuznetsov
19784fc09c popup notifications 1 2014-12-05 17:46:19 +02:00
Yuri Kuznetsov
554f8cc37c big fix in repository 2014-12-04 17:07:45 +02:00
Yuri Kuznetsov
c6153f6af3 calendar fixes 2014-12-04 15:33:54 +02:00
Yuri Kuznetsov
048f5318fc fix calendar 2014-12-04 13:22:14 +02:00
Yuri Kuznetsov
62985c9776 calendar fixes 2014-12-03 18:32:22 +02:00
Yuri Kuznetsov
9b30c02c22 improve export 2014-12-03 16:37:10 +02:00
Yuri Kuznetsov
122f18e8bf remove duration field from mass update 2014-12-03 16:13:46 +02:00
Yuri Kuznetsov
9d316da4e0 fix meeting/call attendees 2014-12-03 15:50:00 +02:00
Yuri Kuznetsov
46439ff394 Merge branch 'hotfix/2.7.3' 2014-12-03 12:25:59 +02:00
Yuri Kuznetsov
f73a60ad9f merge hotfix/2.7.3 2014-12-03 12:04:54 +02:00
Yuri Kuznetsov
c05a153719 cleanup 2014-12-03 12:04:01 +02:00
Yuri Kuznetsov
094f39e987 fix settings array fields 2014-12-03 12:03:17 +02:00
Yuri Kuznetsov
c247def1bc stream changes 2014-12-03 11:20:12 +02:00
Yuri Kuznetsov
c7cd372554 clearnup 2014-12-03 11:06:59 +02:00
Yuri Kuznetsov
ea951a8738 changes in list and stream list 2014-12-03 11:01:09 +02:00
Yuri Kuznetsov
d9973bf353 cleanup 2014-12-03 10:31:51 +02:00
Yuri Kuznetsov
0baaf82909 added disable avatars param 2014-12-03 10:07:30 +02:00
Yuri Kuznetsov
e62cb881c9 Merge branch 'hotfix/2.7.3' 2014-12-03 09:57:05 +02:00
Yuri Kuznetsov
a965f6037f fix array field sortable 2014-12-03 09:55:50 +02:00
Yuri Kuznetsov
d6251f995c stream tab 2014-12-03 09:49:51 +02:00
Yuri Kuznetsov
483c971e3e stream centered 2014-12-02 17:47:28 +02:00
Yuri Kuznetsov
3186dcc65f stream and dashlet scopes 2014-12-02 17:33:22 +02:00
Yuri Kuznetsov
e7aeebee8d Merge remote-tracking branch 'origin/master' 2014-12-02 16:32:21 +02:00
Taras Machyshyn
14c3287131 changed indexes name 2014-12-02 16:32:00 +02:00
Yuri Kuznetsov
4754b2db2f Merge remote-tracking branch 'origin/master' 2014-12-02 16:13:22 +02:00
Yuri Kuznetsov
8fa0fae265 add indexes 2014-12-02 16:13:00 +02:00
Taras Machyshyn
dc4c4bd084 Merge branch 'master' of ssh://172.20.0.1/var/git/espo/backend 2014-12-02 16:12:17 +02:00
Taras Machyshyn
6a5239cedc added camel case for indexes 2014-12-02 16:12:03 +02:00
Taras Machyshyn
9ea9429757 camel case convertation improvements 2014-12-02 16:11:22 +02:00
Yuri Kuznetsov
20f89cf75a layout manager fix 2014-12-02 15:35:30 +02:00
Yuri Kuznetsov
e8acf8aaaf layout manager fixes 2014-12-02 15:05:21 +02:00
Yuri Kuznetsov
0fac47e227 Merge remote-tracking branch 'origin/master' 2014-12-02 13:20:08 +02:00
Yuri Kuznetsov
fe3cc09623 orm refactor 2014-12-02 13:19:45 +02:00
Taras Machyshyn
91445f2f9c indexes improvements 2014-12-02 12:25:38 +02:00
Yuri Kuznetsov
053d516bc9 user avatars in user list view 2014-12-02 11:21:01 +02:00
Yuri Kuznetsov
63a9f67bd9 fix avatar 2014-12-01 16:58:09 +02:00
Yuri Kuznetsov
344f0227e9 Merge branch 'hotfix/2.7.2' 2014-12-01 14:22:04 +02:00
Yuri Kuznetsov
16384b9f72 Merge remote-tracking branch 'origin/master' 2014-11-28 18:14:25 +02:00
Yuri Kuznetsov
db9d8e1006 fix css 2014-11-28 18:14:05 +02:00
Taras Machyshyn
c7dd503d80 Integration Entity minor fixes 2014-11-28 17:49:30 +02:00
Taras Machyshyn
c96265e2a9 Merge branch 'master' of ssh://172.20.0.1/var/git/espo/backend 2014-11-28 17:27:20 +02:00
Taras Machyshyn
ff35f7b1ff Integration entity fixes 2014-11-28 17:27:02 +02:00
Yuri Kuznetsov
ac1748067b fix crypt 2014-11-28 16:34:20 +02:00
Yuri Kuznetsov
a4de09ea36 clenup 2014-11-28 14:22:15 +02:00
Yuri Kuznetsov
0816831fe1 preview attachments in email 2014-11-28 12:57:12 +02:00
Yuri Kuznetsov
0e83325e92 Merge branch 'hotfix/2.7.1' 2014-11-28 10:49:29 +02:00
Yuri Kuznetsov
261e52e64d Merge branch 'hotfix/2.7.1' 2014-11-27 17:22:47 +02:00
Yuri Kuznetsov
f1a990e80a fix email sender 2014-11-27 17:14:14 +02:00
Yuri Kuznetsov
a3a8110ae6 email archive: accountId from contact 2014-11-27 14:44:12 +02:00
Yuri Kuznetsov
82a213555b cleanup 2014-11-27 14:12:02 +02:00
Yuri Kuznetsov
3590ff2e33 Merge remote-tracking branch 'origin/master' 2014-11-27 14:07:47 +02:00
Yuri Kuznetsov
775cfec744 remove comment 2014-11-27 14:07:22 +02:00
Yuri Kuznetsov
9ca1d6608c archive relate by message id 2014-11-27 12:46:57 +02:00
Yuri Kuznetsov
59140b0814 fix side 2014-11-27 11:14:14 +02:00
Yuri Kuznetsov
90cf300334 cleanup 2014-11-27 11:01:11 +02:00
Taras Machyshyn
7bd3c7cb0d Merge branch 'master' of ssh://172.20.0.1/var/git/espo/backend 2014-11-27 10:27:09 +02:00
Taras Machyshyn
ce75cc90f5 added possibility to define 'view' for intergations 2014-11-27 10:26:52 +02:00
Yuri Kuznetsov
00a911434b change user list layout 2014-11-26 17:44:09 +02:00
Yuri Kuznetsov
69840086c9 Merge branch 'hotfix/2.7.1' 2014-11-26 16:48:12 +02:00
Yuri Kuznetsov
b72f6fa2cc manual merge with hotfix/2.7.1 2014-11-26 16:45:26 +02:00
Yuri Kuznetsov
9f9d0cb2dc cleanup 2014-11-26 16:43:11 +02:00
Yuri Kuznetsov
6a036595d6 dont notify about mention to current user 2014-11-26 16:29:47 +02:00
Yuri Kuznetsov
f4c57046dd cleanup 2014-11-26 15:47:58 +02:00
Yuri Kuznetsov
7b507f1bab edit note is wide 2014-11-26 15:35:05 +02:00
Yuri Kuznetsov
c0b8c5e7d2 change avatar size 2014-11-26 15:25:49 +02:00
Yuri Kuznetsov
6e49afddf0 Merge branch 'hotfix/2.7.1' 2014-11-26 14:54:50 +02:00
Yuri Kuznetsov
4908f281a3 ability to disable avatars 2014-11-26 12:37:59 +02:00
Yuri Kuznetsov
796b51d620 avatar for side field 2014-11-26 12:17:58 +02:00
Yuri Kuznetsov
9cc44cf917 modal backdrop param 2014-11-26 11:16:47 +02:00
Yuri Kuznetsov
9f7eecac73 attachment block fix 2014-11-26 10:47:14 +02:00
Yuri Kuznetsov
cba59fe847 EmailReceived Note: dont copy attachments 2014-11-26 10:17:36 +02:00
Yuri Kuznetsov
07df97ab9e ability to use entryPoint w/o build 2014-11-25 18:03:14 +02:00
Yuri Kuznetsov
0a1322843f Fix conflicts on merge with hotfix/2.7.1 2014-11-25 17:48:38 +02:00
Yuri Kuznetsov
6c6a3621e8 avatar 2014-11-25 17:37:16 +02:00
Yuri Kuznetsov
4a075819d1 avatar 3 2014-11-25 17:37:16 +02:00
Yuri Kuznetsov
3aa3914f3a fix notices 2014-11-25 17:37:16 +02:00
Yuri Kuznetsov
eef9c3e35c email sending fix 2014-11-25 17:37:16 +02:00
Taras Machyshyn
fe61fadc09 fileManager improvements 2014-11-25 17:37:16 +02:00
Taras Machyshyn
4c931514a2 E_STRICT notice fixes 2014-11-25 17:37:16 +02:00
Taras Machyshyn
8ad886d871 addded autoload.json for Resources 2014-11-25 16:44:50 +02:00
Yuri Kuznetsov
93b8745599 Merge branch 'hotfix/2.7.1' 2014-11-21 18:19:39 +02:00
Yuri Kuznetsov
fa25dc715f avatar 2 2014-11-21 17:00:30 +02:00
Yuri Kuznetsov
c010cf43bf avatar 1 2014-11-21 16:19:47 +02:00
Yuri Kuznetsov
51bdd15c9e change password 3 2014-11-20 16:25:43 +02:00
Yuri Kuznetsov
b48f10b45f load language on login form 2014-11-20 11:52:10 +02:00
Yuri Kuznetsov
027e928bd3 rename getController for app.js 2014-11-19 16:47:01 +02:00
Yuri Kuznetsov
3cfcfb02de changes in app.js 2014-11-19 16:42:20 +02:00
Yuri Kuznetsov
322df7ff68 Merge branch 'hotfix/2.7.1' 2014-11-19 16:05:45 +02:00
Yuri Kuznetsov
d58b9cac40 change password 2 2014-11-19 16:03:33 +02:00
Yuri Kuznetsov
cca6567bbd fix query 2014-11-19 16:03:33 +02:00
Yuri Kuznetsov
26eb194ccb small change 2014-11-19 16:03:33 +02:00
Yuri Kuznetsov
9b42ae41e7 change password 1 2014-11-19 16:03:33 +02:00
Yuri Kuznetsov
83916cacd9 cleanup 2014-11-19 16:03:33 +02:00
Yuri Kuznetsov
85b484c04e use jsonObject 2014-11-19 16:03:33 +02:00
Yuri Kuznetsov
0d302e99af clearnup 2014-11-19 16:03:33 +02:00
Taras Machyshyn
ae4a6fa3d2 EmailTemplate minor improvements 2014-11-18 17:17:26 +02:00
Taras Machyshyn
8f64fdd025 Merge branch 'master' of ssh://172.20.0.1/var/git/espo/backend 2014-11-18 13:36:53 +02:00
Taras Machyshyn
5ee83505af metedata improvements 2014-11-18 13:36:37 +02:00
Yuri Kuznetsov
4527358a53 Merge remote-tracking branch 'origin/master' 2014-11-18 11:58:54 +02:00
Yuri Kuznetsov
51cca50828 some changes in select manager 2014-11-18 11:50:27 +02:00
Taras Machyshyn
8f1752575a Merge branch 'master' of ssh://172.20.0.1/var/git/espo/backend 2014-11-17 17:06:25 +02:00
Taras Machyshyn
29c39fb4fd use MyISAM engine for database 2014-11-17 17:06:04 +02:00
Yuri Kuznetsov
63663ccf6c Merge branch 'master' of ssh://172.20.0.1/var/git/espo/backend 2014-11-17 16:42:48 +02:00
Yuri Kuznetsov
1ba81c3350 Merge branch 'hotfix/2.7.1' 2014-11-17 16:37:15 +02:00
Taras Machyshyn
f7879f425f added 'indexes' section in entityDefs 2014-11-17 13:11:09 +02:00
Yuri Kuznetsov
93ed5dce6b merge hotfix/2.7.1 2014-11-17 12:07:03 +02:00
Yuri Kuznetsov
a08adcf6d5 change readme 2014-11-14 16:17:30 +02:00
Yuri Kuznetsov
a7bad6aa58 added repository field to package.json 2014-11-14 16:00:07 +02:00
304 changed files with 6086 additions and 1753 deletions

10
.gitignore vendored
View File

@@ -4,7 +4,6 @@
/data/preferences/*
/data/.backup/*
/data/config.php
/custom
/build
/node_modules
/client
@@ -14,3 +13,12 @@
/tests/testData/cache/*
composer.phar
vendor/
/custom/*
!/custom/Espo/
/custom/Espo/*
!/custom/Espo/Custom/
/custom/Espo/Custom/*
!/custom/Espo/Custom/Core/
/custom/Espo/Custom/Core/*
!/custom/Espo/Custom/Core/Loaders/

View File

@@ -1,49 +1,49 @@
module.exports = function (grunt) {
var jsFilesToMinify = [
'client/lib/jquery-2.0.2.min.js',
'client/lib/underscore-min.js',
'client/lib/backbone-min.js',
'client/lib/handlebars.js',
'client/lib/base64.js',
'client/lib/jquery-ui.min.js',
'client/lib/moment.min.js',
'client/lib/moment-timezone-with-data.min.js',
'client/lib/jquery.timepicker.min.js',
'client/lib/jquery.autocomplete.js',
'client/lib/bootstrap.min.js',
'client/lib/bootstrap-datepicker.js',
'client/lib/bull.min.js',
'client/src/namespace.js',
'client/src/exceptions.js',
'client/src/app.js',
'client/src/utils.js',
'client/src/storage.js',
'client/src/loader.js',
'client/src/pre-loader.js',
'client/src/ui.js',
'client/src/acl.js',
'client/src/model.js',
'client/src/model-offline.js',
'client/src/metadata.js',
'client/src/language.js',
'client/src/cache.js',
'client/src/controller.js',
'client/src/router.js',
'client/src/date-time.js',
'client/src/field-manager.js',
'client/src/search-manager.js',
'client/src/collection.js',
'client/src/multi-collection.js',
'client/src/view-helper.js',
'client/src/layout-manager.js',
'client/src/model-factory.js',
'client/src/collection-factory.js',
'client/src/models/settings.js',
'client/src/models/user.js',
'client/src/models/preferences.js',
'client/src/controllers/base.js',
'client/src/view.js',
'client/lib/jquery-2.0.2.min.js',
'client/lib/underscore-min.js',
'client/lib/backbone-min.js',
'client/lib/handlebars.js',
'client/lib/base64.js',
'client/lib/jquery-ui.min.js',
'client/lib/moment.min.js',
'client/lib/moment-timezone-with-data.min.js',
'client/lib/jquery.timepicker.min.js',
'client/lib/jquery.autocomplete.js',
'client/lib/bootstrap.min.js',
'client/lib/bootstrap-datepicker.js',
'client/lib/bull.min.js',
'client/src/namespace.js',
'client/src/exceptions.js',
'client/src/app.js',
'client/src/utils.js',
'client/src/storage.js',
'client/src/loader.js',
'client/src/pre-loader.js',
'client/src/ui.js',
'client/src/acl.js',
'client/src/model.js',
'client/src/model-offline.js',
'client/src/metadata.js',
'client/src/language.js',
'client/src/cache.js',
'client/src/controller.js',
'client/src/router.js',
'client/src/date-time.js',
'client/src/field-manager.js',
'client/src/search-manager.js',
'client/src/collection.js',
'client/src/multi-collection.js',
'client/src/view-helper.js',
'client/src/layout-manager.js',
'client/src/model-factory.js',
'client/src/collection-factory.js',
'client/src/models/settings.js',
'client/src/models/user.js',
'client/src/models/preferences.js',
'client/src/controllers/base.js',
'client/src/view.js',
];
grunt.initConfig({
@@ -106,6 +106,7 @@ module.exports = function (grunt) {
'modules/**',
'img/**',
'css/**',
'sounds/**'
],
dest: 'build/tmp/client',
},
@@ -119,7 +120,7 @@ module.exports = function (grunt) {
cwd: 'frontend/client/lib',
src: '**',
dest: 'build/tmp/client/lib/',
},
},
backend: {
expand: true,
dot: true,
@@ -152,7 +153,7 @@ module.exports = function (grunt) {
options: {
mode: '755'
},
php: {
php: {
options: {
mode: '644'
},
@@ -160,7 +161,7 @@ module.exports = function (grunt) {
'build/EspoCRM-<%= pkg.version %>/**/*.php',
'build/EspoCRM-<%= pkg.version %>/**/*.json',
'build/EspoCRM-<%= pkg.version %>/**/*.config',
'build/EspoCRM-<%= pkg.version %>/**/.htaccess',
'build/EspoCRM-<%= pkg.version %>/**/.htaccess',
'build/EspoCRM-<%= pkg.version %>/client/**/*.js',
'build/EspoCRM-<%= pkg.version %>/client/**/*.css',
'build/EspoCRM-<%= pkg.version %>/client/**/*.tpl',
@@ -230,7 +231,7 @@ module.exports = function (grunt) {
'clean:start',
'mkdir:tmp',
'less',
'cssmin',
'cssmin',
'uglify',
'copy:frontendFolders',
'copy:frontendHtml',
@@ -240,6 +241,6 @@ module.exports = function (grunt) {
'copy:final',
'chmod',
'clean:final',
]);
]);
};

View File

@@ -25,7 +25,7 @@ If your repository is accessible via a web server then you can run EspoCRM by ur
### How to build
You need to have nodejs installed.
You need to have nodejs and Grunt CLI installed.
1. Change to the project's root directory.
2. Install project dependencies with `npm install`.

View File

@@ -25,11 +25,12 @@ namespace Espo\Controllers;
use \Espo\Core\Exceptions\Error;
use \Espo\Core\Exceptions\NotFound;
use \Espo\Core\Exceptions\Forbidden;
use \Espo\Core\Exceptions\BadRequest;
class User extends \Espo\Core\Controllers\Record
{
{
public function actionAcl($params, $data, $request)
{
{
$userId = $request->get('id');
if (empty($userId)) {
throw new Error();
@@ -46,12 +47,54 @@ class User extends \Espo\Core\Controllers\Record
$acl = new \Espo\Core\Acl($user, $this->getConfig(), $this->getContainer()->get('fileManager'), $this->getMetadata());
return $acl->toArray();
return $acl->toArray();
}
public function actionChangeOwnPassword($params, $data)
{
return $this->getService('User')->changePassword($this->getUser()->id, $data['password']);
}
public function actionChangePasswordByRequest($params, $data, $request)
{
if (!$request->isPost()) {
throw new Forbidden();
}
if (empty($data['requestId']) || empty($data['password'])) {
throw new BadRequest();
}
$p = $this->getEntityManager()->getRepository('PasswordChangeRequest')->where(array(
'requestId' => $data['requestId']
))->findOne();
if (!$p) {
throw new Forbidden();
}
$userId = $p->get('userId');
if (!$userId) {
throw new Error();
}
$this->getEntityManager()->removeEntity($p);
return $this->getService('User')->changePassword($userId, $data['password']);
}
public function actionPasswordChangeRequest($params, $data, $request)
{
if (!$request->isPost()) {
throw new Forbidden();
}
if (empty($data['userName']) || empty($data['emailAddress'])) {
throw new BadRequest();
}
$userName = $data['userName'];
$emailAddress = $data['emailAddress'];
return $this->getService('User')->passwordChangeRequest($userName, $emailAddress);
}
}

View File

@@ -218,14 +218,14 @@ class Acl
$userRoles = $this->user->get('roles');
foreach ($userRoles as $role) {
$aclTables[] = json_decode($role->get('data'));
$aclTables[] = $role->get('data');
}
$teams = $this->user->get('teams');
foreach ($teams as $team) {
$teamRoles = $team->get('roles');
foreach ($teamRoles as $role) {
$aclTables[] = json_decode($role->get('data'));
$aclTables[] = $role->get('data');
}
}

View File

@@ -43,6 +43,8 @@ class Application
date_default_timezone_set('UTC');
$GLOBALS['log'] = $this->container->get('log');
$this->initAutoloads();
}
public function getSlim()
@@ -88,6 +90,7 @@ class Application
$html = file_get_contents('main.html');
$html = str_replace('{{cacheTimestamp}}', $config->get('cacheTimestamp', 0), $html);
$html = str_replace('{{useCache}}', $config->get('useCache') ? 'true' : 'false' , $html);
$html = str_replace('{{runScript}}', 'app.start();' , $html);
echo $html;
exit;
}
@@ -215,7 +218,6 @@ class Application
});
}
protected function initRoutes()
{
$routes = new \Espo\Core\Utils\Route($this->getContainer()->get('config'), $this->getMetadata(), $this->getContainer()->get('fileManager'));
@@ -238,5 +240,31 @@ class Application
}
}
}
protected function initAutoloads()
{
$autoload = new \Espo\Core\Utils\Autoload($this->getContainer()->get('config'), $this->getMetadata(), $this->getContainer()->get('fileManager'));
$autoloadList = $autoload->getAll();
if (empty($autoloadList)) {
return;
}
$namespacesPath = 'vendor/composer/autoload_namespaces.php';
$existingNamespaces = file_exists($namespacesPath) ? include($namespacesPath) : array();
if (!empty($existingNamespaces) && is_array($existingNamespaces)) {
$existingNamespaces = array_keys($existingNamespaces);
}
$classLoader = new \Composer\Autoload\ClassLoader();
foreach ($autoloadList as $prefix => $path) {
if (!in_array($prefix, $existingNamespaces)) {
$classLoader->add($prefix, $path);
}
}
$classLoader->register(true);
}
}

View File

@@ -36,6 +36,8 @@ class EntityManager extends Base
'password' => $config->get('database.password'),
'metadata' => $this->getContainer()->get('metadata')->getOrmMetadata(),
'repositoryFactoryClassName' => '\\Espo\\Core\\ORM\\RepositoryFactory',
'driver' => $config->get('database.driver'),
'platform' => $config->get('database.platform')
);
$entityManager = new \Espo\Core\ORM\EntityManager($params);

View File

@@ -5,21 +5,28 @@ namespace Espo\Core\Mail;
use \Zend\Mime\Mime as Mime;
class Importer
{
{
private $entityManager;
private $fileManager;
private $config;
public function __construct($entityManager, $fileManager)
public function __construct($entityManager, $fileManager, $config)
{
$this->entityManager = $entityManager;
$this->fileManager = $fileManager;
$this->config = $config;
}
protected function getEntityManager()
{
return $this->entityManager;
}
protected function getConfig()
{
return $this->config;
}
protected function getFileManager()
{
@@ -36,22 +43,22 @@ class Importer
$subject = '--empty--';
}
$email->set('isHtml', false);
$email->set('isHtml', false);
$email->set('name', $subject);
$email->set('status', 'Archived');
$email->set('attachmentsIds', array());
$email->set('attachmentsIds', array());
$email->set('assignedUserId', $userId);
$email->set('teamsIds', $teamsIds);
$fromArr = $this->getAddressListFromMessage($message, 'from');
$fromArr = $this->getAddressListFromMessage($message, 'from');
if (isset($message->from)) {
$email->set('fromName', $message->from);
}
}
$email->set('from', $fromArr[0]);
$email->set('to', implode(';', $this->getAddressListFromMessage($message, 'to')));
$email->set('to', implode(';', $this->getAddressListFromMessage($message, 'to')));
$email->set('cc', implode(';', $this->getAddressListFromMessage($message, 'cc')));
if (isset($message->messageId) && !empty($message->messageId)) {
if (isset($message->messageId) && !empty($message->messageId)) {
$email->set('messageId', $message->messageId);
if (isset($message->deliveredTo)) {
$email->set('messageIdInternal', $message->messageId . '-' . $message->deliveredTo);
@@ -60,29 +67,29 @@ class Importer
if ($this->checkIsDuplicate($email)) {
return false;
}
}
if (isset($message->date)) {
$dt = new \DateTime($message->date);
if ($dt) {
$dateSent = $dt->setTimezone(new \DateTimeZone('UTC'))->format('Y-m-d H:i:s');
$dateSent = $dt->setTimezone(new \DateTimeZone('UTC'))->format('Y-m-d H:i:s');
$email->set('dateSent', $dateSent);
}
}
if (isset($message->deliveryDate)) {
$dt = new \DateTime($message->deliveryDate);
if ($dt) {
$deliveryDate = $dt->setTimezone(new \DateTimeZone('UTC'))->format('Y-m-d H:i:s');
$deliveryDate = $dt->setTimezone(new \DateTimeZone('UTC'))->format('Y-m-d H:i:s');
$email->set('deliveryDate', $deliveryDate);
}
}
$inlineIds = array();
if ($message->isMultipart()) {
if ($message->isMultipart()) {
foreach (new \RecursiveIteratorIterator($message) as $part) {
$this->importPartDataToEmail($email, $part, $inlineIds);
}
}
} else {
$this->importPartDataToEmail($email, $message, $inlineIds);
}
@@ -95,6 +102,39 @@ class Importer
$email->set('body', $body);
}
if (isset($message->references) && !empty($message->references)) {
$reference = str_replace(array('/', '@'), " ", trim($message->references, '<>'));
$parentType = $parentId = null;
$emailSent = PHP_INT_MAX;
$n = sscanf($reference, '%s %s %d %d espo', $parentType, $parentId, $emailSent, $number);
if ($n == 4 && $emailSent < time()) {
if (!empty($parentType) && !empty($parentId)) {
$email->set('parentType', $parentType);
$email->set('parentId', $parentId);
}
}
}
if (!$email->has('parentId')) {
$from = $email->get('from');
if ($from) {
$contact = $this->getEntityManager()->getRepository('Contact')->where(array(
'emailAddress' => $from
))->findOne();
if ($contact) {
if (!$this->getConfig()->get('b2cMode')) {
if ($contact->get('accountId')) {
$email->set('parentType', 'Account');
$email->set('parentId', $contact->get('accountId'));
}
} else {
$email->set('parentType', 'Contact');
$email->set('parentId', $contact->id);
}
}
}
}
$this->getEntityManager()->saveEntity($email);
return $email;
@@ -128,15 +168,15 @@ class Importer
}
protected function importPartDataToEmail(\Espo\Entities\Email $email, $part, &$inlineIds = array())
{
{
try {
$type = strtok($part->contentType, ';');
$encoding = null;
switch ($type) {
case 'text/plain':
$content = $this->getContentFromPart($part);
if (!$email->get('body')) {
case 'text/plain':
$content = $this->getContentFromPart($part);
if (!$email->get('body')) {
$email->set('body', $content);
}
$email->set('bodyPlain', $content);
@@ -147,23 +187,23 @@ class Importer
$email->set('isHtml', true);
break;
default:
$content = $part->getContent();
$content = $part->getContent();
$disposition = null;
$fileName = null;
$contentId = null;
if (isset($part->ContentDisposition)) {
if (isset($part->ContentDisposition)) {
if (strpos($part->ContentDisposition, 'attachment') === 0) {
if (preg_match('/filename="?([^"]+)"?/i', $part->ContentDisposition, $m)) {
$fileName = $m[1];
$disposition = 'attachment';
}
}
} else if (strpos($part->ContentDisposition, 'inline') === 0) {
$contentId = trim($part->contentID, '<>');
$fileName = $contentId;
$disposition = 'inline';
}
}
}
if (isset($part->contentTransferEncoding)) {
@@ -171,7 +211,7 @@ class Importer
}
$attachment = $this->getEntityManager()->getEntity('Attachment');
$attachment->set('name', $fileName);
$attachment->set('name', $fileName);
$attachment->set('type', $type);
if ($disposition == 'inline') {
@@ -188,18 +228,18 @@ class Importer
$this->getEntityManager()->saveEntity($attachment);
$path = 'data/upload/' . $attachment->id;
$this->getFileManager()->putContents($path, $content);
$path = 'data/upload/' . $attachment->id;
$this->getFileManager()->putContents($path, $content);
if ($disposition == 'attachment') {
$attachmentsIds = $email->get('attachmentsIds');
$attachmentsIds[] = $attachment->id;
$email->set('attachmentsIds', $attachmentsIds);
$email->set('attachmentsIds', $attachmentsIds);
} else if ($disposition == 'inline') {
$inlineIds[$contentId] = $attachment->id;
}
}
}
} catch (\Exception $e) {}
} catch (\Exception $e) {}
}
protected function getContentFromPart($part)
@@ -223,7 +263,7 @@ class Importer
$content = base64_decode($content);
}
$charset = 'UTF-8';
$charset = 'UTF-8';
if (isset($part->contentType)) {
$ctHeader = $part->getHeader('Content-Type');
@@ -235,14 +275,14 @@ class Importer
if ($charset !== 'UTF-8') {
$content = mb_convert_encoding($content, 'UTF-8', $charset);
}
}
if (isset($part->contentTransferEncoding)) {
$cteHeader = $part->getHeader('Content-Transfer-Encoding');
if ($cteHeader->getTransferEncoding() == 'quoted-printable') {
$cteHeader = $part->getHeader('Content-Transfer-Encoding');
if ($cteHeader->getTransferEncoding() == 'quoted-printable') {
$content = quoted_printable_decode($content);
}
}
}
}
return $content;
}

View File

@@ -32,8 +32,6 @@ use \Espo\Core\Interfaces\Injectable;
class RDB extends \Espo\ORM\Repositories\RDB implements Injectable
{
public static $mapperClassName = '\\Espo\\Core\\ORM\\DB\\MysqlMapper';
protected $dependencies = array(
'metadata'
);
@@ -247,8 +245,18 @@ class RDB extends \Espo\ORM\Repositories\RDB implements Injectable
foreach ($entity->getRelations() as $name => $defs) {
if (in_array($defs['type'], $relationTypes)) {
$fieldName = $name . 'Ids';
if ($entity->has($fieldName)) {
$specifiedIds = $entity->get($fieldName);
$columnsFieldsName = $name . 'Columns';
if ($entity->has($fieldName) || $entity->has($columnsFieldsName)) {
if ($entity->has($fieldName)) {
$specifiedIds = $entity->get($fieldName);
} else {
$specifiedIds = array();
foreach ($entity->get($columnsFieldsName) as $id => $d) {
$specifiedIds[] = $id;
}
}
if (is_array($specifiedIds)) {
$toRemoveIds = array();
$existingIds = array();
@@ -258,9 +266,8 @@ class RDB extends \Espo\ORM\Repositories\RDB implements Injectable
$defs = array();
$columns = $this->getMetadata()->get("entityDefs." . $entity->getEntityName() . ".fields.{$name}.columns");
if (!empty($columns)) {
$columnData = $entity->get($name . 'Columns');
$columnData = $entity->get($columnsFieldsName);
$defs['additionalColumns'] = $columns;
}
foreach ($entity->get($name, $defs) as $foreignEntity) {

View File

@@ -40,6 +40,8 @@ class Base
protected $metadata;
private $seed = null;
const MIN_LENGTH_FOR_CONTENT_SEARCH = 4;
public function __construct($entityManager, \Espo\Entities\User $user, Acl $acl, $metadata)
@@ -90,6 +92,14 @@ class Base
return $this->metadata->get("entityDefs.{$this->entityName}.collection.textFilterFields", array('name'));
}
protected function getSeed()
{
if (empty($this->seed)) {
$this->seed = $this->entityManager->getEntity($this->entityName);
}
return $this->seed;
}
protected function where($params, &$result)
{
if (!empty($params['where']) && is_array($params['where'])) {
@@ -108,7 +118,7 @@ class Base
if (empty($result['whereClause'])) {
$result['whereClause'] = array();
}
$fieldDefs = $this->entityManager->getEntity($this->entityName)->getFields();
$fieldDefs = $this->getSeed()->getFields();
$fieldList = $this->getTextFilterFields();
$d = array();
foreach ($fieldList as $field) {
@@ -146,14 +156,32 @@ class Base
$joins = array();
$part = array();
foreach ($linkedWith as $link => $ids) {
$joins[] = $link;
$defs = $this->entityManager->getMetadata()->get($this->entityName);
$entityName = $defs['relations'][$link]['entity'];
if ($entityName) {
$part[$entityName . '.id'] = $ids;
foreach ($linkedWith as $link => $idsValue) {
if (is_array($idsValue) && count($idsValue) == 1) {
$idsValue = $idsValue[0];
}
$relDefs = $this->getSeed()->getRelations();
if (!empty($relDefs[$link])) {
$defs = $relDefs[$link];
if ($defs['type'] == 'manyMany') {
$joins[] = $link;
if (!empty($defs['relationName']) && !empty($defs['midKeys'])) {
$key = $defs['midKeys'][1];
$relationName = $defs['relationName'];
$part[$relationName . '.' . $key] = $idsValue;
}
} else if ($defs['type'] == 'belongsTo') {
if (!empty($defs['type']['key'])) {
$key = $defs['type']['key'];
$part[$key] = $idsValue;
}
}
}
}
if (!empty($part)) {
@@ -175,7 +203,7 @@ class Base
$result['whereClause'] = array();
}
$fieldDefs = $this->entityManager->getEntity($this->entityName)->getFields();
$fieldDefs = $this->getSeed()->getFields();
$value = $params['q'];
@@ -222,7 +250,6 @@ class Base
'Team.id' => $this->user->get('teamsIds'),
'assignedUserId' => $this->user->id
);
//$result['whereClause']['Team.id'] = $this->user->get('teamsIds');
}
}

View File

@@ -0,0 +1,127 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM - Open Source CRM application.
* Copyright (C) 2014 Yuri Kuznetsov, Taras Machyshyn, Oleksiy Avramenko
* Website: http://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/.
************************************************************************/
namespace Espo\Core\Utils;
class Autoload
{
protected $data = null;
private $fileManager;
private $config;
private $metadata;
protected $cacheFile = 'data/cache/application/autoload.php';
protected $paths = array(
'corePath' => 'application/Espo/Resources/autoload.json',
'modulePath' => 'application/Espo/Modules/{*}/Resources/autoload.json',
'customPath' => 'custom/Espo/Custom/Resources/autoload.json',
);
public function __construct(Config $config, Metadata $metadata, File\Manager $fileManager)
{
$this->config = $config;
$this->metadata = $metadata;
$this->fileManager = $fileManager;
}
protected function getConfig()
{
return $this->config;
}
protected function getFileManager()
{
return $this->fileManager;
}
protected function getMetadata()
{
return $this->metadata;
}
public function get($key = null, $returns = null)
{
if (!isset($this->data)) {
$this->init();
}
if (!isset($key)) {
return $this->data;
}
return Utill::getValueByKey($this->data, $key, $returns);
}
public function getAll()
{
return $this->get();
}
protected function init()
{
if (file_exists($this->cacheFile) && $this->getConfig()->get('useCache')) {
$this->data = $this->getFileManager()->getContents($this->cacheFile);
return;
}
$this->data = $this->unify();
if ($this->getConfig()->get('useCache')) {
$result = $this->getFileManager()->putContentsPHP($this->cacheFile, $this->data);
if ($result == false) {
throw new \Espo\Core\Exceptions\Error('Autoload: Cannot save unified autoload.');
}
}
}
protected function unify()
{
$data = $this->loadData($this->paths['corePath']);
foreach ($this->getMetadata()->getModuleList() as $moduleName) {
$modulePath = str_replace('{*}', $moduleName, $this->paths['modulePath']);
$data = array_merge($data, $this->loadData($modulePath));
}
$data = array_merge($data, $this->loadData($this->paths['customPath']));
return $data;
}
protected function loadData($autoloadFile, $returns = array())
{
if (file_exists($autoloadFile)) {
$content= $this->getFileManager()->getContents($autoloadFile);
$arrayContent = Json::getArrayData($content);
if (!empty($arrayContent)) {
return $arrayContent;
}
$GLOBALS['log']->error('Autoload::unify() - Empty file or syntax error - ['.$autoloadFile.']');
}
return $returns;
}
}

View File

@@ -18,11 +18,11 @@
*
* You should have received a copy of the GNU General Public License
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
************************************************************************/
************************************************************************/
namespace Espo\Core\Utils;
class Crypt
class Crypt
{
private $config;
@@ -34,9 +34,9 @@ class Crypt
public function __construct($config)
{
$this->config = $config;
$this->config = $config;
$this->cryptKey = $config->get('cryptKey', '');
}
}
protected function getKey()
{

View File

@@ -31,7 +31,6 @@ use Doctrine\DBAL\Schema\TableDiff,
class MySqlPlatform extends \Doctrine\DBAL\Platforms\MySqlPlatform
{
public function getAlterTableSQL(TableDiff $diff)
{
$columnSql = array();
@@ -104,7 +103,6 @@ class MySqlPlatform extends \Doctrine\DBAL\Platforms\MySqlPlatform
return array_merge($sql, $tableSql, $columnSql);
}
protected function getPreAlterTableIndexForeignKeySQL(TableDiff $diff)
{
$sql = array();
@@ -139,7 +137,6 @@ class MySqlPlatform extends \Doctrine\DBAL\Platforms\MySqlPlatform
return $sql;
}
public function getDropIndexSQL($index, $table=null)
{
if ($index instanceof Index) {
@@ -270,5 +267,14 @@ class MySqlPlatform extends \Doctrine\DBAL\Platforms\MySqlPlatform
return 'ALTER TABLE ' . $this->espoQuote($table) . ' ADD PRIMARY KEY (' . $this->getIndexFieldDeclarationListSQL($index->getQuotedColumns($this)) . ')';
}
protected function _getCreateTableSQL($tableName, array $columns, array $options = array())
{
if (!isset($options['engine'])) {
$options['engine'] = 'MyISAM';
}
return parent::_getCreateTableSQL($tableName, $columns, $options);
}
//end: ESPO
}

View File

@@ -151,6 +151,10 @@ class Converter
),
);
if (isset($entityMeta['indexes'])) {
$ormMeta[$entityName]['indexes'] = $entityMeta['indexes'];
}
$ormMeta[$entityName]['fields'] = $this->convertFields($entityName, $entityMeta);
$convertedLinks = $this->convertLinks($entityName, $entityMeta, $ormMeta);

View File

@@ -22,7 +22,7 @@
namespace Espo\Core\Utils\Database\Orm\Relations;
class NoteAttachments extends HasChildren
class Attachments extends HasChildren
{
protected function load($linkName, $entityName)
{

View File

@@ -162,9 +162,20 @@ class Converter
}
$tables[$entityName]->setPrimaryKey($primaryColumns);
//add indexes
if (isset($entityParams['indexes']) && is_array($entityParams['indexes'])) {
foreach ($entityParams['indexes'] as $indexName => $indexParams) {
if (is_array($indexParams['columns'])) {
$tableIndexName = $this->generateIndexName($indexName, $entityName);
$indexList[$tableIndexName] = Util::toUnderScore($indexParams['columns']);
}
}
}
if (!empty($indexList)) {
foreach($indexList as $indexItem) {
$tables[$entityName]->addIndex($indexItem);
foreach($indexList as $indexName => $indexItem) {
$tableIndexName = is_string($indexName) ? $indexName : null;
$tables[$entityName]->addIndex($indexItem, $tableIndexName);
}
}
@@ -361,5 +372,21 @@ class Converter
return $dependentEntities;
}
/**
* Generate index name
*
* @return string
*/
protected function generateIndexName($name, $entityName)
{
$names = array(
'IDX',
);
$names[] = strtoupper( Util::toUnderScore($entityName) );
$names[] = strtoupper( Util::toUnderScore($name) );
return implode('_', $names);
}
}

View File

@@ -198,7 +198,7 @@ class Schema
$result = true;
$connection = $this->getConnection();
foreach ($queries as $sql) {
$GLOBALS['log']->debug('SCHEMA, Execute Query: '.$sql);
$GLOBALS['log']->info('SCHEMA, Execute Query: '.$sql);
try {
$result &= (bool) $connection->executeQuery($sql);
} catch (\Exception $e) {

View File

@@ -18,11 +18,11 @@
*
* You should have received a copy of the GNU General Public License
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
************************************************************************/
************************************************************************/
namespace Espo\Core\Utils;
class DateTime
class DateTime
{
protected $dataFormat;
@@ -48,8 +48,8 @@ class DateTime
$this->dateFormat = $dateFormat;
$this->timeFormat = $timeFormat;
$this->timezone = new \DateTimeZone($timeZone);
}
$this->timezone = new \DateTimeZone($timeZone);
}
protected function getPhpDateFormat()
{
@@ -63,7 +63,7 @@ class DateTime
public function convertSystemDateToGlobal($string)
{
$dateTime = \DateTime::createFromFormat('Y-m-d', $string);
$dateTime = \DateTime::createFromFormat('Y-m-d', $string);
if ($dateTime) {
return $dateTime->format($this->getPhpDateFormat());
}
@@ -72,12 +72,32 @@ class DateTime
public function convertSystemDateTimeToGlobal($string)
{
$dateTime = \DateTime::createFromFormat('Y-m-d H:i:s', $string);
$dateTime = \DateTime::createFromFormat('Y-m-d H:i:s', $string);
if ($dateTime) {
return $dateTime->setTimezone($this->timezone)->format($this->getPhpDateTimeFormat());
}
return null;
}
public function convertSystemDate($string)
{
return $this->convertSystemDateToGlobal($string);
}
public function convertSystemDateTime($string, $timezone = null)
{
$dateTime = \DateTime::createFromFormat('Y-m-d H:i:s', $string);
if (empty($timezone)) {
$timezone = $this->timezone;
} else {
$timezone = new \DateTimeZone($timezone);
}
if ($dateTime) {
return $dateTime->setTimezone($timezone)->format($this->getPhpDateTimeFormat());
}
return null;
}
}

View File

@@ -723,7 +723,7 @@ class Manager
*/
public function getPHPFormat($content)
{
if (empty($content)) {
if (!isset($content)) {
return false;
}

View File

@@ -53,8 +53,6 @@ class Metadata
private $ormCacheFile = 'data/cache/application/ormMetadata.php';
private $moduleList = null;
public function __construct(\Espo\Core\Utils\Config $config, \Espo\Core\Utils\File\Manager $fileManager)
@@ -130,7 +128,7 @@ class Metadata
*/
protected function getData()
{
if (!isset($this->meta)) {
if (empty($this->meta) || !is_array($this->meta)) {
$this->init();
}
@@ -282,7 +280,7 @@ class Metadata
public function getOrmMetadata($reload = false)
{
if (!empty($this->ormMeta) && !$reload) {
if (!empty($this->ormMeta) && is_array($this->ormMeta) && !$reload) {
return $this->ormMeta;
}
@@ -329,7 +327,6 @@ class Metadata
return implode($delim, array($path, 'Repositories', Util::normilizeClassName(ucfirst($entityName))));
}
/**
* Get Scopes
*
@@ -342,10 +339,15 @@ class Metadata
}
$metadata = $this->getMetadataOnly(false);
if (!is_array($metadata)) {
$metadata = $this->getMetadataOnly(false, true);
}
$scopes = array();
foreach ($metadata['scopes'] as $name => $details) {
$scopes[$name] = isset($details['module']) ? $details['module'] : false;
if (is_array($metadata['scopes'])) {
foreach ($metadata['scopes'] as $name => $details) {
$scopes[$name] = isset($details['module']) ? $details['module'] : false;
}
}
return $this->scopes = $scopes;
@@ -418,9 +420,9 @@ class Metadata
*/
public function isScopeExists($scopeName)
{
$scopeModuleMap= $this->getScopes();
$scopeModuleMap = $this->getScopes();
$lowerEntityName= strtolower($scopeName);
$lowerEntityName = strtolower($scopeName);
foreach($scopeModuleMap as $rowEntityName => $rowModuleName) {
if ($lowerEntityName == strtolower($rowEntityName)) {
return true;

View File

@@ -63,13 +63,21 @@ class Util
* Convert name to Camel Case format, ex. camel_case to camelCase
*
* @param string $name
* @param string $symbol
* @param string | array $symbol
* @param boolean $capitaliseFirstChar
*
* @return string
*/
public static function toCamelCase($name, $symbol = '_', $capitaliseFirstChar = false)
{
if (is_array($name)) {
foreach ($name as &$value) {
$value = static::toCamelCase($value, $symbol, $capitaliseFirstChar);
}
return $name;
}
if($capitaliseFirstChar) {
$name[0] = strtoupper($name[0]);
}
@@ -85,12 +93,20 @@ class Util
* Convert name from Camel Case format.
* ex. camelCase to camel-case
*
* @param string $name
* @param string | array $name
*
* @return string
*/
public static function fromCamelCase($name, $symbol = '_')
{
if (is_array($name)) {
foreach ($name as &$value) {
$value = static::fromCamelCase($value, $symbol);
}
return $name;
}
$name[0] = strtolower($name[0]);
return preg_replace_callback('/([A-Z])/', function ($matches) use ($symbol) {
return $symbol . strtolower($matches[1]);
@@ -101,7 +117,7 @@ class Util
* Convert name from Camel Case format to underscore.
* ex. camelCase to camel_case
*
* @param string $name
* @param string | array $name
*
* @return string
*/

View File

@@ -18,24 +18,24 @@
*
* You should have received a copy of the GNU General Public License
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
************************************************************************/
************************************************************************/
namespace Espo\Entities;
class Integration extends \Espo\Core\ORM\Entity
{
{
public function get($name)
{
if ($name == 'id') {
return $this->id;
}
if ($this->hasField($name)) {
if (array_key_exists($name, $this->valuesContainer)) {
return $this->valuesContainer[$name];
}
} else {
if ($this->get('data')) {
if ($this->get('data')) {
$data = $this->get('data');
} else {
$data = new \stdClass();
@@ -46,20 +46,20 @@ class Integration extends \Espo\Core\ORM\Entity
}
return null;
}
public function clear($name)
{
parent::clear($name);
$data = $this->get('data');
if (empty($data)) {
$data = new \stdClass();
}
unset($data->$name);
$this->set('data', $data);
$this->set('data', $data);
}
public function set($p1, $p2)
public function set($p1, $p2 = null)
{
if (is_array($p1)) {
if ($p2 === null) {
@@ -68,49 +68,46 @@ class Integration extends \Espo\Core\ORM\Entity
$this->populateFromArray($p1, $p2);
return;
}
$name = $p1;
$value = $p2;
if ($name == 'id') {
$this->id = $value;
return;
}
if ($this->hasField($name)) {
$this->valuesContainer[$name] = $value;
} else {
if (!$this->get('enabled')) {
return;
}
$data = $this->get('data');
if (empty($data)) {
$data = new \stdClass();
}
$data->$name = $value;
$this->set('data', $data);
$this->set('data', $data);
}
}
public function populateFromArray(array $arr, $onlyAccessible = true, $reset = false)
{
if ($reset) {
$this->reset();
}
foreach ($arr as $field => $value) {
foreach ($arr as $field => $value) {
if (is_string($field)) {
if (is_array($value) || ($value instanceof \stdClass)) {
$value = json_encode($value);
}
if ($this->hasField($field)) {
$fields = $this->getFields();
$fieldDefs = $fields[$field];
if (!is_null($value)) {
switch ($fieldDefs['type']) {
case self::VARCHAR:
case self::VARCHAR:
break;
case self::BOOL:
$value = ($value === 'true' || $value === '1' || $value === true);
@@ -138,20 +135,20 @@ class Integration extends \Espo\Core\ORM\Entity
}
}
}
$this->set($field, $value);
}
}
}
public function toArray()
{
{
$arr = array();
if (isset($this->id)) {
$arr['id'] = $this->id;
}
foreach ($this->fields as $field => $defs) {
foreach ($this->fields as $field => $defs) {
if ($field == 'id') {
continue;
}
@@ -159,17 +156,17 @@ class Integration extends \Espo\Core\ORM\Entity
$arr[$field] = $this->get($field);
}
}
$data = $this->get('data');
if (empty($data)) {
$data = new \stdClass();
}
$dataArr = get_object_vars($data);
$arr = array_merge($arr, $dataArr);
$arr = array_merge($arr, $dataArr);
return $arr;
}
}

View File

@@ -18,7 +18,7 @@
*
* You should have received a copy of the GNU General Public License
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
************************************************************************/
************************************************************************/
namespace Espo\Entities;
@@ -26,16 +26,29 @@ class Note extends \Espo\Core\ORM\Entity
{
public function loadAttachments()
{
$collection = $this->get('attachments');
$data = $this->get('data');
if (!empty($data) && !empty($data->attachmentsIds)) {
$attachmentsIds = $data->attachmentsIds;
$collection = array();
foreach ($attachmentsIds as $id) {
$attachment = $this->entityManager->getEntity('Attachment', $id);
if ($attachment) {
$collection[] = $attachment;
}
}
} else {
$collection = $this->get('attachments');
}
$ids = array();
$names = new \stdClass();
$types = new \stdClass();
$types = new \stdClass();
foreach ($collection as $e) {
$id = $e->id;
$ids[] = $id;
$names->$id = $e->get('name');
$types->$id = $e->get('type');
}
}
$this->set('attachmentsIds', $ids);
$this->set('attachmentsNames', $names);
$this->set('attachmentsTypes', $types);

View File

@@ -20,10 +20,10 @@
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
************************************************************************/
namespace Espo\Core\ORM\DB;
namespace Espo\Entities;
class MysqlMapper extends \Espo\ORM\DB\MysqlMapper
class PasswordChangeRequest extends \Espo\Core\ORM\Entity
{
protected $returnCollection = false;
}

View File

@@ -0,0 +1,85 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM - Open Source CRM application.
* Copyright (C) 2014 Yuri Kuznetsov, Taras Machyshyn, Oleksiy Avramenko
* Website: http://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/.
************************************************************************/
namespace Espo\EntryPoints;
use \Espo\Core\Exceptions\NotFound;
use \Espo\Core\Exceptions\Forbidden;
use \Espo\Core\Exceptions\BadRequest;
use \Espo\Core\Exceptions\Error;
class Avatar extends Image
{
public static $authRequired = true;
public function run()
{
if (empty($_GET['id'])) {
throw new BadRequest();
}
$userId = $_GET['id'];
$user = $this->getEntityManager()->getEntity('User', $userId);
if (!$user) {
throw new NotFound();
}
if (isset($_GET['attachmentId'])) {
$id = $_GET['attachmentId'];
if ($id == 'false') {
$id = false;
}
} else {
$id = $user->get('avatarId');
}
$size = null;
if (!empty($_GET['size'])) {
$size = $_GET['size'];
}
if (!empty($id)) {
$this->show($id, $size);
} else {
$identicon = new \Identicon\Identicon();
if (empty($size)) {
$size = 'small';
}
if (!empty($this->imageSizes[$size])) {
$width = $this->imageSizes[$size][0];
header('Cache-Control: max-age=360000, must-revalidate');
header('Content-Type: image/png');
ob_clean();
flush();
$identicon->displayImage($userId, $width);
exit;
}
}
}
}

View File

@@ -0,0 +1,64 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM - Open Source CRM application.
* Copyright (C) 2014 Yuri Kuznetsov, Taras Machyshyn, Oleksiy Avramenko
* Website: http://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/.
************************************************************************/
namespace Espo\EntryPoints;
use \Espo\Core\Exceptions\NotFound;
use \Espo\Core\Exceptions\Forbidden;
use \Espo\Core\Exceptions\BadRequest;
class ChangePassword extends \Espo\Core\EntryPoints\Base
{
public static $authRequired = false;
public function run()
{
$requestId = $_GET['id'];
if (empty($requestId)) {
throw new BadRequest();
}
$config = $this->getConfig();
$p = $this->getEntityManager()->getRepository('PasswordChangeRequest')->where(array(
'requestId' => $requestId
))->findOne();
if (!$p) {
throw new NotFound();
}
$runScript = "
app.getController('PasswordChangeRequest', function (controller) {
controller.doAction('passwordChange', '{$requestId}');
});
";
$html = file_get_contents('main.html');
$html = str_replace('{{cacheTimestamp}}', $config->get('cacheTimestamp', 0), $html);
$html = str_replace('{{useCache}}', $config->get('useCache') ? 'true' : 'false' , $html);
$html = str_replace('{{runScript}}', $runScript , $html);
echo $html;
exit;
}
}

View File

@@ -18,7 +18,7 @@
*
* You should have received a copy of the GNU General Public License
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
************************************************************************/
************************************************************************/
namespace Espo\EntryPoints;
@@ -37,9 +37,11 @@ class Image extends \Espo\Core\EntryPoints\Base
'image/gif',
);
protected $imageSizes = array(
'x-small' => array(64, 64),
'small' => array(128, 128),
protected $imageSizes = array(
'xxx-small' => array(18, 18),
'xx-small' => array(32, 32),
'x-small' => array(64, 64),
'small' => array(128, 128),
'medium' => array(256, 256),
'large' => array(512, 512),
'x-large' => array(864, 864),
@@ -48,16 +50,17 @@ class Image extends \Espo\Core\EntryPoints\Base
public function run()
{
$id = $_GET['id'];
if (empty($id)) {
{
if (empty($_GET['id'])) {
throw new BadRequest();
}
}
$id = $_GET['id'];
$size = null;
$size = null;
if (!empty($_GET['size'])) {
$size = $_GET['size'];
}
}
$this->show($id, $size);
}
@@ -68,10 +71,10 @@ class Image extends \Espo\Core\EntryPoints\Base
if (!$attachment) {
throw new NotFound();
}
}
if ($attachment->get('parentId') && $attachment->get('parentType')) {
$parent = $this->getEntityManager()->getEntity($attachment->get('parentType'), $attachment->get('parentId'));
$parent = $this->getEntityManager()->getEntity($attachment->get('parentType'), $attachment->get('parentId'));
if ($parent && !$this->getAcl()->check($parent)) {
throw new Forbidden();
}
@@ -94,8 +97,8 @@ class Image extends \Espo\Core\EntryPoints\Base
$thumbFilePath = "data/upload/thumbs/{$attachment->id}_{$size}";
if (!file_exists($thumbFilePath)) {
$targetImage = $this->getThumbImage($filePath, $fileType, $size);
ob_start();
$targetImage = $this->getThumbImage($filePath, $fileType, $size);
ob_start();
switch ($fileType) {
case 'image/jpeg':
@@ -106,31 +109,32 @@ class Image extends \Espo\Core\EntryPoints\Base
break;
case 'image/gif':
imagegif($targetImage);
break;
break;
}
$contents = ob_get_contents();
ob_end_clean();
imagedestroy($targetImage);
$this->getContainer()->get('fileManager')->putContents($thumbFilePath, $contents);
}
$filePath = $thumbFilePath;
imagedestroy($targetImage);
$this->getContainer()->get('fileManager')->putContents($thumbFilePath, $contents);
}
$filePath = $thumbFilePath;
} else {
throw new Error();
}
}
}
if (!empty($size)) {
if (!empty($size)) {
$fileName = $attachment->id . '_' . $size . '.jpg';
} else {
$fileName = $attachment->get('name');
}
header('Content-Disposition:inline;filename="'.$fileName.'"');
}
header('Content-Disposition:inline;filename="'.$fileName.'"');
if (!empty($fileType)) {
header('Content-Type: ' . $fileType);
}
}
header('Pragma: public');
$fileSize = filesize($filePath);
header('Cache-Control: max-age=360000, must-revalidate');
$fileSize = filesize($filePath);
if ($fileSize) {
header('Content-Length: ' . $fileSize);
}
@@ -144,15 +148,14 @@ class Image extends \Espo\Core\EntryPoints\Base
{
list($originalWidth, $originalHeight) = getimagesize($filePath);
list($width, $height) = $this->imageSizes[$size];
if ($originalWidth <= $width && $originalHeight <= $height) {
$targetWidth = $originalWidth;
$targetHeight = $originalHeight;
$targetHeight = $originalHeight;
} else {
if ($originalWidth > $originalHeight) {
$targetWidth = $width;
$targetHeight = $originalHeight / ($originalWidth / $width);
$targetHeight = $originalHeight / ($originalWidth / $width);
if ($targetHeight > $height) {
$targetHeight = $height;
$targetWidth = $originalWidth / ($originalHeight / $height);
@@ -165,13 +168,13 @@ class Image extends \Espo\Core\EntryPoints\Base
$targetHeight = $originalHeight / ($originalWidth / $width);
}
}
}
}
$targetImage = imagecreatetruecolor($targetWidth, $targetHeight);
$targetImage = imagecreatetruecolor($targetWidth, $targetHeight);
switch ($fileType) {
case 'image/jpeg':
$sourceImage = imagecreatefromjpeg($filePath);
imagecopyresized($targetImage, $sourceImage, 0, 0, 0, 0, $targetWidth, $targetHeight, $originalWidth, $originalHeight);
imagecopyresampled ($targetImage, $sourceImage, 0, 0, 0, 0, $targetWidth, $targetHeight, $originalWidth, $originalHeight);
break;
case 'image/png':
$sourceImage = imagecreatefrompng($filePath);
@@ -183,8 +186,8 @@ class Image extends \Espo\Core\EntryPoints\Base
break;
case 'image/gif':
$sourceImage = imagecreatefromgif($filePath);
imagecopyresized($targetImage, $sourceImage, 0, 0, 0, 0, $targetWidth, $targetHeight, $originalWidth, $originalHeight);
break;
imagecopyresampled($targetImage, $sourceImage, 0, 0, 0, 0, $targetWidth, $targetHeight, $originalWidth, $originalHeight);
break;
}

View File

@@ -69,6 +69,9 @@ class Mentions extends \Espo\Core\Hooks\Base
);
$mentionData->$item = (object) $m;
if (!in_array($item, $previousMentionList)) {
if ($user->id == $this->getUser()->id) {
continue;
}
$this->notifyAboutMention($entity, $user);
}
}

View File

@@ -40,30 +40,75 @@ class Invitations
protected function parseInvitationTemplate($contents, $entity, $invitee = null, $uid = null)
{
$contents = str_replace('{name}', $entity->get('name'), $contents);
$contents = str_replace('{eventType}', strtolower($this->language->translate($entity->getEntityName(), 'scopeNames')), $contents);
$contents = str_replace('{dateStart}', $this->dateTime->convertSystemDateTimeToGlobal($entity->get('dateStart')), $contents);
foreach ($entity->getFields() as $field => $d) {
if (empty($d['type'])) continue;
$key = '{'.$field.'}';
switch ($d['type']) {
case 'datetime':
$contents = str_replace($key, $this->dateTime->convertSystemDateTimeToGlobal($entity->get($field)), $contents);
break;
case 'date':
$contents = str_replace($key, $this->dateTime->convertSystemDateToGlobal($entity->get($field)), $contents);
break;
default:
$contents = str_replace($key, $entity->get($field), $contents);
}
}
if ($invitee) {
$contents = str_replace('{inviteeName}', $invitee->get('name'), $contents);
}
$siteUrl = rtrim($this->config->get('siteUrl'), '/');
$url = $siteUrl . '/#' . $entity->getEntityName() . '/view/' . $entity->id;
$contents = str_replace('{url}', $url, $contents);
if ($invitee && $invitee->getEntityName() != 'User') {
$contents = preg_replace('/\{#userOnly\}(.*?)\{\/userOnly\}/s', '', $contents);
}
$contents = str_replace('{#userOnly}', '', $contents);
$contents = str_replace('{/userOnly}', '', $contents);
if ($uid) {
$siteUrl = rtrim($this->config->get('siteUrl'), '/');
$contents = str_replace('{acceptLink}', $siteUrl . '?entryPoint=eventConfirmation&action=accept&uid=' . $uid->get('name'), $contents);
$contents = str_replace('{declineLink}', $siteUrl . '?entryPoint=eventConfirmation&action=decline&uid=' . $uid->get('name'), $contents);
}
return $contents;
}
protected function getTemplate($name)
{
$systemLanguage = $this->config->get('language');
$fileName = 'custom/Espo/Custom/Resources/templates/'.$name.'.'.$systemLanguage.'.tpl';
if (!file_exists($fileName)) {
$fileName = 'application/Espo/Modules/Crm/Resources/templates/'.$name.'.'.$systemLanguage.'.tpl';
}
if (!file_exists($fileName)) {
$fileName = 'custom/Espo/Custom/Resources/templates/'.$name.'.en_US.tpl';
}
if (!file_exists($fileName)) {
$fileName = 'application/Espo/Modules/Crm/Resources/templates/'.$name.'.en_US.tpl';
}
return file_get_contents($fileName);
}
public function sendInvitation(Entity $entity, Entity $invitee, $link)
{
$uid = $this->getEntityManager()->getEntity('UniqueId');
$uid->set('data', json_encode(array(
$uid->set('data', array(
'eventType' => $entity->getEntityName(),
'eventId' => $entity->id,
'inviteeId' => $invitee->id,
'inviteeType' => $invitee->getEntityName(),
'link' => $link
)));
));
$this->getEntityManager()->saveEntity($uid);
$emailAddress = $invitee->get('emailAddress');
@@ -73,32 +118,9 @@ class Invitations
$email = $this->getEntityManager()->getEntity('Email');
$email->set('to', $emailAddress);
$systemLanguage = $this->config->get('language');
$subjectTplFileName = 'custom/Espo/Custom/Resources/templates/InvitationSubject.'.$systemLanguage.'.tpl';
if (!file_exists($subjectTplFileName)) {
$subjectTplFileName = 'application/Espo/Modules/Crm/Resources/templates/InvitationSubject.'.$systemLanguage.'.tpl';
}
if (!file_exists($subjectTplFileName)) {
$subjectTplFileName = 'custom/Espo/Custom/Resources/templates/InvitationSubject.en_US.tpl';
}
if (!file_exists($subjectTplFileName)) {
$subjectTplFileName = 'application/Espo/Modules/Crm/Resources/templates/InvitationSubject.en_US.tpl';
}
$subjectTpl = file_get_contents($subjectTplFileName);
$bodyTplFileName = 'custom/Espo/Custom/Resources/templates/InvitationBody.'.$systemLanguage.'.tpl';
if (!file_exists($bodyTplFileName)) {
$bodyTplFileName = 'application/Espo/Modules/Crm/Resources/templates/InvitationBody.'.$systemLanguage.'.tpl';
}
if (!file_exists($bodyTplFileName)) {
$bodyTplFileName = 'custom/Espo/Custom/Resources/templates/InvitationBody.en_US.tpl';
}
if (!file_exists($bodyTplFileName)) {
$bodyTplFileName = 'application/Espo/Modules/Crm/Resources/templates/InvitationBody.en_US.tpl';
}
$bodyTpl = file_get_contents($bodyTplFileName);
$subjectTpl = $this->getTemplate('InvitationSubject');
$bodyTpl = $this->getTemplate('InvitationBody');
$subject = $this->parseInvitationTemplate($subjectTpl, $entity, $invitee, $uid);
$subject = str_replace(array("\n", "\r"), '', $subject);
@@ -107,7 +129,7 @@ class Invitations
$email->set('subject', $subject);
$email->set('body', $body);
$email->set('isHtml', true);
$email->set('isHtml', true);
$this->getEntityManager()->saveEntity($email);
$attachmentName = ucwords($this->language->translate($entity->getEntityName(), 'scopeNames')).'.ics';
@@ -118,21 +140,21 @@ class Invitations
'contents' => $this->getIscContents($entity),
));
$email->addAttachment($attachment);
$email->addAttachment($attachment);
$emailSender = $this->mailSender;
if ($this->smtpParams) {
$emailSender->useSmtp($this->smtpParams);
}
$emailSender->send($email);
$emailSender->send($email);
$this->getEntityManager()->removeEntity($email);
}
protected function getIscContents(Entity $entity)
{
$user = $entity->get('assignedUser');
$user = $entity->get('assignedUser');
$who = '';
$email = '';

View File

@@ -0,0 +1,121 @@
<?php
namespace Espo\Modules\Crm\Business\Reminder;
use \Espo\ORM\Entity;
class EmailReminder
{
protected $entityManager;
protected $mailSender;
protected $config;
protected $dateTime;
protected $language;
public function __construct($entityManager, $mailSender, $config, $dateTime, $language)
{
$this->entityManager = $entityManager;
$this->mailSender = $mailSender;
$this->config = $config;
$this->dateTime = $dateTime;
$this->language = $language;
}
protected function getEntityManager()
{
return $this->entityManager;
}
protected function parseInvitationTemplate($contents, $entity, $user = null)
{
$contents = str_replace('{eventType}', strtolower($this->language->translate($entity->getEntityName(), 'scopeNames')), $contents);
$preferences = $this->getEntityManager()->getEntity('Preferences', $user->id);
$timezone = $preferences->get('timeZone');
foreach ($entity->getFields() as $field => $d) {
if (empty($d['type'])) continue;
$key = '{'.$field.'}';
switch ($d['type']) {
case 'datetime':
$contents = str_replace($key, $this->dateTime->convertSystemDateTime($entity->get($field), $timezone), $contents);
break;
case 'date':
$contents = str_replace($key, $this->dateTime->convertSystemDateToGlobal($entity->get($field)), $contents);
break;
default:
$contents = str_replace($key, $entity->get($field), $contents);
}
}
if ($user) {
$contents = str_replace('{userName}', $user->get('name'), $contents);
}
$siteUrl = rtrim($this->config->get('siteUrl'), '/');
$url = $siteUrl . '/#' . $entity->getEntityName() . '/view/' . $entity->id;
$contents = str_replace('{url}', $url, $contents);
return $contents;
}
protected function getTemplate($name)
{
$systemLanguage = $this->config->get('language');
$fileName = 'custom/Espo/Custom/Resources/templates/'.$name.'.'.$systemLanguage.'.tpl';
if (!file_exists($fileName)) {
$fileName = 'application/Espo/Modules/Crm/Resources/templates/'.$name.'.'.$systemLanguage.'.tpl';
}
if (!file_exists($fileName)) {
$fileName = 'custom/Espo/Custom/Resources/templates/'.$name.'.en_US.tpl';
}
if (!file_exists($fileName)) {
$fileName = 'application/Espo/Modules/Crm/Resources/templates/'.$name.'.en_US.tpl';
}
return file_get_contents($fileName);
}
public function send(Entity $reminder)
{
$user = $this->getEntityManager()->getEntity('User', $reminder->get('userId'));
$entity = $this->getEntityManager()->getEntity($reminder->get('entityType'), $reminder->get('entityId'));
$emailAddress = $user->get('emailAddress');
if (empty($user) || empty($emailAddress) || empty($entity)) {
return;
}
$email = $this->getEntityManager()->getEntity('Email');
$email->set('to', $emailAddress);
$subjectTpl = $this->getTemplate('ReminderSubject');
$bodyTpl = $this->getTemplate('ReminderBody');
$subject = $this->parseInvitationTemplate($subjectTpl, $entity, $user);
$subject = str_replace(array("\n", "\r"), '', $subject);
$body = $this->parseInvitationTemplate($bodyTpl, $entity, $user);
$email->set('subject', $subject);
$email->set('body', $body);
$email->set('isHtml', true);
$this->getEntityManager()->saveEntity($email);
$emailSender = $this->mailSender;
$emailSender->send($email);
$this->getEntityManager()->removeEntity($email);
}
}

View File

@@ -28,7 +28,7 @@ use \Espo\Core\Exceptions\Error,
class Activities extends \Espo\Core\Controllers\Base
{
public static $defaultAction = 'index';
public static $defaultAction = 'index';
public function actionListCalendarEvents($params, $data, $request)
{
@@ -48,6 +48,27 @@ class Activities extends \Espo\Core\Controllers\Base
return $service->getEvents($this->getUser()->id, $from, $to);
}
public function actionPopupNotifications()
{
$userId = $this->getUser()->id;
return $this->getService('Activities')->getPopupNotifications($userId);
}
public function actionRemovePopupNotification($params, $data, $request)
{
if (!$request->isPost()) {
throw new BadRequest();
}
if (empty($data['id'])) {
throw new BadRequest();
}
$id = $data['id'];
return $this->getService('Activities')->removeReminder($id);
}
public function actionList($params, $data, $request)
{
$name = $params['name'];
@@ -68,7 +89,7 @@ class Activities extends \Espo\Core\Controllers\Base
$scope = null;
if (!empty($where) && !empty($where['scope']) && $where['scope'] !== 'false') {
$scope = $where['scope'];
}
}
$service = $this->getService('Activities');

View File

@@ -0,0 +1,28 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM - Open Source CRM application.
* Copyright (C) 2014 Yuri Kuznetsov, Taras Machyshyn, Oleksiy Avramenko
* Website: http://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/.
************************************************************************/
namespace Espo\Modules\Crm\Entities;
class Reminder extends \Espo\Core\ORM\Entity
{
}

View File

@@ -34,7 +34,7 @@ class EventConfirmation extends \Espo\Core\EntryPoints\Base
public static $authRequired = false;
public function run()
{
{
$uid = $_GET['uid'];
$action = $_GET['action'];
if (empty($uid) || empty($action)) {
@@ -52,7 +52,7 @@ class EventConfirmation extends \Espo\Core\EntryPoints\Base
return;
}
$data = json_decode($uniqueId->get('data'));
$data = $uniqueId->get('data');
$eventType = $data->eventType;
$eventId = $data->eventId;
@@ -63,7 +63,7 @@ class EventConfirmation extends \Espo\Core\EntryPoints\Base
if (!empty($eventType) && !empty($eventId) && !empty($inviteeType) && !empty($inviteeId) && !empty($link)) {
$event = $this->getEntityManager()->getEntity($eventType, $eventId);
$invitee = $this->getEntityManager()->getEntity($inviteeType, $inviteeId);
if ($event && $invitee) {
if ($event && $invitee) {
$relDefs = $event->getRelations();
$tableName = Util::toUnderscore($relDefs[$link]['relationName']);
@@ -88,9 +88,9 @@ class EventConfirmation extends \Espo\Core\EntryPoints\Base
echo $status;
return;
}
}
}
throw new Error();
}
throw new Error();
}
}

View File

@@ -0,0 +1,68 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM - Open Source CRM application.
* Copyright (C) 2014 Yuri Kuznetsov, Taras Machyshyn, Oleksiy Avramenko
* Website: http://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/.
************************************************************************/
namespace Espo\Modules\Crm\Jobs;
use \Espo\Core\Exceptions;
class SendEmailReminders extends \Espo\Core\Jobs\Base
{
const MAX_PORTION_SIZE = 10;
public function run()
{
$dt = new \DateTime();
$now = $dt->format('Y-m-d H:i:s');
$nowShifted = $dt->sub(new \DateInterval('PT1H'))->format('Y-m-d H:i:s');
$collection = $this->getEntityManager()->getRepository('Reminder')->where(array(
'type' => 'Email',
'remindAt<=' => $now,
'startAt>' => $nowShifted,
))->find();
if (!empty($collection)) {
$emailReminder = new \Espo\Modules\Crm\Business\Reminder\EmailReminder(
$this->getEntityManager(),
$this->getContainer()->get('mailSender'),
$this->getConfig(),
$this->getContainer()->get('dateTime'),
$this->getContainer()->get('language')
);
$pdo = $this->getEntityManager()->getPDO();
}
foreach ($collection as $i => $entity) {
if ($i >= self::MAX_PORTION_SIZE) {
break;
}
$emailReminder->send($entity);
$sql = "DELETE FROM `reminder` WHERE id = ". $pdo->quote($entity->id);
$pdo->query($sql);
$this->getEntityManager()->removeEntity($entity);
}
return true;
}
}

View File

@@ -24,27 +24,8 @@ namespace Espo\Modules\Crm\Repositories;
use Espo\ORM\Entity;
class Call extends \Espo\Core\ORM\Repositories\RDB
{
protected function beforeSave(Entity $entity)
{
parent::beforeSave($entity);
$parentId = $entity->get('parentId');
$parentType = $entity->get('parentType');
if (!empty($parentId) || !empty($parentType)) {
$parent = $this->getEntityManager()->getEntity($parentType, $parentId);
if (!empty($parent)) {
if ($parent->getEntityName() == 'Account') {
$accountId = $parent->id;
} else if ($parent->has('accountId')) {
$accountId = $parent->get('accountId');
}
if (!empty($accountId)) {
$entity->set('accountId', $accountId);
}
}
}
}
class Call extends Meeting
{
}

View File

@@ -18,31 +18,31 @@
*
* You should have received a copy of the GNU General Public License
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
************************************************************************/
************************************************************************/
namespace Espo\Modules\Crm\Repositories;
use Espo\ORM\Entity;
class Contact extends \Espo\Core\ORM\Repositories\RDB
{
{
public function handleSelectParams(&$params)
{
parent::handleSelectParams($params);
parent::handleSelectParams($params);
if (empty($params['customJoin'])) {
$params['customJoin'] = '';
$params['customJoin'] = '';
}
$params['customJoin'] .= "
LEFT JOIN `account_contact` AS accountContact
$params['customJoin'] .= "
LEFT JOIN `account_contact` AS accountContact
ON accountContact.contact_id = contact.id AND accountContact.account_id = contact.account_id AND accountContact.deleted = 0
";
}
public function save(Entity $entity)
public function afterSave(Entity $entity)
{
$result = parent::save($entity);
$result = parent::afterSave($entity);
$accountIdChanged = $entity->has('accountId') && $entity->get('accountId') != $entity->getFetched('accountId');
$titleChanged = $entity->has('title') && $entity->get('title') != $entity->getFetched('title');
@@ -52,7 +52,7 @@ class Contact extends \Espo\Core\ORM\Repositories\RDB
if (empty($accountId)) {
$this->unrelate($entity, 'accounts', $entity->getFetched('accountId'));
return $result;
}
}
}
if ($titleChanged) {
@@ -62,20 +62,22 @@ class Contact extends \Espo\Core\ORM\Repositories\RDB
return $result;
}
}
}
}
if ($accountIdChanged || $titleChanged) {
if ($accountIdChanged || $titleChanged) {
$pdo = $this->getEntityManager()->getPDO();
$sql = "
SELECT id, role FROM account_contact
WHERE
WHERE
account_id = ".$pdo->quote($accountId)." AND
contact_id = ".$pdo->quote($entity->id)." AND
deleted = 0
deleted = 0
";
$sth = $pdo->prepare($sql);
$sth->execute();
if ($row = $sth->fetch()) {
if ($titleChanged && $entity->get('title') != $row['role']) {
$this->updateRelation($entity, 'accounts', $accountId, array(
@@ -83,11 +85,13 @@ class Contact extends \Espo\Core\ORM\Repositories\RDB
));
}
} else {
$this->relate($entity, 'accounts', $accountId, array(
'role' => $entity->get('title')
));
if ($accountIdChanged) {
$this->relate($entity, 'accounts', $accountId, array(
'role' => $entity->get('title')
));
}
}
}
}
return $result;
}

View File

@@ -18,16 +18,16 @@
*
* You should have received a copy of the GNU General Public License
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
************************************************************************/
************************************************************************/
namespace Espo\Modules\Crm\Repositories;
use Espo\ORM\Entity;
class Meeting extends \Espo\Core\ORM\Repositories\RDB
{
{
protected function beforeSave(Entity $entity)
{
{
parent::beforeSave($entity);
$parentId = $entity->get('parentId');
@@ -44,7 +44,143 @@ class Meeting extends \Espo\Core\ORM\Repositories\RDB
$entity->set('accountId', $accountId);
}
}
}
}
$assignedUserId = $entity->get('assignedUserId');
if ($assignedUserId && $entity->has('usersIds')) {
$usersIds = $entity->get('usersIds');
if (!is_array($usersIds)) {
$usersIds = array();
}
if (!in_array($assignedUserId, $usersIds)) {
$usersIds[] = $assignedUserId;
$entity->set('usersIds', $usersIds);
$hash = $entity->get('usersNames');
if ($hash instanceof \stdClass) {
$hash->$assignedUserId = $entity->get('assignedUserName');
$entity->set('usersNames', $hash);
}
}
}
}
public function getEntityReminders(Entity $entity)
{
$pdo = $this->getEntityManager()->getPDO();
$reminders = array();
$sql = "
SELECT id, `seconds`, `type`
FROM `reminder`
WHERE
`entity_type` = ".$pdo->quote($entity->getEntityName())." AND
`entity_id` = ".$pdo->quote($entity->id)." AND
`deleted` = 0
ORDER BY `seconds` ASC
";
$sth = $pdo->prepare($sql);
$sth->execute();
$rows = $sth->fetchAll(\PDO::FETCH_ASSOC);
foreach ($rows as $row) {
$o = new \StdClass();
$o->seconds = intval($row['seconds']);
$o->type = $row['type'];
$reminders[] = $o;
}
return $reminders;
$entity->set('reminders', $reminders);
}
protected function afterSave(Entity $entity)
{
parent::afterSave($entity);
if (
$entity->isNew() ||
$entity->isFieldChanged('assignedUserId') ||
$entity->isFieldChanged('dateStart') ||
$entity->has('reminders')
) {
$pdo = $this->getEntityManager()->getPDO();
$reminderTypeList = $this->getMetadata()->get('entityDefs.Reminder.fields.type.options');
if (!$entity->has('reminders')) {
$reminders = $this->getEntityReminders($entity);
} else {
$reminders = $entity->get('reminders');
}
if (!$entity->isNew()) {
$sql = "
DELETE FROM `reminder`
WHERE
entity_id = ".$pdo->quote($entity->id)." AND
entity_type = ".$pdo->quote($entity->getEntityName())." AND
deleted = 0
";
$pdo->query($sql);
}
if (empty($reminders) || !is_array($reminders)) return;
$entityType = $entity->getEntityName();
$dateStart = $entity->get('dateStart');
$assignedUserId = $entity->get('assignedUserId');
if (!$dateStart || !$assignedUserId) {
$e = $this->get($entity->id);
if ($e) {
$dateStart = $e->get('dateStart');
$assignedUserId = $e->get('assignedUserId');
}
}
if (!$dateStart || !$assignedUserId) {
return;
}
$dateStartObj = new \DateTime($dateStart);
if (!$dateStartObj) {
return;
}
foreach ($reminders as $item) {
$remindAt = clone $dateStartObj;
$seconds = intval($item->seconds);
$type = $item->type;
if (!in_array($type , $reminderTypeList)) continue;
$remindAt->sub(new \DateInterval('PT' . $seconds . 'S'));
$id = uniqid();
$sql = "
INSERT
INTO `reminder`
(id, entity_id, entity_type, `type`, user_id, remind_at, start_at, `seconds`)
VALUES (
".$pdo->quote($id).",
".$pdo->quote($entity->id).",
".$pdo->quote($entityType).",
".$pdo->quote($type).",
".$pdo->quote($assignedUserId).",
".$pdo->quote($remindAt->format('Y-m-d H:i:s')).",
".$pdo->quote($dateStart).",
".$pdo->quote($seconds)."
)
";
$pdo->query($sql);
}
}
}
}

View File

@@ -10,7 +10,8 @@
"description": "Beschreibung",
"users": "Benutzer",
"contacts": "Kontakte",
"leads": "Interessenten"
"leads": "Interessenten",
"reminders": "Erinnerungen"
},
"links": {
},
@@ -34,7 +35,7 @@
"Create Call": "Anruf erstellen",
"Set Held": "Auf gehalten setzen",
"Set Not Held": "Auf nicht gehalten setzen",
"Send Invitations": "Einladungen versenden"
"Send Invitations": "Einladungen versenden"
},
"presetFilters": {
"planned": "Geplant",

View File

@@ -86,5 +86,11 @@
"opportunity": "Verkaufschance",
"contact": "Kontakt",
"parent": "Bezieht sich auf"
},
"options": {
"reminderTypes": {
"Popup": "Popup",
"Email": "E-Mail"
}
}
}

View File

@@ -10,14 +10,15 @@
"port": "Port",
"monitoredFolders": "Überwachte Ordner",
"trashFolder": "Papierkorb Ordner",
"ssl": "SSL",
"ssl": "SSL",
"createCase": "Fall erstellen",
"reply": "Antworten",
"caseDistribution": "Fall Verteilung",
"replyEmailTemplate": "Vorlage E-Mail Antwort",
"replyFromAddress": "Von Adresse antworten",
"replyToAddress": "Antwort an Adresse",
"replyFromName": "Von Name antworten"
"replyFromName": "Von Name antworten",
"targetUserPosition": "Position Zielbenutzer"
},
"tooltips": {
"reply": "Absender informieren, dass die E-Mail empfangen wurde.",
@@ -44,7 +45,7 @@
"Create InboundEmail": "Eingehende E-Mail erstellen",
"IMAP": "IMAP",
"Actions": "Aktionen",
"Main": "Wichtig"
"Main": "Wichtig"
},
"messages": {
"couldNotConnectToImap": "Kann keine Verbindung zum IMAP Server herstellen"

View File

@@ -9,7 +9,8 @@
"description": "Beschreibung",
"users": "Benutzer",
"contacts": "Kontakte",
"leads": "Interessenten"
"leads": "Interessenten",
"reminders": "Erinnerungen"
},
"links": {
},
@@ -29,7 +30,9 @@
"Create Meeting": "Meeting erstellen",
"Set Held": "Auf gehalten setzen",
"Set Not Held": "Auf nicht gehalten setzen",
"Send Invitations": "Einladungen versenden"
"Send Invitations": "Einladungen versenden",
"on time": "Aktuelle Zeit",
"before": "Bevor"
},
"presetFilters": {
"planned": "Geplant",

View File

@@ -23,7 +23,7 @@
"Qualification": "Qualifikation",
"Needs Analysis": "Bedarfserhebung",
"Value Proposition": "Richtangebot",
"Id. Decision Makers": "Entscheider ident.",
"Id. Decision Makers": "Id. Entscheider",
"Perception Analysis": "Analyse Sichtweise",
"Proposal/Price Quote": "Preisangebot",
"Negotiation/Review": "Verhandlung/Überarbeitung",

View File

@@ -0,0 +1,9 @@
{
"options": {
"job": {
"CheckInboundEmails": "Eingehende E-Mail überprüfen",
"SendEmailReminders": "E-Mail Erinnerungen senden"
}
}
}

View File

@@ -10,7 +10,8 @@
"description": "Description",
"users": "Users",
"contacts": "Contacts",
"leads": "Leads"
"leads": "Leads",
"reminders": "Reminders"
},
"links": {
},
@@ -34,7 +35,7 @@
"Create Call": "Create Call",
"Set Held": "Set Held",
"Set Not Held": "Set Not Held",
"Send Invitations": "Send Invitations"
"Send Invitations": "Send Invitations"
},
"presetFilters": {
"planned": "Planned",

View File

@@ -86,5 +86,11 @@
"opportunity": "Opportunity",
"contact": "Contact",
"parent": "Parent"
},
"options": {
"reminderTypes": {
"Popup": "Popup",
"Email": "Email"
}
}
}

View File

@@ -9,7 +9,8 @@
"description": "Description",
"users": "Users",
"contacts": "Contacts",
"leads": "Leads"
"leads": "Leads",
"reminders": "Reminders"
},
"links": {
},
@@ -29,7 +30,9 @@
"Create Meeting": "Create Meeting",
"Set Held": "Set Held",
"Set Not Held": "Set Not Held",
"Send Invitations": "Send Invitations"
"Send Invitations": "Send Invitations",
"on time": "on time",
"before": "before"
},
"presetFilters": {
"planned": "Planned",

View File

@@ -0,0 +1,9 @@
{
"options": {
"job": {
"CheckInboundEmails": "Check Inbound Emails",
"SendEmailReminders": "Send Email Reminders"
}
}
}

View File

@@ -1 +1,6 @@
[{"name":"name","width":30,"link":true},{"name":"website"},{"name":"phoneNumber"},{"name":"billingAddressCity"}]
[
{"name":"name","width":30,"link":true},
{"name":"website", "notSortable": true},
{"name":"phoneNumber", "notSortable": true},
{"name":"billingAddressCity"}
]

View File

@@ -1 +0,0 @@
[{"name":"name","link":true},{"name":"website"},{"name":"phoneNumber"}]

View File

@@ -1 +1,4 @@
[{"name":"name","width":35,"link":"true"},{"name":"billingAddressCity"}]
[
{"name":"name","width":50,"link":"true"},
{"name":"billingAddressCity"}
]

View File

@@ -2,12 +2,11 @@
{
"label":"Overview",
"rows": [
[
{"name":"name"},{"name":"parent"}],
[{"name":"status"}, {"name":"direction"}],
[{"name":"dateStart"}],
[{"name":"duration"}],
[{"name":"description"},false]
[{"name":"name"},{"name":"parent"}],
[{"name":"status"}, {"name":"direction"}],
[{"name":"dateStart"}, {"name":"reminders"}],
[{"name":"duration"}],
[{"name":"description", "fullWidth": true}]
]
}
]

View File

@@ -1 +1,7 @@
[{"name":"name","width":30,"link":true},{"name":"parent","width":18},{"name":"status"},{"name":"dateStart"},{"name":"assignedUser"}]
[
{"name":"name","width":30,"link":true},
{"name":"parent","width":18},
{"name":"status"},
{"name":"dateStart"},
{"name":"assignedUser"}
]

View File

@@ -1 +0,0 @@
[{"name":"name","link":true},{"name":"parent"},{"name":"dateStart"}]

View File

@@ -1 +1,5 @@
[{"name":"name","width":35,"link":"true"},{"name":"status"},{"name":"dateStart"}]
[
{"name":"name","width":35,"link":"true"},
{"name":"status"},
{"name":"dateStart"}
]

View File

@@ -1 +1,6 @@
[{"name":"number","width":18},{"name":"name","width":35,"link":true},{"name":"status"},{"name":"priority"}]
[
{"name":"number","width":18},
{"name":"name","width":35,"link":true},
{"name":"status"},
{"name":"priority"}
]

View File

@@ -1 +1,6 @@
[{"name":"name","width":30,"link":true},{"name":"account","width":20},{"name":"emailAddress"},{"name":"phoneNumber"}]
[
{"name":"name","width":30,"link":true},
{"name":"account","width":20},
{"name":"emailAddress", "notSortable": true},
{"name":"phoneNumber", "notSortable": true}
]

View File

@@ -1 +1,5 @@
[{"name":"name","link":true},{"name":"account"},{"name":"phoneNumber"},{"name":"emailAddress"}]
[
{"name":"name","link":true, "width": "40"},
{"name":"account"},
{"name":"emailAddress", "notSortable": true}
]

View File

@@ -1,6 +1,6 @@
[
{"name":"name","width":32,"link":"true"},
{"name":"account"},
{"name":"emailAddress"},
{"name":"phoneNumber"}
{"name":"emailAddress", "notSortable": true},
{"name":"phoneNumber", "notSortable": true}
]

View File

@@ -1 +1,6 @@
[{"name":"name","width":30,"link":true},{"name":"host"},{"name":"status"},{"name":"createCase"}]
[
{"name":"name","width":30,"link":true},
{"name":"host", "notSortable": true},
{"name":"status"},
{"name":"createCase", "notSortable": true}
]

View File

@@ -1 +1,7 @@
[{"name":"name","width":25,"link":"true"},{"name":"status"},{"name":"emailAddress"},{"name":"assignedUser"},{"name":"createdAt"}]
[
{"name":"name", "width":25, "link":"true"},
{"name":"status"},
{"name":"emailAddress", "notSortable": true},
{"name":"assignedUser"},
{"name":"createdAt"}
]

View File

@@ -1 +0,0 @@
[{"name":"name","link":true},{"name":"website"},{"name":"emailAddress"}]

View File

@@ -1 +1,5 @@
[{"name":"name","width":35,"link":true},{"name":"status"},{"name":"emailAddress"}]
[
{"name":"name", "width":32, "link":true},
{"name":"status"},
{"name":"emailAddress", "notSortable": true}
]

View File

@@ -1 +1,12 @@
[{"label":"Overview","rows":[[{"name":"name"},{"name":"parent"}],[{"name":"status"},false],[{"name":"dateStart"},{"name":"dateEnd"}],[{"name":"duration"},false],[{"name":"description"},false]]}]
[
{
"label":"Overview",
"rows": [
[{"name":"name"},{"name":"parent"}],
[{"name":"status"},false],
[{"name":"dateStart"},{"name":"dateEnd"}],
[{"name":"duration"},{"name":"reminders"}],
[{"name":"description", "fullWidth": true}]
]
}
]

View File

@@ -1 +1,7 @@
[{"name":"name","width":30,"link":true},{"name":"parent","width":18},{"name":"status"},{"name":"dateStart"},{"name":"assignedUser"}]
[
{"name":"name","width":30,"link":true},
{"name":"parent","width":18},
{"name":"status"},
{"name":"dateStart"},
{"name":"assignedUser"}
]

View File

@@ -1 +0,0 @@
[{"name":"name"},{"name":"parent"},{"name":"dateStart"}]

View File

@@ -1 +1,5 @@
[{"name":"name","width":35,"link":"true"},{"name":"status"},{"name":"dateStart"}]
[
{"name":"name","width":35,"link":"true"},
{"name":"status"},
{"name":"dateStart"}
]

View File

@@ -1 +1,8 @@
[{"name":"name","width":25,"link":true},{"name":"account","width":18},{"name":"stage"},{"name":"amount"},{"name":"assignedUser"},{"name":"createdAt"}]
[
{"name":"name","width":25,"link":true},
{"name":"account","width":18},
{"name":"stage"},
{"name":"amount"},
{"name":"assignedUser"},
{"name":"createdAt"}
]

View File

@@ -1 +0,0 @@
[{"name":"name","link":true},{"name":"account"},{"name":"status"},{"name":"closeDate"}]

View File

@@ -1 +1,6 @@
[{"name":"name","width":30,"link":true},{"name":"account"},{"name":"stage"},{"name":"createdAt"}]
[
{"name":"name", "width": 32, "link": true},
{"name":"account"},
{"name":"stage"},
{"name":"createdAt"}
]

View File

@@ -1 +1,7 @@
[{"name":"name","width":30,"link":true},{"name":"emailAddress"},{"name":"phoneNumber"},{"name":"accountName"},{"name":"createdAt"}]
[
{"name":"name","width":30,"link":true},
{"name":"emailAddress", "notSortable": true},
{"name":"phoneNumber", "notSortable": true},
{"name":"accountName"},
{"name":"createdAt"}
]

View File

@@ -1 +1,5 @@
[{"name":"name","width":30,"link":true},{"name":"emailAddress"},{"name":"phoneNumber"}]
[
{"name":"name","width":30,"link":true},
{"name":"emailAddress", "notSortable": true},
{"name":"phoneNumber", "notSortable": true}
]

View File

@@ -1 +1,13 @@
[{"label":"Overview","rows":[[{"name":"name"},{"name":"parent"}],[{"name":"status"},{"name":"priority"}],[{"name":"dateStart"},false],[{"name":"dateEnd"},false],[{"name":"description"},false]]}]
[
{
"label":"Overview",
"rows": [
[{"name":"name"},{"name":"parent"}],
[{"name":"status"},
{"name":"priority"}],
[{"name":"dateStart"},false],
[{"name":"dateEnd"},false],
[{"name":"description", "fullWidth": true}]
]
}
]

View File

@@ -1 +1,8 @@
[{"name":"name","width":30,"link":true},{"name":"status"},{"name":"priority"},{"name":"dateEnd"},{"name":"assignedUser"},{"name":"createdAt"}]
[
{"name":"name","width":30,"link":true},
{"name":"status"},
{"name":"priority"},
{"name":"dateEnd"},
{"name":"assignedUser"},
{"name":"createdAt"}
]

View File

@@ -1 +0,0 @@
[{"name":"name","link":true},{"name":"status"},{"name":"parent"}]

View File

@@ -1 +1,7 @@
[{"name":"name","width":30,"link":"true"},{"name":"status"},{"name":"priority"},{"name":"dateEnd"},{"name":"createdAt"}]
[
{"name":"name","width":30,"link":"true"},
{"name":"status"},
{"name":"priority"},
{"name":"dateEnd"},
{"name":"createdAt"}
]

View File

@@ -0,0 +1,7 @@
{
"event": {
"url": "Activities/action/popupNotifications",
"interval": 15,
"view": "Crm:Meeting.PopupNotification"
}
}

View File

@@ -162,5 +162,13 @@
"collection": {
"sortBy": "name",
"asc": true
},
"indexes": {
"name": {
"columns": ["name", "deleted"]
},
"assignedUser": {
"columns": ["assignedUserId", "deleted"]
}
}
}

View File

@@ -30,6 +30,11 @@
"options": [300, 600, 900, 1800, 2700, 3600, 7200],
"default": 300
},
"reminders": {
"type": "jsonArray",
"notStorable": true,
"view": "Crm:Meeting.Fields.Reminders"
},
"direction": {
"type": "enum",
"options": ["Outbound", "Inbound"],
@@ -166,5 +171,22 @@
"collection": {
"sortBy": "dateStart",
"asc": false
},
"indexes": {
"dateStartStatus": {
"columns": ["dateStart", "status"]
},
"dateStart": {
"columns": ["dateStart", "deleted"]
},
"status": {
"columns": ["status", "deleted"]
},
"assignedUser": {
"columns": ["assignedUserId", "deleted"]
},
"assignedUserStatus": {
"columns": ["assignedUserId", "status"]
}
}
}

View File

@@ -5,7 +5,8 @@
"required": true
},
"number": {
"type": "autoincrement"
"type": "autoincrement",
"index": true
},
"status": {
"type": "enum",
@@ -116,5 +117,16 @@
"sortBy": "number",
"asc": false,
"boolFilters": ["onlyMy"]
},
"indexes": {
"status": {
"columns": ["status", "deleted"]
},
"assignedUser": {
"columns": ["assignedUserId", "deleted"]
},
"assignedUserStatus": {
"columns": ["assignedUserId", "status"]
}
}
}

View File

@@ -190,5 +190,16 @@
"collection": {
"sortBy": "name",
"asc": true
},
"indexes": {
"firstName": {
"columns": ["firstName", "deleted"]
},
"name": {
"columns": ["firstName", "lastName"]
},
"assignedUser": {
"columns": ["assignedUserId", "deleted"]
}
}
}

View File

@@ -43,7 +43,7 @@
},
"opportunityAmountConverted": {
"type": "currencyConverted",
"readOnly": true
"readOnly": true
},
"website": {
"type": "url"
@@ -199,5 +199,28 @@
"collection": {
"sortBy": "createdAt",
"asc": false
},
"indexes": {
"firstName": {
"columns": ["firstName", "deleted"]
},
"name": {
"columns": ["firstName", "lastName"]
},
"status": {
"columns": ["status", "deleted"]
},
"createdAt": {
"columns": ["createdAt", "deleted"]
},
"createdAtStatus": {
"columns": ["createdAt", "status"]
},
"assignedUser": {
"columns": ["assignedUserId", "deleted"]
},
"assignedUserStatus": {
"columns": ["assignedUserId", "status"]
}
}
}

View File

@@ -30,6 +30,11 @@
"options": [900, 1800, 3600, 7200, 10800, 86400],
"default": 3600
},
"reminders": {
"type": "jsonArray",
"notStorable": true,
"view": "Crm:Meeting.Fields.Reminders"
},
"description": {
"type": "text"
},
@@ -161,5 +166,22 @@
"collection": {
"sortBy": "dateStart",
"asc": false
},
"indexes": {
"dateStartStatus": {
"columns": ["dateStart", "status"]
},
"dateStart": {
"columns": ["dateStart", "deleted"]
},
"status": {
"columns": ["status", "deleted"]
},
"assignedUser": {
"columns": ["assignedUserId", "deleted"]
},
"assignedUserStatus": {
"columns": ["assignedUserId", "status"]
}
}
}

View File

@@ -163,5 +163,22 @@
"Negotiation/Review": 90,
"Closed Won": 100,
"Closed Lost": 0
},
"indexes": {
"stage": {
"columns": ["stage", "deleted"]
},
"assignedUser": {
"columns": ["assignedUserId", "deleted"]
},
"createdAt": {
"columns": ["createdAt", "deleted"]
},
"createdAtStage": {
"columns": ["createdAt", "stage"]
},
"assignedUserStage": {
"columns": ["assignedUserId", "stage"]
}
}
}

View File

@@ -0,0 +1,40 @@
{
"fields": {
"remindAt": {
"type": "datetime",
"index": true
},
"startAt": {
"type": "datetime",
"index": true
},
"type": {
"type": "enum",
"options": ["Popup", "Email"],
"maxLength": 36,
"index": true,
"default": "Popup"
},
"seconds": {
"type": "enumInt",
"options": [0, 60, 120, 300, 900, 1800, 3600, 7200, 10800, 18000, 86400],
"default": 0
},
"entityType": {
"type": "varchar",
"maxLength": 100
},
"entityId": {
"type": "varchar",
"maxLength": 50
},
"userId": {
"type": "varchar",
"maxLength": 50
}
},
"collection": {
"sortBy": "remindAt",
"asc": false
}
}

View File

@@ -109,5 +109,16 @@
"collection": {
"sortBy": "createdAt",
"asc": false
},
"indexes": {
"firstName": {
"columns": ["firstName", "deleted"]
},
"name": {
"columns": ["firstName", "lastName"]
},
"assignedUser": {
"columns": ["assignedUserId", "deleted"]
}
}
}

View File

@@ -89,5 +89,28 @@
"collection": {
"sortBy": "createdAt",
"asc": false
},
"indexes": {
"dateStartStatus": {
"columns": ["dateStart", "status"]
},
"dateEndStatus": {
"columns": ["dateEnd", "status"]
},
"dateStart": {
"columns": ["dateStart", "deleted"]
},
"dateEnd": {
"columns": ["dateStart", "deleted"]
},
"status": {
"columns": ["status", "deleted"]
},
"assignedUser": {
"columns": ["assignedUserId", "deleted"]
},
"assignedUserStatus": {
"columns": ["assignedUserId", "status"]
}
}
}

View File

@@ -0,0 +1,9 @@
{
"entity": true,
"layouts": false,
"tab": false,
"acl": false,
"module": "Crm",
"customizable": false,
"importable": false
}

View File

@@ -1,5 +1,8 @@
<p>Subject: {name}</p>
<p>Date & time: {dateStart}</p>
<p>Start at: {dateStart}</p>
<p>
<a href="{acceptLink}">Accept</a>, <a href="{declineLink}">Decline</a>
</p>
{#userOnly}
<p><a href="{url}">Open entry</a></p>
{/userOnly}

View File

@@ -1 +1 @@
Invitation to the {eventType} '{name}'
Invitation to {eventType} '{name}'

View File

@@ -0,0 +1,4 @@
<p>Subject: {name}</p>
<p>Start at: {dateStart}</p>
<p><a href="{url}">Open entry</a></p>

View File

@@ -0,0 +1 @@
Reminder about {eventType} '{name}'

View File

@@ -430,11 +430,73 @@ class Activities extends \Espo\Core\Services\Base
";
$sth = $pdo->prepare($sql);
$sth->execute();
$sth = $pdo->prepare($sql);
$sth->execute();
$rows = $sth->fetchAll(PDO::FETCH_ASSOC);
return $rows;
}
public function removeReminder($id)
{
$pdo = $this->getPDO();
$sql = "
DELETE FROM `reminder`
WHERE id = ".$pdo->quote($id)."
";
if (!$this->getUser()->isAdmin()) {
$sql .= " AND user_id = " . $pdo->quote($this->getUser()->id);
}
$pdo->query($sql);
return true;
}
public function getPopupNotifications($userId)
{
$pdo = $this->getPDO();
$dt = new \DateTime();
$now = $dt->format('Y-m-d H:i:s');
$nowShifted = $dt->sub(new \DateInterval('PT1H'))->format('Y-m-d H:i:s');
$sql = "
SELECT id, entity_type AS 'entityType', entity_id AS 'entityId'
FROM `reminder`
WHERE
`type` = 'Popup' AND
`user_id` = ".$pdo->quote($userId)." AND
`remind_at` <= '{$now}' AND
`start_at` > '{$nowShifted}' AND
`deleted` = 0
";
$sth = $pdo->prepare($sql);
$sth->execute();
$rows = $sth->fetchAll(PDO::FETCH_ASSOC);
$result = array();
foreach ($rows as $row) {
$entity = $this->getEntityManager()->getEntity($row['entityType'], $row['entityId']);
$data = null;
if ($entity) {
$data = array(
'id' => $entity->id,
'entityType' => $row['entityType'],
'dateStart' => $entity->get('dateStart'),
'name' => $entity->get('name')
);
}
$result[] = array(
'id' => $row['id'],
'data' => $data
);
}
return $result;
}
}

View File

@@ -127,7 +127,7 @@ class InboundEmail extends \Espo\Services\Record
throw new Error();
}
$importer = new \Espo\Core\Mail\Importer($this->getEntityManager(), $this->getFileManager());
$importer = new \Espo\Core\Mail\Importer($this->getEntityManager(), $this->getFileManager(), $this->getConfig());
$maxSize = $this->getConfig()->get('emailMessageMaxSize');

View File

@@ -99,25 +99,17 @@ class Meeting extends \Espo\Services\Record
return true;
}
protected function storeEntity(Entity $entity)
protected function loadAdditionalFields(Entity $entity)
{
$assignedUserId = $entity->get('assignedUserId');
if ($assignedUserId && $entity->has('usersIds')) {
$usersIds = $entity->get('usersIds');
if (!is_array($usersIds)) {
$usersIds = array();
}
if (!in_array($assignedUserId, $usersIds)) {
$usersIds[] = $assignedUserId;
$entity->set('usersIds', $usersIds);
$hash = $entity->get('usersNames');
if ($hash instanceof \stdClass) {
$hash->assignedUserId = $entity->get('assignedUserName');
$entity->set('usersNames', $hash);
}
}
}
return parent::storeEntity($entity);
parent::loadAdditionalFields($entity);
$this->loadRemindersField($entity);
}
protected function loadRemindersField(Entity $entity)
{
$reminders = $this->getRepository()->getEntityReminders($entity);
$entity->set('reminders', $reminders);
}
}

View File

@@ -43,7 +43,7 @@ abstract class Mapper implements IMapper
protected $fieldsMapCache = array();
protected $aliasesCache = array();
protected $returnCollection = true;
protected $returnCollection = false;
protected $collectionClass = "\\Espo\\ORM\\EntityCollection";
@@ -76,7 +76,7 @@ abstract class Mapper implements IMapper
'additionalColumnsConditions'
);
public function __construct(PDO $pdo, \Espo\ORM\EntityFactory $entityFactory, Query $query) {
public function __construct(PDO $pdo, \Espo\ORM\EntityFactory $entityFactory, Query\Base $query) {
$this->pdo = $pdo;
$this->query = $query;
$this->entityFactory = $entityFactory;
@@ -340,7 +340,7 @@ abstract class Mapper implements IMapper
$setArr = array();
foreach ($columnData as $column => $value) {
$setArr[] = $this->toDb($column) . " = " . $this->pdo->quote($value);
$setArr[] = "`".$this->toDb($column) . "` = " . $this->pdo->quote($value);
}
if (empty($setArr)) {
return true;

View File

@@ -20,14 +20,14 @@
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
************************************************************************/
namespace Espo\ORM\DB;
namespace Espo\ORM\DB\Query;
use Espo\ORM\Entity;
use Espo\ORM\IEntity;
use Espo\ORM\EntityFactory;
use PDO;
class Query
class Base
{
protected static $selectParamList = array(
'select',

View File

@@ -0,0 +1,33 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM - Open Source CRM application.
* Copyright (C) 2014 Yuri Kuznetsov, Taras Machyshyn, Oleksiy Avramenko
* Website: http://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/.
************************************************************************/
namespace Espo\ORM\DB\Query;
use Espo\ORM\Entity;
use Espo\ORM\IEntity;
use Espo\ORM\EntityFactory;
use PDO;
class Mysql extends Base
{
}

View File

@@ -41,12 +41,28 @@ class EntityManager
protected $query;
protected $driverPlatformMap = array(
'pdo_mysql' => 'Mysql',
'mysqli' => 'Mysql',
);
public function __construct($params)
{
$this->params = $params;
$this->metadata = new Metadata();
if (empty($this->params['platform'])) {
if (empty($this->params['driver'])) {
throw new \Exception('No database driver specified.');
}
$driver = $this->params['driver'];
if (empty($this->driverPlatformMap[$driver])) {
throw new \Exception("Database driver '{$driver}' is not supported.");
}
$this->params['platform'] = $this->driverPlatformMap[$this->params['driver']];
}
if (!empty($params['metadata'])) {
$this->setMetadata($params['metadata']);
}
@@ -69,16 +85,36 @@ class EntityManager
public function getQuery()
{
if (empty($this->query)) {
$this->query = new DB\Query($this->getPDO(), $this->entityFactory);
$platform = $this->params['platform'];
$className = '\\Espo\\ORM\\DB\\Query\\' . ucfirst($platform);
$this->query = new $className($this->getPDO(), $this->entityFactory);
}
return $this->query;
}
public function getMapper($className)
protected function getMapperClassName($name)
{
if (empty($this->mappers[$className])) {
// TODO use factory
$className = null;
switch ($name) {
case 'RDB':
$platform = $this->params['platform'];
$className = '\\Espo\\ORM\\DB\\' . ucfirst($platform) . 'Mapper';
break;
}
return $className;
}
public function getMapper($name)
{
if ($name{0} == '\\') {
$className = $name;
} else {
$className = $this->getMapperClassName($name);
}
if (empty($this->mappers[$className])) {
$this->mappers[$className] = new $className($this->getPDO(), $this->entityFactory, $this->getQuery());
}
return $this->mappers[$className];
@@ -90,7 +126,9 @@ class EntityManager
$port = empty($params['port']) ? '' : 'port=' . $params['port'] . ';';
$this->pdo = new \PDO('mysql:host='.$params['host'].';'.$port.'dbname=' . $params['dbname'] . ';charset=utf8', $params['user'], $params['password']);
$platform = strtolower($params['platform']);
$this->pdo = new \PDO($platform . ':host='.$params['host'].';'.$port.'dbname=' . $params['dbname'] . ';charset=utf8', $params['user'], $params['password']);
$this->pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
}

View File

@@ -31,9 +31,6 @@ use \Espo\ORM\IEntity;
class RDB extends \Espo\ORM\Repository
{
public static $mapperClassName = '\\Espo\\ORM\\DB\\MysqlMapper';
/**
* @var Object Mapper.
*/
@@ -66,7 +63,7 @@ class RDB extends \Espo\ORM\Repository
protected function getMapper()
{
if (empty($this->mapper)) {
$this->mapper = $this->getEntityManager()->getMapper(self::$mapperClassName);
$this->mapper = $this->getEntityManager()->getMapper('RDB');
}
return $this->mapper;
}

View File

@@ -25,12 +25,12 @@ namespace Espo\Repositories;
use Espo\ORM\Entity;
class Email extends \Espo\Core\ORM\Repositories\RDB
{
{
protected function prepareAddressess(Entity $entity, $type)
{
$eaRepositoty = $this->getEntityManager()->getRepository('EmailAddress');
$address = $entity->get($type);
$address = $entity->get($type);
$ids = array();
if (!empty($address) || !filter_var($address, FILTER_VALIDATE_EMAIL)) {
$arr = array_map(function ($e) {
@@ -38,7 +38,7 @@ class Email extends \Espo\Core\ORM\Repositories\RDB
}, explode(';', $address));
$ids = $eaRepositoty->getIds($arr);
}
}
$entity->set($type . 'EmailAddressesIds', $ids);
}
@@ -46,9 +46,9 @@ class Email extends \Espo\Core\ORM\Repositories\RDB
{
$eaRepositoty = $this->getEntityManager()->getRepository('EmailAddress');
$from = trim($entity->get('from'));
$from = trim($entity->get('from'));
if (!empty($from)) {
$ids = $eaRepositoty->getIds(array($from));
$ids = $eaRepositoty->getIds(array($from));
if (!empty($ids)) {
$entity->set('fromEmailAddressId', $ids[0]);
}
@@ -76,13 +76,13 @@ class Email extends \Espo\Core\ORM\Repositories\RDB
$entity->set('accountId', $accountId);
}
}
} else {
} else {
// TODO find account by from address
}
}
protected function beforeRemove(Entity $entity)
{
{
parent::beforeRemove($entity);
$attachments = $entity->get('attachments');
foreach ($attachments as $attachment) {

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