development

This commit is contained in:
yuri
2016-01-05 18:04:15 +02:00
parent e4d6b13d27
commit db7e33fa61
23 changed files with 302 additions and 78 deletions
+80
View File
@@ -0,0 +1,80 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM - Open Source CRM application.
* Copyright (C) 2014-2015 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/.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU General Public License version 3.
*
* In accordance with Section 7(b) of the GNU General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
namespace Espo\Acl;
use \Espo\Entities\User;
use \Espo\ORM\Entity;
class Attachment extends \Espo\Core\Acl\Base
{
public function checkEntityRead(User $user, Entity $entity, $data)
{
if ($user->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;
}
}
+80
View File
@@ -0,0 +1,80 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM - Open Source CRM application.
* Copyright (C) 2014-2015 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/.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU General Public License version 3.
*
* In accordance with Section 7(b) of the GNU General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
namespace Espo\AclPortal;
use \Espo\Entities\User;
use \Espo\ORM\Entity;
class Attachment extends \Espo\Core\AclPortal\Base
{
public function checkEntityRead(User $user, Entity $entity, $data)
{
if ($user->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;
}
}
+1 -1
View File
@@ -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);
}
+1 -4
View File
@@ -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();
+1 -1
View File
@@ -113,7 +113,7 @@ class Base extends \Espo\Core\Acl\Base
return false;
}
}
return false;
return true;
}
public function checkReadOnlyAccount(User $user, $data)
+1 -5
View File
@@ -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)) {
if (!$this->getAcl()->checkEntity($attachment)) {
throw new Forbidden();
}
}
$fileName = "data/upload/{$attachment->id}";
if (!file_exists($fileName)) {
throw new NotFound();
}
+1 -4
View File
@@ -79,12 +79,9 @@ 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)) {
if (!$this->getAcl()->checkEntity($attachment)) {
throw new Forbidden();
}
}
$filePath = "data/upload/{$attachment->id}";
@@ -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
},
+26 -3
View File
@@ -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);
+2 -1
View File
@@ -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();
+2
View File
@@ -227,6 +227,8 @@ Espo.define('acl-manager', ['acl'], function (Acl) {
});
AclManager.extend = Backbone.Router.extend;
return AclManager;
});
+13 -1
View File
@@ -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];
},
});
+5 -5
View File
@@ -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;
}
}
+7 -1
View File
@@ -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);
}
});
});
+6 -3
View File
@@ -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});
@@ -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 = '<img src="?entryPoint=image&size=small&id=' + id + '" title="' + name + '">';
preview = '<img src="' + this.getImageUrl(id, 'small') + '" title="' + name + '">';
}
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 = '<a data-action="showImagePreview" data-id="' + id + '" href="?entryPoint=image&id=' + id + '"><img src="?entryPoint=image&size=medium&id=' + id + '"></a>';
preview = '<a data-action="showImagePreview" data-id="' + id + '" href="' + this.getImageUrl(id) + '"><img src="?entryPoint=image&size=medium&id=' + id + '"></a>';
}
return preview;
},
@@ -364,7 +383,7 @@ Espo.define('views/fields/attachment-multiple', 'views/fields/base', function (D
previews.push('<div class="attachment-preview">' + this.getDetailPreview(name, type, id) + '</div>');
continue;
}
var line = '<div class="attachment-block"><span class="glyphicon glyphicon-paperclip small"></span> <a href="?entryPoint=download&id=' + id + '">' + name + '</a></div>';
var line = '<div class="attachment-block"><span class="glyphicon glyphicon-paperclip small"></span> <a href="' + this.getDownloadUrl(id) + '">' + name + '</a></div>';
names.push(line);
}
var string = previews.join('') + names.join('');
+28 -12
View File
@@ -32,11 +32,11 @@ Espo.define('views/fields/file', 'views/fields/link', function (Dep) {
type: 'file',
listTemplate: 'fields.file.detail',
listTemplate: 'fields/file/detail',
detailTemplate: 'fields.file.detail',
detailTemplate: 'fields/file/detail',
editTemplate: 'fields.file.edit',
editTemplate: 'fields/file/edit',
showPreview: false,
@@ -68,7 +68,7 @@ Espo.define('views/fields/file', 'views/fields/link', function (Dep) {
},
'click a[data-action="showImagePreview"]': function (e) {
var id = $(e.currentTarget).data('id');
this.createView('preview', 'Modals.ImagePreview', {
this.createView('preview', 'views/modals/image-preview', {
id: id,
model: this.model,
name: this.nameHash[id]
@@ -80,7 +80,7 @@ Espo.define('views/fields/file', 'views/fields/link', function (Dep) {
e.preventDefault();
var id = this.model.get(this.idName);
this.createView('preview', 'Modals.ImagePreview', {
this.createView('preview', 'views/modals/image-preview', {
id: id,
model: this.model,
name: this.model.get(this.nameName)
@@ -147,7 +147,7 @@ Espo.define('views/fields/file', 'views/fields/link', function (Dep) {
case 'image/png':
case 'image/jpeg':
case 'image/gif':
preview = '<a data-action="showImagePreview" data-id="' + id + '" href="?entryPoint=image&id=' + id + '"><img src="?entryPoint=image&size='+this.previewSize+'&id=' + id + '"></a>';
preview = '<a data-action="showImagePreview" data-id="' + id + '" href="' + this.getImageUrl(id) + '"><img src="?entryPoint=image&size='+this.previewSize+'&id=' + id + '"></a>';
}
return preview;
},
@@ -159,7 +159,7 @@ Espo.define('views/fields/file', 'views/fields/link', function (Dep) {
case 'image/png':
case 'image/jpeg':
case 'image/gif':
preview = '<img src="?entryPoint=image&size=small&id=' + id + '" title="' + name + '">';
preview = '<img src="' + this.getImageUrl(id, 'small') + '" title="' + name + '">';
}
return preview;
@@ -180,12 +180,31 @@ Espo.define('views/fields/file', 'views/fields/link', function (Dep) {
if (this.showPreview && ~this.previewTypeList.indexOf(type)) {
string = '<div class="attachment-preview">' + this.getDetailPreview(name, type, id) + '</div>';
} else {
string = '<span class="glyphicon glyphicon-paperclip small"></span> <a href="?entryPoint=download&id=' + id + '">' + name + '</a>';
string = '<span class="glyphicon glyphicon-paperclip small"></span> <a href="'+ this.getDownloadUrl(id) +'">' + name + '</a>';
}
return string;
}
},
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;
},
deleteAttachment: function () {
var id = this.model.get(this.idName);
var o = {};
@@ -236,11 +255,8 @@ Espo.define('views/fields/file', 'views/fields/link', function (Dep) {
contentType: 'multipart/encrypted',
timeout: 0,
}).done(function (data) {
attachment.id = data.attachmentId;
console.log(attachment.id);
attachment.set('name', fileParams.name);
console.log(attachment.id);
attachment.set('type', fileParams.type || 'text/plain');
attachment.set('size', fileParams.size);
attachment.set('role', 'Attachment');
@@ -304,7 +320,7 @@ Espo.define('views/fields/file', 'views/fields/link', function (Dep) {
fetch: function () {
return {};
},
}
});
});
+1 -1
View File
@@ -26,7 +26,7 @@
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
Espo.define('Views.Fields.Image', 'Views.Fields.File', function (Dep) {
Espo.define('views/fields/image', 'views/fields/file', function (Dep) {
return Dep.extend({
+2 -4
View File
@@ -129,10 +129,8 @@ Espo.define('views/record/base', ['view', 'view-record-helper'], function (Dep,
var view = this.getFieldView(name);
if (view) {
if (!view.readOnly) {
view.setReadOnly(locked);
}
}
},
setFieldNotReadOnly: function (name) {
@@ -457,12 +455,12 @@ Espo.define('views/record/base', ['view', 'view-record-helper'], function (Dep,
var defaultHash = {};
if (this.model.hasField('assignedUser')) {
if (this.model.hasField('assignedUser') && !this.getUser().get('portalId')) {
defaultHash['assignedUserId'] = this.getUser().id;
defaultHash['assignedUserName'] = this.getUser().get('name');
}
var defaultTeamId = this.getUser().get('defaultTeamId');
if (defaultTeamId) {
if (defaultTeamId && !this.getUser().get('portalId')) {
if (this.model.hasField('teams') && !this.model.getFieldParam('teams', 'default')) {
defaultHash['teamsIds'] = [defaultTeamId];
defaultHash['teamsNames'] = {};
@@ -205,19 +205,19 @@ Espo.define('views/record/panels/bottom', 'view', function (Dep) {
o.inlineEditDisabled = true;
}
if (this.recordHelper.getFieldStateParam(name, 'hidden')) {
if (this.recordHelper.getFieldStateParam(field, 'hidden')) {
o.disabled = true;
}
if (this.recordHelper.getFieldStateParam(name, 'hiddenLocked')) {
if (this.recordHelper.getFieldStateParam(field, 'hiddenLocked')) {
o.disabledLocked = true;
}
if (this.recordHelper.getFieldStateParam(name, 'readOnly')) {
if (this.recordHelper.getFieldStateParam(field, 'readOnly')) {
o.readOnly = true;
}
if (this.recordHelper.getFieldStateParam(name, 'required') !== null) {
o.defs.params.required = this.recordHelper.getFieldStateParam(name, 'required');
if (this.recordHelper.getFieldStateParam(field, 'required') !== null) {
o.defs.params.required = this.recordHelper.getFieldStateParam(field, 'required');
}
if (!readOnlyLocked && this.recordHelper.getFieldStateParam(name, 'readOnlyLocked')) {
if (!readOnlyLocked && this.recordHelper.getFieldStateParam(field, 'readOnlyLocked')) {
readOnlyLocked = true;
}
@@ -150,19 +150,19 @@ Espo.define('views/record/panels/side', 'view', function (Dep) {
o.inlineEditDisabled = true;
}
if (this.recordHelper.getFieldStateParam(name, 'hidden')) {
if (this.recordHelper.getFieldStateParam(field, 'hidden')) {
o.disabled = true;
}
if (this.recordHelper.getFieldStateParam(name, 'hiddenLocked')) {
if (this.recordHelper.getFieldStateParam(field, 'hiddenLocked')) {
o.disabledLocked = true;
}
if (this.recordHelper.getFieldStateParam(name, 'readOnly')) {
if (this.recordHelper.getFieldStateParam(field, 'readOnly')) {
o.readOnly = true;
}
if (this.recordHelper.getFieldStateParam(name, 'required') !== null) {
o.defs.params.required = this.recordHelper.getFieldStateParam(name, 'required');
if (this.recordHelper.getFieldStateParam(field, 'required') !== null) {
o.defs.params.required = this.recordHelper.getFieldStateParam(field, 'required');
}
if (!readOnlyLocked && this.recordHelper.getFieldStateParam(name, 'readOnlyLocked')) {
if (!readOnlyLocked && this.recordHelper.getFieldStateParam(field, 'readOnlyLocked')) {
readOnlyLocked = true;
}
+1 -11
View File
@@ -27,15 +27,5 @@
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
include "../bootstrap.php";
$app = new \Espo\Core\Application();
if (!empty($_GET['entryPoint'])) {
$app->runEntryPoint($_GET['entryPoint']);
exit;
}
$clientManager = $app->getContainer()->get('clientManager');
$clientManager->display();
include "../index.php";
+3 -1
View File
@@ -30,13 +30,15 @@
include "bootstrap.php";
$app = new \Espo\Core\Application();
if (!$app->isInstalled()) {
header("Location: install/");
exit;
}
if (!empty($_GET['entryPoint'])) {
if (!empty($_GET['portalId'])) {
$app = new \Espo\Core\ApplicationPortal($_GET['portalId']);
}
$app->runEntryPoint($_GET['entryPoint']);
exit;
}