diff --git a/application/Espo/Acl/Attachment.php b/application/Espo/Acl/Attachment.php
new file mode 100644
index 0000000000..361ba7044e
--- /dev/null
+++ b/application/Espo/Acl/Attachment.php
@@ -0,0 +1,80 @@
+isAdmin()) {
+ return true;
+ }
+
+ if ($entity->get('parentId') && $entity->get('parentType')) {
+ $parent = $this->getEntityManager()->getEntity($entity->get('parentType'), $entity->get('parentId'));
+ if ($parent) {
+ if ($parent->getEntityType() === 'Note') {
+ if ($parent->get('parentId') && $parent->get('parentType')) {
+ $parentOfParent = $this->getEntityManager()->getEntity($parent->get('parentType'), $parent->get('parentId'));
+ if ($this->getAclManager()->checkEntity($user, $parentOfParent)) {
+ return true;
+ }
+ } else {
+ return true;
+ }
+ } else {
+ if ($this->getAclManager()->checkEntity($user, $parent)) {
+ return true;
+ }
+ }
+ }
+ } else {
+ return true;
+ }
+
+ if ($this->checkEntity($user, $entity, $data, 'read')) {
+ return true;
+ }
+
+ return false;
+ }
+
+ public function checkIsOwner(User $user, Entity $entity)
+ {
+ if ($user->id === $entity->get('createdById')) {
+ return true;
+ }
+ return false;
+ }
+}
+
diff --git a/application/Espo/AclPortal/Attachment.php b/application/Espo/AclPortal/Attachment.php
new file mode 100644
index 0000000000..0582f77c88
--- /dev/null
+++ b/application/Espo/AclPortal/Attachment.php
@@ -0,0 +1,80 @@
+isAdmin()) {
+ return true;
+ }
+
+ if ($entity->get('parentId') && $entity->get('parentType')) {
+ $parent = $this->getEntityManager()->getEntity($entity->get('parentType'), $entity->get('parentId'));
+ if ($parent) {
+ if ($parent->getEntityType() === 'Note') {
+ if ($parent->get('parentId') && $parent->get('parentType')) {
+ $parentOfParent = $this->getEntityManager()->getEntity($parent->get('parentType'), $parent->get('parentId'));
+ if ($this->getAclManager()->checkEntity($user, $parentOfParent)) {
+ return true;
+ }
+ } else {
+ return true;
+ }
+ } else {
+ if ($this->getAclManager()->checkEntity($user, $parent)) {
+ return true;
+ }
+ }
+ }
+ } else {
+ return true;
+ }
+
+ if ($this->checkEntity($user, $entity, $data, 'read')) {
+ return true;
+ }
+
+ return false;
+ }
+
+ public function checkIsOwner(User $user, Entity $entity)
+ {
+ if ($user->id === $entity->get('createdById')) {
+ return true;
+ }
+ return false;
+ }
+}
+
diff --git a/application/Espo/Core/Acl.php b/application/Espo/Core/Acl.php
index 537a4218d6..36352cb75b 100644
--- a/application/Espo/Core/Acl.php
+++ b/application/Espo/Core/Acl.php
@@ -89,7 +89,7 @@ class Acl
return $this->getAclManager()->checkScope($this->getUser(), $scope, $action);
}
- public function checkEntity(Entity $entity, $action)
+ public function checkEntity(Entity $entity, $action = 'read')
{
return $this->getAclManager()->checkEntity($this->getUser(), $entity, $action);
}
diff --git a/application/Espo/Core/AclManager.php b/application/Espo/Core/AclManager.php
index b57389ca0c..758dae10fb 100644
--- a/application/Espo/Core/AclManager.php
+++ b/application/Espo/Core/AclManager.php
@@ -153,9 +153,6 @@ class AclManager
public function check(User $user, $subject, $action = null)
{
- if ($user->isAdmin()) {
- return true;
- }
if (is_string($subject)) {
return $this->checkScope($user, $subject, $action);
} else {
@@ -166,7 +163,7 @@ class AclManager
}
}
- public function checkEntity(User $user, Entity $entity, $action)
+ public function checkEntity(User $user, Entity $entity, $action = 'read')
{
$scope = $entity->getEntityType();
diff --git a/application/Espo/Core/AclPortal/Base.php b/application/Espo/Core/AclPortal/Base.php
index 62185b7786..215ad071d5 100644
--- a/application/Espo/Core/AclPortal/Base.php
+++ b/application/Espo/Core/AclPortal/Base.php
@@ -113,7 +113,7 @@ class Base extends \Espo\Core\Acl\Base
return false;
}
}
- return false;
+ return true;
}
public function checkReadOnlyAccount(User $user, $data)
diff --git a/application/Espo/EntryPoints/Download.php b/application/Espo/EntryPoints/Download.php
index 1d0919ee89..c92dfdc4f1 100644
--- a/application/Espo/EntryPoints/Download.php
+++ b/application/Espo/EntryPoints/Download.php
@@ -61,16 +61,12 @@ class Download extends \Espo\Core\EntryPoints\Base
throw new NotFound();
}
- if ($attachment->get('parentId') && $attachment->get('parentType')) {
- $parent = $this->getEntityManager()->getEntity($attachment->get('parentType'), $attachment->get('parentId'));
- if (!$this->getAcl()->check($parent)) {
- throw new Forbidden();
- }
+ if (!$this->getAcl()->checkEntity($attachment)) {
+ throw new Forbidden();
}
$fileName = "data/upload/{$attachment->id}";
-
if (!file_exists($fileName)) {
throw new NotFound();
}
diff --git a/application/Espo/EntryPoints/Image.php b/application/Espo/EntryPoints/Image.php
index 26cb6f6312..97ef315317 100644
--- a/application/Espo/EntryPoints/Image.php
+++ b/application/Espo/EntryPoints/Image.php
@@ -79,11 +79,8 @@ class Image extends \Espo\Core\EntryPoints\Base
throw new NotFound();
}
- if ($attachment->get('parentId') && $attachment->get('parentType')) {
- $parent = $this->getEntityManager()->getEntity($attachment->get('parentType'), $attachment->get('parentId'));
- if ($parent && !$this->getAcl()->check($parent)) {
- throw new Forbidden();
- }
+ if (!$this->getAcl()->checkEntity($attachment)) {
+ throw new Forbidden();
}
$filePath = "data/upload/{$attachment->id}";
diff --git a/application/Espo/Modules/Crm/Resources/metadata/entityDefs/Contact.json b/application/Espo/Modules/Crm/Resources/metadata/entityDefs/Contact.json
index 2e12abcb87..7107f0d7d0 100644
--- a/application/Espo/Modules/Crm/Resources/metadata/entityDefs/Contact.json
+++ b/application/Espo/Modules/Crm/Resources/metadata/entityDefs/Contact.json
@@ -20,7 +20,9 @@
},
"accountId": {
"where": {
- "=": "contact.id IN (SELECT contact_id FROM account_contact WHERE deleted = 0 AND account_id = {value})"
+ "=": "contact.id IN (SELECT contact_id FROM account_contact WHERE deleted = 0 AND account_id = {value})",
+ "IN": "contact.id IN (SELECT contact_id FROM account_contact WHERE deleted = 0 AND account_id IN {value})",
+ "NOT IN": "contact.id IN (SELECT contact_id FROM account_contact WHERE deleted = 0 AND account_id NOT IN {value})"
},
"disabled": true
},
diff --git a/application/Espo/ORM/DB/Query/Base.php b/application/Espo/ORM/DB/Query/Base.php
index d5a3f752a6..214d2b03ef 100644
--- a/application/Espo/ORM/DB/Query/Base.php
+++ b/application/Espo/ORM/DB/Query/Base.php
@@ -591,6 +591,7 @@ abstract class Base
$field = 'AND';
}
+
if (!in_array($field, self::$sqlOperators)) {
$isComplex = false;
@@ -622,8 +623,17 @@ abstract class Base
$fieldDefs = $entity->fields[$field];
- if (!empty($fieldDefs['where']) && !empty($fieldDefs['where'][$operator])) {
- $whereParts[] = str_replace('{value}', $this->pdo->quote($value), $fieldDefs['where'][$operator]);
+ $operatorModified = $operator;
+ if (is_array($value)) {
+ if ($operator == '=') {
+ $operatorModified = 'IN';
+ } else if ($operator == '<>') {
+ $operatorModified = 'NOT IN';
+ }
+ }
+
+ if (!empty($fieldDefs['where']) && !empty($fieldDefs['where'][$operatorModified])) {
+ $whereParts[] = str_replace('{value}', $this->stringifyValue($value), $fieldDefs['where'][$operatorModified]);
} else {
if ($fieldDefs['type'] == IEntity::FOREIGN) {
$leftPart = '';
@@ -669,7 +679,6 @@ abstract class Base
} else {
$whereParts[] = " 0";
}
-
}
}
} else {
@@ -679,6 +688,20 @@ abstract class Base
return implode(" " . $sqlOp . " ", $whereParts);
}
+ public function stringifyValue($value)
+ {
+ if (is_array($value)) {
+ $arr = [];
+ foreach ($value as $v) {
+ $arr[] = $this->pdo->quote($v);
+ }
+ $stringValue = implode(', ', $arr);
+ } else {
+ $stringValue = $this->pdo->quote($value);
+ }
+ return $stringValue;
+ }
+
public function sanitize($string)
{
return preg_replace('/[^A-Za-z0-9_]+/', '', $string);
diff --git a/application/Espo/Services/Stream.php b/application/Espo/Services/Stream.php
index 21873506da..8bacd6ab2c 100644
--- a/application/Espo/Services/Stream.php
+++ b/application/Espo/Services/Stream.php
@@ -526,7 +526,7 @@ class Stream extends \Espo\Core\Services\Base
throw new NotFound();
}
- if (!$this->getAcl($entity, 'stream')) {
+ if (!$this->getAcl()->checkEntity($entity, 'stream')) {
throw new Forbidden();
}
@@ -587,6 +587,7 @@ class Stream extends \Espo\Core\Services\Base
'order' => 'DESC'
));
+
foreach ($collection as $e) {
if ($e->get('type') == 'Post' || $e->get('type') == 'EmailReceived') {
$e->loadAttachments();
diff --git a/frontend/client/src/acl-manager.js b/frontend/client/src/acl-manager.js
index 3010380d61..4a4e0c5e9f 100644
--- a/frontend/client/src/acl-manager.js
+++ b/frontend/client/src/acl-manager.js
@@ -227,6 +227,8 @@ Espo.define('acl-manager', ['acl'], function (Acl) {
});
+ AclManager.extend = Backbone.Router.extend;
+
return AclManager;
});
diff --git a/frontend/client/src/acl-portal-manager.js b/frontend/client/src/acl-portal-manager.js
index 1c013f2240..d1828c510e 100644
--- a/frontend/client/src/acl-portal-manager.js
+++ b/frontend/client/src/acl-portal-manager.js
@@ -28,7 +28,7 @@
-Espo.define('acl-portal-manager', ['acl-manager'], function (Dep) {
+Espo.define('acl-portal-manager', ['acl-manager', 'acl-portal'], function (Dep, AclPortal) {
return Dep.extend({
@@ -38,7 +38,19 @@ Espo.define('acl-portal-manager', ['acl-manager'], function (Dep) {
checkIsOwnContact: function (model) {
return this.getImplementation(model.name).checkIsOwnContact(model);
- }
+ },
+
+ getImplementation: function (scope) {
+ if (!(scope in this.implementationHash)) {
+ var implementationClass = AclPortal;
+ if (scope in this.implementationClassMap) {
+ implementationClass = this.implementationClassMap[scope];
+ }
+ var obj = new implementationClass(this.getUser(), scope);
+ this.implementationHash[scope] = obj;
+ }
+ return this.implementationHash[scope];
+ },
});
diff --git a/frontend/client/src/acl-portal.js b/frontend/client/src/acl-portal.js
index 264a94f2e7..c8671a5b20 100644
--- a/frontend/client/src/acl-portal.js
+++ b/frontend/client/src/acl-portal.js
@@ -127,7 +127,7 @@ Espo.define('acl-portal', ['acl'], function (Dep) {
return result;
}
}
- return false;
+ return true;
},
checkModel: function (model, data, action, precise) {
@@ -161,7 +161,7 @@ Espo.define('acl-portal', ['acl'], function (Dep) {
if (model.hasField('account')) {
if (model.get('accountId')) {
- if (~accountIdList.indexOf(model.get('accountId')) {
+ if (~accountIdList.indexOf(model.get('accountId'))) {
return true;
}
}
@@ -174,7 +174,7 @@ Espo.define('acl-portal', ['acl'], function (Dep) {
result = null;
}
(model.getLinkMultipleIdList('accounts')).forEach(function (id) {
- if (~accountIdList.indexOf(id) {
+ if (~accountIdList.indexOf(id)) {
result = true;
}
}, this);
@@ -182,7 +182,7 @@ Espo.define('acl-portal', ['acl'], function (Dep) {
if (model.hasField('parent') && model.hasLink('parent')) {
if (model.get('parentType') === 'Account') {
- if (!accountIdList.indexOf(model.get('parentId')) {
+ if (!accountIdList.indexOf(model.get('parentId'))) {
return true;
}
}
@@ -205,7 +205,7 @@ Espo.define('acl-portal', ['acl'], function (Dep) {
if (model.hasField('contact')) {
if (model.get('contactId')) {
- if (contactId === model.get('contactId') {
+ if (contactId === model.get('contactId')) {
return true;
}
}
diff --git a/frontend/client/src/app-portal.js b/frontend/client/src/app-portal.js
index a910f9bcd6..e40c282774 100644
--- a/frontend/client/src/app-portal.js
+++ b/frontend/client/src/app-portal.js
@@ -26,10 +26,16 @@
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
-Espo.define('app-portal', ['app'], function (Dep) {
+Espo.define('app-portal', ['app', 'acl-portal-manager'], function (Dep, AclPortalManager) {
return Dep.extend({
+ aclName: 'aclPortal',
+
+ createAclManager: function () {
+ return new AclPortalManager(this.user);
+ }
+
});
});
diff --git a/frontend/client/src/app.js b/frontend/client/src/app.js
index db47b78f29..f8cad9b476 100644
--- a/frontend/client/src/app.js
+++ b/frontend/client/src/app.js
@@ -76,7 +76,7 @@ Espo.define(
this.user = new User();
this.preferences = new Preferences();
this.preferences.settings = this.settings;
- this.acl = new AclManager(this.user);
+ this.acl = this.createAclManager();
this.themeManager = new ThemeManager(this.settings, this.preferences, this.metadata);
@@ -170,7 +170,7 @@ Espo.define(
var clientDefs = this.metadata.get('clientDefs') || {};
Object.keys(clientDefs).forEach(function (scope) {
var o = clientDefs[scope];
- var implClassName = (o || {}).acl;
+ var implClassName = (o || {})[this.aclName || 'acl'];
if (implClassName) {
promiseList.push(new Promise(function (resolve) {
this.loader.load(implClassName, function (implClass) {
@@ -285,8 +285,11 @@ Espo.define(
this.dateTime.setSettingsAndPreferences(this.settings, this.preferences);
},
- initView: function () {
+ createAclManager: function () {
+ return new AclManager(this.user);
+ },
+ initView: function () {
var helper = this.viewHelper = new ViewHelper();
helper.layoutManager = new LayoutManager({cache: this.cache});
diff --git a/frontend/client/src/views/fields/attachment-multiple.js b/frontend/client/src/views/fields/attachment-multiple.js
index ce93d4c32e..80170eb6ba 100644
--- a/frontend/client/src/views/fields/attachment-multiple.js
+++ b/frontend/client/src/views/fields/attachment-multiple.js
@@ -169,6 +169,25 @@ Espo.define('views/fields/attachment-multiple', 'views/fields/base', function (D
}
},
+ getImageUrl: function (id, size) {
+ var url = '?entryPoint=image&id=' + id;
+ if (size) {
+ size += '&size=' + size;
+ }
+ if (this.getUser().get('portalId')) {
+ url += '&portalId=' + this.getUser().get('portalId');
+ }
+ return url;
+ },
+
+ getDownloadUrl: function (id) {
+ var url = '?entryPoint=download&id=' + id;
+ if (this.getUser().get('portalId')) {
+ url += '&portalId=' + this.getUser().get('portalId');
+ }
+ return url;
+ },
+
removeId: function (id) {
var arr = _.clone(this.model.get(this.idsName));
var i = arr.indexOf(id);
@@ -212,7 +231,7 @@ Espo.define('views/fields/attachment-multiple', 'views/fields/base', function (D
case 'image/png':
case 'image/jpeg':
case 'image/gif':
- preview = '';
+ preview = '
';
}
return preview;
@@ -345,7 +364,7 @@ Espo.define('views/fields/attachment-multiple', 'views/fields/base', function (D
var preview = name;
if (this.isTypeIsImage(type)) {
- preview = '
';
+ preview = '
';
}
return preview;
},
@@ -364,7 +383,7 @@ Espo.define('views/fields/attachment-multiple', 'views/fields/base', function (D
previews.push('