mirror of
https://github.com/espocrm/espocrm.git
synced 2026-03-04 03:27:01 +00:00
Compare commits
131 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ffb94b960d | ||
|
|
f73ac0779b | ||
|
|
8003af682e | ||
|
|
cbf7bf18f2 | ||
|
|
ceaa0a8322 | ||
|
|
8ca410d994 | ||
|
|
5fc7f2509d | ||
|
|
f2e1ba3780 | ||
|
|
93ac871640 | ||
|
|
187a8a02a1 | ||
|
|
bcf5686eec | ||
|
|
027507b61e | ||
|
|
49b3d17952 | ||
|
|
e0958cfeec | ||
|
|
00e12b50b7 | ||
|
|
44b8b00106 | ||
|
|
e0855e3092 | ||
|
|
cb0d70430a | ||
|
|
042575ce6b | ||
|
|
5cd18b57d2 | ||
|
|
d1e46e3d9a | ||
|
|
1a3348f2c2 | ||
|
|
75edc5c165 | ||
|
|
60184ebbbe | ||
|
|
ddb6cb7483 | ||
|
|
2ddd44a2fe | ||
|
|
ce55866445 | ||
|
|
2bc7f85a58 | ||
|
|
a27df2c41f | ||
|
|
170581dd28 | ||
|
|
00b0c904ae | ||
|
|
fdab17265f | ||
|
|
96d0a7db00 | ||
|
|
2835928ac0 | ||
|
|
4c22b42b99 | ||
|
|
64ddaa40a8 | ||
|
|
bc7006e193 | ||
|
|
1ae0a9df28 | ||
|
|
a4f9280eba | ||
|
|
2528fc34c8 | ||
|
|
979f07bf9b | ||
|
|
52bff4de1c | ||
|
|
4c017361f8 | ||
|
|
84aa1339d9 | ||
|
|
987cd4a121 | ||
|
|
e50ad5106f | ||
|
|
071cbcb0fb | ||
|
|
43f1cb9af9 | ||
|
|
220c55e9b4 | ||
|
|
927610efb0 | ||
|
|
78fcaf1fa3 | ||
|
|
3bd9af031d | ||
|
|
16ee3d68e8 | ||
|
|
d49ee3c187 | ||
|
|
2e544f1ccf | ||
|
|
1c51125e66 | ||
|
|
1ed47f5d0d | ||
|
|
133fa0cb36 | ||
|
|
295904bf4c | ||
|
|
951230f7e1 | ||
|
|
ee58886206 | ||
|
|
41575f6f13 | ||
|
|
18ae33d417 | ||
|
|
36daf5a762 | ||
|
|
8a41a2cd05 | ||
|
|
61497d2a01 | ||
|
|
a3045a88f3 | ||
|
|
fd072520a9 | ||
|
|
6ef9d8428f | ||
|
|
33a7176165 | ||
|
|
044de7d744 | ||
|
|
801ba05cd8 | ||
|
|
0dc5a4e4f3 | ||
|
|
230ed63e67 | ||
|
|
7316866a1a | ||
|
|
c7be54d9c9 | ||
|
|
dcc118ec5d | ||
|
|
84e98054b7 | ||
|
|
f37308f5a5 | ||
|
|
9688b3be3c | ||
|
|
6490a1ca97 | ||
|
|
c15fc89f52 | ||
|
|
9207030ccb | ||
|
|
018eb44de6 | ||
|
|
bc92b96a6b | ||
|
|
ee57c9223c | ||
|
|
f799ffc61f | ||
|
|
bdb1f2f3b6 | ||
|
|
fd755df67b | ||
|
|
e9edd01d08 | ||
|
|
dea7e0b33e | ||
|
|
25b50baa1a | ||
|
|
8a53657b9c | ||
|
|
c6ecbf4942 | ||
|
|
56d11807a1 | ||
|
|
cf3b4b284a | ||
|
|
953a59cb57 | ||
|
|
f60061a387 | ||
|
|
c627d84273 | ||
|
|
5a16ee0493 | ||
|
|
14252d6e9e | ||
|
|
c0de3e1c4e | ||
|
|
8b4070f9ae | ||
|
|
f4ef5fc36f | ||
|
|
b1e184c6d1 | ||
|
|
fbf665fd76 | ||
|
|
4b98ea79e0 | ||
|
|
2e916a2ba8 | ||
|
|
26a4d1c6ff | ||
|
|
33fef4c90e | ||
|
|
2dc15294fc | ||
|
|
9b846b45bc | ||
|
|
bc65975f74 | ||
|
|
a5308831f2 | ||
|
|
83f0a81eb7 | ||
|
|
9e28d4a261 | ||
|
|
a9b3320302 | ||
|
|
884ccb5265 | ||
|
|
838288a463 | ||
|
|
f4b9356173 | ||
|
|
beaa4a29ba | ||
|
|
987ba2faa5 | ||
|
|
a24f697dca | ||
|
|
c7947d0c46 | ||
|
|
16aca57f17 | ||
|
|
a225748840 | ||
|
|
fff1be9d07 | ||
|
|
b73ca3f1bc | ||
|
|
93a4f999b6 | ||
|
|
3030643ce4 | ||
|
|
46913c3574 |
227
application/Espo/Controllers/EntityManager.php
Normal file
227
application/Espo/Controllers/EntityManager.php
Normal file
@@ -0,0 +1,227 @@
|
||||
<?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/.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Controllers;
|
||||
|
||||
use \Espo\Core\Exceptions\BadRequest;
|
||||
use \Espo\Core\Exceptions\Forbidden;
|
||||
use \Espo\Core\Exceptions\Error;
|
||||
|
||||
class EntityManager extends \Espo\Core\Controllers\Base
|
||||
{
|
||||
protected function checkControllerAccess()
|
||||
{
|
||||
if (!$this->getUser()->isAdmin()) {
|
||||
throw new Forbidden();
|
||||
}
|
||||
}
|
||||
|
||||
public function actionCreateEntity($params, $data, $request)
|
||||
{
|
||||
if (!$request->isPost()) {
|
||||
throw new BadRequest();
|
||||
}
|
||||
|
||||
if (empty($data['name']) || empty($data['type'])) {
|
||||
throw new BadRequest();
|
||||
}
|
||||
|
||||
$name = $data['name'];
|
||||
$type = $data['type'];
|
||||
|
||||
$name = filter_var($name, \FILTER_SANITIZE_STRING);
|
||||
$type = filter_var($type, \FILTER_SANITIZE_STRING);
|
||||
|
||||
$params = array();
|
||||
|
||||
if (!empty($data['labelSingular'])) {
|
||||
$params['labelSingular'] = $data['labelSingular'];
|
||||
}
|
||||
if (!empty($data['labelPlural'])) {
|
||||
$params['labelPlural'] = $data['labelPlural'];
|
||||
}
|
||||
if (!empty($data['stream'])) {
|
||||
$params['stream'] = $data['stream'];
|
||||
}
|
||||
|
||||
$result = $this->getContainer()->get('entityManagerUtil')->create($name, $type, $params);
|
||||
|
||||
if ($result) {
|
||||
$tabList = $this->getConfig()->get('tabList', []);
|
||||
$tabList[] = $name;
|
||||
$this->getConfig()->set('tabList', $tabList);
|
||||
$this->getConfig()->save();
|
||||
|
||||
$this->getContainer()->get('dataManager')->rebuild();
|
||||
} else {
|
||||
throw new Error();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function actionUpdateEntity($params, $data, $request)
|
||||
{
|
||||
if (!$request->isPost()) {
|
||||
throw new BadRequest();
|
||||
}
|
||||
if (empty($data['name'])) {
|
||||
throw new BadRequest();
|
||||
}
|
||||
$name = $data['name'];
|
||||
$name = filter_var($name, \FILTER_SANITIZE_STRING);
|
||||
|
||||
$result = $this->getContainer()->get('entityManagerUtil')->update($name, $data);
|
||||
|
||||
if ($result) {
|
||||
$this->getContainer()->get('dataManager')->clearCache();
|
||||
} else {
|
||||
throw new Error();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function actionRemoveEntity($params, $data, $request)
|
||||
{
|
||||
if (!$request->isPost()) {
|
||||
throw new BadRequest();
|
||||
}
|
||||
|
||||
if (empty($data['name'])) {
|
||||
throw new BadRequest();
|
||||
}
|
||||
$name = $data['name'];
|
||||
$name = filter_var($name, \FILTER_SANITIZE_STRING);
|
||||
|
||||
$result = $this->getContainer()->get('entityManagerUtil')->delete($name);
|
||||
|
||||
if ($result) {
|
||||
$tabList = $this->getConfig()->get('tabList', []);
|
||||
if (($key = array_search($name, $tabList)) !== false) {
|
||||
unset($tabList[$key]);
|
||||
$tabList = array_values($tabList);
|
||||
}
|
||||
$this->getConfig()->set('tabList', $tabList);
|
||||
$this->getConfig()->save();
|
||||
|
||||
$this->getContainer()->get('dataManager')->clearCache();
|
||||
} else {
|
||||
throw new Error();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function actionCreateLink($params, $data, $request)
|
||||
{
|
||||
if (!$request->isPost()) {
|
||||
throw new BadRequest();
|
||||
}
|
||||
|
||||
$paramList = [
|
||||
'entity',
|
||||
'entityForeign',
|
||||
'link',
|
||||
'linkForeign',
|
||||
'label',
|
||||
'labelForeign',
|
||||
'linkType'
|
||||
];
|
||||
|
||||
$d = array();
|
||||
foreach ($paramList as $item) {
|
||||
if (empty($data[$item])) {
|
||||
throw new BadRequest();
|
||||
}
|
||||
$d[$item] = filter_var($data[$item], \FILTER_SANITIZE_STRING);
|
||||
}
|
||||
|
||||
$result = $this->getContainer()->get('entityManagerUtil')->createLink($d);
|
||||
|
||||
if ($result) {
|
||||
$this->getContainer()->get('dataManager')->rebuild();
|
||||
} else {
|
||||
throw new Error();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function actionUpdateLink($params, $data, $request)
|
||||
{
|
||||
if (!$request->isPost()) {
|
||||
throw new BadRequest();
|
||||
}
|
||||
|
||||
$paramList = [
|
||||
'entity',
|
||||
'entityForeign',
|
||||
'link',
|
||||
'linkForeign',
|
||||
'label',
|
||||
'labelForeign'
|
||||
];
|
||||
|
||||
$d = array();
|
||||
foreach ($paramList as $item) {
|
||||
$d[$item] = filter_var($data[$item], \FILTER_SANITIZE_STRING);
|
||||
}
|
||||
|
||||
$result = $this->getContainer()->get('entityManagerUtil')->updateLink($d);
|
||||
|
||||
if ($result) {
|
||||
$this->getContainer()->get('dataManager')->clearCache();
|
||||
} else {
|
||||
throw new Error();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function actionRemoveLink($params, $data, $request)
|
||||
{
|
||||
if (!$request->isPost()) {
|
||||
throw new BadRequest();
|
||||
}
|
||||
|
||||
$paramList = [
|
||||
'entity',
|
||||
'link',
|
||||
];
|
||||
$d = array();
|
||||
foreach ($paramList as $item) {
|
||||
$d[$item] = filter_var($data[$item], \FILTER_SANITIZE_STRING);
|
||||
}
|
||||
|
||||
$result = $this->getContainer()->get('entityManagerUtil')->deleteLink($d);
|
||||
|
||||
if ($result) {
|
||||
$this->getContainer()->get('dataManager')->clearCache();
|
||||
} else {
|
||||
throw new Error();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,6 +79,28 @@ class Container
|
||||
return $className;
|
||||
}
|
||||
|
||||
protected function loadLog()
|
||||
{
|
||||
$logConfig = $this->get('config')->get('logger');
|
||||
|
||||
$log = new \Espo\Core\Utils\Log('Espo');
|
||||
|
||||
$levelCode = $log->getLevelCode($logConfig['level']);
|
||||
|
||||
if ($logConfig['isRotate']) {
|
||||
$handler = new \Espo\Core\Utils\Log\Monolog\Handler\RotatingFileHandler($logConfig['path'], $logConfig['maxRotateFiles'], $levelCode);
|
||||
} else {
|
||||
$handler = new \Espo\Core\Utils\Log\Monolog\Handler\StreamHandler($logConfig['path'], $levelCode);
|
||||
}
|
||||
$log->pushHandler($handler);
|
||||
|
||||
$errorHandler = new \Monolog\ErrorHandler($log);
|
||||
$errorHandler->registerExceptionHandler(null, false);
|
||||
$errorHandler->registerErrorHandler(array(), false);
|
||||
|
||||
return $log;
|
||||
}
|
||||
|
||||
protected function loadContainer()
|
||||
{
|
||||
return $this;
|
||||
|
||||
@@ -25,6 +25,7 @@ namespace Espo\Core\Controllers;
|
||||
use \Espo\Core\Exceptions\Error;
|
||||
use \Espo\Core\Exceptions\Forbidden;
|
||||
use \Espo\Core\Exceptions\NotFound;
|
||||
use \Espo\Core\Exceptions\BadRequest;
|
||||
use \Espo\Core\Utils\Util;
|
||||
|
||||
class Record extends Base
|
||||
@@ -301,5 +302,24 @@ class Record extends Base
|
||||
$id = $params['id'];
|
||||
return $this->getRecordService()->unfollow($id);
|
||||
}
|
||||
|
||||
public function actionMerge($params, $data, $request)
|
||||
{
|
||||
if (!$request->isPost()) {
|
||||
throw new BadRequest();
|
||||
}
|
||||
|
||||
if (empty($data['targetId']) || empty($data['sourceIds']) || !is_array($data['sourceIds'])) {
|
||||
throw new BadRequest();
|
||||
}
|
||||
$targetId = $data['targetId'];
|
||||
$sourceIds = $data['sourceIds'];
|
||||
|
||||
if (!$this->getAcl()->check($this->name, 'edit')) {
|
||||
throw new Forbidden();
|
||||
}
|
||||
|
||||
return $this->getRecordService()->merge($targetId, $sourceIds);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -170,7 +170,9 @@ class HookManager
|
||||
$sortedHookList = array_merge($sortedHookList, $hookDetails);
|
||||
}
|
||||
|
||||
$hooks[$scopeName][$hookName] = isset($hooks[$scopeName][$hookName]) ? array_merge($hooks[$scopeName][$hookName], $sortedHookList) : $sortedHookList;
|
||||
$normalizedScopeName = Util::normilizeScopeName($scopeName);
|
||||
|
||||
$hooks[$normalizedScopeName][$hookName] = isset($hooks[$normalizedScopeName][$hookName]) ? array_merge($hooks[$normalizedScopeName][$hookName], $sortedHookList) : $sortedHookList;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,31 +22,17 @@
|
||||
|
||||
namespace Espo\Core\Loaders;
|
||||
|
||||
use Espo\Core\Utils,
|
||||
Espo\Core\Utils\Log\Monolog\Handler;
|
||||
|
||||
class Log extends Base
|
||||
class EntityManagerUtil extends Base
|
||||
{
|
||||
public function load()
|
||||
{
|
||||
$logConfig = $this->getContainer()->get('config')->get('logger');
|
||||
$entityManager = new \Espo\Core\Utils\EntityManager(
|
||||
$this->getContainer()->get('metadata'),
|
||||
$this->getContainer()->get('language'),
|
||||
$this->getContainer()->get('fileManager')
|
||||
);
|
||||
|
||||
$log = new Utils\Log('Espo');
|
||||
|
||||
$levelCode = $log->getLevelCode($logConfig['level']);
|
||||
|
||||
if ($logConfig['isRotate']) {
|
||||
$handler = new Handler\RotatingFileHandler($logConfig['path'], $logConfig['maxRotateFiles'], $levelCode);
|
||||
} else {
|
||||
$handler = new Handler\StreamHandler($logConfig['path'], $levelCode);
|
||||
}
|
||||
$log->pushHandler($handler);
|
||||
|
||||
$errorHandler = new \Monolog\ErrorHandler($log);
|
||||
$errorHandler->registerExceptionHandler(null, false);
|
||||
$errorHandler->registerErrorHandler(array(), false);
|
||||
|
||||
return $log;
|
||||
return $entityManager;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,7 +108,6 @@ class Importer
|
||||
|
||||
if ($message->isMultipart()) {
|
||||
foreach (new \RecursiveIteratorIterator($message) as $part) {
|
||||
echo "-";
|
||||
$this->importPartDataToEmail($email, $part, $inlineIds);
|
||||
}
|
||||
} else {
|
||||
@@ -198,71 +197,82 @@ class Importer
|
||||
$type = strtok($part->contentType, ';');
|
||||
$encoding = null;
|
||||
|
||||
switch ($type) {
|
||||
case 'text/plain':
|
||||
$content = $this->getContentFromPart($part);
|
||||
if (!$email->get('body')) {
|
||||
$email->set('body', $content);
|
||||
}
|
||||
$email->set('bodyPlain', $content);
|
||||
break;
|
||||
case 'text/html':
|
||||
$content = $this->getContentFromPart($part);
|
||||
$email->set('body', $content);
|
||||
$email->set('isHtml', true);
|
||||
break;
|
||||
default:
|
||||
$content = $part->getContent();
|
||||
$disposition = null;
|
||||
$isAttachment = true;
|
||||
|
||||
$fileName = null;
|
||||
$contentId = null;
|
||||
|
||||
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 ($type == 'text/plain' || $type == 'text/html') {
|
||||
$isAttachment = false;
|
||||
$content = $this->getContentFromPart($part);
|
||||
if ($type == 'text/plain') {
|
||||
if ($email->get('bodyPlain')) {
|
||||
$isAttachment = true;
|
||||
} else {
|
||||
$email->set('bodyPlain', $content);
|
||||
if (!$email->get('body')) {
|
||||
$email->set('body', $content);
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($part->contentTransferEncoding)) {
|
||||
$encoding = strtolower($part->getHeader('Content-Transfer-Encoding')->getTransferEncoding());
|
||||
}
|
||||
|
||||
$attachment = $this->getEntityManager()->getEntity('Attachment');
|
||||
$attachment->set('name', $fileName);
|
||||
$attachment->set('type', $type);
|
||||
|
||||
if ($disposition == 'inline') {
|
||||
$attachment->set('role', 'Inline Attachment');
|
||||
} else if ($type == 'text/html') {
|
||||
if ($email->get('isHtml')) {
|
||||
$isAttachment = true;
|
||||
} else {
|
||||
$attachment->set('role', 'Attachment');
|
||||
$email->set('body', $content);
|
||||
$email->set('isHtml', true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($encoding == 'base64') {
|
||||
$content = base64_decode($content);
|
||||
if ($isAttachment) {
|
||||
$content = $part->getContent();
|
||||
$disposition = null;
|
||||
|
||||
$fileName = null;
|
||||
$contentId = null;
|
||||
|
||||
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';
|
||||
}
|
||||
}
|
||||
|
||||
$attachment->set('size', strlen($content));
|
||||
if (isset($part->contentTransferEncoding)) {
|
||||
$encoding = strtolower($part->getHeader('Content-Transfer-Encoding')->getTransferEncoding());
|
||||
}
|
||||
|
||||
$this->getEntityManager()->saveEntity($attachment);
|
||||
$attachment = $this->getEntityManager()->getEntity('Attachment');
|
||||
$attachment->set('name', $fileName);
|
||||
$attachment->set('type', $type);
|
||||
|
||||
$path = 'data/upload/' . $attachment->id;
|
||||
$this->getFileManager()->putContents($path, $content);
|
||||
if ($disposition == 'inline') {
|
||||
$attachment->set('role', 'Inline Attachment');
|
||||
} else {
|
||||
$attachment->set('role', 'Attachment');
|
||||
}
|
||||
|
||||
if ($disposition == 'attachment') {
|
||||
$attachmentsIds = $email->get('attachmentsIds');
|
||||
$attachmentsIds[] = $attachment->id;
|
||||
$email->set('attachmentsIds', $attachmentsIds);
|
||||
} else if ($disposition == 'inline') {
|
||||
$inlineIds[$contentId] = $attachment->id;
|
||||
}
|
||||
if ($encoding == 'base64') {
|
||||
$content = base64_decode($content);
|
||||
}
|
||||
|
||||
$attachment->set('size', strlen($content));
|
||||
|
||||
$this->getEntityManager()->saveEntity($attachment);
|
||||
|
||||
$path = 'data/upload/' . $attachment->id;
|
||||
$this->getFileManager()->putContents($path, $content);
|
||||
|
||||
if ($disposition == 'attachment') {
|
||||
$attachmentsIds = $email->get('attachmentsIds');
|
||||
$attachmentsIds[] = $attachment->id;
|
||||
$email->set('attachmentsIds', $attachmentsIds);
|
||||
} else if ($disposition == 'inline') {
|
||||
$inlineIds[$contentId] = $attachment->id;
|
||||
}
|
||||
}
|
||||
} catch (\Exception $e) {}
|
||||
}
|
||||
@@ -298,16 +308,16 @@ 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') {
|
||||
$content = quoted_printable_decode($content);
|
||||
}
|
||||
}
|
||||
|
||||
if ($charset !== 'UTF-8') {
|
||||
$content = mb_convert_encoding($content, 'UTF-8', $charset);
|
||||
}
|
||||
}
|
||||
return $content;
|
||||
}
|
||||
|
||||
@@ -38,6 +38,8 @@ class RDB extends \Espo\ORM\Repositories\RDB implements Injectable
|
||||
|
||||
protected $injections = array();
|
||||
|
||||
private $restoreData = null;
|
||||
|
||||
public function inject($name, $object)
|
||||
{
|
||||
$this->injections[$name] = $object;
|
||||
@@ -139,6 +141,14 @@ class RDB extends \Espo\ORM\Repositories\RDB implements Injectable
|
||||
{
|
||||
parent::beforeRemove($entity);
|
||||
$this->getEntityManager()->getHookManager()->process($this->entityName, 'beforeRemove', $entity);
|
||||
|
||||
$nowString = date('Y-m-d H:i:s', time());
|
||||
if ($entity->hasField('modifiedAt')) {
|
||||
$entity->set('modifiedAt', $nowString);
|
||||
}
|
||||
if ($entity->hasField('modifiedById')) {
|
||||
$entity->set('modifiedById', $this->getEntityManager()->getUser()->id);
|
||||
}
|
||||
}
|
||||
|
||||
protected function afterRemove(Entity $entity)
|
||||
@@ -167,6 +177,10 @@ class RDB extends \Espo\ORM\Repositories\RDB implements Injectable
|
||||
|
||||
protected function afterSave(Entity $entity)
|
||||
{
|
||||
if (!empty($this->restoreData)) {
|
||||
$entity->set($this->restoreData);
|
||||
$this->restoreData = null;
|
||||
}
|
||||
parent::afterSave($entity);
|
||||
|
||||
$this->handleEmailAddressSave($entity);
|
||||
@@ -220,12 +234,10 @@ class RDB extends \Espo\ORM\Repositories\RDB implements Injectable
|
||||
$entity->clear('createdById');
|
||||
$entity->clear('createdAt');
|
||||
}
|
||||
$this->restoreData = $restoreData;
|
||||
|
||||
$result = parent::save($entity);
|
||||
|
||||
$entity->set($restoreData);
|
||||
|
||||
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
@@ -283,9 +295,18 @@ class RDB extends \Espo\ORM\Repositories\RDB implements Injectable
|
||||
$data->$columnName = $foreignEntity->get($columnField);
|
||||
}
|
||||
$existingColumnsData->$foreignId = $data;
|
||||
$entity->setFetched($columnsFieldsName, $existingColumnsData);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if ($entity->has($fieldName)) {
|
||||
$entity->setFetched($fieldName, $existingIds);
|
||||
}
|
||||
if ($entity->has($columnsFieldsName) && !empty($columns)) {
|
||||
$entity->setFetched($columnsFieldsName, $existingColumnsData);
|
||||
}
|
||||
|
||||
foreach ($existingIds as $id) {
|
||||
if (!in_array($id, $specifiedIds)) {
|
||||
$toRemoveIds[] = $id;
|
||||
|
||||
@@ -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\Core;
|
||||
|
||||
@@ -29,11 +29,11 @@ use \Espo\Core\Utils\Util;
|
||||
class SelectManagerFactory
|
||||
{
|
||||
private $entityManager;
|
||||
|
||||
|
||||
private $user;
|
||||
|
||||
|
||||
private $acl;
|
||||
|
||||
|
||||
private $metadata;
|
||||
|
||||
public function __construct($entityManager, \Espo\Entities\User $user, Acl $acl, $metadata)
|
||||
@@ -43,26 +43,28 @@ class SelectManagerFactory
|
||||
$this->acl = $acl;
|
||||
$this->metadata = $metadata;
|
||||
}
|
||||
|
||||
|
||||
public function create($entityName)
|
||||
{
|
||||
$className = '\\Espo\\Custom\\SelectManagers\\' . Util::normilizeClassName($entityName);
|
||||
$normalizedName = Util::normilizeClassName($entityName);
|
||||
|
||||
$className = '\\Espo\\Custom\\SelectManagers\\' . $normalizedName;
|
||||
if (!class_exists($className)) {
|
||||
$moduleName = $this->metadata->getScopeModuleName($entityName);
|
||||
if ($moduleName) {
|
||||
$className = '\\Espo\\Modules\\' . $moduleName . '\\SelectManagers\\' . Util::normilizeClassName($entityName);
|
||||
$className = '\\Espo\\Modules\\' . $moduleName . '\\SelectManagers\\' . $normalizedName;
|
||||
} else {
|
||||
$className = '\\Espo\\SelectManagers\\' . Util::normilizeClassName($entityName);
|
||||
}
|
||||
$className = '\\Espo\\SelectManagers\\' . $normalizedName;
|
||||
}
|
||||
if (!class_exists($className)) {
|
||||
$className = '\\Espo\\Core\\SelectManagers\\Base';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$selectManager = new $className($this->entityManager, $this->user, $this->acl, $this->metadata);
|
||||
$selectManager->setEntityName($entityName);
|
||||
|
||||
|
||||
return $selectManager;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -49,9 +49,20 @@ class Base
|
||||
$this->entityManager = $entityManager;
|
||||
$this->user = $user;
|
||||
$this->acl = $acl;
|
||||
|
||||
$this->metadata = $metadata;
|
||||
}
|
||||
|
||||
protected function getEntityManager()
|
||||
{
|
||||
return $this->entityManager;
|
||||
}
|
||||
|
||||
protected function getUser()
|
||||
{
|
||||
return $this->user;
|
||||
}
|
||||
|
||||
public function setEntityName($entityName)
|
||||
{
|
||||
$this->entityName = $entityName;
|
||||
@@ -100,6 +111,28 @@ class Base
|
||||
return $this->seed;
|
||||
}
|
||||
|
||||
protected function textFilter($value, &$result)
|
||||
{
|
||||
$fieldDefs = $this->getSeed()->getFields();
|
||||
$fieldList = $this->getTextFilterFields();
|
||||
$d = array();
|
||||
|
||||
foreach ($fieldList as $field) {
|
||||
if (
|
||||
strlen($item['value']) >= self::MIN_LENGTH_FOR_CONTENT_SEARCH
|
||||
&&
|
||||
!empty($fieldDefs[$field]['type']) && $fieldDefs[$field]['type'] == 'text'
|
||||
) {
|
||||
$d[$field . '*'] = '%' . $value . '%';
|
||||
} else {
|
||||
$d[$field . '*'] = $value . '%';
|
||||
}
|
||||
}
|
||||
$result['whereClause'][] = array(
|
||||
'OR' => $d
|
||||
);
|
||||
}
|
||||
|
||||
protected function where($params, &$result)
|
||||
{
|
||||
if (!empty($params['where']) && is_array($params['where'])) {
|
||||
@@ -112,29 +145,11 @@ class Base
|
||||
if (!empty($p)) {
|
||||
$params['where'][] = $p;
|
||||
}
|
||||
$this->boolFilter($filter, $result);
|
||||
}
|
||||
} else if ($item['type'] == 'textFilter' && !empty($item['value'])) {
|
||||
if (!empty($item['value'])) {
|
||||
if (empty($result['whereClause'])) {
|
||||
$result['whereClause'] = array();
|
||||
}
|
||||
$fieldDefs = $this->getSeed()->getFields();
|
||||
$fieldList = $this->getTextFilterFields();
|
||||
$d = array();
|
||||
foreach ($fieldList as $field) {
|
||||
if (
|
||||
strlen($item['value']) >= self::MIN_LENGTH_FOR_CONTENT_SEARCH
|
||||
&&
|
||||
!empty($fieldDefs[$field]['type']) && $fieldDefs[$field]['type'] == 'text'
|
||||
) {
|
||||
$d[$field . '*'] = '%' . $item['value'] . '%';
|
||||
} else {
|
||||
$d[$field . '*'] = $item['value'] . '%';
|
||||
}
|
||||
}
|
||||
$where[] = array(
|
||||
'OR' => $d
|
||||
);
|
||||
$this->textFilter($item['value'], $result);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -191,17 +206,13 @@ class Base
|
||||
|
||||
}
|
||||
|
||||
$result['whereClause'] = $where;
|
||||
$result['whereClause'] = array_merge($result['whereClause'], $where);
|
||||
}
|
||||
}
|
||||
|
||||
protected function q($params, &$result)
|
||||
{
|
||||
if (!empty($params['q'])) {
|
||||
if (empty($result['whereClause'])) {
|
||||
$result['whereClause'] = array();
|
||||
}
|
||||
|
||||
$fieldDefs = $this->getSeed()->getFields();
|
||||
|
||||
$value = $params['q'];
|
||||
@@ -229,33 +240,40 @@ class Base
|
||||
protected function access(&$result)
|
||||
{
|
||||
if ($this->acl->checkReadOnlyOwn($this->entityName)) {
|
||||
|
||||
if (!array_key_exists('whereClause', $result)) {
|
||||
$result['whereClause'] = array();
|
||||
}
|
||||
$result['whereClause']['assignedUserId'] = $this->user->id;
|
||||
$this->accessOnlyOwn($result);
|
||||
}
|
||||
if (!$this->user->isAdmin() && $this->acl->checkReadOnlyTeam($this->entityName)) {
|
||||
if (!array_key_exists('whereClause', $result)) {
|
||||
$result['whereClause'] = array();
|
||||
}
|
||||
$result['distinct'] = true;
|
||||
if (!array_key_exists('joins', $result)) {
|
||||
$result['joins'] = array();
|
||||
}
|
||||
if (!in_array('teams', $result['joins'])) {
|
||||
$result['leftJoins'][] = 'teams';
|
||||
}
|
||||
|
||||
$result['whereClause'][] = array(
|
||||
'OR' => array(
|
||||
'Team.id' => $this->user->get('teamsIds'),
|
||||
'assignedUserId' => $this->user->id
|
||||
)
|
||||
);
|
||||
$this->accessOnlyTeam($result);
|
||||
}
|
||||
}
|
||||
|
||||
protected function accessOnlyOwn(&$result)
|
||||
{
|
||||
if (!$this->getSeed()->hasField('assignedUserId')) {
|
||||
return;
|
||||
}
|
||||
$result['whereClause'][] = array(
|
||||
'assignedUserId' => $this->getUser()->id
|
||||
);
|
||||
}
|
||||
|
||||
protected function accessOnlyTeam(&$result)
|
||||
{
|
||||
if (!$this->getSeed()->hasField('teamsIds')) {
|
||||
return;
|
||||
}
|
||||
$result['distinct'] = true;
|
||||
if (!in_array('teams', $result['joins'])) {
|
||||
$result['leftJoins'][] = 'teams';
|
||||
}
|
||||
$result['whereClause'][] = array(
|
||||
'OR' => array(
|
||||
'Team.id' => $this->user->get('teamsIds'),
|
||||
'assignedUserId' => $this->getUser()->id
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
public function getAclParams()
|
||||
{
|
||||
$result = array();
|
||||
@@ -265,7 +283,11 @@ class Base
|
||||
|
||||
public function getSelectParams(array $params, $withAcl = false)
|
||||
{
|
||||
$result = array();
|
||||
$result = array(
|
||||
'joins' => array(),
|
||||
'leftJoins' => array(),
|
||||
'whereClause' => array()
|
||||
);
|
||||
|
||||
$this->order($params, $result);
|
||||
$this->limit($params, $result);
|
||||
@@ -411,6 +433,14 @@ class Base
|
||||
return $part;
|
||||
}
|
||||
|
||||
protected function boolFilter($filterName, &$result)
|
||||
{
|
||||
$method = 'boolFilter' . ucfirst($filterName);
|
||||
if (method_exists($this, $method)) {
|
||||
$this->$method($result);
|
||||
}
|
||||
}
|
||||
|
||||
protected function getBoolFilterWhere($filterName)
|
||||
{
|
||||
$method = 'getBoolFilterWhere' . ucfirst($filterName);
|
||||
@@ -419,12 +449,10 @@ class Base
|
||||
}
|
||||
}
|
||||
|
||||
protected function getBoolFilterWhereOnlyMy()
|
||||
protected function boolFilterOnlyMy(&$result)
|
||||
{
|
||||
return array(
|
||||
'type' => 'equals',
|
||||
'field' => 'assignedUserId',
|
||||
'value' => $this->user->id,
|
||||
$result['whereClause'][] = array(
|
||||
'assignedUserId' => $this->getUser()->id
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,31 +48,6 @@ class ServiceFactory
|
||||
$this->container = $container;
|
||||
}
|
||||
|
||||
protected function init()
|
||||
{
|
||||
$config = $this->getContainer()->get('config');
|
||||
|
||||
if (file_exists($this->cacheFile) && $config->get('useCache')) {
|
||||
$this->data = $this->getFileManager()->getPhpContents($this->cacheFile);
|
||||
} else {
|
||||
$this->data = $this->getClassNameHash($this->paths['corePath']);
|
||||
|
||||
foreach ($this->getContainer()->get('metadata')->getModuleList() as $moduleName) {
|
||||
$path = str_replace('{*}', $moduleName, $this->paths['modulePath']);
|
||||
$this->data = array_merge($this->data, $this->getClassNameHash($path));
|
||||
}
|
||||
|
||||
$this->data = array_merge($this->data, $this->getClassNameHash($this->paths['customPath']));
|
||||
|
||||
if ($config->get('useCache')) {
|
||||
$result = $this->getFileManager()->putPhpContents($this->cacheFile, $this->data);
|
||||
if ($result == false) {
|
||||
throw new \Espo\Core\Exceptions\Error();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function getFileManager()
|
||||
{
|
||||
return $this->container->get('fileManager');
|
||||
@@ -83,15 +58,19 @@ class ServiceFactory
|
||||
return $this->container;
|
||||
}
|
||||
|
||||
protected function init()
|
||||
{
|
||||
$classParser = $this->getContainer()->get('classParser');
|
||||
$classParser->setAllowedMethods(null);
|
||||
$this->data = $classParser->getData($this->paths, $this->cacheFile);
|
||||
}
|
||||
|
||||
protected function getClassName($name)
|
||||
{
|
||||
$name = Util::normilizeClassName($name);
|
||||
|
||||
if (!isset($this->data)) {
|
||||
$this->init();
|
||||
}
|
||||
|
||||
$name = ucfirst($name);
|
||||
if (isset($this->data[$name])) {
|
||||
return $this->data[$name];
|
||||
}
|
||||
@@ -127,28 +106,5 @@ class ServiceFactory
|
||||
}
|
||||
throw new Error("Class '$className' does not exist");
|
||||
}
|
||||
|
||||
// TODO delegate to another class
|
||||
protected function getClassNameHash($dirs)
|
||||
{
|
||||
if (is_string($dirs)) {
|
||||
$dirs = (array) $dirs;
|
||||
}
|
||||
|
||||
$data = array();
|
||||
|
||||
foreach ($dirs as $dir) {
|
||||
if (file_exists($dir)) {
|
||||
$fileList = $this->getFileManager()->getFileList($dir, false, '\.php$', true);
|
||||
foreach ($fileList as $file) {
|
||||
$filePath = Util::concatPath($dir, $file);
|
||||
$className = Util::getClassName($filePath);
|
||||
$fileName = $this->getFileManager()->getFileName($filePath);
|
||||
$data[$fileName] = $className;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
30
application/Espo/Core/Templates/Controllers/Base.php
Normal file
30
application/Espo/Core/Templates/Controllers/Base.php
Normal file
@@ -0,0 +1,30 @@
|
||||
<?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/.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Core\Templates\Controllers;
|
||||
|
||||
|
||||
class Base extends \Espo\Core\Controllers\Record
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
30
application/Espo/Core/Templates/Controllers/Person.php
Normal file
30
application/Espo/Core/Templates/Controllers/Person.php
Normal file
@@ -0,0 +1,30 @@
|
||||
<?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/.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Core\Templates\Controllers;
|
||||
|
||||
|
||||
class Person extends \Espo\Core\Controllers\Record
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
29
application/Espo/Core/Templates/Entities/Base.php
Normal file
29
application/Espo/Core/Templates/Entities/Base.php
Normal file
@@ -0,0 +1,29 @@
|
||||
<?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/.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Core\Templates\Entities;
|
||||
|
||||
class Base extends \Espo\Core\ORM\Entity
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
29
application/Espo/Core/Templates/Entities/Person.php
Normal file
29
application/Espo/Core/Templates/Entities/Person.php
Normal file
@@ -0,0 +1,29 @@
|
||||
<?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/.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Core\Templates\Entities;
|
||||
|
||||
class Person extends \Espo\Core\Entities\Person
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"controller": "Controllers.Record",
|
||||
"boolFilters": ["onlyMy"]
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
{
|
||||
"fields": {
|
||||
"name": {
|
||||
"type": "varchar",
|
||||
"required": true
|
||||
},
|
||||
"description": {
|
||||
"type": "text"
|
||||
},
|
||||
"createdAt": {
|
||||
"type": "datetime",
|
||||
"readOnly": true
|
||||
},
|
||||
"modifiedAt": {
|
||||
"type": "datetime",
|
||||
"readOnly": true
|
||||
},
|
||||
"createdBy": {
|
||||
"type": "link",
|
||||
"readOnly": true
|
||||
},
|
||||
"modifiedBy": {
|
||||
"type": "link",
|
||||
"readOnly": true
|
||||
},
|
||||
"assignedUser": {
|
||||
"type": "link",
|
||||
"required": true
|
||||
},
|
||||
"teams": {
|
||||
"type": "linkMultiple"
|
||||
}
|
||||
},
|
||||
"links": {
|
||||
"createdBy": {
|
||||
"type": "belongsTo",
|
||||
"entity": "User"
|
||||
},
|
||||
"modifiedBy": {
|
||||
"type": "belongsTo",
|
||||
"entity": "User"
|
||||
},
|
||||
"assignedUser": {
|
||||
"type": "belongsTo",
|
||||
"entity": "User"
|
||||
},
|
||||
"teams": {
|
||||
"type": "hasMany",
|
||||
"entity": "Team",
|
||||
"relationName": "EntityTeam"
|
||||
}
|
||||
},
|
||||
"collection": {
|
||||
"sortBy": "createdAt",
|
||||
"asc": false
|
||||
},
|
||||
"indexes": {
|
||||
"name": {
|
||||
"columns": ["name", "deleted"]
|
||||
},
|
||||
"assignedUser": {
|
||||
"columns": ["assignedUserId", "deleted"]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"controller": "Controllers.Record",
|
||||
"boolFilters": ["onlyMy"]
|
||||
}
|
||||
110
application/Espo/Core/Templates/Metadata/Person/entityDefs.json
Normal file
110
application/Espo/Core/Templates/Metadata/Person/entityDefs.json
Normal file
@@ -0,0 +1,110 @@
|
||||
{
|
||||
"fields": {
|
||||
"name": {
|
||||
"type": "personName"
|
||||
},
|
||||
"salutationName": {
|
||||
"type": "enum",
|
||||
"options": ["", "Mr.", "Mrs.", "Dr."]
|
||||
},
|
||||
"firstName": {
|
||||
"type": "varchar",
|
||||
"maxLength": 100,
|
||||
"default": ""
|
||||
},
|
||||
"lastName": {
|
||||
"type": "varchar",
|
||||
"maxLength": 100,
|
||||
"required": true,
|
||||
"default": ""
|
||||
},
|
||||
"description": {
|
||||
"type": "text"
|
||||
},
|
||||
"createdAt": {
|
||||
"type": "datetime",
|
||||
"readOnly": true
|
||||
},
|
||||
"emailAddress": {
|
||||
"type": "email"
|
||||
},
|
||||
"phoneNumber": {
|
||||
"type": "phone",
|
||||
"typeList": ["Mobile", "Office", "Home", "Fax", "Other"],
|
||||
"defaultType": "Mobile"
|
||||
},
|
||||
"address": {
|
||||
"type": "address"
|
||||
},
|
||||
"addressStreet": {
|
||||
"type": "text",
|
||||
"maxLength": 255,
|
||||
"dbType": "varchar"
|
||||
},
|
||||
"addressCity": {
|
||||
"type": "varchar"
|
||||
},
|
||||
"addressState": {
|
||||
"type": "varchar"
|
||||
},
|
||||
"addressCountry": {
|
||||
"type": "varchar"
|
||||
},
|
||||
"addressPostalCode": {
|
||||
"type": "varchar"
|
||||
},
|
||||
"modifiedAt": {
|
||||
"type": "datetime",
|
||||
"readOnly": true
|
||||
},
|
||||
"createdBy": {
|
||||
"type": "link",
|
||||
"readOnly": true
|
||||
},
|
||||
"modifiedBy": {
|
||||
"type": "link",
|
||||
"readOnly": true
|
||||
},
|
||||
"assignedUser": {
|
||||
"type": "link",
|
||||
"required": true
|
||||
},
|
||||
"teams": {
|
||||
"type": "linkMultiple"
|
||||
}
|
||||
},
|
||||
"links": {
|
||||
"createdBy": {
|
||||
"type": "belongsTo",
|
||||
"entity": "User"
|
||||
},
|
||||
"modifiedBy": {
|
||||
"type": "belongsTo",
|
||||
"entity": "User"
|
||||
},
|
||||
"assignedUser": {
|
||||
"type": "belongsTo",
|
||||
"entity": "User"
|
||||
},
|
||||
"teams": {
|
||||
"type": "hasMany",
|
||||
"entity": "Team",
|
||||
"relationName": "EntityTeam"
|
||||
}
|
||||
},
|
||||
"collection": {
|
||||
"sortBy": "createdAt",
|
||||
"asc": false
|
||||
},
|
||||
"indexes": {
|
||||
"firstName": {
|
||||
"columns": ["firstName", "deleted"]
|
||||
},
|
||||
"name": {
|
||||
"columns": ["firstName", "lastName"]
|
||||
},
|
||||
"assignedUser": {
|
||||
"columns": ["assignedUserId", "deleted"]
|
||||
}
|
||||
}
|
||||
}
|
||||
30
application/Espo/Core/Templates/Repositories/Base.php
Normal file
30
application/Espo/Core/Templates/Repositories/Base.php
Normal file
@@ -0,0 +1,30 @@
|
||||
<?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/.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Core\Templates\Repositories;
|
||||
|
||||
|
||||
class Base extends \Espo\Core\ORM\Repositories\RDB
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
30
application/Espo/Core/Templates/Repositories/Person.php
Normal file
30
application/Espo/Core/Templates/Repositories/Person.php
Normal file
@@ -0,0 +1,30 @@
|
||||
<?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/.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Core\Templates\Repositories;
|
||||
|
||||
|
||||
class Person extends \Espo\Core\ORM\Repositories\RDB
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
30
application/Espo/Core/Templates/Services/Base.php
Normal file
30
application/Espo/Core/Templates/Services/Base.php
Normal file
@@ -0,0 +1,30 @@
|
||||
<?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/.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Core\Templates\Services;
|
||||
|
||||
|
||||
class Base extends \Espo\Services\Record
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
30
application/Espo/Core/Templates/Services/Person.php
Normal file
30
application/Espo/Core/Templates/Services/Person.php
Normal file
@@ -0,0 +1,30 @@
|
||||
<?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/.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Core\Templates\Services;
|
||||
|
||||
|
||||
class Person extends \Espo\Services\Record
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
@@ -22,9 +22,11 @@
|
||||
|
||||
namespace Espo\Core\Upgrades\Actions;
|
||||
|
||||
use Espo\Core\Utils\Util,
|
||||
Espo\Core\Utils\Json,
|
||||
Espo\Core\Exceptions\Error;
|
||||
use Espo\Core\Utils\Util;
|
||||
use Espo\Core\Utils\System;
|
||||
use Espo\Core\Utils\Json;
|
||||
use Espo\Core\Exceptions\Error;
|
||||
use vierbergenlars\SemVer;
|
||||
|
||||
abstract class Base
|
||||
{
|
||||
@@ -179,46 +181,55 @@ abstract class Base
|
||||
*/
|
||||
protected function isAcceptable()
|
||||
{
|
||||
$manifest = $this->getManifest();
|
||||
|
||||
$res = $this->checkPackageType();
|
||||
$res &= $this->checkVersions();
|
||||
|
||||
//check php version
|
||||
if (isset($manifest['php'])) {
|
||||
$res &= $this->checkVersions($manifest['php'], System::getPhpVersion(), 'Your PHP version does not support this installation package.');
|
||||
}
|
||||
|
||||
//check acceptableVersions
|
||||
if (isset($manifest['acceptableVersions'])) {
|
||||
$res &= $this->checkVersions($manifest['acceptableVersions'], $this->getConfig()->get('version'), 'Your EspoCRM version doesn\'t match for this installation package.');
|
||||
}
|
||||
|
||||
return (bool) $res;
|
||||
}
|
||||
|
||||
protected function checkVersions()
|
||||
protected function checkVersions($versionList, $currentVersion, $errorMessage = '')
|
||||
{
|
||||
$manifest = $this->getManifest();
|
||||
|
||||
/** check acceptable versions */
|
||||
$version = $manifest['acceptableVersions'];
|
||||
if (empty($version)) {
|
||||
if (empty($versionList)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$currentVersion = $this->getConfig()->get('version');
|
||||
|
||||
if (is_string($version)) {
|
||||
$version = (array) $version;
|
||||
if (is_string($versionList)) {
|
||||
$versionList = (array) $versionList;
|
||||
}
|
||||
|
||||
foreach ($version as $strVersion) {
|
||||
try {
|
||||
$semver = new SemVer\version($currentVersion);
|
||||
} catch (\Exception $e) {
|
||||
$GLOBALS['log']->error('Cannot recognize currentVersion ['.$currentVersion.'], error: '.$e->getMessage().'.');
|
||||
return;
|
||||
}
|
||||
|
||||
$strVersion = trim($strVersion);
|
||||
foreach ($versionList as $version) {
|
||||
|
||||
if ($strVersion == $currentVersion) {
|
||||
return true;
|
||||
$isInRange = false;
|
||||
try {
|
||||
$isInRange = $semver->satisfies(new SemVer\expression($version));
|
||||
} catch (\Exception $e) {
|
||||
$GLOBALS['log']->error('Version identification error: '.$e->getMessage().'.');
|
||||
}
|
||||
|
||||
$strVersion = str_replace('\\', '', $strVersion);
|
||||
$strVersion = preg_quote($strVersion);
|
||||
$strVersion = str_replace('\\*', '+', $strVersion);
|
||||
|
||||
if (preg_match('/^'.$strVersion.'/', $currentVersion)) {
|
||||
if ($isInRange) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
$this->throwErrorAndRemovePackage('Your EspoCRM version doesn\'t match for this installation package.');
|
||||
$this->throwErrorAndRemovePackage($errorMessage);
|
||||
}
|
||||
|
||||
protected function checkPackageType()
|
||||
@@ -491,5 +502,8 @@ abstract class Base
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
protected function clearCache()
|
||||
{
|
||||
return $this->getContainer()->get('dataManager')->clearCache();
|
||||
}
|
||||
}
|
||||
@@ -83,6 +83,8 @@ class Install extends \Espo\Core\Upgrades\Actions\Base
|
||||
|
||||
$this->afterRunAction();
|
||||
|
||||
$this->clearCache();
|
||||
|
||||
/* delete unziped files */
|
||||
$this->deletePackageFiles();
|
||||
|
||||
|
||||
@@ -65,6 +65,8 @@ class Uninstall extends \Espo\Core\Upgrades\Actions\Base
|
||||
|
||||
$this->afterRunAction();
|
||||
|
||||
$this->clearCache();
|
||||
|
||||
/* delete backup files */
|
||||
$this->deletePackageFiles();
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
namespace Espo\Core\Utils;
|
||||
|
||||
use \Espo\Core\Exceptions\Error;
|
||||
use \Espo\Core\Exceptions\Forbidden;
|
||||
|
||||
class Auth
|
||||
{
|
||||
@@ -72,6 +73,10 @@ class Auth
|
||||
$user = $this->authentication->login($username, $password, $authToken);
|
||||
|
||||
if ($user) {
|
||||
if (!$user->isActive()) {
|
||||
$GLOBALS['log']->debug("AUTH: Trying to login as user '".$user->get('userName')."' which is not active.");
|
||||
return false;
|
||||
}
|
||||
$entityManager->setUser($user);
|
||||
$this->container->setUser($user);
|
||||
$GLOBALS['log']->debug('AUTH: Result of authenticate is [true]');
|
||||
|
||||
@@ -262,7 +262,7 @@ class Config
|
||||
}
|
||||
|
||||
if (empty($this->adminItems)) {
|
||||
$this->adminItems= Util::merge($data['systemItems'], $data['adminItems']);
|
||||
$this->adminItems = array_merge($data['systemItems'], $data['adminItems']);
|
||||
}
|
||||
|
||||
return $this->adminItems;
|
||||
|
||||
419
application/Espo/Core/Utils/EntityManager.php
Normal file
419
application/Espo/Core/Utils/EntityManager.php
Normal file
@@ -0,0 +1,419 @@
|
||||
<?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/.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Core\Utils;
|
||||
|
||||
use \Espo\Core\Exceptions\Error;
|
||||
use \Espo\Core\Exceptions\Forbidden;
|
||||
use \Espo\Core\Exceptions\Conflict;
|
||||
use \Espo\Core\Utils\Json;
|
||||
|
||||
class EntityManager
|
||||
{
|
||||
private $metadata;
|
||||
|
||||
private $language;
|
||||
|
||||
private $fileManager;
|
||||
|
||||
private $metadataUtils;
|
||||
|
||||
public function __construct(Metadata $metadata, Language $language, File\Manager $fileManager)
|
||||
{
|
||||
$this->metadata = $metadata;
|
||||
$this->language = $language;
|
||||
$this->fileManager = $fileManager;
|
||||
|
||||
$this->metadataUtils = new \Espo\Core\Utils\Metadata\Utils($this->metadata);
|
||||
}
|
||||
|
||||
protected function getMetadata()
|
||||
{
|
||||
return $this->metadata;
|
||||
}
|
||||
|
||||
protected function getLanguage()
|
||||
{
|
||||
return $this->language;
|
||||
}
|
||||
|
||||
protected function getFileManager()
|
||||
{
|
||||
return $this->fileManager;
|
||||
}
|
||||
|
||||
protected function getMetadataUtils()
|
||||
{
|
||||
return $this->metadataUtils;
|
||||
}
|
||||
|
||||
public function create($name, $type, $params = array())
|
||||
{
|
||||
if ($this->getMetadata()->get('scopes.' . $name)) {
|
||||
throw new Conflict('Entity ['.$name.'] already exists.');
|
||||
}
|
||||
if (empty($name) || empty($type)) {
|
||||
throw new Error();
|
||||
}
|
||||
|
||||
$normalizedName = Util::normilizeClassName($name);
|
||||
|
||||
$contents = "<" . "?" . "php\n".
|
||||
"namespace Espo\Custom\Entities;\n".
|
||||
"class {$normalizedName} extends \Espo\Core\Templates\Entities\\{$type}\n".
|
||||
"{\n".
|
||||
"}\n";
|
||||
|
||||
$filePath = "custom/Espo/Custom/Entities/{$normalizedName}.php";
|
||||
$this->getFileManager()->putContents($filePath, $contents);
|
||||
|
||||
$contents = "<" . "?" . "php\n".
|
||||
"namespace Espo\Custom\Controllers;\n".
|
||||
"class {$normalizedName} extends \Espo\Core\Templates\Controllers\\{$type}\n".
|
||||
"{\n".
|
||||
"}\n";
|
||||
$filePath = "custom/Espo/Custom/Controllers/{$normalizedName}.php";
|
||||
$this->getFileManager()->putContents($filePath, $contents);
|
||||
|
||||
$contents = "<" . "?" . "php\n".
|
||||
"namespace Espo\Custom\Services;\n".
|
||||
"class {$normalizedName} extends \Espo\Core\Templates\Services\\{$type}\n".
|
||||
"{\n".
|
||||
"}\n";
|
||||
$filePath = "custom/Espo/Custom/Services/{$normalizedName}.php";
|
||||
$this->getFileManager()->putContents($filePath, $contents);
|
||||
|
||||
$contents = "<" . "?" . "php\n".
|
||||
"namespace Espo\Custom\Repositories;\n".
|
||||
"class {$normalizedName} extends \Espo\Core\Templates\Repositories\\{$type}\n".
|
||||
"{\n".
|
||||
"}\n";
|
||||
|
||||
$filePath = "custom/Espo/Custom/Repositories/{$normalizedName}.php";
|
||||
$this->getFileManager()->putContents($filePath, $contents);
|
||||
|
||||
$stream = false;
|
||||
if (!empty($params['stream'])) {
|
||||
$stream = $params['stream'];
|
||||
}
|
||||
$labelSingular = $name;
|
||||
if (!empty($params['labelSingular'])) {
|
||||
$labelSingular = $params['labelSingular'];
|
||||
}
|
||||
$labelPlural = $name;
|
||||
if (!empty($params['labelPlural'])) {
|
||||
$labelPlural = $params['labelPlural'];
|
||||
}
|
||||
$labelCreate = $this->getLanguage()->translate('Create') . ' ' . $labelSingular;
|
||||
|
||||
$scopeData = array(
|
||||
'entity' => true,
|
||||
'layouts' => true,
|
||||
'tab' => true,
|
||||
'acl' => true,
|
||||
'module' => 'Custom',
|
||||
'isCustom' => true,
|
||||
'customizable' => true,
|
||||
'importable' => true,
|
||||
'type' => $type,
|
||||
'stream' => $stream
|
||||
);
|
||||
$this->getMetadata()->set('scopes', $name, $scopeData);
|
||||
|
||||
$filePath = "application/Espo/Core/Templates/Metadata/{$type}/entityDefs.json";
|
||||
$entityDefsData = Json::decode($this->getFileManager()->getContents($filePath), true);
|
||||
$this->getMetadata()->set('entityDefs', $name, $entityDefsData);
|
||||
|
||||
$filePath = "application/Espo/Core/Templates/Metadata/{$type}/clientDefs.json";
|
||||
$clientDefsData = Json::decode($this->getFileManager()->getContents($filePath), true);
|
||||
$this->getMetadata()->set('clientDefs', $name, $clientDefsData);
|
||||
|
||||
$this->getLanguage()->set('Global', 'scopeNames', $name, $labelSingular);
|
||||
$this->getLanguage()->set('Global', 'scopeNamesPlural', $name, $labelPlural);
|
||||
$this->getLanguage()->set($name, 'labels', 'Create ' . $name, $labelCreate);
|
||||
|
||||
$this->getMetadata()->save();
|
||||
$this->getLanguage()->save();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function update($name, $data)
|
||||
{
|
||||
if (!$this->getMetadata()->get('scopes.' . $name)) {
|
||||
throw new Error('Entity ['.$name.'] does not exist.');
|
||||
}
|
||||
|
||||
if (isset($data['stream'])) {
|
||||
$scopeData = array(
|
||||
'stream' => (true == $data['stream'])
|
||||
);
|
||||
$this->getMetadata()->set('scopes', $name, $scopeData);
|
||||
}
|
||||
|
||||
if (!empty($data['labelSingular'])) {
|
||||
$labelSingular = $data['labelSingular'];
|
||||
$this->getLanguage()->set('Global', 'scopeNames', $name, $labelSingular);
|
||||
$labelCreate = $this->getLanguage()->translate('Create') . ' ' . $labelSingular;
|
||||
$this->getLanguage()->set($name, 'labels', 'Create ' . $name, $labelCreate);
|
||||
}
|
||||
|
||||
if (!empty($data['labelPlural'])) {
|
||||
$labelPlural = $data['labelPlural'];
|
||||
$this->getLanguage()->set('Global', 'scopeNamesPlural', $name, $labelPlural);
|
||||
}
|
||||
|
||||
$this->getMetadata()->save();
|
||||
$this->getLanguage()->save();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function delete($name)
|
||||
{
|
||||
if (!$this->isCustom($name)) {
|
||||
throw new Forbidden;
|
||||
}
|
||||
|
||||
$normalizedName = Util::normilizeClassName($name);
|
||||
|
||||
$unsets = array(
|
||||
'entityDefs',
|
||||
'clientDefs',
|
||||
'scopes'
|
||||
);
|
||||
$res = $this->getMetadata()->delete('entityDefs', $name);
|
||||
$res = $this->getMetadata()->delete('clientDefs', $name);
|
||||
$res = $this->getMetadata()->delete('scopes', $name);
|
||||
|
||||
$this->getFileManager()->removeFile("custom/Espo/Custom/Resources/metadata/entityDefs/{$name}.json");
|
||||
$this->getFileManager()->removeFile("custom/Espo/Custom/Resources/metadata/clientDefs/{$name}.json");
|
||||
$this->getFileManager()->removeFile("custom/Espo/Custom/Resources/metadata/scopes/{$name}.json");
|
||||
|
||||
$this->getFileManager()->removeFile("custom/Espo/Custom/Entities/{$normalizedName}.php");
|
||||
$this->getFileManager()->removeFile("custom/Espo/Custom/Services/{$normalizedName}.php");
|
||||
$this->getFileManager()->removeFile("custom/Espo/Custom/Controllers/{$normalizedName}.php");
|
||||
$this->getFileManager()->removeFile("custom/Espo/Custom/Repositories/{$normalizedName}.php");
|
||||
|
||||
try {
|
||||
$this->getLanguage()->delete('Global', 'scopeNames', $name);
|
||||
$this->getLanguage()->delete('Global', 'scopeNamesPlural', $name);
|
||||
} catch (\Exception $e) {}
|
||||
|
||||
$this->getMetadata()->save();
|
||||
$this->getLanguage()->save();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function isCustom($name)
|
||||
{
|
||||
return $this->getMetadata()->get('scopes.' . $name . '.isCustom');
|
||||
}
|
||||
|
||||
public function createLink(array $params)
|
||||
{
|
||||
$linkType = $params['linkType'];
|
||||
|
||||
$entity = $params['entity'];
|
||||
$link = $params['link'];
|
||||
$entityForeign = $params['entityForeign'];
|
||||
$linkForeign = $params['linkForeign'];
|
||||
|
||||
$label = $params['label'];
|
||||
$labelForeign = $params['labelForeign'];
|
||||
|
||||
if (empty($linkType)) {
|
||||
throw new Error();
|
||||
}
|
||||
if (empty($entity) || empty($entityForeign)) {
|
||||
throw new Error();
|
||||
}
|
||||
if (empty($entityForeign) || empty($linkForeign)) {
|
||||
throw new Error();
|
||||
}
|
||||
|
||||
if ($this->getMetadata()->get('entityDefs.' . $entity . '.links.' . $link)) {
|
||||
throw new Conflict('Link ['.$entity.'::'.$link.'] already exists.');
|
||||
}
|
||||
if ($this->getMetadata()->get('entityDefs.' . $entityForeign . '.links.' . $linkForeign)) {
|
||||
throw new Conflict('Link ['.$entityForeign.'::'.$linkForeign.'] already exists.');
|
||||
}
|
||||
|
||||
switch ($linkType) {
|
||||
case 'oneToMany':
|
||||
if ($this->getMetadata()->get('entityDefs.' . $entityForeign . '.field.' . $linkForeign)) {
|
||||
throw new Conflict('Field ['.$entityForeign.'::'.$linkForeign.'] already exists.');
|
||||
}
|
||||
$dataLeft = array(
|
||||
'links' => array(
|
||||
$link => array(
|
||||
'type' => 'hasMany',
|
||||
'foreign' => $linkForeign,
|
||||
'entity' => $entityForeign,
|
||||
'isCustom' => true
|
||||
)
|
||||
)
|
||||
);
|
||||
$dataRight = array(
|
||||
'fields' => array(
|
||||
$linkForeign => array(
|
||||
'type' => 'link'
|
||||
)
|
||||
),
|
||||
'links' => array(
|
||||
$linkForeign => array(
|
||||
'type' => 'belongsTo',
|
||||
'foreign' => $link,
|
||||
'entity' => $entity,
|
||||
'isCustom' => true
|
||||
)
|
||||
)
|
||||
);
|
||||
break;
|
||||
case 'manyToOne':
|
||||
if ($this->getMetadata()->get('entityDefs.' . $entity . '.field.' . $link)) {
|
||||
throw new Conflict('Field ['.$entity.'::'.$link.'] already exists.');
|
||||
}
|
||||
$dataLeft = array(
|
||||
'fields' => array(
|
||||
$link => array(
|
||||
'type' => 'link'
|
||||
)
|
||||
),
|
||||
'links' => array(
|
||||
$link => array(
|
||||
'type' => 'belongsTo',
|
||||
'foreign' => $linkForeign,
|
||||
'entity' => $entityForeign,
|
||||
'isCustom' => true
|
||||
)
|
||||
)
|
||||
);
|
||||
$dataRight = array(
|
||||
'links' => array(
|
||||
$linkForeign => array(
|
||||
'type' => 'hasMany',
|
||||
'foreign' => $link,
|
||||
'entity' => $entity,
|
||||
'isCustom' => true
|
||||
)
|
||||
)
|
||||
);
|
||||
break;
|
||||
case 'manyToMany':
|
||||
$dataLeft = array(
|
||||
'links' => array(
|
||||
$link => array(
|
||||
'type' => 'hasMany',
|
||||
'foreign' => $linkForeign,
|
||||
'entity' => $entityForeign,
|
||||
'isCustom' => true
|
||||
)
|
||||
)
|
||||
);
|
||||
$dataRight = array(
|
||||
'links' => array(
|
||||
$linkForeign => array(
|
||||
'type' => 'hasMany',
|
||||
'foreign' => $link,
|
||||
'entity' => $entity,
|
||||
'isCustom' => true
|
||||
)
|
||||
)
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
$this->getMetadata()->set('entityDefs', $entity, $dataLeft);
|
||||
$this->getMetadata()->set('entityDefs', $entityForeign, $dataRight);
|
||||
$this->getMetadata()->save();
|
||||
|
||||
$this->getLanguage()->set($entity, 'fields', $link, $label);
|
||||
$this->getLanguage()->set($entity, 'links', $link, $label);
|
||||
$this->getLanguage()->set($entityForeign, 'fields', $linkForeign, $labelForeign);
|
||||
$this->getLanguage()->set($entityForeign, 'links', $linkForeign, $labelForeign);
|
||||
|
||||
$this->getLanguage()->save();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function updateLink(array $params)
|
||||
{
|
||||
$entity = $params['entity'];
|
||||
$link = $params['link'];
|
||||
$entityForeign = $params['entityForeign'];
|
||||
$linkForeign = $params['linkForeign'];
|
||||
|
||||
$label = $params['label'];
|
||||
$labelForeign = $params['labelForeign'];
|
||||
|
||||
if (empty($entity) || empty($entityForeign)) {
|
||||
throw new Error();
|
||||
}
|
||||
if (empty($entityForeign) || empty($linkForeign)) {
|
||||
throw new Error();
|
||||
}
|
||||
|
||||
$this->getLanguage()->set($entity, 'fields', $link, $label);
|
||||
$this->getLanguage()->set($entity, 'links', $link, $label);
|
||||
$this->getLanguage()->set($entityForeign, 'fields', $linkForeign, $labelForeign);
|
||||
$this->getLanguage()->set($entityForeign, 'links', $linkForeign, $labelForeign);
|
||||
|
||||
$this->getLanguage()->save();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function deleteLink(array $params)
|
||||
{
|
||||
$entity = $params['entity'];
|
||||
$link = $params['link'];
|
||||
|
||||
if (!$this->getMetadata()->get("entityDefs.{$entity}.links.{$link}.isCustom")) {
|
||||
throw new Error();
|
||||
}
|
||||
|
||||
$entityForeign = $this->getMetadata()->get("entityDefs.{$entity}.links.{$link}.entity");
|
||||
$linkForeign = $this->getMetadata()->get("entityDefs.{$entity}.links.{$link}.foreign");
|
||||
|
||||
if (empty($entity) || empty($entityForeign)) {
|
||||
throw new Error();
|
||||
}
|
||||
if (empty($entityForeign) || empty($linkForeign)) {
|
||||
throw new Error();
|
||||
}
|
||||
|
||||
$this->getMetadata()->delete('entityDefs', $entity, array(
|
||||
'fields.' . $link,
|
||||
'links.' . $link
|
||||
));
|
||||
$this->getMetadata()->delete('entityDefs', $entityForeign, array(
|
||||
'fields.' . $linkForeign,
|
||||
'links.' . $linkForeign
|
||||
));
|
||||
$this->getMetadata()->save();
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -151,6 +151,7 @@ class FieldManager
|
||||
protected function deleteLabel($name, $scope)
|
||||
{
|
||||
$this->getLanguage()->delete($scope, 'fields', $name);
|
||||
$this->getLanguage()->delete($scope, 'options', $name);
|
||||
return $this->getLanguage()->save();
|
||||
}
|
||||
|
||||
@@ -177,6 +178,7 @@ class FieldManager
|
||||
$unnecessaryFields = array(
|
||||
'name',
|
||||
'label',
|
||||
'translatedOptions',
|
||||
);
|
||||
|
||||
foreach ($unnecessaryFields as $fieldName) {
|
||||
|
||||
@@ -60,7 +60,7 @@ class ClassParser
|
||||
return $this->metadata;
|
||||
}
|
||||
|
||||
public function setAllowedMethods(array $methods)
|
||||
public function setAllowedMethods($methods)
|
||||
{
|
||||
$this->allowedMethods = $methods;
|
||||
}
|
||||
@@ -129,11 +129,18 @@ class ClassParser
|
||||
$filePath = Util::concatPath($dir, $file);
|
||||
$className = Util::getClassName($filePath);
|
||||
$fileName = $this->getFileManager()->getFileName($filePath);
|
||||
$fileName = ucfirst($fileName);
|
||||
|
||||
$scopeName = ucfirst($fileName);
|
||||
$normalizedScopeName = Util::normilizeScopeName($scopeName);
|
||||
|
||||
if (empty($this->allowedMethods)) {
|
||||
$data[$normalizedScopeName] = $className;
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ($this->allowedMethods as $methodName) {
|
||||
if (method_exists($className, $methodName)) {
|
||||
$data[$fileName] = $className;
|
||||
$data[$normalizedScopeName] = $className;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -319,7 +319,7 @@ class Manager
|
||||
* Unset some element of content data
|
||||
*
|
||||
* @param string | array $path
|
||||
* @param array | string $unsets [description]
|
||||
* @param array | string $unsets
|
||||
* @return bool
|
||||
*/
|
||||
public function unsetContents($path, $unsets, $isJSON = true)
|
||||
@@ -332,7 +332,12 @@ class Manager
|
||||
|
||||
$currentDataArray = Utils\Json::getArrayData($currentData);
|
||||
|
||||
$unsettedData = Utils\Util::unsetInArray($currentDataArray, $unsets);
|
||||
$unsettedData = Utils\Util::unsetInArray($currentDataArray, $unsets, true);
|
||||
|
||||
if (is_null($unsettedData) || (is_array($unsettedData) && empty($unsettedData))) {
|
||||
$fullPath = $this->concatPaths($path);
|
||||
return $this->unlink($fullPath);
|
||||
}
|
||||
|
||||
if ($isJSON) {
|
||||
return $this->putContentsJson($path, $unsettedData);
|
||||
@@ -593,6 +598,24 @@ class Manager
|
||||
$items = (array) $items;
|
||||
}
|
||||
|
||||
$permissionDeniedList = array();
|
||||
foreach ($items as $item) {
|
||||
if (isset($dirPath)) {
|
||||
$item = Utils\Util::concatPath($dirPath, $item);
|
||||
}
|
||||
|
||||
if (!is_writable($item)) {
|
||||
$permissionDeniedList[] = $item;
|
||||
} else if (!is_writable(dirname($item))) {
|
||||
$permissionDeniedList[] = dirname($item);
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($permissionDeniedList)) {
|
||||
$betterPermissionList = $this->getPermissionUtils()->arrangePermissionList($permissionDeniedList);
|
||||
throw new Error("Permission denied in <br>". implode(", <br>", $betterPermissionList));
|
||||
}
|
||||
|
||||
$result = true;
|
||||
foreach ($items as $item) {
|
||||
if (isset($dirPath)) {
|
||||
|
||||
@@ -41,6 +41,13 @@ class Metadata
|
||||
*/
|
||||
private $name = 'metadata';
|
||||
|
||||
/**
|
||||
* Path to modules
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $pathToModules = 'application/Espo/Modules';
|
||||
|
||||
private $cacheFile = 'data/cache/application/metadata.php';
|
||||
|
||||
private $paths = array(
|
||||
@@ -369,6 +376,8 @@ class Metadata
|
||||
|
||||
public function setOrmMetadata(array $ormMeta)
|
||||
{
|
||||
$result = true;
|
||||
|
||||
if ($this->getConfig()->get('useCache')) {
|
||||
$result = $this->getFileManager()->putPhpContents($this->ormCacheFile, $ormMeta);
|
||||
if ($result == false) {
|
||||
@@ -404,30 +413,27 @@ class Metadata
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Scopes
|
||||
* Load modules
|
||||
*
|
||||
* @return array
|
||||
* @return void
|
||||
*/
|
||||
public function getScopes()
|
||||
protected function loadModuleList()
|
||||
{
|
||||
if (!empty($this->scopes)) {
|
||||
return $this->scopes;
|
||||
}
|
||||
$modules = $this->getFileManager()->getFileList($this->pathToModules, false, '', false);
|
||||
|
||||
$scopeList = $this->get('scopes');
|
||||
if (!is_array($scopeList)) {
|
||||
$this->init(true);
|
||||
$scopeList = $this->get('scopes');
|
||||
}
|
||||
|
||||
$scopes = array();
|
||||
if (is_array($scopeList)) {
|
||||
foreach ($scopeList as $name => $details) {
|
||||
$scopes[$name] = isset($details['module']) ? $details['module'] : false;
|
||||
$modulesToSort = array();
|
||||
if (is_array($modules)) {
|
||||
foreach ($modules as $moduleName) {
|
||||
if (!empty($moduleName) && !isset($modulesToSort[$moduleName])) {
|
||||
$modulesToSort[$moduleName] = $this->getModuleConfig()->get($moduleName . '.order', $this->defaultModuleOrder);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $this->scopes = $scopes;
|
||||
krsort($modulesToSort);
|
||||
asort($modulesToSort);
|
||||
|
||||
$this->moduleList = array_keys($modulesToSort);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -437,24 +443,10 @@ class Metadata
|
||||
*/
|
||||
public function getModuleList()
|
||||
{
|
||||
if (!empty($this->moduleList)) {
|
||||
return $this->moduleList;
|
||||
if (!isset($this->moduleList)) {
|
||||
$this->loadModuleList();
|
||||
}
|
||||
|
||||
$scopes = $this->getScopes();
|
||||
|
||||
$modulesToSort = array();
|
||||
foreach ($scopes as $moduleName) {
|
||||
if (!empty($moduleName) && !isset($modulesToSort[$moduleName])) {
|
||||
$modulesToSort[$moduleName] = $this->getModuleConfig()->get($moduleName . '.order', $this->defaultModuleOrder);
|
||||
}
|
||||
}
|
||||
|
||||
krsort($modulesToSort);
|
||||
asort($modulesToSort);
|
||||
|
||||
$this->moduleList = array_keys($modulesToSort);
|
||||
|
||||
return $this->moduleList;
|
||||
}
|
||||
|
||||
@@ -490,26 +482,4 @@ class Metadata
|
||||
|
||||
return $path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if scope exists
|
||||
*
|
||||
* @param string $scopeName
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isScopeExists($scopeName)
|
||||
{
|
||||
$scopeModuleMap = $this->getScopes();
|
||||
|
||||
$lowerEntityName = strtolower($scopeName);
|
||||
foreach($scopeModuleMap as $rowEntityName => $rowModuleName) {
|
||||
if ($lowerEntityName == strtolower($rowEntityName)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -101,4 +101,20 @@ class System
|
||||
{
|
||||
return (defined("PHP_BINDIR"))? PHP_BINDIR.DIRECTORY_SEPARATOR.'php' : 'php';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get php version (only digits and dots)
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function getPhpVersion()
|
||||
{
|
||||
$version = phpversion();
|
||||
|
||||
if (preg_match('/^[0-9\.]+[0-9]/', $version, $matches)) {
|
||||
return $matches[0];
|
||||
}
|
||||
|
||||
return $version;
|
||||
}
|
||||
}
|
||||
@@ -159,7 +159,7 @@ class Util
|
||||
if ($appendKey !== false) {
|
||||
unset($newValue[$appendKey]);
|
||||
$newValue = array_merge($currentArray[$newName], $newValue);
|
||||
} else if (!static::isSingleArray($newValue)) {
|
||||
} else if (!static::isSingleArray($newValue) || !static::isSingleArray($currentArray[$newName])) {
|
||||
$newValue = static::merge($currentArray[$newName], $newValue);
|
||||
}
|
||||
|
||||
@@ -288,6 +288,23 @@ class Util
|
||||
return $name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove 'Obj' if name is reserved PHP word.
|
||||
*
|
||||
* @param string $name
|
||||
* @return string
|
||||
*/
|
||||
public static function normilizeScopeName($name)
|
||||
{
|
||||
foreach (self::$reservedWords as $reservedWord) {
|
||||
if ($reservedWord.'Obj' == $name) {
|
||||
return $reservedWord;
|
||||
}
|
||||
}
|
||||
|
||||
return $name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Naming according to prefix or postfix type
|
||||
*
|
||||
@@ -351,10 +368,11 @@ class Util
|
||||
* array('EntityName1.unset1', 'EntityName1.unset2', .....)
|
||||
* OR
|
||||
* 'EntityName1.unset1'
|
||||
* @param bool $unsetParentEmptyArray - If unset empty parent array after unsets
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function unsetInArray(array $content, $unsets)
|
||||
public static function unsetInArray(array $content, $unsets, $unsetParentEmptyArray = false)
|
||||
{
|
||||
if (empty($unsets)) {
|
||||
return $content;
|
||||
@@ -379,11 +397,19 @@ class Util
|
||||
|
||||
$unsetElem = $currVal . "['{$lastKey}']";
|
||||
|
||||
$currVal = "
|
||||
$evalString = "
|
||||
if (isset({$unsetElem}) || ( is_array({$currVal}) && array_key_exists('{$lastKey}', {$currVal}) )) {
|
||||
unset({$unsetElem});
|
||||
} ";
|
||||
eval($currVal);
|
||||
eval($evalString);
|
||||
|
||||
if ($unsetParentEmptyArray) {
|
||||
$evalString = "
|
||||
if (is_array({$currVal}) && empty({$currVal})) {
|
||||
unset({$currVal});
|
||||
} ";
|
||||
eval($evalString);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,10 +82,10 @@ return array (
|
||||
'authenticationMethod' => 'Espo',
|
||||
'globalSearchEntityList' =>
|
||||
array (
|
||||
0 => 'Account',
|
||||
1 => 'Contact',
|
||||
2 => 'Lead',
|
||||
3 => 'Opportunity',
|
||||
'Account',
|
||||
'Contact',
|
||||
'Lead',
|
||||
'Opportunity',
|
||||
),
|
||||
"tabList" => array("Account", "Contact", "Lead", "Opportunity", "Calendar", "Meeting", "Call", "Task", "Case", "Email"),
|
||||
"quickCreateList" => array("Account", "Contact", "Lead", "Opportunity", "Meeting", "Call", "Task", "Case"),
|
||||
|
||||
@@ -18,23 +18,22 @@
|
||||
*
|
||||
* 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 Email extends \Espo\Core\ORM\Entity
|
||||
{
|
||||
|
||||
protected function _getSubject()
|
||||
{
|
||||
return $this->get('name');
|
||||
}
|
||||
|
||||
|
||||
protected function _setSubject($value)
|
||||
{
|
||||
return $this->set('name', $value);
|
||||
}
|
||||
|
||||
|
||||
public function addAttachment(\Espo\Entities\Attachment $attachment)
|
||||
{
|
||||
if (!empty($this->id)) {
|
||||
@@ -45,16 +44,16 @@ class Email extends \Espo\Core\ORM\Entity
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function getBodyPlain()
|
||||
{
|
||||
$bodyPlain = $this->get('bodyPlain');
|
||||
$bodyPlain = $this->get('bodyPlain');
|
||||
if (!empty($bodyPlain)) {
|
||||
return $bodyPlain;
|
||||
}
|
||||
|
||||
$body = $this->get('body');
|
||||
|
||||
|
||||
$breaks = array("<br />","<br>","<br/>","<br />","<br />","<br/>","<br>");
|
||||
$body = str_ireplace($breaks, "\r\n", $body);
|
||||
$body = strip_tags($body);
|
||||
@@ -65,7 +64,7 @@ class Email extends \Espo\Core\ORM\Entity
|
||||
{
|
||||
return $this->getBodyPlain();
|
||||
}
|
||||
|
||||
|
||||
public function getBodyForSending()
|
||||
{
|
||||
$body = $this->get('body');
|
||||
@@ -75,19 +74,19 @@ class Email extends \Espo\Core\ORM\Entity
|
||||
$body = str_replace("?entryPoint=attachment&id={$attachment->id}", "cid:{$attachment->id}", $body);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$body = str_replace("<table class=\"table table-bordered\">", "<table class=\"table table-bordered\" width=\"100%\">", $body);
|
||||
|
||||
|
||||
return $body;
|
||||
}
|
||||
|
||||
|
||||
public function getInlineAttachments()
|
||||
{
|
||||
$attachmentList = array();
|
||||
$body = $this->get('body');
|
||||
if (!empty($body)) {
|
||||
if (preg_match_all("/\?entryPoint=attachment&id=([^&=\"']+)/", $body, $matches)) {
|
||||
if (!empty($matches[1]) && is_array($matches[1])) {
|
||||
if (preg_match_all("/\?entryPoint=attachment&id=([^&=\"']+)/", $body, $matches)) {
|
||||
if (!empty($matches[1]) && is_array($matches[1])) {
|
||||
foreach($matches[1] as $id) {
|
||||
$attachment = $this->entityManager->getEntity('Attachment', $id);
|
||||
if ($attachment) {
|
||||
@@ -96,7 +95,7 @@ class Email extends \Espo\Core\ORM\Entity
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
return $attachmentList;
|
||||
}
|
||||
|
||||
@@ -18,14 +18,19 @@
|
||||
*
|
||||
* 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 User extends \Espo\Core\Entities\Person
|
||||
{
|
||||
{
|
||||
public function isAdmin()
|
||||
{
|
||||
return $this->get('isAdmin');
|
||||
}
|
||||
|
||||
public function isActive()
|
||||
{
|
||||
return $this->get('isActive');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -29,45 +29,49 @@ use \Espo\Core\Exceptions\BadRequest;
|
||||
class Download extends \Espo\Core\EntryPoints\Base
|
||||
{
|
||||
public static $authRequired = true;
|
||||
|
||||
|
||||
protected $fileTypesToShowInline = array(
|
||||
'application/pdf',
|
||||
'application/vnd.ms-word',
|
||||
'application/vnd.ms-excel',
|
||||
'application/vnd.oasis.opendocument.text',
|
||||
'application/vnd.oasis.opendocument.spreadsheet',
|
||||
'text/plain',
|
||||
);
|
||||
|
||||
|
||||
public function run()
|
||||
{
|
||||
{
|
||||
$id = $_GET['id'];
|
||||
if (empty($id)) {
|
||||
throw new BadRequest();
|
||||
}
|
||||
|
||||
|
||||
$attachment = $this->getEntityManager()->getEntity('Attachment', $id);
|
||||
|
||||
|
||||
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 (!$this->getAcl()->check($parent)) {
|
||||
throw new Forbidden();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$fileName = "data/upload/{$attachment->id}";
|
||||
|
||||
|
||||
if (!file_exists($fileName)) {
|
||||
throw new NotFound();
|
||||
}
|
||||
|
||||
|
||||
$type = $attachment->get('type');
|
||||
|
||||
|
||||
$disposition = 'attachment';
|
||||
if (in_array($type, $this->fileTypesToShowInline)) {
|
||||
$disposition = 'inline';
|
||||
}
|
||||
|
||||
|
||||
|
||||
header('Content-Description: File Transfer');
|
||||
if ($type) {
|
||||
header('Content-Type: ' . $type);
|
||||
@@ -80,7 +84,7 @@ class Download extends \Espo\Core\EntryPoints\Base
|
||||
ob_clean();
|
||||
flush();
|
||||
readfile($fileName);
|
||||
exit;
|
||||
}
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -78,17 +78,7 @@ class Notifications extends \Espo\Core\Hooks\Base
|
||||
}
|
||||
}
|
||||
if (!empty($userIdList)) {
|
||||
$job = $this->getEntityManager()->getEntity('Job');
|
||||
$job->set(array(
|
||||
'serviceName' => 'Notification',
|
||||
'method' => 'notifyAboutNoteFromJob',
|
||||
'data' => array(
|
||||
'userIdList' => $userIdList,
|
||||
'noteId' => $entity->id
|
||||
),
|
||||
'executeTime' => date('Y-m-d H:i:s'),
|
||||
));
|
||||
$this->getEntityManager()->saveEntity($job);
|
||||
$this->getNotificationService()->notifyAboutNote($userIdList, $entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ class Cleanup extends \Espo\Core\Jobs\Base
|
||||
{
|
||||
$this->cleanupJobs();
|
||||
$this->cleanupScheduledJobLog();
|
||||
$this->cleanupAttachments();
|
||||
}
|
||||
|
||||
protected function cleanupJobs()
|
||||
@@ -68,5 +69,22 @@ class Cleanup extends \Espo\Core\Jobs\Base
|
||||
$datetime->modify($this->period);
|
||||
return $datetime->format($format);
|
||||
}
|
||||
|
||||
protected function cleanupAttachments()
|
||||
{
|
||||
$dateBefore = date('Y-m-d H:i:s', time() - 3600 * 24);
|
||||
|
||||
$collection = $this->getEntityManager()->getRepository('Attachment')->where(array(
|
||||
'role' => array(
|
||||
'Import File',
|
||||
'Export File'
|
||||
),
|
||||
'createdAt<' => $dateBefore
|
||||
))->limit(0, 100)->find();
|
||||
|
||||
foreach ($collection as $e) {
|
||||
$this->getEntityManager()->removeEntity($e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<?php
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
@@ -27,21 +27,21 @@ use \Espo\ORM\Entity;
|
||||
class Invitations
|
||||
{
|
||||
protected $entityManager;
|
||||
|
||||
|
||||
protected $smtpParams;
|
||||
|
||||
protected $mailSender;
|
||||
|
||||
|
||||
protected $config;
|
||||
|
||||
|
||||
protected $dateTime;
|
||||
|
||||
|
||||
protected $language;
|
||||
|
||||
|
||||
protected $fileManager;
|
||||
|
||||
|
||||
protected $ics;
|
||||
|
||||
|
||||
public function __construct($entityManager, $smtpParams, $mailSender, $config, $dateTime, $language, $fileManager)
|
||||
{
|
||||
$this->entityManager = $entityManager;
|
||||
@@ -52,17 +52,17 @@ class Invitations
|
||||
$this->language = $language;
|
||||
$this->fileManager = $fileManager;
|
||||
}
|
||||
|
||||
|
||||
protected function getEntityManager()
|
||||
{
|
||||
return $this->entityManager;
|
||||
}
|
||||
|
||||
|
||||
protected function parseInvitationTemplate($contents, $entity, $invitee = null, $uid = null)
|
||||
{
|
||||
|
||||
|
||||
$contents = str_replace('{eventType}', strtolower($this->language->translate($entity->getEntityName(), 'scopeNames')), $contents);
|
||||
|
||||
|
||||
foreach ($entity->getFields() as $field => $d) {
|
||||
if (empty($d['type'])) continue;
|
||||
$key = '{'.$field.'}';
|
||||
@@ -97,6 +97,7 @@ class Invitations
|
||||
if ($uid) {
|
||||
$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);
|
||||
$contents = str_replace('{tentativeLink}', $siteUrl . '?entryPoint=eventConfirmation&action=tentativeLink&uid=' . $uid->get('name'), $contents);
|
||||
}
|
||||
return $contents;
|
||||
}
|
||||
@@ -118,7 +119,7 @@ class Invitations
|
||||
|
||||
return file_get_contents($fileName);
|
||||
}
|
||||
|
||||
|
||||
public function sendInvitation(Entity $entity, Entity $invitee, $link)
|
||||
{
|
||||
$uid = $this->getEntityManager()->getEntity('UniqueId');
|
||||
@@ -144,14 +145,14 @@ class Invitations
|
||||
|
||||
$subject = $this->parseInvitationTemplate($subjectTpl, $entity, $invitee, $uid);
|
||||
$subject = str_replace(array("\n", "\r"), '', $subject);
|
||||
|
||||
|
||||
$body = $this->parseInvitationTemplate($bodyTpl, $entity, $invitee, $uid);
|
||||
|
||||
$email->set('subject', $subject);
|
||||
$email->set('body', $body);
|
||||
$email->set('isHtml', true);
|
||||
$this->getEntityManager()->saveEntity($email);
|
||||
|
||||
|
||||
$attachmentName = ucwords($this->language->translate($entity->getEntityName(), 'scopeNames')).'.ics';
|
||||
$attachment = $this->getEntityManager()->getEntity('Attachment');
|
||||
$attachment->set(array(
|
||||
@@ -159,7 +160,7 @@ class Invitations
|
||||
'type' => 'text/calendar',
|
||||
'contents' => $this->getIscContents($entity),
|
||||
));
|
||||
|
||||
|
||||
$email->addAttachment($attachment);
|
||||
|
||||
$emailSender = $this->mailSender;
|
||||
@@ -168,21 +169,21 @@ class Invitations
|
||||
$emailSender->useSmtp($this->smtpParams);
|
||||
}
|
||||
$emailSender->send($email);
|
||||
|
||||
|
||||
$this->getEntityManager()->removeEntity($email);
|
||||
}
|
||||
|
||||
|
||||
protected function getIscContents(Entity $entity)
|
||||
{
|
||||
$user = $entity->get('assignedUser');
|
||||
|
||||
|
||||
$who = '';
|
||||
$email = '';
|
||||
if ($user) {
|
||||
$who = $user->get('name');
|
||||
$email = $user->get('emailAddress');
|
||||
}
|
||||
|
||||
|
||||
$ics = new Ics('//EspoCRM//EspoCRM Calendar//EN', array(
|
||||
'startDate' => strtotime($entity->get('dateStart')),
|
||||
'endDate' => strtotime($entity->get('dateEnd')),
|
||||
@@ -192,9 +193,9 @@ class Invitations
|
||||
'email' => $email,
|
||||
'description' => $entity->get('description'),
|
||||
));
|
||||
|
||||
|
||||
return $ics->get();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ use \Espo\Core\Exceptions\Error;
|
||||
class EventConfirmation extends \Espo\Core\EntryPoints\Base
|
||||
{
|
||||
public static $authRequired = false;
|
||||
|
||||
|
||||
public function run()
|
||||
{
|
||||
$uid = $_GET['uid'];
|
||||
@@ -40,40 +40,42 @@ class EventConfirmation extends \Espo\Core\EntryPoints\Base
|
||||
if (empty($uid) || empty($action)) {
|
||||
throw new BadRequest();
|
||||
}
|
||||
|
||||
if (!in_array($action, array('accept', 'decline'))) {
|
||||
|
||||
if (!in_array($action, array('accept', 'decline', 'tentative'))) {
|
||||
throw new BadRequest();
|
||||
}
|
||||
|
||||
|
||||
$uniqueId = $this->getEntityManager()->getRepository('UniqueId')->where(array('name' => $uid))->findOne();
|
||||
|
||||
|
||||
if (!$uniqueId) {
|
||||
throw new NotFound();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
$data = $uniqueId->get('data');
|
||||
|
||||
|
||||
$eventType = $data->eventType;
|
||||
$eventId = $data->eventId;
|
||||
$inviteeType = $data->inviteeType;
|
||||
$inviteeId = $data->inviteeId;
|
||||
$link = $data->link;
|
||||
|
||||
|
||||
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) {
|
||||
$relDefs = $event->getRelations();
|
||||
$tableName = Util::toUnderscore($relDefs[$link]['relationName']);
|
||||
|
||||
|
||||
$status = 'None';
|
||||
if ($action == 'accept') {
|
||||
$status = 'Accepted';
|
||||
} else if ($action == 'decline') {
|
||||
$status = 'Declined';
|
||||
} else if ($action == 'tentative') {
|
||||
$status = 'Tentative';
|
||||
}
|
||||
|
||||
|
||||
$pdo = $this->getEntityManager()->getPDO();
|
||||
$sql = "
|
||||
UPDATE `{$tableName}` SET status = '{$status}'
|
||||
@@ -82,14 +84,14 @@ class EventConfirmation extends \Espo\Core\EntryPoints\Base
|
||||
|
||||
$sth = $pdo->prepare($sql);
|
||||
$sth->execute();
|
||||
|
||||
|
||||
$this->getEntityManager()->getRepository('UniqueId')->remove($uniqueId);
|
||||
|
||||
|
||||
echo $status;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
throw new Error();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,10 @@
|
||||
"contacts": "Contacts",
|
||||
"opportunities": "Opportunities",
|
||||
"cases": "Cases",
|
||||
"documents": "Documents"
|
||||
"documents": "Documents",
|
||||
"meetingsPrimary": "Meetings (Internal)",
|
||||
"callsPrimary": "Calls (Internal)",
|
||||
"tasksPrimary": "Tasks (Internal)"
|
||||
},
|
||||
"options": {
|
||||
"type": {
|
||||
@@ -26,13 +29,32 @@
|
||||
"Reseller": "Reseller"
|
||||
},
|
||||
"industry": {
|
||||
"Apparel": "Apparel",
|
||||
"Agriculture": "Agriculture",
|
||||
"Advertising": "Advertising",
|
||||
"Apparel & Accessories": "Apparel & Accessories",
|
||||
"Automotive": "Automotive",
|
||||
"Banking": "Banking",
|
||||
"Computer Software": "Computer Software",
|
||||
"Biotechnology": "Biotechnology",
|
||||
"Chemical": "Chemical",
|
||||
"Computer": "Computer",
|
||||
"Education": "Education",
|
||||
"Electronics": "Electronics",
|
||||
"Entertainment & Leisure": "Entertainment & Leisure",
|
||||
"Finance": "Finance",
|
||||
"Insurance": "Insurance"
|
||||
"Food & Beverage": "Food & Beverage",
|
||||
"Grocery": "Grocery",
|
||||
"Insurance": "Insurance",
|
||||
"Legal": "Legal",
|
||||
"Publishing": "Publishing",
|
||||
"Real Estate": "Real Estate",
|
||||
"Service": "Service",
|
||||
"Sports": "Sports",
|
||||
"Sofware": "Sofware",
|
||||
"Technology": "Technology",
|
||||
"Telecommunications": "Telecommunications",
|
||||
"Television": "Television",
|
||||
"Transportation": "Transportation",
|
||||
"Venture Capital": "Venture Capital"
|
||||
}
|
||||
},
|
||||
"labels": {
|
||||
|
||||
@@ -29,7 +29,8 @@
|
||||
"acceptanceStatus": {
|
||||
"None": "None",
|
||||
"Accepted": "Accepted",
|
||||
"Declined": "Declined"
|
||||
"Declined": "Declined",
|
||||
"Tentative": "Tentative"
|
||||
}
|
||||
},
|
||||
"labels": {
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
"trashFolder": "Trash Folder",
|
||||
"ssl": "SSL",
|
||||
"createCase": "Create Case",
|
||||
"reply": "Reply",
|
||||
"reply": "Auto-Reply",
|
||||
"caseDistribution": "Case Distribution",
|
||||
"replyEmailTemplate": "Reply Email Template",
|
||||
"replyFromAddress": "Reply From Address",
|
||||
@@ -23,10 +23,11 @@
|
||||
"tooltips": {
|
||||
"reply": "Notify email senders that their emails has been received.",
|
||||
"createCase": "Automatically create case from incoming emails.",
|
||||
"replyToAddress": "Specify email address of this mailbox to make response come here.",
|
||||
"replyToAddress": "Specify email address of this mailbox to make responses come here.",
|
||||
"caseDistribution": "How cases will be assigned to. Assigned directly to the user or among the team.",
|
||||
"assignToUser": "User emails/cases will be assigned to.",
|
||||
"team": "Team emails/cases will be related to."
|
||||
"team": "Team emails/cases will be related to.",
|
||||
"targetUserPosition": "Define the position of users which will be destributed with cases."
|
||||
},
|
||||
"links": {
|
||||
},
|
||||
|
||||
@@ -24,7 +24,8 @@
|
||||
"acceptanceStatus": {
|
||||
"None": "None",
|
||||
"Accepted": "Accepted",
|
||||
"Declined": "Declined"
|
||||
"Declined": "Declined",
|
||||
"Tentative": "Tentative"
|
||||
}
|
||||
},
|
||||
"labels": {
|
||||
|
||||
@@ -1 +1,7 @@
|
||||
["billingAddress","sicCode","industry","type"]
|
||||
[
|
||||
"assignedUser",
|
||||
"billingAddress",
|
||||
"emailAddress",
|
||||
"industry",
|
||||
"type"
|
||||
]
|
||||
@@ -1 +1,10 @@
|
||||
["status","dateStart","parent"]
|
||||
[
|
||||
"assignedUser",
|
||||
"dateStart",
|
||||
"direction",
|
||||
"status",
|
||||
"parent",
|
||||
"users",
|
||||
"contacts",
|
||||
"leads"
|
||||
]
|
||||
@@ -1 +1 @@
|
||||
["status"]
|
||||
["status", "assignedUser", "users"]
|
||||
@@ -1 +1,10 @@
|
||||
["number","status","priority","account","contact","description"]
|
||||
[
|
||||
"account",
|
||||
"assignedUser",
|
||||
"contact",
|
||||
"number",
|
||||
"status",
|
||||
"priority",
|
||||
"description",
|
||||
"teams"
|
||||
]
|
||||
@@ -1 +1,7 @@
|
||||
["account","emailAddress","address", "title"]
|
||||
[
|
||||
"account",
|
||||
"assignedUser",
|
||||
"emailAddress",
|
||||
"address",
|
||||
"title"
|
||||
]
|
||||
|
||||
@@ -7,8 +7,8 @@
|
||||
{"name":"status"}
|
||||
],
|
||||
[
|
||||
{"name":"team"}
|
||||
|
||||
{"name":"team"},
|
||||
{"name":"replyToAddress"}
|
||||
],
|
||||
[
|
||||
{"name":"assignToUser"}
|
||||
@@ -41,10 +41,6 @@
|
||||
],
|
||||
[
|
||||
{"name":"targetUserPosition"},
|
||||
{"name":"replyToAddress"}
|
||||
],
|
||||
[
|
||||
false,
|
||||
{"name":"replyFromAddress"}
|
||||
],
|
||||
[
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
[
|
||||
"assignedUser",
|
||||
"address",
|
||||
"status",
|
||||
"source",
|
||||
"opportunityAmountConverted",
|
||||
"teams",
|
||||
"address"
|
||||
"teams"
|
||||
]
|
||||
|
||||
@@ -1 +1,9 @@
|
||||
["status","dateStart","parent"]
|
||||
[
|
||||
"assignedUser",
|
||||
"dateStart",
|
||||
"status",
|
||||
"parent",
|
||||
"users",
|
||||
"contacts",
|
||||
"leads"
|
||||
]
|
||||
@@ -1 +1 @@
|
||||
["status"]
|
||||
["status", "assignedUser", "users"]
|
||||
@@ -1,10 +1,11 @@
|
||||
[
|
||||
"teams",
|
||||
"assignedUser",
|
||||
"stage",
|
||||
"probability",
|
||||
"account",
|
||||
"amountConverted",
|
||||
"assignedUser",
|
||||
"closeDate",
|
||||
"leadSource"
|
||||
"contacts",
|
||||
"leadSource",
|
||||
"probability",
|
||||
"stage",
|
||||
"teams"
|
||||
]
|
||||
|
||||
@@ -1 +1,4 @@
|
||||
["address"]
|
||||
[
|
||||
"address",
|
||||
"emailAddress"
|
||||
]
|
||||
@@ -1 +1,7 @@
|
||||
["status","priority","dateStart","dateEnd"]
|
||||
[
|
||||
"assignedUser",
|
||||
"status",
|
||||
"priority",
|
||||
"dateStart",
|
||||
"dateEnd"
|
||||
]
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
"true" : [
|
||||
{
|
||||
"action": "show",
|
||||
"fields": ["replyEmailTemplate", "replyFromAddress", "replyFromName", "replyToAddress"]
|
||||
"fields": ["replyEmailTemplate", "replyFromAddress", "replyFromName"]
|
||||
}, {
|
||||
"action": "setRequired",
|
||||
"fields": ["replyEmailTemplate"]
|
||||
@@ -58,7 +58,7 @@
|
||||
"default": [
|
||||
{
|
||||
"action": "hide",
|
||||
"fields": ["replyEmailTemplate", "replyFromAddress", "replyFromName", "replyToAddress"]
|
||||
"fields": ["replyEmailTemplate", "replyFromAddress", "replyFromName"]
|
||||
}, {
|
||||
"action": "setNotRequired",
|
||||
"fields": ["replyEmailTemplate"]
|
||||
|
||||
@@ -21,7 +21,35 @@
|
||||
},
|
||||
"industry": {
|
||||
"type": "enum",
|
||||
"options": ["", "Apparel", "Banking", "Computer Software", "Education", "Electronics", "Finance", "Insurance"]
|
||||
"options": [
|
||||
"",
|
||||
"Agriculture",
|
||||
"Advertising",
|
||||
"Apparel & Accessories",
|
||||
"Automotive",
|
||||
"Banking",
|
||||
"Biotechnology",
|
||||
"Chemical",
|
||||
"Computer",
|
||||
"Education",
|
||||
"Electronics",
|
||||
"Entertainment & Leisure",
|
||||
"Finance",
|
||||
"Food & Beverage",
|
||||
"Grocery",
|
||||
"Insurance",
|
||||
"Legal",
|
||||
"Publishing",
|
||||
"Real Estate",
|
||||
"Service",
|
||||
"Sports",
|
||||
"Sofware",
|
||||
"Technology",
|
||||
"Telecommunications",
|
||||
"Television",
|
||||
"Transportation",
|
||||
"Venture Capital"
|
||||
]
|
||||
},
|
||||
"sicCode": {
|
||||
"type": "varchar",
|
||||
@@ -116,7 +144,8 @@
|
||||
"teams": {
|
||||
"type": "hasMany",
|
||||
"entity": "Team",
|
||||
"relationName": "EntityTeam"
|
||||
"relationName": "EntityTeam",
|
||||
"layoutRelationshipsDisabled": true
|
||||
},
|
||||
"contacts": {
|
||||
"type": "hasMany",
|
||||
@@ -138,25 +167,47 @@
|
||||
"entity": "Document",
|
||||
"foreign": "accounts"
|
||||
},
|
||||
"meetingsPrimary": {
|
||||
"type": "hasMany",
|
||||
"entity": "Meeting",
|
||||
"foreign": "account",
|
||||
"layoutRelationshipsDisabled": true
|
||||
},
|
||||
"emailsPrimary": {
|
||||
"type": "hasMany",
|
||||
"entity": "Email",
|
||||
"foreign": "account",
|
||||
"layoutRelationshipsDisabled": true
|
||||
},
|
||||
"callsPrimary": {
|
||||
"type": "hasMany",
|
||||
"entity": "Call",
|
||||
"foreign": "account",
|
||||
"layoutRelationshipsDisabled": true
|
||||
},
|
||||
"meetings": {
|
||||
"type": "hasChildren",
|
||||
"entity": "Meeting",
|
||||
"foreign": "parent"
|
||||
"foreign": "parent",
|
||||
"layoutRelationshipsDisabled": true
|
||||
},
|
||||
"calls": {
|
||||
"type": "hasChildren",
|
||||
"entity": "Call",
|
||||
"foreign": "parent"
|
||||
"foreign": "parent",
|
||||
"layoutRelationshipsDisabled": true
|
||||
},
|
||||
"tasks": {
|
||||
"type": "hasChildren",
|
||||
"entity": "Task",
|
||||
"foreign": "parent"
|
||||
"foreign": "parent",
|
||||
"layoutRelationshipsDisabled": true
|
||||
},
|
||||
"emails": {
|
||||
"type": "hasChildren",
|
||||
"entity": "Email",
|
||||
"foreign": "parent"
|
||||
"foreign": "parent",
|
||||
"layoutRelationshipsDisabled": true
|
||||
}
|
||||
},
|
||||
"collection": {
|
||||
|
||||
@@ -54,11 +54,12 @@
|
||||
"type": "enum",
|
||||
"notStorable": true,
|
||||
"disabled": true,
|
||||
"options": ["None", "Accepted", "Declined"]
|
||||
"options": ["None", "Accepted", "Tentative", "Declined"]
|
||||
},
|
||||
"users": {
|
||||
"type": "linkMultiple",
|
||||
"disabled": true,
|
||||
"layoutDetailDisabled": true,
|
||||
"layoutListDisabled": true,
|
||||
"view": "Crm:Meeting.Fields.Attendees",
|
||||
"columns": {
|
||||
"status": "acceptanceStatus"
|
||||
@@ -66,7 +67,8 @@
|
||||
},
|
||||
"contacts": {
|
||||
"type": "linkMultiple",
|
||||
"disabled": true,
|
||||
"layoutDetailDisabled": true,
|
||||
"layoutListDisabled": true,
|
||||
"view": "Crm:Meeting.Fields.Contacts",
|
||||
"columns": {
|
||||
"status": "acceptanceStatus"
|
||||
@@ -74,7 +76,8 @@
|
||||
},
|
||||
"leads": {
|
||||
"type": "linkMultiple",
|
||||
"disabled": true,
|
||||
"layoutDetailDisabled": true,
|
||||
"layoutListDisabled": true,
|
||||
"view": "Crm:Meeting.Fields.Attendees",
|
||||
"columns": {
|
||||
"status": "acceptanceStatus"
|
||||
@@ -124,7 +127,8 @@
|
||||
"teams": {
|
||||
"type": "hasMany",
|
||||
"entity": "Team",
|
||||
"relationName": "EntityTeam"
|
||||
"relationName": "EntityTeam",
|
||||
"layoutRelationshipsDisabled": true
|
||||
},
|
||||
"users": {
|
||||
"type": "hasMany",
|
||||
@@ -136,7 +140,8 @@
|
||||
"len": "36",
|
||||
"default": "None"
|
||||
}
|
||||
}
|
||||
},
|
||||
"layoutRelationshipsDisabled": true
|
||||
},
|
||||
"contacts": {
|
||||
"type": "hasMany",
|
||||
@@ -148,7 +153,8 @@
|
||||
"len": "36",
|
||||
"default": "None"
|
||||
}
|
||||
}
|
||||
},
|
||||
"layoutRelationshipsDisabled": true
|
||||
},
|
||||
"leads": {
|
||||
"type": "hasMany",
|
||||
@@ -160,7 +166,8 @@
|
||||
"len": "36",
|
||||
"default": "None"
|
||||
}
|
||||
}
|
||||
},
|
||||
"layoutRelationshipsDisabled": true
|
||||
},
|
||||
"parent": {
|
||||
"type": "belongsToParent",
|
||||
|
||||
@@ -40,6 +40,10 @@
|
||||
"type": "link",
|
||||
"view": "Crm:Case.Fields.Contact"
|
||||
},
|
||||
"inboundEmail": {
|
||||
"type": "link",
|
||||
"readOnly": true
|
||||
},
|
||||
"createdAt": {
|
||||
"type": "datetime",
|
||||
"readOnly": true
|
||||
@@ -80,7 +84,12 @@
|
||||
"teams": {
|
||||
"type": "hasMany",
|
||||
"entity": "Team",
|
||||
"relationName": "EntityTeam"
|
||||
"relationName": "EntityTeam",
|
||||
"layoutRelationshipsDisabled": true
|
||||
},
|
||||
"inboundEmail": {
|
||||
"type": "belongsTo",
|
||||
"entity": "InboundEmail"
|
||||
},
|
||||
"account": {
|
||||
"type": "belongsTo",
|
||||
@@ -95,22 +104,26 @@
|
||||
"meetings": {
|
||||
"type": "hasChildren",
|
||||
"entity": "Meeting",
|
||||
"foreign": "parent"
|
||||
"foreign": "parent",
|
||||
"layoutRelationshipsDisabled": true
|
||||
},
|
||||
"calls": {
|
||||
"type": "hasChildren",
|
||||
"entity": "Call",
|
||||
"foreign": "parent"
|
||||
"foreign": "parent",
|
||||
"layoutRelationshipsDisabled": true
|
||||
},
|
||||
"tasks": {
|
||||
"type": "hasChildren",
|
||||
"entity": "Task",
|
||||
"foreign": "parent"
|
||||
"foreign": "parent",
|
||||
"layoutRelationshipsDisabled": true
|
||||
},
|
||||
"emails": {
|
||||
"type": "hasChildren",
|
||||
"entity": "Email",
|
||||
"foreign": "parent"
|
||||
"foreign": "parent",
|
||||
"layoutRelationshipsDisabled": true
|
||||
}
|
||||
},
|
||||
"collection": {
|
||||
|
||||
@@ -140,7 +140,8 @@
|
||||
"teams": {
|
||||
"type": "hasMany",
|
||||
"entity": "Team",
|
||||
"relationName": "EntityTeam"
|
||||
"relationName": "EntityTeam",
|
||||
"layoutRelationshipsDisabled": true
|
||||
},
|
||||
"account": {
|
||||
"type": "belongsTo",
|
||||
@@ -155,7 +156,8 @@
|
||||
"type": "varchar",
|
||||
"len": 50
|
||||
}
|
||||
}
|
||||
},
|
||||
"layoutRelationshipsDisabled": true
|
||||
},
|
||||
"opportunities": {
|
||||
"type": "hasMany",
|
||||
@@ -170,21 +172,26 @@
|
||||
"meetings": {
|
||||
"type": "hasMany",
|
||||
"entity": "Meeting",
|
||||
"foreign": "contacts"
|
||||
"foreign": "contacts",
|
||||
"layoutRelationshipsDisabled": true
|
||||
},
|
||||
"calls": {
|
||||
"type": "hasMany",
|
||||
"entity": "Call",
|
||||
"foreign": "contacts"
|
||||
"foreign": "contacts",
|
||||
"layoutRelationshipsDisabled": true
|
||||
},
|
||||
"tasks": {
|
||||
"type": "hasChildren",
|
||||
"entity": "Task",
|
||||
"foreign": "parent"
|
||||
"foreign": "parent",
|
||||
"layoutRelationshipsDisabled": true
|
||||
},
|
||||
"activities": {
|
||||
"type": "joint",
|
||||
"links": ["meetings", "calls", "tasks"]
|
||||
"emails": {
|
||||
"type": "hasChildren",
|
||||
"entity": "Task",
|
||||
"foreign": "parent",
|
||||
"layoutRelationshipsDisabled": true
|
||||
}
|
||||
},
|
||||
"collection": {
|
||||
|
||||
@@ -89,7 +89,8 @@
|
||||
"teams": {
|
||||
"type": "hasMany",
|
||||
"entity": "Team",
|
||||
"relationName": "EntityTeam"
|
||||
"relationName": "EntityTeam",
|
||||
"layoutRelationshipsDisabled": true
|
||||
}
|
||||
},
|
||||
"collection": {
|
||||
|
||||
@@ -57,7 +57,8 @@
|
||||
},
|
||||
"targetUserPosition": {
|
||||
"type": "enum",
|
||||
"view": "Crm:InboundEmail.Fields.TargetUserPosition"
|
||||
"view": "Crm:InboundEmail.Fields.TargetUserPosition",
|
||||
"tooltip": true
|
||||
},
|
||||
"reply": {
|
||||
"type": "bool",
|
||||
@@ -71,7 +72,8 @@
|
||||
},
|
||||
"replyToAddress": {
|
||||
"type": "varchar",
|
||||
"tooltip": true
|
||||
"tooltip": true,
|
||||
"required": true
|
||||
},
|
||||
"replyFromName": {
|
||||
"type": "varchar"
|
||||
|
||||
@@ -145,7 +145,8 @@
|
||||
"teams": {
|
||||
"type": "hasMany",
|
||||
"entity": "Team",
|
||||
"relationName": "EntityTeam"
|
||||
"relationName": "EntityTeam",
|
||||
"layoutRelationshipsDisabled": true
|
||||
},
|
||||
"opportunities": {
|
||||
"type": "hasMany",
|
||||
@@ -155,17 +156,20 @@
|
||||
"meetings": {
|
||||
"type": "hasMany",
|
||||
"entity": "Meeting",
|
||||
"foreign": "leads"
|
||||
"foreign": "leads",
|
||||
"layoutRelationshipsDisabled": true
|
||||
},
|
||||
"calls": {
|
||||
"type": "hasMany",
|
||||
"entity": "Call",
|
||||
"foreign": "leads"
|
||||
"foreign": "leads",
|
||||
"layoutRelationshipsDisabled": true
|
||||
},
|
||||
"tasks": {
|
||||
"type": "hasChildren",
|
||||
"entity": "Task",
|
||||
"foreign": "parent"
|
||||
"foreign": "parent",
|
||||
"layoutRelationshipsDisabled": true
|
||||
},
|
||||
"createdAccount": {
|
||||
"type": "belongsTo",
|
||||
@@ -198,7 +202,8 @@
|
||||
},
|
||||
"collection": {
|
||||
"sortBy": "createdAt",
|
||||
"asc": false
|
||||
"asc": false,
|
||||
"textFilterFields": ["name", "accountName"]
|
||||
},
|
||||
"indexes": {
|
||||
"firstName": {
|
||||
|
||||
@@ -49,19 +49,21 @@
|
||||
"type": "enum",
|
||||
"notStorable": true,
|
||||
"disabled": true,
|
||||
"options": ["None", "Accepted", "Declined"]
|
||||
"options": ["None", "Accepted", "Tentative", "Declined"]
|
||||
},
|
||||
"users": {
|
||||
"type": "linkMultiple",
|
||||
"view": "Crm:Meeting.Fields.Attendees",
|
||||
"disabled": true,
|
||||
"layoutDetailDisabled": true,
|
||||
"layoutListDisabled": true,
|
||||
"columns": {
|
||||
"status": "acceptanceStatus"
|
||||
}
|
||||
},
|
||||
"contacts": {
|
||||
"type": "linkMultiple",
|
||||
"disabled": true,
|
||||
"layoutDetailDisabled": true,
|
||||
"layoutListDisabled": true,
|
||||
"view": "Crm:Meeting.Fields.Contacts",
|
||||
"columns": {
|
||||
"status": "acceptanceStatus"
|
||||
@@ -70,7 +72,8 @@
|
||||
"leads": {
|
||||
"type": "linkMultiple",
|
||||
"view": "Crm:Meeting.Fields.Attendees",
|
||||
"disabled": true,
|
||||
"layoutDetailDisabled": true,
|
||||
"layoutListDisabled": true,
|
||||
"columns": {
|
||||
"status": "acceptanceStatus"
|
||||
}
|
||||
@@ -119,7 +122,8 @@
|
||||
"teams": {
|
||||
"type": "hasMany",
|
||||
"entity": "Team",
|
||||
"relationName": "EntityTeam"
|
||||
"relationName": "EntityTeam",
|
||||
"layoutRelationshipsDisabled": true
|
||||
},
|
||||
"users": {
|
||||
"type": "hasMany",
|
||||
@@ -129,9 +133,10 @@
|
||||
"status": {
|
||||
"type": "varchar",
|
||||
"len": "36",
|
||||
"default": "None"
|
||||
"default": "None"
|
||||
}
|
||||
}
|
||||
},
|
||||
"layoutRelationshipsDisabled": true
|
||||
},
|
||||
"contacts": {
|
||||
"type": "hasMany",
|
||||
@@ -143,7 +148,8 @@
|
||||
"len": "36",
|
||||
"default": "None"
|
||||
}
|
||||
}
|
||||
},
|
||||
"layoutRelationshipsDisabled": true
|
||||
},
|
||||
"leads": {
|
||||
"type": "hasMany",
|
||||
@@ -153,9 +159,10 @@
|
||||
"status": {
|
||||
"type": "varchar",
|
||||
"len": "36",
|
||||
"default": "None"
|
||||
"default": "None"
|
||||
}
|
||||
}
|
||||
},
|
||||
"layoutRelationshipsDisabled": true
|
||||
},
|
||||
"parent": {
|
||||
"type": "belongsToParent",
|
||||
|
||||
@@ -104,7 +104,8 @@
|
||||
"teams": {
|
||||
"type": "hasMany",
|
||||
"entity": "Team",
|
||||
"relationName": "EntityTeam"
|
||||
"relationName": "EntityTeam",
|
||||
"layoutRelationshipsDisabled": true
|
||||
},
|
||||
"account": {
|
||||
"type": "belongsTo",
|
||||
@@ -125,22 +126,26 @@
|
||||
"meetings": {
|
||||
"type": "hasChildren",
|
||||
"entity": "Meeting",
|
||||
"foreign": "parent"
|
||||
"foreign": "parent",
|
||||
"layoutRelationshipsDisabled": true
|
||||
},
|
||||
"calls": {
|
||||
"type": "hasChildren",
|
||||
"entity": "Call",
|
||||
"foreign": "parent"
|
||||
"foreign": "parent",
|
||||
"layoutRelationshipsDisabled": true
|
||||
},
|
||||
"tasks": {
|
||||
"type": "hasChildren",
|
||||
"entity": "Task",
|
||||
"foreign": "parent"
|
||||
"foreign": "parent",
|
||||
"layoutRelationshipsDisabled": true
|
||||
},
|
||||
"emails": {
|
||||
"type": "hasChildren",
|
||||
"entity": "Email",
|
||||
"foreign": "parent"
|
||||
"foreign": "parent",
|
||||
"layoutRelationshipsDisabled": true
|
||||
},
|
||||
"documents": {
|
||||
"type": "hasMany",
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
},
|
||||
"seconds": {
|
||||
"type": "enumInt",
|
||||
"options": [0, 60, 120, 300, 900, 1800, 3600, 7200, 10800, 18000, 86400],
|
||||
"options": [0, 60, 120, 300, 600, 900, 1800, 3600, 7200, 10800, 18000, 86400],
|
||||
"default": 0
|
||||
},
|
||||
"entityType": {
|
||||
|
||||
@@ -103,7 +103,8 @@
|
||||
"teams": {
|
||||
"type": "hasMany",
|
||||
"entity": "Team",
|
||||
"relationName": "EntityTeam"
|
||||
"relationName": "EntityTeam",
|
||||
"layoutRelationshipsDisabled": true
|
||||
}
|
||||
},
|
||||
"collection": {
|
||||
|
||||
@@ -78,7 +78,8 @@
|
||||
"teams": {
|
||||
"type": "hasMany",
|
||||
"entity": "Team",
|
||||
"relationName": "EntityTeam"
|
||||
"relationName": "EntityTeam",
|
||||
"layoutRelationshipsDisabled": true
|
||||
},
|
||||
"parent": {
|
||||
"type": "belongsToParent",
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
{
|
||||
"entity": true,
|
||||
"layouts": false,
|
||||
"layouts": true,
|
||||
"tab": true,
|
||||
"acl": true,
|
||||
"module": "Crm",
|
||||
"customizable": false,
|
||||
"customizable": true,
|
||||
"importable": false
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<p>Subject: {name}</p>
|
||||
<p>Start at: {dateStart}</p>
|
||||
<p>
|
||||
<a href="{acceptLink}">Accept</a>, <a href="{declineLink}">Decline</a>
|
||||
<a href="{acceptLink}">Accept</a>, <a href="{declineLink}">Decline</a>, <a href="{tentativeLink}">Tentative</a>
|
||||
</p>
|
||||
{#userOnly}
|
||||
<p><a href="{url}">Open entry</a></p>
|
||||
|
||||
@@ -18,19 +18,19 @@
|
||||
*
|
||||
* 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\SelectManagers;
|
||||
|
||||
class Call extends \Espo\Core\SelectManagers\Base
|
||||
{
|
||||
|
||||
protected function getBoolFilterWhereOnlyMy()
|
||||
{
|
||||
return array(
|
||||
'type' => 'linkedWith',
|
||||
'field' => 'users',
|
||||
'value' => array($this->user->id)
|
||||
protected function boolFilterOnlyMy(&$result)
|
||||
{
|
||||
if (!in_array('users', $result['joins'])) {
|
||||
$result['joins'][] = 'users';
|
||||
}
|
||||
$result['whereClause'][] = array(
|
||||
'user.id' => $this->getUser()->id
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,19 +18,19 @@
|
||||
*
|
||||
* 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\SelectManagers;
|
||||
|
||||
class Meeting extends \Espo\Core\SelectManagers\Base
|
||||
{
|
||||
|
||||
protected function getBoolFilterWhereOnlyMy()
|
||||
{
|
||||
return array(
|
||||
'type' => 'linkedWith',
|
||||
'field' => 'users',
|
||||
'value' => array($this->user->id)
|
||||
protected function boolFilterOnlyMy(&$result)
|
||||
{
|
||||
if (!in_array('users', $result['joins'])) {
|
||||
$result['joins'][] = 'users';
|
||||
}
|
||||
$result['whereClause'][] = array(
|
||||
'user.id' => $this->getUser()->id
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,22 +24,17 @@ namespace Espo\Modules\Crm\SelectManagers;
|
||||
|
||||
class Task extends \Espo\Core\SelectManagers\Base
|
||||
{
|
||||
|
||||
protected function getBoolFilterWhereActive()
|
||||
{
|
||||
return array(
|
||||
'type' => 'notIn',
|
||||
'field' => 'status',
|
||||
'value' => array('Completed', 'Canceled')
|
||||
protected function boolFilterActive(&$result)
|
||||
{
|
||||
$result['whereClause'][] = array(
|
||||
'status!=' => array('Completed', 'Canceled')
|
||||
);
|
||||
}
|
||||
|
||||
protected function getBoolFilterWhereInactive()
|
||||
{
|
||||
return array(
|
||||
'type' => 'in',
|
||||
'field' => 'status',
|
||||
'value' => array('Completed', 'Canceled')
|
||||
|
||||
protected function boolFilterInative(&$result)
|
||||
{
|
||||
$result['whereClause'][] = array(
|
||||
'status' => array('Completed', 'Canceled')
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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\Modules\Crm\Services;
|
||||
|
||||
@@ -33,7 +33,21 @@ class Account extends \Espo\Services\Record
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
protected $mergeLinkList = array(
|
||||
'opportunities',
|
||||
'cases',
|
||||
'documents',
|
||||
'contacts',
|
||||
'tasks',
|
||||
'meetings',
|
||||
'calls',
|
||||
'emails',
|
||||
'meetingsPrimary',
|
||||
'callsPrimary',
|
||||
'emailsPrimary'
|
||||
);
|
||||
|
||||
protected function getDuplicateWhereClause(Entity $entity)
|
||||
{
|
||||
return array(
|
||||
|
||||
37
application/Espo/Modules/Crm/Services/CaseObj.php
Normal file
37
application/Espo/Modules/Crm/Services/CaseObj.php
Normal file
@@ -0,0 +1,37 @@
|
||||
<?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/.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Modules\Crm\Services;
|
||||
|
||||
use \Espo\ORM\Entity;
|
||||
|
||||
class CaseObj extends \Espo\Services\Record
|
||||
{
|
||||
protected $mergeLinkList = array(
|
||||
'tasks',
|
||||
'meetings',
|
||||
'calls',
|
||||
'emails'
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
@@ -207,6 +207,8 @@ class InboundEmail extends \Espo\Services\Record
|
||||
$email = $importer->importMessage($message, $userId, array($teamId));
|
||||
|
||||
if ($email) {
|
||||
$this->noteAboutEmail($email);
|
||||
|
||||
if ($inboundEmail->get('createCase')) {
|
||||
$this->createCase($inboundEmail, $email);
|
||||
} else {
|
||||
@@ -244,8 +246,23 @@ class InboundEmail extends \Espo\Services\Record
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function noteAboutEmail($email)
|
||||
{
|
||||
if ($email->get('parentType') && $email->get('parentId')) {
|
||||
$parent = $this->getEntityManager()->getEntity($email->get('parentType'), $email->get('parentId'));
|
||||
if ($parent) {
|
||||
$this->getServiceFactory()->create('Stream')->noteEmailReceived($parent, $email);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function createCase($inboundEmail, $email)
|
||||
{
|
||||
if ($email->get('parentType') == 'Case' && $email->get('parentId')) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (preg_match('/\[#([0-9]+)[^0-9]*\]/', $email->get('name'), $m)) {
|
||||
$caseNumber = $m[1];
|
||||
$case = $this->getEntityManager()->getRepository('Case')->where(array(
|
||||
@@ -262,7 +279,8 @@ class InboundEmail extends \Espo\Services\Record
|
||||
'caseDistribution' => $inboundEmail->get('caseDistribution'),
|
||||
'teamId' => $inboundEmail->get('teamId'),
|
||||
'userId' => $inboundEmail->get('assignToUserId'),
|
||||
'targetUserPosition' => $inboundEmail->get('targetUserPosition')
|
||||
'targetUserPosition' => $inboundEmail->get('targetUserPosition'),
|
||||
'inboundEmailId' => $inboundEmail->id
|
||||
);
|
||||
$case = $this->emailToCase($email, $params);
|
||||
$user = $this->getEntityManager()->getEntity('User', $case->get('assignedUserId'));
|
||||
@@ -303,6 +321,10 @@ class InboundEmail extends \Espo\Services\Record
|
||||
}
|
||||
$case->set('assignedUserId', $userId);
|
||||
|
||||
if (!empty($params['inboundEmailId'])) {
|
||||
$case->set('inboundEmailId', $params['inboundEmailId']);
|
||||
}
|
||||
|
||||
$teamId = false;
|
||||
if (!empty($params['teamId'])) {
|
||||
$teamId = $params['teamId'];
|
||||
|
||||
@@ -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\Modules\Crm\Services;
|
||||
|
||||
@@ -29,6 +29,13 @@ use \Espo\ORM\Entity;
|
||||
|
||||
class Lead extends \Espo\Services\Record
|
||||
{
|
||||
protected $mergeLinkList = array(
|
||||
'tasks',
|
||||
'meetings',
|
||||
'calls',
|
||||
'emails'
|
||||
);
|
||||
|
||||
protected function getDuplicateWhereClause(Entity $entity)
|
||||
{
|
||||
$data = array(
|
||||
@@ -47,11 +54,11 @@ class Lead extends \Espo\Services\Record
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
|
||||
public function convert($id, $recordsData)
|
||||
{
|
||||
$lead = $this->getEntity($id);
|
||||
|
||||
|
||||
if (!$this->getAcl()->check($lead, 'edit')) {
|
||||
throw new Forbidden();
|
||||
}
|
||||
@@ -89,13 +96,13 @@ class Lead extends \Espo\Services\Record
|
||||
|
||||
$lead->set('status', 'Converted');
|
||||
$entityManager->saveEntity($lead);
|
||||
|
||||
|
||||
if ($meetings = $lead->get('meetings')) {
|
||||
foreach ($meetings as $meeting) {
|
||||
if (!empty($contact)) {
|
||||
$entityManager->getRepository('Meeting')->relate($meeting, 'contacts', $contact);
|
||||
}
|
||||
|
||||
|
||||
if (!empty($opportunity)) {
|
||||
$meeting->set('parentId', $opportunity->id);
|
||||
$meeting->set('parentType', 'Opportunity');
|
||||
|
||||
@@ -29,6 +29,13 @@ use \Espo\Core\Exceptions\Forbidden;
|
||||
|
||||
class Opportunity extends \Espo\Services\Record
|
||||
{
|
||||
protected $mergeLinkList = array(
|
||||
'tasks',
|
||||
'meetings',
|
||||
'calls',
|
||||
'emails'
|
||||
);
|
||||
|
||||
public function reportSalesPipeline($dateFrom, $dateTo)
|
||||
{
|
||||
$pdo = $this->getEntityManager()->getPDO();
|
||||
|
||||
@@ -173,10 +173,12 @@ class Base
|
||||
}
|
||||
|
||||
if (empty($params['aggregation'])) {
|
||||
return $this->composeSelectQuery($this->toDb($entity->getEntityName()), $selectPart, $joinsPart, $wherePart, $orderPart, $params['offset'], $params['limit'], $params['distinct'], null, $groupByPart);
|
||||
$sql = $this->composeSelectQuery($this->toDb($entity->getEntityName()), $selectPart, $joinsPart, $wherePart, $orderPart, $params['offset'], $params['limit'], $params['distinct'], null, $groupByPart);
|
||||
} else {
|
||||
return $this->composeSelectQuery($this->toDb($entity->getEntityName()), $selectPart, $joinsPart, $wherePart, null, null, null, false, $params['aggregation']);
|
||||
$sql = $this->composeSelectQuery($this->toDb($entity->getEntityName()), $selectPart, $joinsPart, $wherePart, null, null, null, false, $params['aggregation']);
|
||||
}
|
||||
|
||||
return $sql;
|
||||
}
|
||||
|
||||
protected function getFunctionPart($function, $part, $entityName, $distinct = false)
|
||||
|
||||
@@ -291,6 +291,11 @@ abstract class Entity implements IEntity
|
||||
return $this->has($fieldName) && ($this->get($fieldName) != $this->getFetched($fieldName));
|
||||
}
|
||||
|
||||
public function setFetched($fieldName, $value)
|
||||
{
|
||||
$this->fetchedValuesContainer[$fieldName] = $value;
|
||||
}
|
||||
|
||||
public function getFetched($fieldName)
|
||||
{
|
||||
if (isset($this->fetchedValuesContainer[$fieldName])) {
|
||||
|
||||
@@ -30,29 +30,29 @@ class Attachment extends \Espo\Core\ORM\Repositories\RDB
|
||||
{
|
||||
$this->dependencies[] = 'fileManager';
|
||||
}
|
||||
|
||||
|
||||
protected function getFileManager()
|
||||
{
|
||||
return $this->getInjection('fileManager');
|
||||
}
|
||||
|
||||
|
||||
public function save(Entity $entity)
|
||||
{
|
||||
$isNew = $entity->isNew();
|
||||
$result = parent::save($entity);
|
||||
|
||||
|
||||
if ($isNew) {
|
||||
if (!empty($entity->id) && $entity->has('contents')) {
|
||||
$contents = $entity->get('contents');
|
||||
$this->getFileManager()->putContents('data/upload/' . $entity->id, $contents);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
|
||||
protected function afterRemove(Entity $entity)
|
||||
{
|
||||
{
|
||||
parent::afterRemove($entity);
|
||||
$this->getFileManager()->removeFile('data/upload/' . $entity->id);
|
||||
}
|
||||
|
||||
@@ -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\Repositories;
|
||||
|
||||
@@ -29,39 +29,69 @@ class Email extends \Espo\Core\ORM\Repositories\RDB
|
||||
protected function prepareAddressess(Entity $entity, $type)
|
||||
{
|
||||
$eaRepositoty = $this->getEntityManager()->getRepository('EmailAddress');
|
||||
|
||||
|
||||
$address = $entity->get($type);
|
||||
$ids = array();
|
||||
if (!empty($address) || !filter_var($address, FILTER_VALIDATE_EMAIL)) {
|
||||
$arr = array_map(function ($e) {
|
||||
return trim($e);
|
||||
}, explode(';', $address));
|
||||
|
||||
|
||||
$ids = $eaRepositoty->getIds($arr);
|
||||
foreach ($ids as $id) {
|
||||
$this->setUsersIdsByEmailAddressId($entity, $id);
|
||||
}
|
||||
}
|
||||
$entity->set($type . 'EmailAddressesIds', $ids);
|
||||
}
|
||||
|
||||
|
||||
protected function setUsersIdsByEmailAddressId(Entity $entity, $emailAddressId)
|
||||
{
|
||||
$user = $this->getEntityManager()->getRepository('EmailAddress')->getEntityByAddressId($emailAddressId, 'User');
|
||||
if ($user) {
|
||||
$usersIds = $entity->get('usersIds');
|
||||
if (empty($usersIds)) {
|
||||
$usersIds = array();
|
||||
}
|
||||
if (!in_array($user->id, $usersIds)) {
|
||||
$usersIds[] = $user->id;
|
||||
}
|
||||
$entity->set('usersIds', $usersIds);
|
||||
}
|
||||
}
|
||||
|
||||
protected function beforeSave(Entity $entity)
|
||||
{
|
||||
$eaRepositoty = $this->getEntityManager()->getRepository('EmailAddress');
|
||||
|
||||
|
||||
$entity->set('usersIds', array());
|
||||
|
||||
$from = trim($entity->get('from'));
|
||||
if (!empty($from)) {
|
||||
$ids = $eaRepositoty->getIds(array($from));
|
||||
if (!empty($ids)) {
|
||||
$entity->set('fromEmailAddressId', $ids[0]);
|
||||
$this->setUsersIdsByEmailAddressId($entity, $ids[0]);
|
||||
}
|
||||
} else {
|
||||
$entity->set('fromEmailAddressId', null);
|
||||
}
|
||||
|
||||
|
||||
$this->prepareAddressess($entity, 'to');
|
||||
$this->prepareAddressess($entity, 'cc');
|
||||
$this->prepareAddressess($entity, 'bcc');
|
||||
|
||||
|
||||
$usersIds = $entity->get('usersIds');
|
||||
$assignedUserId = $entity->get('assignedUserId');
|
||||
|
||||
if (!empty($assignedUserId) && !in_array($assignedUserId, $usersIds)) {
|
||||
$usersIds[] = $assignedUserId;
|
||||
}
|
||||
$entity->set('usersIds', $usersIds);
|
||||
|
||||
parent::beforeSave($entity);
|
||||
|
||||
|
||||
|
||||
$parentId = $entity->get('parentId');
|
||||
$parentType = $entity->get('parentType');
|
||||
if (!empty($parentId) || !empty($parentType)) {
|
||||
@@ -80,7 +110,7 @@ class Email extends \Espo\Core\ORM\Repositories\RDB
|
||||
// TODO find account by from address
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected function beforeRemove(Entity $entity)
|
||||
{
|
||||
parent::beforeRemove($entity);
|
||||
@@ -89,5 +119,6 @@ class Email extends \Espo\Core\ORM\Repositories\RDB
|
||||
$this->getEntityManager()->removeEntity($attachment);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -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\Repositories;
|
||||
|
||||
@@ -27,8 +27,8 @@ use Espo\ORM\Entity;
|
||||
class EmailAddress extends \Espo\Core\ORM\Repositories\RDB
|
||||
{
|
||||
public function getIds($arr = array())
|
||||
{
|
||||
$ids = array();
|
||||
{
|
||||
$ids = array();
|
||||
if (!empty($arr)) {
|
||||
$a = array_map(function ($item) {
|
||||
return strtolower($item);
|
||||
@@ -58,20 +58,20 @@ class EmailAddress extends \Espo\Core\ORM\Repositories\RDB
|
||||
}
|
||||
return $ids;
|
||||
}
|
||||
|
||||
|
||||
public function getEmailAddressData(Entity $entity)
|
||||
{
|
||||
$data = array();
|
||||
|
||||
$pdo = $this->getEntityManager()->getPDO();
|
||||
|
||||
$pdo = $this->getEntityManager()->getPDO();
|
||||
$sql = "
|
||||
SELECT email_address.name, email_address.invalid, email_address.opt_out AS optOut, entity_email_address.primary
|
||||
SELECT email_address.name, email_address.invalid, email_address.opt_out AS optOut, entity_email_address.primary
|
||||
FROM entity_email_address
|
||||
JOIN email_address ON email_address.id = entity_email_address.email_address_id AND email_address.deleted = 0
|
||||
WHERE
|
||||
entity_email_address.entity_id = ".$pdo->quote($entity->id)." AND
|
||||
entity_email_address.entity_type = ".$pdo->quote($entity->getEntityName())." AND
|
||||
entity_email_address.deleted = 0
|
||||
WHERE
|
||||
entity_email_address.entity_id = ".$pdo->quote($entity->id)." AND
|
||||
entity_email_address.entity_type = ".$pdo->quote($entity->getEntityName())." AND
|
||||
entity_email_address.deleted = 0
|
||||
ORDER BY entity_email_address.primary DESC
|
||||
";
|
||||
$sth = $pdo->prepare($sql);
|
||||
@@ -82,27 +82,56 @@ class EmailAddress extends \Espo\Core\ORM\Repositories\RDB
|
||||
$obj->emailAddress = $row['name'];
|
||||
$obj->primary = ($row['primary'] == '1') ? true : false;
|
||||
$obj->optOut = ($row['optOut'] == '1') ? true : false;
|
||||
$obj->invalid = ($row['invalid'] == '1') ? true : false;
|
||||
$obj->invalid = ($row['invalid'] == '1') ? true : false;
|
||||
$data[] = $obj;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
|
||||
public function getByAddress($address)
|
||||
{
|
||||
return $this->where(array('lower' => strtolower($address)))->findOne();
|
||||
}
|
||||
|
||||
public function getEntityByAddress($address)
|
||||
|
||||
public function getEntityByAddressId($emailAddressId, $entityType = null)
|
||||
{
|
||||
$pdo = $this->getEntityManager()->getPDO();
|
||||
$pdo = $this->getEntityManager()->getPDO();
|
||||
$sql = "
|
||||
SELECT entity_email_address.entity_type AS 'entityType', entity_email_address.entity_id AS 'entityId'
|
||||
FROM entity_email_address
|
||||
WHERE
|
||||
entity_email_address.email_address_id = ".$pdo->quote($emailAddressId)." AND
|
||||
entity_email_address.deleted = 0
|
||||
ORDER BY entity_email_address.primary DESC, FIELD(entity_email_address.entity_type, 'User', 'Contact', 'Lead', 'Account')
|
||||
";
|
||||
|
||||
$sth = $pdo->prepare($sql);
|
||||
$sth->execute();
|
||||
while ($row = $sth->fetch()) {
|
||||
if (!empty($row['entityType']) && !empty($row['entityId'])) {
|
||||
if (!empty($entityType)) {
|
||||
if ($entityType != $row['entityType']) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
$entity = $this->getEntityManager()->getEntity($row['entityType'], $row['entityId']);
|
||||
if ($entity) {
|
||||
return $entity;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function getEntityByAddress($address, $entityType = null)
|
||||
{
|
||||
$pdo = $this->getEntityManager()->getPDO();
|
||||
$sql = "
|
||||
SELECT entity_email_address.entity_type AS 'entityType', entity_email_address.entity_id AS 'entityId'
|
||||
FROM entity_email_address
|
||||
JOIN email_address ON email_address.id = entity_email_address.email_address_id AND email_address.deleted = 0
|
||||
WHERE
|
||||
WHERE
|
||||
email_address.lower = ".$pdo->quote(strtolower($address))." AND
|
||||
entity_email_address.deleted = 0
|
||||
ORDER BY entity_email_address.primary DESC, FIELD(entity_email_address.entity_type, 'User', 'Contact', 'Lead', 'Account')
|
||||
@@ -112,6 +141,11 @@ class EmailAddress extends \Espo\Core\ORM\Repositories\RDB
|
||||
$sth->execute();
|
||||
while ($row = $sth->fetch()) {
|
||||
if (!empty($row['entityType']) && !empty($row['entityId'])) {
|
||||
if (!empty($entityType)) {
|
||||
if ($entityType != $row['entityType']) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
$entity = $this->getEntityManager()->getEntity($row['entityType'], $row['entityId']);
|
||||
if ($entity) {
|
||||
return $entity;
|
||||
@@ -119,24 +153,24 @@ class EmailAddress extends \Espo\Core\ORM\Repositories\RDB
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function storeEntityEmailAddress(Entity $entity)
|
||||
{
|
||||
$email = trim($entity->get('emailAddress'));
|
||||
$emailAddressData = null;
|
||||
|
||||
|
||||
if ($entity->has('emailAddressData')) {
|
||||
$emailAddressData = $entity->get('emailAddressData');
|
||||
}
|
||||
|
||||
$pdo = $this->getEntityManager()->getPDO();
|
||||
|
||||
|
||||
$pdo = $this->getEntityManager()->getPDO();
|
||||
|
||||
if ($emailAddressData !== null && is_array($emailAddressData)) {
|
||||
$previousEmailAddressData = array();
|
||||
if (!$entity->isNew()) {
|
||||
$previousEmailAddressData = $this->getEmailAddressData($entity);
|
||||
}
|
||||
|
||||
|
||||
$hash = array();
|
||||
foreach ($emailAddressData as $row) {
|
||||
$key = $row->emailAddress;
|
||||
@@ -144,11 +178,11 @@ class EmailAddress extends \Espo\Core\ORM\Repositories\RDB
|
||||
$hash[$key] = array(
|
||||
'primary' => $row->primary ? true : false,
|
||||
'optOut' => $row->optOut ? true : false,
|
||||
'invalid' => $row->invalid ? true : false,
|
||||
'invalid' => $row->invalid ? true : false,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$hashPrev = array();
|
||||
foreach ($previousEmailAddressData as $row) {
|
||||
$key = $row->emailAddress;
|
||||
@@ -156,25 +190,25 @@ class EmailAddress extends \Espo\Core\ORM\Repositories\RDB
|
||||
$hashPrev[$key] = array(
|
||||
'primary' => $row->primary ? true : false,
|
||||
'optOut' => $row->optOut ? true : false,
|
||||
'invalid' => $row->invalid ? true : false,
|
||||
'invalid' => $row->invalid ? true : false,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$primary = false;
|
||||
}
|
||||
|
||||
$primary = false;
|
||||
$toCreate = array();
|
||||
$toUpdate = array();
|
||||
$toUpdate = array();
|
||||
$toRemove = array();
|
||||
|
||||
|
||||
|
||||
foreach ($hash as $key => $data) {
|
||||
$new = true;
|
||||
$changed = false;
|
||||
|
||||
|
||||
if ($hash[$key]['primary']) {
|
||||
$primary = $key;
|
||||
}
|
||||
|
||||
|
||||
if (array_key_exists($key, $hashPrev)) {
|
||||
$new = false;
|
||||
$changed = $hash[$key]['optOut'] != $hashPrev[$key]['optOut'] || $hash[$key]['invalid'] != $hashPrev[$key]['invalid'];
|
||||
@@ -182,23 +216,23 @@ class EmailAddress extends \Espo\Core\ORM\Repositories\RDB
|
||||
if ($hash[$key]['primary'] == $hashPrev[$key]['primary']) {
|
||||
$primary = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if ($new) {
|
||||
$toCreate[] = $key;
|
||||
}
|
||||
}
|
||||
if ($changed) {
|
||||
$toUpdate[] = $key;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($hashPrev as $key => $data) {
|
||||
|
||||
foreach ($hashPrev as $key => $data) {
|
||||
if (!array_key_exists($key, $hash)) {
|
||||
$toRemove[] = $key;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
foreach ($toRemove as $address) {
|
||||
$emailAddress = $this->getByAddress($address);
|
||||
if ($emailAddress) {
|
||||
@@ -211,10 +245,10 @@ class EmailAddress extends \Espo\Core\ORM\Repositories\RDB
|
||||
email_address_id = ".$pdo->quote($emailAddress->id)."
|
||||
";
|
||||
$sth = $pdo->prepare($query);
|
||||
$sth->execute();
|
||||
$sth->execute();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
foreach ($toUpdate as $address) {
|
||||
$emailAddress = $this->getByAddress($address);
|
||||
if ($emailAddress) {
|
||||
@@ -225,17 +259,17 @@ class EmailAddress extends \Espo\Core\ORM\Repositories\RDB
|
||||
$this->save($emailAddress);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
foreach ($toCreate as $address) {
|
||||
$emailAddress = $this->getByAddress($address);
|
||||
if (!$emailAddress) {
|
||||
$emailAddress = $this->get();
|
||||
|
||||
|
||||
$emailAddress->set(array(
|
||||
'name' => $address,
|
||||
'optOut' => $hash[$address]['optOut'],
|
||||
'invalid' => $hash[$address]['invalid'],
|
||||
));
|
||||
));
|
||||
$this->save($emailAddress);
|
||||
} else {
|
||||
if ($emailAddress->get('optOut') != $hash[$address]['optOut'] || $emailAddress->get('invalid') != $hash[$address]['invalid']) {
|
||||
@@ -246,9 +280,9 @@ class EmailAddress extends \Espo\Core\ORM\Repositories\RDB
|
||||
$this->save($emailAddress);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$query = "
|
||||
INSERT entity_email_address
|
||||
INSERT entity_email_address
|
||||
(entity_id, entity_type, email_address_id, `primary`)
|
||||
VALUES
|
||||
(
|
||||
@@ -261,7 +295,7 @@ class EmailAddress extends \Espo\Core\ORM\Repositories\RDB
|
||||
$sth = $pdo->prepare($query);
|
||||
$sth->execute();
|
||||
}
|
||||
|
||||
|
||||
if ($primary) {
|
||||
$emailAddress = $this->getByAddress($primary);
|
||||
if ($emailAddress) {
|
||||
@@ -271,27 +305,26 @@ class EmailAddress extends \Espo\Core\ORM\Repositories\RDB
|
||||
WHERE
|
||||
entity_id = ".$pdo->quote($entity->id)." AND
|
||||
entity_type = ".$pdo->quote($entity->getEntityName())." AND
|
||||
`primary` = 1 AND
|
||||
`primary` = 1 AND
|
||||
deleted = 0
|
||||
";
|
||||
$sth = $pdo->prepare($query);
|
||||
$sth->execute();
|
||||
|
||||
|
||||
$query = "
|
||||
UPDATE entity_email_address
|
||||
SET `primary` = 1
|
||||
WHERE
|
||||
entity_id = ".$pdo->quote($entity->id)." AND
|
||||
entity_type = ".$pdo->quote($entity->getEntityName())." AND
|
||||
email_address_id = ".$pdo->quote($emailAddress->id)." AND
|
||||
email_address_id = ".$pdo->quote($emailAddress->id)." AND
|
||||
deleted = 0
|
||||
";
|
||||
$sth = $pdo->prepare($query);
|
||||
$sth->execute();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
$entityRepository = $this->getEntityManager()->getRepository($entity->getEntityName());
|
||||
if (!empty($email)) {
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
"Customization": "Customization",
|
||||
"Available Fields": "Available Fields",
|
||||
"Layout": "Layout",
|
||||
"Entity Manager": "Entity Manager",
|
||||
"Add Panel": "Add Panel",
|
||||
"Add Field": "Add Field",
|
||||
"Settings": "Settings",
|
||||
@@ -23,7 +24,6 @@
|
||||
"Email Templates": "Email Templates",
|
||||
"Import": "Import",
|
||||
"Layout Manager": "Layout Manager",
|
||||
"Field Manager": "Field Manager",
|
||||
"User Interface": "User Interface",
|
||||
"Auth Tokens": "Auth Tokens",
|
||||
"Authentication": "Authentication",
|
||||
@@ -40,7 +40,11 @@
|
||||
"Install": "Install",
|
||||
"Ready for installation": "Ready for installation",
|
||||
"Uninstalling...": "Uninstalling...",
|
||||
"Uninstalled": "Uninstalled"
|
||||
"Uninstalled": "Uninstalled",
|
||||
"Create Entity": "Create Entity",
|
||||
"Edit Entity": "Edit Entity",
|
||||
"Create Link": "Create Link",
|
||||
"Edit Link": "Edit Link"
|
||||
},
|
||||
"layouts": {
|
||||
"list": "List",
|
||||
@@ -127,7 +131,7 @@
|
||||
"emailTemplates": "Templates for outbound emails.",
|
||||
"import": "Import data from CSV file.",
|
||||
"layoutManager": "Customize layouts (list, detail, edit, search, mass update).",
|
||||
"fieldManager": "Create new fields or customize existing ones.",
|
||||
"entityManager": "Create custom entities, edit existing ones. Manage field and relationships.",
|
||||
"userInterface": "Configure UI.",
|
||||
"authTokens": "Active auth sessions. IP address and last access date.",
|
||||
"authentication": "Authentication settings.",
|
||||
|
||||
37
application/Espo/Resources/i18n/en_US/EntityManager.json
Normal file
37
application/Espo/Resources/i18n/en_US/EntityManager.json
Normal file
@@ -0,0 +1,37 @@
|
||||
{
|
||||
"labels": {
|
||||
"Fields": "Fields",
|
||||
"Relationships": "Relationships"
|
||||
},
|
||||
"fields": {
|
||||
"name": "Name",
|
||||
"type": "Type",
|
||||
"labelSingular": "Label Singular",
|
||||
"labelPlural": "Label Plural",
|
||||
"stream": "Stream",
|
||||
"label": "Label",
|
||||
"linkType": "Link Type",
|
||||
"entityForeign": "Foreign Entity",
|
||||
"linkForeign": "Foreign Link",
|
||||
"link": "Link",
|
||||
"labelForeign": "Foreign Label"
|
||||
},
|
||||
"options": {
|
||||
"type": {
|
||||
"": "None",
|
||||
"Base": "Base",
|
||||
"Person": "Person"
|
||||
},
|
||||
"linkType": {
|
||||
"manyToMany": "Many-to-Many",
|
||||
"oneToMany": "One-to-Many",
|
||||
"manyToOne": "Many-to-One",
|
||||
"parentToChildren": "Parent-to-Children",
|
||||
"childrenToParent": "Children-to-Parent"
|
||||
}
|
||||
},
|
||||
"messages": {
|
||||
"entityCreated": "Entity has been created",
|
||||
"linkAlreadyExists": "Conflict: link already exists."
|
||||
}
|
||||
}
|
||||
@@ -43,6 +43,8 @@
|
||||
"Loading...": "Loading...",
|
||||
"Uploading...": "Uploading...",
|
||||
"Sending...": "Sending...",
|
||||
"Merging...": "Merging...",
|
||||
"Merged": "Merged",
|
||||
"Removed": "Removed",
|
||||
"Posted": "Posted",
|
||||
"Linked": "Linked",
|
||||
@@ -57,8 +59,8 @@
|
||||
"Unlinking...": "Unlinking...",
|
||||
"Posting...": "Posting...",
|
||||
"Username can not be empty!": "Username can not be empty!",
|
||||
"Cache is not enabled": "Cache is not enabled",
|
||||
"Cache has been cleared": "Cache has been cleared",
|
||||
"Cache is not enabled": "Cache is not enabled",
|
||||
"Cache has been cleared": "Cache has been cleared",
|
||||
"Rebuild has been done": "Rebuild has been done",
|
||||
"Saving...": "Saving...",
|
||||
"Modified": "Modified",
|
||||
@@ -167,7 +169,9 @@
|
||||
"noRecordsUpdated": "No records were updated",
|
||||
"massRemoveResult": "{count} records have been removed",
|
||||
"massRemoveResultSingle": "{count} record has been removed",
|
||||
"noRecordsRemoved": "No records were removed"
|
||||
"noRecordsRemoved": "No records were removed",
|
||||
"clickToRefresh": "Click to refresh",
|
||||
"streamPostInfo": "Type <strong>@username</strong> to mention users in the post.\n\nAvailable markdown syntax:\n`<code>code</code>`\n**<strong>strong text</strong>**\n*<em>emphasized text</em>*\n~<del>deleted text</del>~\n> blockquote\n(url)[link]"
|
||||
},
|
||||
"boolFilters": {
|
||||
"onlyMy": "Only My",
|
||||
@@ -186,7 +190,10 @@
|
||||
"createdAt": "Created At",
|
||||
"modifiedAt": "Modified At",
|
||||
"createdBy": "Created By",
|
||||
"modifiedBy": "Modified By"
|
||||
"modifiedBy": "Modified By",
|
||||
"description": "Description",
|
||||
"address": "Address",
|
||||
"phoneNumber": "Phone"
|
||||
},
|
||||
"links": {
|
||||
"assignedUser": "Assigned User",
|
||||
@@ -198,10 +205,10 @@
|
||||
"users": "Users"
|
||||
},
|
||||
"dashlets": {
|
||||
"Stream": "Stream"
|
||||
"Stream": "Stream"
|
||||
},
|
||||
"streamMessages": {
|
||||
"create": "{user} created {entityType} {entity}",
|
||||
"create": "{user} created {entityType} {entity}",
|
||||
"createAssigned": "{user} created {entityType} {entity} assigned to {assignee}",
|
||||
"assign": "{user} assigned {entityType} {entity} to {assignee}",
|
||||
"post": "{user} posted on {entityType} {entity}",
|
||||
@@ -209,11 +216,10 @@
|
||||
"status": "{user} updated {field} on {entityType} {entity}",
|
||||
"update": "{user} updated {entityType} {entity}",
|
||||
"createRelated": "{user} created {relatedEntityType} {relatedEntity} linked to {entityType} {entity}",
|
||||
"emailReceived": "Email {email} has been received for {entityType} {entity}",
|
||||
"emailReceivedInitial": "Email {email} has been received and created {entityType} {entity}",
|
||||
|
||||
"mentionInPost": "{user} mentioned {mentioned} on {entityType} {entity}",
|
||||
|
||||
"createThis": "{user} created this {entityType}",
|
||||
|
||||
"createThis": "{user} created this {entityType}",
|
||||
"createAssignedThis": "{user} created this {entityType} assigned to {assignee}",
|
||||
"assignThis": "{user} assigned this {entityType} to {assignee}",
|
||||
"postThis": "{user} posted",
|
||||
@@ -221,8 +227,22 @@
|
||||
"statusThis": "{user} updated {field}",
|
||||
"updateThis": "{user} updated this {entityType}",
|
||||
"createRelatedThis": "{user} created {relatedEntityType} {relatedEntity} linked to this {entityType}",
|
||||
|
||||
"emailReceivedFromThis": "Email {email} has been received from {from}",
|
||||
"emailReceivedInitialFromThis": "Email {email} has been received from {from} and created this {entityType}",
|
||||
|
||||
"emailReceivedThis": "Email {email} has been received",
|
||||
"emailReceivedInitialThis": "Email {email} has been received and created this {entityType}"
|
||||
"emailReceivedInitialThis": "Email {email} has been received and created this {entityType}",
|
||||
|
||||
"emailReceivedFrom": "Email {email} related to {entityType} {entity} has been received from {from}",
|
||||
"emailReceivedFromInitial": "Email {email} has been received from {from} and created {entityType} {entity}",
|
||||
|
||||
"emailReceived": "Email {email} related to {entityType} {entity} has been received",
|
||||
"emailReceivedInitial": "Email {email} has been received and created {entityType} {entity}",
|
||||
"emailReceivedInitialFrom": "Email {email} has been received from {from} and created {entityType} {entity}",
|
||||
|
||||
"emailSent": "{by} has sent email {email} related to {entityType} {entity}",
|
||||
"emailSentThis": "{by} has sent email {email}"
|
||||
},
|
||||
"lists": {
|
||||
"monthNames": ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
|
||||
|
||||
@@ -10,12 +10,12 @@
|
||||
"defaultCurrency": "Default Currency",
|
||||
"baseCurrency": "Base Currency",
|
||||
"currencyRates": "Rate Values",
|
||||
|
||||
|
||||
"currencyList": "Currency List",
|
||||
"language": "Language",
|
||||
|
||||
|
||||
"companyLogo": "Company Logo",
|
||||
|
||||
|
||||
"smtpServer": "Server",
|
||||
"smtpPort": "Port",
|
||||
"smtpAuth": "Auth",
|
||||
@@ -26,14 +26,16 @@
|
||||
"outboundEmailFromName": "From Name",
|
||||
"outboundEmailFromAddress": "From Address",
|
||||
"outboundEmailIsShared": "Is Shared",
|
||||
|
||||
|
||||
"recordsPerPage": "Records Per Page",
|
||||
"recordsPerPageSmall": "Records Per Page (Small)",
|
||||
"recordsPerPageSmall": "Records Per Page (Small)",
|
||||
"tabList": "Tab List",
|
||||
"quickCreateList": "Quick Create List",
|
||||
|
||||
|
||||
"exportDelimiter": "Export Delimiter",
|
||||
|
||||
|
||||
"globalSearchEntityList": "Global Search Entity List",
|
||||
|
||||
"authenticationMethod": "Authentication Method",
|
||||
"ldapHost": "Host",
|
||||
"ldapPort": "Port",
|
||||
|
||||
@@ -13,7 +13,8 @@
|
||||
"passwordConfirm": "Confirm Password",
|
||||
"newPassword": "New Password",
|
||||
"newPasswordConfirm": "Confirm New Password",
|
||||
"avatar": "Avatar"
|
||||
"avatar": "Avatar",
|
||||
"isActive": "Is Active"
|
||||
},
|
||||
"links": {
|
||||
"teams": "Teams",
|
||||
@@ -36,13 +37,14 @@
|
||||
"defaultTeam": "All records created by this user will be related to this team by default.",
|
||||
"userName": "Letters a-z, numbers 0-9 and underscores are allowed.",
|
||||
"isAdmin": "Admin user can access everything.",
|
||||
"isActive": "If unchecked then user won't be able to login.",
|
||||
"teams": "Teams which this user belongs to. Access control level is inherited from team's roles.",
|
||||
"roles": "Additional access roles. Use it if user doesn't belong to any team or you need to extend access control level only for this user."
|
||||
},
|
||||
"messages": {
|
||||
"passwordWillBeSent": "Password will be sent to user's email address.",
|
||||
"accountInfoEmailSubject": "Account info",
|
||||
"accountInfoEmailBody": "Your account information:\n\nUsername: {userName}\nPassword: {password}\n\n{siteUrl}",
|
||||
"accountInfoEmailSubject": "EspoCRM User Access Info",
|
||||
"accountInfoEmailBody": "Your access information:\n\nUsername: {userName}\nPassword: {password}\n\n{siteUrl}",
|
||||
"passwordChangeLinkEmailSubject": "Change Password Request",
|
||||
"passwordChangeLinkEmailBody": "You can change your password by this link {link}. This unique url will be exprired soon.",
|
||||
"passwordChanged": "Password has been changed",
|
||||
|
||||
@@ -3,6 +3,5 @@
|
||||
"dateSent",
|
||||
"status",
|
||||
"parent",
|
||||
"body",
|
||||
"teams"
|
||||
]
|
||||
|
||||
@@ -3,14 +3,14 @@
|
||||
"label": "System",
|
||||
"rows": [
|
||||
[{"name": "useCache"},{"name": "companyLogo"}],
|
||||
[{"name": "disableExport"}]
|
||||
[{"name": "disableExport"},{"name": "globalSearchEntityList"}]
|
||||
]
|
||||
},
|
||||
{
|
||||
"label": "Locale",
|
||||
"rows": [
|
||||
[{"name": "dateFormat"}, {"name": "timeZone"}],
|
||||
[{"name": "timeFormat"}, {"name": "weekStart"}],
|
||||
[{"name": "timeFormat"}, {"name": "weekStart"}],
|
||||
[{"name": "thousandSeparator"}, {"name": "decimalMark"}],
|
||||
[{"name": "language"}]
|
||||
]
|
||||
|
||||
@@ -10,8 +10,9 @@
|
||||
{
|
||||
"label": "Teams and Access Control",
|
||||
"rows": [
|
||||
[{"name":"defaultTeam"}, {"name":"isAdmin"}],
|
||||
[{"name":"teams"}, {"name":"roles"}]
|
||||
[{"name":"defaultTeam"}, {"name":"isActive"}],
|
||||
[{"name":"teams"}, {"name":"isAdmin"}],
|
||||
[{"name":"roles"}, false]
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
[
|
||||
{"name":"name","width":30,"link":true},
|
||||
{"name":"userName"},
|
||||
{"name":"emailAddress"}
|
||||
{"name":"emailAddress"},
|
||||
{"name":"isActive", "width":10}
|
||||
]
|
||||
|
||||
@@ -108,9 +108,9 @@
|
||||
"description":"layoutManager"
|
||||
},
|
||||
{
|
||||
"url":"#Admin/fieldManager",
|
||||
"label":"Field Manager",
|
||||
"description":"fieldManager"
|
||||
"url":"#Admin/entityManager",
|
||||
"label":"Entity Manager",
|
||||
"description":"entityManager"
|
||||
},
|
||||
{
|
||||
"url":"#Admin/userInterface",
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
{
|
||||
"controller": "Controllers.EmailAccount",
|
||||
"recordViews":{
|
||||
"recordViews": {
|
||||
"list":"EmailAccount.Record.List",
|
||||
"detail": "EmailAccount.Record.Detail",
|
||||
"edit": "EmailAccount.Record.Edit"
|
||||
},
|
||||
"views": {
|
||||
"list": "EmailAccount.List"
|
||||
},
|
||||
"disableSearchPanel": true
|
||||
}
|
||||
|
||||
@@ -131,6 +131,9 @@
|
||||
},
|
||||
"teams": {
|
||||
"type": "linkMultiple"
|
||||
},
|
||||
"users": {
|
||||
"type": "linkMultiple"
|
||||
}
|
||||
},
|
||||
"links": {
|
||||
@@ -151,6 +154,11 @@
|
||||
"entity": "Team",
|
||||
"relationName": "EntityTeam"
|
||||
},
|
||||
"users": {
|
||||
"type": "hasMany",
|
||||
"entity": "User",
|
||||
"foreign": "emails"
|
||||
},
|
||||
"attachments": {
|
||||
"type": "hasChildren",
|
||||
"entity": "Attachment",
|
||||
@@ -176,7 +184,7 @@
|
||||
"additionalColumns": {
|
||||
"addressType": {
|
||||
"type": "varchar",
|
||||
"len": "4"
|
||||
"len": "4"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -190,7 +198,7 @@
|
||||
"additionalColumns": {
|
||||
"addressType": {
|
||||
"type": "varchar",
|
||||
"len": "4"
|
||||
"len": "4"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -211,8 +219,7 @@
|
||||
},
|
||||
"collection": {
|
||||
"sortBy": "dateSent",
|
||||
"asc": false,
|
||||
"textFilterFields": ["name", "body", "bodyPlain"]
|
||||
"asc": false
|
||||
},
|
||||
"indexes": {
|
||||
"dateSentAssignedUser": {
|
||||
|
||||
@@ -16,6 +16,10 @@
|
||||
"type": "linkMultiple",
|
||||
"view": "Stream.Fields.AttachmentMultiple"
|
||||
},
|
||||
"number": {
|
||||
"type": "autoincrement",
|
||||
"index": true
|
||||
},
|
||||
"createdAt": {
|
||||
"type": "datetime",
|
||||
"readOnly": true
|
||||
@@ -54,7 +58,7 @@
|
||||
}
|
||||
},
|
||||
"collection": {
|
||||
"sortBy": "createdAt",
|
||||
"sortBy": "number",
|
||||
"asc": false
|
||||
},
|
||||
"streamRelated": {
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
{
|
||||
"fields": {
|
||||
"number": {
|
||||
"type": "autoincrement",
|
||||
"index": true
|
||||
},
|
||||
"data": {
|
||||
"type": "jsonObject"
|
||||
},
|
||||
@@ -28,7 +32,7 @@
|
||||
}
|
||||
},
|
||||
"collection": {
|
||||
"sortBy": "createdAt",
|
||||
"sortBy": "number",
|
||||
"asc": false
|
||||
},
|
||||
"indexes": {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user