mirror of
https://github.com/espocrm/espocrm.git
synced 2026-03-07 06:27:00 +00:00
Compare commits
132 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7e264780ec | ||
|
|
0750a89f74 | ||
|
|
5b52668359 | ||
|
|
3c91f8450d | ||
|
|
f820168283 | ||
|
|
bdb90a0016 | ||
|
|
521fa42bf1 | ||
|
|
1b272ea0c7 | ||
|
|
b055adfd87 | ||
|
|
42ea6e0d7c | ||
|
|
20b21622ed | ||
|
|
34c61a2fc5 | ||
|
|
cf8198dc98 | ||
|
|
c57fd2c55e | ||
|
|
3669bf4e11 | ||
|
|
b6bbdada06 | ||
|
|
804a618408 | ||
|
|
a1192474d9 | ||
|
|
dd28642370 | ||
|
|
b92295cbb1 | ||
|
|
d186cd2b1a | ||
|
|
9ebee47d5c | ||
|
|
3fdc7c8cf7 | ||
|
|
b298b26bab | ||
|
|
0a38857e64 | ||
|
|
2497e9bd88 | ||
|
|
bee44612e5 | ||
|
|
8946b4ad11 | ||
|
|
d4b78b9977 | ||
|
|
f9614385df | ||
|
|
7352abebe9 | ||
|
|
5c57b34893 | ||
|
|
0d873b7302 | ||
|
|
ccca6a8f55 | ||
|
|
bad9cc848f | ||
|
|
e3ab16a7bf | ||
|
|
6db068e625 | ||
|
|
64b1ac3ccf | ||
|
|
ca038970ff | ||
|
|
4d852c8515 | ||
|
|
b96ea5100b | ||
|
|
fd86727bfc | ||
|
|
8dacf66242 | ||
|
|
bbb4271dd1 | ||
|
|
8b72499c19 | ||
|
|
0a9a5a47af | ||
|
|
4304724315 | ||
|
|
8dc3c146e0 | ||
|
|
7cefff04a7 | ||
|
|
dd6d6995b9 | ||
|
|
3606dc39e3 | ||
|
|
03fef2fc8c | ||
|
|
704e548654 | ||
|
|
6120d5501d | ||
|
|
3f8c338265 | ||
|
|
ec07b23af8 | ||
|
|
cc349ca545 | ||
|
|
b16ec66eb3 | ||
|
|
22667e17f1 | ||
|
|
5741834d53 | ||
|
|
0df39cdc4f | ||
|
|
70489e8846 | ||
|
|
c4558ec633 | ||
|
|
5b500ae42a | ||
|
|
cdbe6a2383 | ||
|
|
48c650eab2 | ||
|
|
0c815b5761 | ||
|
|
9b104b9a16 | ||
|
|
97248e2b18 | ||
|
|
b3aa244f4e | ||
|
|
45c59ff242 | ||
|
|
0038c3666d | ||
|
|
7cfebbf87e | ||
|
|
0cd3666a96 | ||
|
|
d4a5f68398 | ||
|
|
1a30a9c29a | ||
|
|
cd75ddbbd7 | ||
|
|
55aedb7194 | ||
|
|
aa303d164f | ||
|
|
1723f63b21 | ||
|
|
55fef695ec | ||
|
|
42ee41ae54 | ||
|
|
676f480db8 | ||
|
|
233408b076 | ||
|
|
9dfec82f6e | ||
|
|
3c791b6363 | ||
|
|
5a0e5f6804 | ||
|
|
3376ab06d0 | ||
|
|
e8b22f9331 | ||
|
|
0b9104f992 | ||
|
|
566d0e50a6 | ||
|
|
be5a6a0ee0 | ||
|
|
4df460d6cb | ||
|
|
7443c969b1 | ||
|
|
3b5b1fb1b3 | ||
|
|
5737927e2d | ||
|
|
c59b837315 | ||
|
|
ce75b68df0 | ||
|
|
d17257fc95 | ||
|
|
ed996eff43 | ||
|
|
8f70c32687 | ||
|
|
d134db65a3 | ||
|
|
deaa3f3f73 | ||
|
|
5f5f6dcf38 | ||
|
|
8e373a9c33 | ||
|
|
f7a367d755 | ||
|
|
fea113269b | ||
|
|
5299da7a21 | ||
|
|
9a06ee3221 | ||
|
|
5b489e2341 | ||
|
|
5b0e4f910d | ||
|
|
575a4f12cc | ||
|
|
9b77f74e53 | ||
|
|
7be801c647 | ||
|
|
a95471a239 | ||
|
|
8982993813 | ||
|
|
cc48dc2a65 | ||
|
|
9e0d8632f5 | ||
|
|
2f751085e0 | ||
|
|
f5faae13c4 | ||
|
|
015fd4014c | ||
|
|
ff9c68d7f6 | ||
|
|
323138eebf | ||
|
|
43f15252bb | ||
|
|
25ad4ffa43 | ||
|
|
0cd3a464d8 | ||
|
|
71e9b313bb | ||
|
|
9b13f82c55 | ||
|
|
20cf214187 | ||
|
|
2d06568470 | ||
|
|
59ccd121c5 | ||
|
|
caa0256f19 |
@@ -31,8 +31,13 @@ class App extends \Espo\Core\Controllers\Base
|
||||
$preferences = $this->getPreferences()->toArray();
|
||||
unset($preferences['smtpPassword']);
|
||||
|
||||
$user = $this->getUser();
|
||||
if (!$user->has('teamsIds')) {
|
||||
$user->loadLinkMultipleField('teams');
|
||||
}
|
||||
|
||||
return array(
|
||||
'user' => $this->getUser()->toArray(),
|
||||
'user' => $user->toArray(),
|
||||
'acl' => $this->getAcl()->getMap(),
|
||||
'preferences' => $preferences,
|
||||
'token' => $this->getUser()->get('token')
|
||||
|
||||
@@ -34,9 +34,9 @@ class Table
|
||||
|
||||
private $cacheFile;
|
||||
|
||||
private $actionList = array('read', 'edit', 'delete');
|
||||
private $actionList = ['read', 'edit', 'delete'];
|
||||
|
||||
private $levelList = array('all', 'team', 'own', 'no');
|
||||
private $levelList = ['all', 'team', 'own', 'no'];
|
||||
|
||||
protected $fileManager;
|
||||
|
||||
@@ -112,12 +112,14 @@ class Table
|
||||
{
|
||||
$aclTables = [];
|
||||
$assignmentPermissionList = [];
|
||||
$userPermissionList = [];
|
||||
|
||||
$userRoles = $this->user->get('roles');
|
||||
|
||||
foreach ($userRoles as $role) {
|
||||
$aclTables[] = $role->get('data');
|
||||
$assignmentPermissionList[] = $role->get('assignmentPermission');
|
||||
$userPermissionList[] = $role->get('userPermission');
|
||||
}
|
||||
|
||||
$teams = $this->user->get('teams');
|
||||
@@ -126,11 +128,14 @@ class Table
|
||||
foreach ($teamRoles as $role) {
|
||||
$aclTables[] = $role->get('data');
|
||||
$assignmentPermissionList[] = $role->get('assignmentPermission');
|
||||
$userPermissionList[] = $role->get('userPermission');
|
||||
}
|
||||
}
|
||||
|
||||
$this->data['table'] = $this->merge($aclTables);
|
||||
$this->data['assignmentPermission'] = $this->mergeValues($assignmentPermissionList, 'all');
|
||||
|
||||
$this->data['assignmentPermission'] = $this->mergeValues($assignmentPermissionList, $this->metadata->get('app.acl.valueDefaults.assignmentPermission', 'all'));
|
||||
$this->data['userPermission'] = $this->mergeValues($userPermissionList, $this->metadata->get('app.acl.valueDefaults.userPermission', 'no'));
|
||||
}
|
||||
|
||||
private function initSolid()
|
||||
@@ -222,8 +227,11 @@ class Table
|
||||
foreach ($scopeList as $scope) {
|
||||
if (!array_key_exists($scope, $data)) {
|
||||
$aclType = $this->metadata->get('scopes.' . $scope . '.acl');
|
||||
if (!empty($aclType) && ($aclType === true || $aclType === 'record')) {
|
||||
$data[$scope] = $this->metadata->get('app.acl.recordDefault');
|
||||
if ($aclType === true) {
|
||||
$aclType = 'recordAllTeamOwnNo';
|
||||
}
|
||||
if (!empty($aclType)) {
|
||||
$data[$scope] = $this->metadata->get('app.acl.defaults.' . $aclType, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -115,7 +115,7 @@ class AclManager
|
||||
if ($user->isAdmin()) {
|
||||
return true;
|
||||
}
|
||||
$this->getTable($user)->get($permission);
|
||||
return $this->getTable($user)->get($permission);
|
||||
}
|
||||
|
||||
public function checkReadOnlyTeam(User $user, $scope)
|
||||
|
||||
@@ -105,22 +105,23 @@ class Application
|
||||
$container = $this->getContainer();
|
||||
|
||||
$slim->get('/', function() {});
|
||||
$slim->post('/', function() {});
|
||||
|
||||
$entryPointManager = new \Espo\Core\EntryPointManager($container);
|
||||
|
||||
$auth = $this->getAuth();
|
||||
$apiAuth = new \Espo\Core\Utils\Api\Auth($auth, $entryPointManager->checkAuthRequired($entryPoint), true);
|
||||
$slim->add($apiAuth);
|
||||
try {
|
||||
$auth = $this->getAuth();
|
||||
$apiAuth = new \Espo\Core\Utils\Api\Auth($auth, $entryPointManager->checkAuthRequired($entryPoint), true);
|
||||
$slim->add($apiAuth);
|
||||
|
||||
$slim->hook('slim.before.dispatch', function () use ($entryPoint, $entryPointManager, $container) {
|
||||
try {
|
||||
$slim->hook('slim.before.dispatch', function () use ($entryPoint, $entryPointManager, $container) {
|
||||
$entryPointManager->run($entryPoint);
|
||||
} catch (\Exception $e) {
|
||||
$container->get('output')->processError($e->getMessage(), $e->getCode(), true);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$slim->run();
|
||||
$slim->run();
|
||||
} catch (\Exception $e) {
|
||||
$container->get('output')->processError($e->getMessage(), $e->getCode(), true);
|
||||
}
|
||||
}
|
||||
|
||||
public function runCron()
|
||||
|
||||
@@ -236,6 +236,7 @@ class Container
|
||||
return new \Espo\Core\Utils\Language(
|
||||
$this->get('fileManager'),
|
||||
$this->get('config'),
|
||||
$this->get('metadata'),
|
||||
$this->get('preferences')
|
||||
);
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ class Record extends Base
|
||||
$service = $this->getServiceFactory()->create($name);
|
||||
} else {
|
||||
$service = $this->getServiceFactory()->create($this->defaultRecordServiceName);
|
||||
$service->setEntityName($name);
|
||||
$service->setEntityType($name);
|
||||
}
|
||||
|
||||
return $service;
|
||||
@@ -69,13 +69,17 @@ class Record extends Base
|
||||
return $entity->toArray();
|
||||
}
|
||||
|
||||
public function actionPatch($params, $data)
|
||||
public function actionPatch($params, $data, $request)
|
||||
{
|
||||
return $this->actionUpdate($params, $data);
|
||||
return $this->actionUpdate($params, $data, $request);
|
||||
}
|
||||
|
||||
public function actionCreate($params, $data)
|
||||
public function actionCreate($params, $data, $request)
|
||||
{
|
||||
if (!$request->isPost()) {
|
||||
throw BadRequest();
|
||||
}
|
||||
|
||||
if (!$this->getAcl()->check($this->name, 'edit')) {
|
||||
throw new Forbidden();
|
||||
}
|
||||
@@ -89,8 +93,12 @@ class Record extends Base
|
||||
throw new Error();
|
||||
}
|
||||
|
||||
public function actionUpdate($params, $data)
|
||||
public function actionUpdate($params, $data, $request)
|
||||
{
|
||||
if (!$request->isPut() && !$request->isPatch()) {
|
||||
throw BadRequest();
|
||||
}
|
||||
|
||||
if (!$this->getAcl()->check($this->name, 'edit')) {
|
||||
throw new Forbidden();
|
||||
}
|
||||
@@ -135,7 +143,7 @@ class Record extends Base
|
||||
|
||||
return array(
|
||||
'total' => $result['total'],
|
||||
'list' => $result['collection']->toArray()
|
||||
'list' => isset($result['collection']) ? $result['collection']->toArray() : $result['list']
|
||||
);
|
||||
}
|
||||
|
||||
@@ -170,12 +178,16 @@ class Record extends Base
|
||||
|
||||
return array(
|
||||
'total' => $result['total'],
|
||||
'list' => $result['collection']->toArray()
|
||||
'list' => isset($result['collection']) ? $result['collection']->toArray() : $result['list']
|
||||
);
|
||||
}
|
||||
|
||||
public function actionDelete($params)
|
||||
public function actionDelete($params, $data, $request)
|
||||
{
|
||||
if (!$request->isDelete()) {
|
||||
throw BadRequest();
|
||||
}
|
||||
|
||||
$id = $params['id'];
|
||||
|
||||
if ($this->getRecordService()->deleteEntity($id)) {
|
||||
@@ -212,6 +224,10 @@ class Record extends Base
|
||||
|
||||
public function actionMassUpdate($params, $data, $request)
|
||||
{
|
||||
if (!$request->isPost()) {
|
||||
throw new BadRequest();
|
||||
}
|
||||
|
||||
if (!$this->getAcl()->check($this->name, 'edit')) {
|
||||
throw new Forbidden();
|
||||
}
|
||||
@@ -235,6 +251,9 @@ class Record extends Base
|
||||
|
||||
public function actionMassDelete($params, $data, $request)
|
||||
{
|
||||
if (!$request->isPost()) {
|
||||
throw new BadRequest();
|
||||
}
|
||||
if (!$this->getAcl()->check($this->name, 'delete')) {
|
||||
throw new Forbidden();
|
||||
}
|
||||
@@ -254,10 +273,14 @@ class Record extends Base
|
||||
return $idsRemoved;
|
||||
}
|
||||
|
||||
public function actionCreateLink($params, $data)
|
||||
public function actionCreateLink($params, $data, $request)
|
||||
{
|
||||
if (!$request->isPost()) {
|
||||
throw new BadRequest();
|
||||
}
|
||||
|
||||
if (empty($params['id']) || empty($params['link'])) {
|
||||
throw BadRequest();
|
||||
throw new BadRequest();
|
||||
}
|
||||
|
||||
$id = $params['id'];
|
||||
@@ -294,13 +317,17 @@ class Record extends Base
|
||||
throw new Error();
|
||||
}
|
||||
|
||||
public function actionRemoveLink($params, $data)
|
||||
public function actionRemoveLink($params, $data, $request)
|
||||
{
|
||||
if (!$request->isDelete()) {
|
||||
throw new BadRequest();
|
||||
}
|
||||
|
||||
$id = $params['id'];
|
||||
$link = $params['link'];
|
||||
|
||||
if (empty($params['id']) || empty($params['link'])) {
|
||||
throw BadRequest();
|
||||
throw new BadRequest();
|
||||
}
|
||||
|
||||
$foreignIds = array();
|
||||
@@ -326,8 +353,11 @@ class Record extends Base
|
||||
throw new Error();
|
||||
}
|
||||
|
||||
public function actionFollow($params)
|
||||
public function actionFollow($params, $data, $request)
|
||||
{
|
||||
if (!$request->isPut()) {
|
||||
throw new BadRequest();
|
||||
}
|
||||
if (!$this->getAcl()->check($this->name, 'read')) {
|
||||
throw new Forbidden();
|
||||
}
|
||||
@@ -335,8 +365,11 @@ class Record extends Base
|
||||
return $this->getRecordService()->follow($id);
|
||||
}
|
||||
|
||||
public function actionUnfollow($params)
|
||||
public function actionUnfollow($params, $data, $request)
|
||||
{
|
||||
if (!$request->isDelete()) {
|
||||
throw new BadRequest();
|
||||
}
|
||||
if (!$this->getAcl()->check($this->name, 'read')) {
|
||||
throw new Forbidden();
|
||||
}
|
||||
|
||||
@@ -28,9 +28,9 @@ use \Espo\Core\Exceptions\NotFound,
|
||||
|
||||
class EntryPointManager
|
||||
{
|
||||
private $container;
|
||||
|
||||
private $fileManager;
|
||||
private $container;
|
||||
|
||||
private $fileManager;
|
||||
|
||||
protected $data = null;
|
||||
|
||||
@@ -38,7 +38,7 @@ class EntryPointManager
|
||||
|
||||
protected $allowedMethods = array(
|
||||
'run',
|
||||
);
|
||||
);
|
||||
|
||||
/**
|
||||
* @var array - path to entryPoint files
|
||||
@@ -46,14 +46,14 @@ class EntryPointManager
|
||||
private $paths = array(
|
||||
'corePath' => 'application/Espo/EntryPoints',
|
||||
'modulePath' => 'application/Espo/Modules/{*}/EntryPoints',
|
||||
'customPath' => 'custom/Espo/Custom/EntryPoints',
|
||||
'customPath' => 'custom/Espo/Custom/EntryPoints',
|
||||
);
|
||||
|
||||
|
||||
public function __construct(\Espo\Core\Container $container)
|
||||
{
|
||||
$this->container = $container;
|
||||
$this->fileManager = $container->get('fileManager');
|
||||
$this->container = $container;
|
||||
$this->fileManager = $container->get('fileManager');
|
||||
}
|
||||
|
||||
protected function getContainer()
|
||||
@@ -69,16 +69,16 @@ class EntryPointManager
|
||||
public function checkAuthRequired($name)
|
||||
{
|
||||
$className = $this->getClassName($name);
|
||||
if ($className === false) {
|
||||
if (!$className) {
|
||||
throw new NotFound();
|
||||
}
|
||||
return $className::$authRequired;
|
||||
return $className::$authRequired;
|
||||
}
|
||||
|
||||
public function run($name)
|
||||
public function run($name)
|
||||
{
|
||||
$className = $this->getClassName($name);
|
||||
if ($className === false) {
|
||||
if (!$className) {
|
||||
throw new NotFound();
|
||||
}
|
||||
$entryPoint = new $className($this->container);
|
||||
@@ -89,7 +89,7 @@ class EntryPointManager
|
||||
protected function getClassName($name)
|
||||
{
|
||||
$name = Util::normilizeClassName($name);
|
||||
|
||||
|
||||
if (!isset($this->data)) {
|
||||
$this->init();
|
||||
}
|
||||
@@ -98,8 +98,8 @@ class EntryPointManager
|
||||
if (isset($this->data[$name])) {
|
||||
return $this->data[$name];
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -108,8 +108,7 @@ class EntryPointManager
|
||||
$classParser = $this->getContainer()->get('classParser');
|
||||
$classParser->setAllowedMethods($this->allowedMethods);
|
||||
$this->data = $classParser->getData($this->paths, $this->cacheFile);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -164,6 +164,10 @@ class Sender
|
||||
$message->addFrom($fromAddress, $fromName);
|
||||
}
|
||||
|
||||
if (!$email->get('from')) {
|
||||
$email->set('from', $fromAddress);
|
||||
}
|
||||
|
||||
if (!empty($params['replyToAddress'])) {
|
||||
$replyToName = null;
|
||||
if (!empty($params['replyToName'])) {
|
||||
|
||||
@@ -162,8 +162,6 @@ class RDB extends \Espo\ORM\Repositories\RDB implements Injectable
|
||||
|
||||
public function remove(Entity $entity, array $options = array())
|
||||
{
|
||||
$this->getEntityManager()->getHookManager()->process($this->entityName, 'beforeRemove', $entity, $options);
|
||||
|
||||
$result = parent::remove($entity, $options);
|
||||
if ($result) {
|
||||
$this->getEntityManager()->getHookManager()->process($this->entityName, 'afterRemove', $entity, $options);
|
||||
|
||||
@@ -209,10 +209,9 @@ class Base
|
||||
$defs = $relDefs[$link];
|
||||
if ($defs['type'] == 'manyMany') {
|
||||
$joins[] = $link;
|
||||
if (!empty($defs['relationName']) && !empty($defs['midKeys'])) {
|
||||
if (!empty($defs['midKeys'])) {
|
||||
$key = $defs['midKeys'][1];
|
||||
$relationName = lcfirst($defs['relationName']);
|
||||
$part[$relationName . '.' . $key] = $idsValue;
|
||||
$part[$link . 'Middle.' . $key] = $idsValue;
|
||||
}
|
||||
} else if ($defs['type'] == 'belongsTo') {
|
||||
if (!empty($defs['key'])) {
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"entity": true,
|
||||
"layouts": true,
|
||||
"tab": true,
|
||||
"acl": true,
|
||||
"customizable": true,
|
||||
"importable": true,
|
||||
"notifications": true
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"entity": true,
|
||||
"layouts": true,
|
||||
"tab": true,
|
||||
"acl": true,
|
||||
"customizable": true,
|
||||
"importable": false,
|
||||
"notifications": false
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"entity": true,
|
||||
"layouts": true,
|
||||
"tab": true,
|
||||
"acl": true,
|
||||
"customizable": true,
|
||||
"importable": true,
|
||||
"notifications": true
|
||||
}
|
||||
@@ -57,10 +57,10 @@ class Install extends \Espo\Core\Upgrades\Actions\Base
|
||||
//check permissions copied and deleted files
|
||||
$this->checkIsWritable();
|
||||
|
||||
$this->backupExistingFiles();
|
||||
|
||||
$this->beforeRunAction();
|
||||
|
||||
$this->backupExistingFiles();
|
||||
|
||||
/* run before install script */
|
||||
$this->runScript('before');
|
||||
|
||||
|
||||
@@ -56,17 +56,17 @@ class Uninstall extends \Espo\Core\Upgrades\Actions\Base
|
||||
|
||||
/* copy core files */
|
||||
if (!$this->copyFiles()) {
|
||||
throw new $this->throwErrorAndRemovePackage('Cannot copy files.');
|
||||
$this->throwErrorAndRemovePackage('Cannot copy files.');
|
||||
}
|
||||
|
||||
/* remove extension files, saved in fileList */
|
||||
if (!$this->deleteFiles(true)) {
|
||||
throw new $this->throwErrorAndRemovePackage('Permission denied to delete files.');
|
||||
$this->throwErrorAndRemovePackage('Permission denied to delete files.');
|
||||
}
|
||||
}
|
||||
|
||||
if (!$this->systemRebuild()) {
|
||||
throw new $this->throwErrorAndRemovePackage('Error occurred while EspoCRM rebuild.');
|
||||
$this->throwErrorAndRemovePackage('Error occurred while EspoCRM rebuild.');
|
||||
}
|
||||
|
||||
/* run after uninstall script */
|
||||
|
||||
@@ -33,16 +33,8 @@ class Converter
|
||||
|
||||
private $schemaConverter;
|
||||
|
||||
|
||||
|
||||
private $schemaFromMetadata = null;
|
||||
|
||||
/**
|
||||
* @var array $meta - metadata array
|
||||
*/
|
||||
//private $meta;
|
||||
|
||||
|
||||
public function __construct(\Espo\Core\Utils\Metadata $metadata, \Espo\Core\Utils\File\Manager $fileManager)
|
||||
{
|
||||
$this->metadata = $metadata;
|
||||
@@ -53,7 +45,6 @@ class Converter
|
||||
$this->schemaConverter = new Schema\Converter($this->fileManager);
|
||||
}
|
||||
|
||||
|
||||
protected function getMetadata()
|
||||
{
|
||||
return $this->metadata;
|
||||
@@ -69,13 +60,11 @@ class Converter
|
||||
return $this->schemaConverter;
|
||||
}
|
||||
|
||||
|
||||
public function getSchemaFromMetadata($entityList = null)
|
||||
{
|
||||
$ormMeta = $this->getMetadata()->getOrmMetadata();
|
||||
$entityDefs = $this->getMetadata()->get('entityDefs');
|
||||
|
||||
$this->schemaFromMetadata = $this->getSchemaConverter()->process($ormMeta, $entityDefs, $entityList);
|
||||
$this->schemaFromMetadata = $this->getSchemaConverter()->process($ormMeta, $entityList);
|
||||
|
||||
return $this->schemaFromMetadata;
|
||||
}
|
||||
@@ -98,11 +87,4 @@ class Converter
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
?>
|
||||
}
|
||||
@@ -22,12 +22,13 @@
|
||||
|
||||
namespace Espo\Core\Utils\Database\DBAL\Platforms;
|
||||
|
||||
use Doctrine\DBAL\Schema\TableDiff,
|
||||
Doctrine\DBAL\Schema\Index,
|
||||
Doctrine\DBAL\Schema\Table,
|
||||
Doctrine\DBAL\Schema\Constraint,
|
||||
Doctrine\DBAL\Schema\ForeignKeyConstraint;
|
||||
|
||||
use Doctrine\DBAL\Schema\TableDiff;
|
||||
use Doctrine\DBAL\Schema\Index;
|
||||
use Doctrine\DBAL\Schema\Table;
|
||||
use Doctrine\DBAL\Schema\Constraint;
|
||||
use Doctrine\DBAL\Schema\ForeignKeyConstraint;
|
||||
use Doctrine\DBAL\Schema\ColumnDiff;
|
||||
use Doctrine\DBAL\Schema\Column;
|
||||
|
||||
class MySqlPlatform extends \Doctrine\DBAL\Platforms\MySqlPlatform
|
||||
{
|
||||
@@ -39,21 +40,52 @@ class MySqlPlatform extends \Doctrine\DBAL\Platforms\MySqlPlatform
|
||||
$queryParts[] = 'RENAME TO ' . $diff->newName;
|
||||
}
|
||||
|
||||
foreach ($diff->addedColumns as $column) {
|
||||
if ($this->onSchemaAlterTableAddColumn($column, $diff, $columnSql)) {
|
||||
//espo: It works not correctly. It can rename some existing fields
|
||||
foreach ($diff->renamedColumns as $oldColumnName => $column) {
|
||||
if ($this->onSchemaAlterTableRenameColumn($oldColumnName, $column, $diff, $columnSql)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
//espo: remaned autoincrement field
|
||||
if ($column->getAutoincrement()) {
|
||||
$diff->removedColumns[$oldColumnName] = new Column($oldColumnName, $column->getType(), $column->toArray());
|
||||
|
||||
$columnName = $column->getQuotedName($this);
|
||||
$diff->addedColumns[$columnName] = $column;
|
||||
continue;
|
||||
}
|
||||
//END espo
|
||||
|
||||
$columnArray = $column->toArray();
|
||||
$columnArray['comment'] = $this->getColumnComment($column);
|
||||
$queryParts[] = 'ADD ' . $this->getColumnDeclarationSQL($column->getQuotedName($this), $columnArray);
|
||||
}
|
||||
/*$queryParts[] = 'CHANGE ' . $oldColumnName . ' '
|
||||
. $this->getColumnDeclarationSQL($column->getQuotedName($this), $columnArray); */
|
||||
$queryParts[] = 'ADD ' . $this->getColumnDeclarationSQL($column->getQuotedName($this), $columnArray); //espo: fixed the problem
|
||||
} //espo: END
|
||||
|
||||
foreach ($diff->removedColumns as $column) {
|
||||
if ($this->onSchemaAlterTableRemoveColumn($column, $diff, $columnSql)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
//espo: remove autoincrement option
|
||||
if ($column->getAutoincrement()) {
|
||||
|
||||
$columnName = $column->getQuotedName($this);
|
||||
|
||||
$changedColumn = clone $column;
|
||||
$changedColumn->setNotNull(false);
|
||||
$changedColumn->setAutoincrement(false);
|
||||
|
||||
$changedProperties = array(
|
||||
'notnull',
|
||||
'autoincrement',
|
||||
);
|
||||
|
||||
$diff->changedColumns[$columnName] = new ColumnDiff($columnName, $changedColumn, $changedProperties, $column);
|
||||
}
|
||||
//END espo
|
||||
|
||||
//$queryParts[] = 'DROP ' . $column->getQuotedName($this); //espo: no needs to remove columns
|
||||
}
|
||||
|
||||
@@ -71,19 +103,15 @@ class MySqlPlatform extends \Doctrine\DBAL\Platforms\MySqlPlatform
|
||||
. $this->getColumnDeclarationSQL($column->getQuotedName($this), $columnArray);
|
||||
}
|
||||
|
||||
//espo: It works not correctly. It can rename some existing fields
|
||||
foreach ($diff->renamedColumns as $oldColumnName => $column) {
|
||||
if ($this->onSchemaAlterTableRenameColumn($oldColumnName, $column, $diff, $columnSql)) {
|
||||
foreach ($diff->addedColumns as $column) {
|
||||
if ($this->onSchemaAlterTableAddColumn($column, $diff, $columnSql)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$columnArray = $column->toArray();
|
||||
$columnArray['comment'] = $this->getColumnComment($column);
|
||||
/*$queryParts[] = 'CHANGE ' . $oldColumnName . ' '
|
||||
. $this->getColumnDeclarationSQL($column->getQuotedName($this), $columnArray); */
|
||||
$queryParts[] = 'ADD ' . $this->getColumnDeclarationSQL($column->getQuotedName($this), $columnArray); //espo: fixed the problem
|
||||
} //espo: END
|
||||
|
||||
$queryParts[] = 'ADD ' . $this->getColumnDeclarationSQL($column->getQuotedName($this), $columnArray);
|
||||
}
|
||||
|
||||
$sql = array();
|
||||
$tableSql = array();
|
||||
|
||||
@@ -79,7 +79,7 @@ class Converter
|
||||
|
||||
protected $idParams = array(
|
||||
'dbType' => 'varchar',
|
||||
'len' => '24',
|
||||
'len' => 24,
|
||||
);
|
||||
|
||||
/**
|
||||
@@ -107,9 +107,9 @@ class Converter
|
||||
return $this->metadata;
|
||||
}
|
||||
|
||||
protected function getEntityDefs()
|
||||
protected function getEntityDefs($reload = false)
|
||||
{
|
||||
if (empty($this->entityDefs)) {
|
||||
if (empty($this->entityDefs) || $reload) {
|
||||
$this->entityDefs = $this->getMetadata()->get('entityDefs');
|
||||
}
|
||||
|
||||
@@ -131,9 +131,14 @@ class Converter
|
||||
return $this->metadataHelper;
|
||||
}
|
||||
|
||||
/**
|
||||
* Orm metadata convertation process
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function process()
|
||||
{
|
||||
$entityDefs = $this->getEntityDefs();
|
||||
$entityDefs = $this->getEntityDefs(true);
|
||||
|
||||
$ormMeta = array();
|
||||
foreach($entityDefs as $entityName => $entityMeta) {
|
||||
@@ -185,12 +190,12 @@ class Converter
|
||||
switch ($fieldParams['type']) {
|
||||
case 'id':
|
||||
if ($fieldParams['dbType'] != 'int') {
|
||||
$fieldParams = array_merge($fieldParams, $this->idParams);
|
||||
$fieldParams = array_merge($this->idParams, $fieldParams);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'foreignId':
|
||||
$fieldParams = array_merge($fieldParams, $this->idParams);
|
||||
$fieldParams = array_merge($this->idParams, $fieldParams);
|
||||
$fieldParams['notNull'] = false;
|
||||
break;
|
||||
|
||||
@@ -221,6 +226,11 @@ class Converter
|
||||
*/
|
||||
protected function convertFields($entityName, &$entityMeta)
|
||||
{
|
||||
//List of unmerged fields with default field defenitions in $outputMeta
|
||||
$unmergedFields = array(
|
||||
'name',
|
||||
);
|
||||
|
||||
$outputMeta = array(
|
||||
'id' => array(
|
||||
'type' => Entity::ID,
|
||||
@@ -230,6 +240,10 @@ class Converter
|
||||
'type' => isset($entityMeta['fields']['name']['type']) ? $entityMeta['fields']['name']['type'] : Entity::VARCHAR,
|
||||
'notStorable' => true,
|
||||
),
|
||||
'deleted' => array(
|
||||
'type' => Entity::BOOL,
|
||||
'default' => false,
|
||||
),
|
||||
);
|
||||
|
||||
foreach($entityMeta['fields'] as $fieldName => $fieldParams) {
|
||||
@@ -238,8 +252,14 @@ class Converter
|
||||
$fieldTypeMeta = $this->getMetadataHelper()->getFieldDefsByType($fieldParams);
|
||||
|
||||
$fieldDefs = $this->convertField($entityName, $fieldName, $fieldParams, $fieldTypeMeta);
|
||||
|
||||
if ($fieldDefs !== false) {
|
||||
$outputMeta[$fieldName] = $fieldDefs; //push fieldDefs to the main array
|
||||
//push fieldDefs to the ORM metadata array
|
||||
if (isset($outputMeta[$fieldName]) && !in_array($fieldName, $unmergedFields)) {
|
||||
$outputMeta[$fieldName] = array_merge($outputMeta[$fieldName], $fieldDefs);
|
||||
} else {
|
||||
$outputMeta[$fieldName] = $fieldDefs;
|
||||
}
|
||||
}
|
||||
|
||||
/** check and set the linkDefs from 'fields' metadata */
|
||||
@@ -254,13 +274,6 @@ class Converter
|
||||
}
|
||||
}
|
||||
|
||||
if (!isset($outputMeta['deleted'])) {
|
||||
$outputMeta['deleted'] = array(
|
||||
'type' => Entity::BOOL,
|
||||
'default' => false,
|
||||
);
|
||||
}
|
||||
|
||||
return $outputMeta;
|
||||
}
|
||||
|
||||
@@ -308,6 +321,15 @@ class Converter
|
||||
'type' => 'varchar',
|
||||
'notStorable' => true,
|
||||
);
|
||||
|
||||
$ormMeta[$entityName]['fields']['followersIds'] = array(
|
||||
'type' => 'jsonArray',
|
||||
'notStorable' => true,
|
||||
);
|
||||
$ormMeta[$entityName]['fields']['followersNames'] = array(
|
||||
'type' => 'jsonObject',
|
||||
'notStorable' => true,
|
||||
);
|
||||
}
|
||||
} //END: add a field 'isFollowed' for stream => true
|
||||
|
||||
@@ -316,12 +338,6 @@ class Converter
|
||||
|
||||
protected function convertField($entityName, $fieldName, array $fieldParams, $fieldTypeMeta = null)
|
||||
{
|
||||
/** set default type if exists */
|
||||
if (!isset($fieldParams['type']) || empty($fieldParams['type'])) {
|
||||
$GLOBALS['log']->debug('Field type does not exist for '.$entityName.':'.$fieldName.'. Use default type ['.$this->defaultFieldType.']');
|
||||
$fieldParams['type'] = $this->defaultFieldType;
|
||||
} /** END: set default type if exists */
|
||||
|
||||
/** merge fieldDefs option from field definition */
|
||||
if (!isset($fieldTypeMeta)) {
|
||||
$fieldTypeMeta = $this->getMetadataHelper()->getFieldDefsByType($fieldParams);
|
||||
@@ -362,8 +378,6 @@ class Converter
|
||||
return array();
|
||||
}
|
||||
|
||||
$entityDefs = $this->getEntityDefs();
|
||||
|
||||
$relationships = array();
|
||||
foreach($entityMeta['links'] as $linkName => $linkParams) {
|
||||
|
||||
|
||||
@@ -28,8 +28,6 @@ class RelationManager
|
||||
{
|
||||
private $metadata;
|
||||
|
||||
private $entityDefs;
|
||||
|
||||
public function __construct(\Espo\Core\Utils\Metadata $metadata)
|
||||
{
|
||||
$this->metadata = $metadata;
|
||||
@@ -40,16 +38,6 @@ class RelationManager
|
||||
return $this->metadata;
|
||||
}
|
||||
|
||||
protected function getEntityDefs()
|
||||
{
|
||||
if (empty($this->entityDefs)) {
|
||||
$this->entityDefs = $this->getMetadata()->get('entityDefs');
|
||||
}
|
||||
|
||||
return $this->entityDefs;
|
||||
}
|
||||
|
||||
|
||||
public function getLinkEntityName($entityName, $linkParams)
|
||||
{
|
||||
return isset($linkParams['entity']) ? $linkParams['entity'] : $entityName;
|
||||
@@ -110,7 +98,7 @@ class RelationManager
|
||||
|
||||
public function convert($linkName, $linkParams, $entityName, $ormMeta)
|
||||
{
|
||||
$entityDefs = $this->getEntityDefs();
|
||||
$entityDefs = $this->getMetadata()->get('entityDefs');
|
||||
|
||||
$foreignEntityName = $this->getLinkEntityName($entityName, $linkParams);
|
||||
$foreignLink = $this->getForeignLink($linkName, $linkParams, $entityDefs[$foreignEntityName]);
|
||||
|
||||
@@ -47,11 +47,10 @@ class Converter
|
||||
'unique' => 'unique',
|
||||
);
|
||||
|
||||
|
||||
//todo: same array in Converters\Orm
|
||||
protected $idParams = array(
|
||||
'dbType' => 'varchar',
|
||||
'len' => '24',
|
||||
'len' => 24,
|
||||
);
|
||||
|
||||
//todo: same array in Converters\Orm
|
||||
@@ -68,8 +67,6 @@ class Converter
|
||||
{
|
||||
$this->fileManager = $fileManager;
|
||||
|
||||
$this->dbalSchema = new \Espo\Core\Utils\Database\DBAL\Schema\Schema();
|
||||
|
||||
$this->typeList = array_keys(\Doctrine\DBAL\Types\Type::getTypesMap());
|
||||
}
|
||||
|
||||
@@ -78,13 +75,31 @@ class Converter
|
||||
return $this->fileManager;
|
||||
}
|
||||
|
||||
protected function getSchema()
|
||||
/**
|
||||
* Get schema
|
||||
*
|
||||
* @param boolean $reload
|
||||
*
|
||||
* @return \Doctrine\DBAL\Schema\Schema
|
||||
*/
|
||||
protected function getSchema($reload = false)
|
||||
{
|
||||
if (!isset($this->dbalSchema) || $reload) {
|
||||
$this->dbalSchema = new \Espo\Core\Utils\Database\DBAL\Schema\Schema();
|
||||
}
|
||||
|
||||
return $this->dbalSchema;
|
||||
}
|
||||
|
||||
|
||||
public function process(array $ormMeta, $entityDefs, $entityList = null)
|
||||
/**
|
||||
* Schema convertation process
|
||||
*
|
||||
* @param array $ormMeta
|
||||
* @param array|null $entityList
|
||||
*
|
||||
* @return \Doctrine\DBAL\Schema\Schema
|
||||
*/
|
||||
public function process(array $ormMeta, $entityList = null)
|
||||
{
|
||||
$GLOBALS['log']->debug('Schema\Converter - Start: building schema');
|
||||
|
||||
@@ -106,7 +121,7 @@ class Converter
|
||||
$ormMeta = array_intersect_key($ormMeta, array_flip($dependentEntities));
|
||||
}
|
||||
|
||||
$schema = $this->getSchema();
|
||||
$schema = $this->getSchema(true);
|
||||
|
||||
$tables = array();
|
||||
foreach ($ormMeta as $entityName => $entityParams) {
|
||||
|
||||
@@ -126,20 +126,17 @@ class EntityManager
|
||||
}
|
||||
$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,
|
||||
'notifications' => true
|
||||
);
|
||||
$this->getMetadata()->set('scopes', $name, $scopeData);
|
||||
$filePath = "application/Espo/Core/Templates/Metadata/{$type}/scopes.json";
|
||||
$scopesDataContents = $this->getFileManager()->getContents($filePath);
|
||||
$scopesDataContents = str_replace('{entityType}', $name, $scopesDataContents);
|
||||
$scopesData = Json::decode($entityDefsDataContents, true);
|
||||
|
||||
$scopesData['stream'] = $stream;
|
||||
$scopesData['type'] = $type;
|
||||
$scopesData['module'] = 'Custom';
|
||||
$scopesData['isCustom'] = true;
|
||||
|
||||
$this->getMetadata()->set('scopes', $name, $scopesData);
|
||||
|
||||
$filePath = "application/Espo/Core/Templates/Metadata/{$type}/entityDefs.json";
|
||||
$entityDefsDataContents = $this->getFileManager()->getContents($filePath);
|
||||
|
||||
@@ -28,10 +28,12 @@ use Espo\Core\Utils\Json;
|
||||
class FileUnifier
|
||||
{
|
||||
private $fileManager;
|
||||
private $metadata;
|
||||
|
||||
public function __construct(\Espo\Core\Utils\File\Manager $fileManager)
|
||||
public function __construct(\Espo\Core\Utils\File\Manager $fileManager, \Espo\Core\Utils\Metadata $metadata = null)
|
||||
{
|
||||
$this->fileManager = $fileManager;
|
||||
$this->metadata = $metadata;
|
||||
}
|
||||
|
||||
protected function getFileManager()
|
||||
@@ -39,6 +41,11 @@ class FileUnifier
|
||||
return $this->fileManager;
|
||||
}
|
||||
|
||||
protected function getMetadata()
|
||||
{
|
||||
return $this->metadata;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unite files content
|
||||
*
|
||||
@@ -53,7 +60,7 @@ class FileUnifier
|
||||
|
||||
if (!empty($paths['modulePath'])) {
|
||||
$moduleDir = strstr($paths['modulePath'], '{*}', true);
|
||||
$moduleList = $this->getFileManager()->getFileList($moduleDir, false, '', false);
|
||||
$moduleList = isset($this->metadata) ? $this->getMetadata()->getModuleList() : $this->getFileManager()->getFileList($moduleDir, false, '', false);
|
||||
|
||||
foreach ($moduleList as $moduleName) {
|
||||
$moduleFilePath = str_replace('{*}', $moduleName, $paths['modulePath']);
|
||||
|
||||
@@ -242,7 +242,7 @@ class Manager
|
||||
public function putContentsJson($path, $data)
|
||||
{
|
||||
if (!Utils\Json::isJSON($data)) {
|
||||
$data = Utils\Json::encode($data, JSON_PRETTY_PRINT);
|
||||
$data = Utils\Json::encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
|
||||
}
|
||||
|
||||
return $this->putContents($path, $data, LOCK_EX);
|
||||
@@ -283,7 +283,7 @@ class Manager
|
||||
$data = Utils\Util::merge($savedDataArray, $newDataArray);
|
||||
|
||||
if ($isReturnJson) {
|
||||
$data = Utils\Json::encode($data, JSON_PRETTY_PRINT);
|
||||
$data = Utils\Json::encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
|
||||
}
|
||||
|
||||
if ($isPhp) {
|
||||
|
||||
@@ -27,15 +27,17 @@ use Espo\Core\Utils;
|
||||
class Unifier
|
||||
{
|
||||
private $fileManager;
|
||||
private $metadata;
|
||||
|
||||
protected $params = array(
|
||||
'unsetFileName' => 'unset.json',
|
||||
'defaultsPath' => 'application/Espo/Core/defaults',
|
||||
);
|
||||
|
||||
public function __construct(\Espo\Core\Utils\File\Manager $fileManager)
|
||||
public function __construct(\Espo\Core\Utils\File\Manager $fileManager, \Espo\Core\Utils\Metadata $metadata = null)
|
||||
{
|
||||
$this->fileManager = $fileManager;
|
||||
$this->metadata = $metadata;
|
||||
}
|
||||
|
||||
protected function getFileManager()
|
||||
@@ -43,6 +45,11 @@ class Unifier
|
||||
return $this->fileManager;
|
||||
}
|
||||
|
||||
protected function getMetadata()
|
||||
{
|
||||
return $this->metadata;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unite file content to the file
|
||||
*
|
||||
@@ -58,11 +65,12 @@ class Unifier
|
||||
|
||||
if (!empty($paths['modulePath'])) {
|
||||
$customDir = strstr($paths['modulePath'], '{*}', true);
|
||||
$dirList = $this->getFileManager()->getFileList($customDir, false, '', false);
|
||||
|
||||
foreach ($dirList as $dirName) {
|
||||
$curPath = str_replace('{*}', $dirName, $paths['modulePath']);
|
||||
$content = Utils\Util::merge($content, $this->unifySingle($curPath, $name, $recursively, $dirName));
|
||||
$moduleList = isset($this->metadata) ? $this->getMetadata()->getModuleList() : $this->getFileManager()->getFileList($customDir, false, '', false);
|
||||
|
||||
foreach ($moduleList as $moduleName) {
|
||||
$curPath = str_replace('{*}', $moduleName, $paths['modulePath']);
|
||||
$content = Utils\Util::merge($content, $this->unifySingle($curPath, $name, $recursively, $moduleName));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@ class Language
|
||||
{
|
||||
private $fileManager;
|
||||
private $config;
|
||||
private $metadata;
|
||||
private $preferences;
|
||||
private $unifier;
|
||||
|
||||
@@ -62,13 +63,14 @@ class Language
|
||||
);
|
||||
|
||||
|
||||
public function __construct(\Espo\Core\Utils\File\Manager $fileManager, \Espo\Core\Utils\Config $config, \Espo\Entities\Preferences $preferences = null)
|
||||
public function __construct(File\Manager $fileManager, Config $config, Metadata $metadata, \Espo\Entities\Preferences $preferences = null)
|
||||
{
|
||||
$this->fileManager = $fileManager;
|
||||
$this->config = $config;
|
||||
$this->metadata = $metadata;
|
||||
$this->preferences = $preferences;
|
||||
|
||||
$this->unifier = new \Espo\Core\Utils\File\Unifier($this->fileManager);
|
||||
$this->unifier = new \Espo\Core\Utils\File\Unifier($this->fileManager, $this->metadata);
|
||||
}
|
||||
|
||||
protected function getFileManager()
|
||||
@@ -81,6 +83,11 @@ class Language
|
||||
return $this->config;
|
||||
}
|
||||
|
||||
protected function getMetadata()
|
||||
{
|
||||
return $this->metadata;
|
||||
}
|
||||
|
||||
protected function getPreferences()
|
||||
{
|
||||
return $this->preferences;
|
||||
|
||||
@@ -136,7 +136,7 @@ class Layout
|
||||
}
|
||||
|
||||
$layoutPath = $this->getLayoutPath($controllerName, true);
|
||||
$data = Json::encode($layoutData, \JSON_PRETTY_PRINT);
|
||||
$data = Json::encode($layoutData, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
|
||||
|
||||
$result &= $this->getFileManager()->putContents(array($layoutPath, $layoutName.'.json'), $data);
|
||||
}
|
||||
|
||||
@@ -28,8 +28,6 @@ class Metadata
|
||||
{
|
||||
protected $meta = null;
|
||||
|
||||
protected $scopes = array();
|
||||
|
||||
private $config;
|
||||
private $unifier;
|
||||
private $fileManager;
|
||||
@@ -92,7 +90,7 @@ class Metadata
|
||||
protected function getUnifier()
|
||||
{
|
||||
if (!isset($this->unifier)) {
|
||||
$this->unifier = new \Espo\Core\Utils\File\Unifier($this->fileManager);
|
||||
$this->unifier = new \Espo\Core\Utils\File\Unifier($this->fileManager, $this);
|
||||
}
|
||||
|
||||
return $this->unifier;
|
||||
@@ -153,6 +151,7 @@ class Metadata
|
||||
if (file_exists($this->cacheFile) && !$reload) {
|
||||
$this->meta = $this->getFileManager()->getPhpContents($this->cacheFile);
|
||||
} else {
|
||||
$this->clearVars();
|
||||
$this->meta = $this->getUnifier()->unify($this->name, $this->paths, true);
|
||||
$this->meta = $this->setLanguageFromConfig($this->meta);
|
||||
$this->meta = $this->addAdditionalFields($this->meta);
|
||||
@@ -531,4 +530,16 @@ class Metadata
|
||||
|
||||
return $path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear metadata variables when reload meta
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function clearVars()
|
||||
{
|
||||
$this->meta = null;
|
||||
$this->moduleList = null;
|
||||
$this->ormMeta = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -186,19 +186,25 @@ class Util
|
||||
*/
|
||||
public static function unsetInArrayByValue($needle, array $haystack, $reIndex = true)
|
||||
{
|
||||
$doReindex = false;
|
||||
|
||||
foreach($haystack as $key => $value) {
|
||||
if (is_array($value)) {
|
||||
$haystack[$key] = static::unsetInArrayByValue($needle, $value);
|
||||
} else if ($needle === $value) {
|
||||
|
||||
unset($haystack[$key]);
|
||||
|
||||
if ($reIndex) {
|
||||
array_splice($haystack, $key, 1);
|
||||
} else {
|
||||
unset($haystack[$key]);
|
||||
$doReindex = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($doReindex) {
|
||||
$haystack = array_values($haystack);
|
||||
}
|
||||
|
||||
return $haystack;
|
||||
}
|
||||
|
||||
|
||||
@@ -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\Controllers;
|
||||
|
||||
@@ -29,7 +29,7 @@ use \Espo\Core\Exceptions\Error,
|
||||
class Activities extends \Espo\Core\Controllers\Base
|
||||
{
|
||||
public static $defaultAction = 'index';
|
||||
|
||||
|
||||
public function actionListCalendarEvents($params, $data, $request)
|
||||
{
|
||||
if (!$this->getAcl()->check('Calendar')) {
|
||||
@@ -38,12 +38,12 @@ class Activities extends \Espo\Core\Controllers\Base
|
||||
|
||||
$from = $request->get('from');
|
||||
$to = $request->get('to');
|
||||
|
||||
|
||||
if (empty($from) || empty($to)) {
|
||||
throw new BadRequest();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
$service = $this->getService('Activities');
|
||||
return $service->getEvents($this->getUser()->id, $from, $to);
|
||||
}
|
||||
@@ -51,7 +51,7 @@ class Activities extends \Espo\Core\Controllers\Base
|
||||
public function actionPopupNotifications()
|
||||
{
|
||||
$userId = $this->getUser()->id;
|
||||
|
||||
|
||||
return $this->getService('Activities')->getPopupNotifications($userId);
|
||||
}
|
||||
|
||||
@@ -72,30 +72,37 @@ class Activities extends \Espo\Core\Controllers\Base
|
||||
public function actionList($params, $data, $request)
|
||||
{
|
||||
$name = $params['name'];
|
||||
|
||||
if (!in_array($name, array('activities', 'history'))) {
|
||||
|
||||
if (!in_array($name, ['activities', 'history'])) {
|
||||
throw new BadRequest();
|
||||
}
|
||||
|
||||
$entityName = $params['scope'];
|
||||
|
||||
if (empty($params['scope'])) {
|
||||
throw new BadRequest();
|
||||
}
|
||||
if (empty($params['id'])) {
|
||||
throw new BadRequest();
|
||||
}
|
||||
|
||||
$entityType = $params['scope'];
|
||||
$id = $params['id'];
|
||||
|
||||
|
||||
$offset = intval($request->get('offset'));
|
||||
$maxSize = intval($request->get('maxSize'));
|
||||
$asc = $request->get('asc') === 'true';
|
||||
$sortBy = $request->get('sortBy');
|
||||
$where = $request->get('where');
|
||||
|
||||
|
||||
$scope = null;
|
||||
if (!empty($where) && !empty($where['scope']) && $where['scope'] !== 'false') {
|
||||
$scope = $where['scope'];
|
||||
if (is_array($where) && !empty($where[0]) && $where[0] !== 'false') {
|
||||
$scope = $where[0];
|
||||
}
|
||||
|
||||
|
||||
$service = $this->getService('Activities');
|
||||
|
||||
$methodName = 'get' . ucfirst($name);
|
||||
|
||||
return $service->$methodName($entityName, $id, array(
|
||||
|
||||
return $service->$methodName($entityType, $id, array(
|
||||
'scope' => $scope,
|
||||
'offset' => $offset,
|
||||
'maxSize' => $maxSize,
|
||||
|
||||
28
application/Espo/Modules/Crm/Controllers/DocumentFolder.php
Normal file
28
application/Espo/Modules/Crm/Controllers/DocumentFolder.php
Normal file
@@ -0,0 +1,28 @@
|
||||
<?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\Controllers;
|
||||
|
||||
class DocumentFolder extends \Espo\Core\Templates\Controllers\CategoryTree
|
||||
{
|
||||
|
||||
}
|
||||
@@ -22,7 +22,27 @@
|
||||
|
||||
namespace Espo\Modules\Crm\Controllers;
|
||||
|
||||
use \Espo\Core\Exceptions\Error;
|
||||
use \Espo\Core\Exceptions\Forbidden;
|
||||
use \Espo\Core\Exceptions\BadRequest;
|
||||
|
||||
class TargetList extends \Espo\Core\Controllers\Record
|
||||
{
|
||||
public function actionUnlinkAll($params, $data, $request)
|
||||
{
|
||||
if (!$request->isPost()) {
|
||||
throw new BadRequest();
|
||||
}
|
||||
|
||||
if (empty($data['id'])) {
|
||||
throw new BadRequest();
|
||||
}
|
||||
|
||||
if (empty($data['link'])) {
|
||||
throw new BadRequest();
|
||||
}
|
||||
|
||||
return $this->getRecordService()->unlinkAll($data['id'], $data['link']);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -18,11 +18,11 @@
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
|
||||
************************************************************************/
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Modules\Crm\Entities;
|
||||
|
||||
class Document extends \Espo\Core\Entities\Person
|
||||
class Document extends \Espo\Core\ORM\Entity
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
29
application/Espo/Modules/Crm/Entities/DocumentFolder.php
Normal file
29
application/Espo/Modules/Crm/Entities/DocumentFolder.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\Modules\Crm\Entities;
|
||||
|
||||
class DocumentFolder extends \Espo\Core\Templates\Entities\CategoryTree
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
@@ -92,6 +92,10 @@ class Contact extends \Espo\Core\ORM\Repositories\RDB
|
||||
}
|
||||
}
|
||||
|
||||
if ($entity->has('targetListId') && $entity->isNew()) {
|
||||
$this->relate($entity, 'targetLists', $entity->get('targetListId'));
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
||||
27
application/Espo/Modules/Crm/Repositories/DocumentFolder.php
Normal file
27
application/Espo/Modules/Crm/Repositories/DocumentFolder.php
Normal file
@@ -0,0 +1,27 @@
|
||||
<?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\Repositories;
|
||||
|
||||
class DocumentFolder extends \Espo\Core\Repositories\CategoryTree
|
||||
{
|
||||
}
|
||||
@@ -23,7 +23,8 @@
|
||||
"tasksPrimary": "Tasks (expanded)",
|
||||
"emailsPrimary": "Emails (expanded)",
|
||||
"targetLists": "Target Lists",
|
||||
"campaignLogRecords": "Campaign Log"
|
||||
"campaignLogRecords": "Campaign Log",
|
||||
"campaign": "Campaign"
|
||||
},
|
||||
"options": {
|
||||
"type": {
|
||||
|
||||
@@ -15,7 +15,8 @@
|
||||
"hardBouncedCount": "Hard Bounced",
|
||||
"softBouncedCount": "Soft Bounced",
|
||||
"leadCreatedCount": "Leads Created",
|
||||
"revenue": "Revenue"
|
||||
"revenue": "Revenue",
|
||||
"revenueConverted": "revenue (converted)"
|
||||
},
|
||||
"links": {
|
||||
"targetLists": "Target Lists",
|
||||
|
||||
@@ -20,7 +20,8 @@
|
||||
"opportunities": "Opportunities",
|
||||
"cases": "Cases",
|
||||
"targetLists": "Target Lists",
|
||||
"campaignLogRecords": "Campaign Log"
|
||||
"campaignLogRecords": "Campaign Log",
|
||||
"campaign": "Campaign"
|
||||
},
|
||||
"labels": {
|
||||
"Create Contact": "Create Contact"
|
||||
|
||||
@@ -11,11 +11,14 @@
|
||||
"source": "Source",
|
||||
"publishDate": "Publish Date",
|
||||
"expirationDate": "Expiration Date",
|
||||
"description": "Description"
|
||||
"description": "Description",
|
||||
"accounts": "Accounts",
|
||||
"folder": "Folder"
|
||||
},
|
||||
"links": {
|
||||
"accounts": "Accounts",
|
||||
"opportunities": "Opportunities"
|
||||
"opportunities": "Opportunities",
|
||||
"folder": "Folder"
|
||||
},
|
||||
"options": {
|
||||
"status": {
|
||||
@@ -23,6 +26,13 @@
|
||||
"Draft": "Draft",
|
||||
"Expired": "Expired",
|
||||
"Canceled": "Canceled"
|
||||
},
|
||||
"type": {
|
||||
"": "None",
|
||||
"Contract": "Contract",
|
||||
"NDA": "NDA",
|
||||
"EULA": "EULA",
|
||||
"License Agreement": "License Agreement"
|
||||
}
|
||||
},
|
||||
"presetFilters": {
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"labels": {
|
||||
"Create DocumentFolder": "Create Document Folder",
|
||||
"Manage Categories": "Manage Folders"
|
||||
},
|
||||
"links": {
|
||||
"documents": "Documents"
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
"labels": {
|
||||
"Create Lead": "Create Lead",
|
||||
"Create Contact": "Create Contact",
|
||||
"Create Task": "Create Task"
|
||||
"Create Task": "Create Task",
|
||||
"Create Case": "Create Case"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
"Task": "Task",
|
||||
"Case": "Case",
|
||||
"Document": "Document",
|
||||
"DocumentFolder": "Document Folder",
|
||||
"Campaign": "Campaign",
|
||||
"TargetList": "Target List"
|
||||
},
|
||||
@@ -26,6 +27,7 @@
|
||||
"Task": "Tasks",
|
||||
"Case": "Cases",
|
||||
"Document": "Documents",
|
||||
"DocumentFolder": "Document Folders",
|
||||
"Campaign": "Campaigns",
|
||||
"TargetList": "Target Lists"
|
||||
},
|
||||
@@ -40,7 +42,7 @@
|
||||
"OpportunitiesByStage": "Opportunities by Stage",
|
||||
"OpportunitiesByLeadSource": "Opportunities by Lead Source",
|
||||
"SalesByMonth": "Sales by Month",
|
||||
"SalesPipeline": "Sales Pipeline"
|
||||
"SalesPipeline": "Sales Pipeline"
|
||||
},
|
||||
"labels": {
|
||||
"Create InboundEmail": "Create Inbound Email",
|
||||
|
||||
@@ -27,7 +27,8 @@
|
||||
},
|
||||
"links": {
|
||||
"targetLists": "Target Lists",
|
||||
"campaignLogRecords": "Campaign Log"
|
||||
"campaignLogRecords": "Campaign Log",
|
||||
"campaign": "Campaign"
|
||||
},
|
||||
"options": {
|
||||
"status": {
|
||||
@@ -39,6 +40,7 @@
|
||||
"Dead": "Dead"
|
||||
},
|
||||
"source": {
|
||||
"": "None",
|
||||
"Call": "Call",
|
||||
"Email": "Email",
|
||||
"Existing Customer": "Existing Customer",
|
||||
|
||||
@@ -16,7 +16,8 @@
|
||||
},
|
||||
"links": {
|
||||
"contacts": "Contacts",
|
||||
"documents": "Documents"
|
||||
"documents": "Documents",
|
||||
"campaign": "Campaign"
|
||||
},
|
||||
"options": {
|
||||
"stage": {
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
}
|
||||
},
|
||||
"labels": {
|
||||
"Create TargetList": "Create Target List"
|
||||
"Create TargetList": "Create Target List",
|
||||
"Opted Out": "Opted Out"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
"links": {
|
||||
"contacts": "Контакти",
|
||||
"opportunities": "Угоди",
|
||||
"cases": "Кейси",
|
||||
"cases": "Звернення",
|
||||
"documents": "Документи",
|
||||
"meetingsPrimary": "Meetings (expanded)",
|
||||
"callsPrimary": "Calls (expanded)",
|
||||
@@ -37,7 +37,7 @@
|
||||
"Advertising": "Реклама",
|
||||
"Apparel & Accessories": "Одяг та аксесуари",
|
||||
"Automotive": "Автомобільні",
|
||||
"Banking": "Банкінґ",
|
||||
"Banking": "Банкінг",
|
||||
"Biotechnology": "Біотехнології",
|
||||
"Chemical": "Хемія",
|
||||
"Computer": "Комп'ютери",
|
||||
@@ -53,8 +53,8 @@
|
||||
"Real Estate": "Нерухомість",
|
||||
"Service": "Сервіс",
|
||||
"Sports": "Спорт",
|
||||
"Software": "Проґрамне забезпечення",
|
||||
"Technology": "Технолоґія",
|
||||
"Software": "Програмне забезпечення",
|
||||
"Technology": "Технологія",
|
||||
"Telecommunications": "Телекомунікації",
|
||||
"Television": "Телебачення",
|
||||
"Transportation": "Транспорт",
|
||||
@@ -62,7 +62,7 @@
|
||||
}
|
||||
},
|
||||
"labels": {
|
||||
"Create Account": "Створити контраґента",
|
||||
"Create Account": "Створити контрагента",
|
||||
"Copy Billing": "Копія рахунку"
|
||||
},
|
||||
"presetFilters": {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"fields": {
|
||||
"name": "Ім'я",
|
||||
"parent": "Джерело",
|
||||
"status": "статус",
|
||||
"status": "Статус",
|
||||
"dateStart": "Дата початку",
|
||||
"dateEnd": "Дата закінчення",
|
||||
"direction": "Напрямок",
|
||||
@@ -12,7 +12,7 @@
|
||||
"contacts": "Контакти",
|
||||
"leads": "Ліди",
|
||||
"reminders": "Нагадування",
|
||||
"account": "Контраґент"
|
||||
"account": "Контрагент"
|
||||
},
|
||||
"links": {
|
||||
},
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"fields": {
|
||||
"name": "Ім'я",
|
||||
"description": "Опис",
|
||||
"status": "статус",
|
||||
"status": "Статус",
|
||||
"type": "Тип",
|
||||
"startDate": "Дата Початку",
|
||||
"endDate": "Дата Закінчення",
|
||||
@@ -19,7 +19,7 @@
|
||||
},
|
||||
"links": {
|
||||
"targetLists": "Цільові списки",
|
||||
"accounts": "Контраґенти",
|
||||
"accounts": "Контрагенти",
|
||||
"contacts": "Контакти",
|
||||
"leads": "Ліди",
|
||||
"opportunities": "Угоди",
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
{
|
||||
"fields": {
|
||||
"name": "Ім'я",
|
||||
"number": "Нумер",
|
||||
"status": "статус",
|
||||
"account": "Контраґент",
|
||||
"number": "Номер",
|
||||
"status": "Статус",
|
||||
"account": "Контрагент",
|
||||
"contact": "Контакт",
|
||||
"priority": "Пріоритет",
|
||||
"type": "Тип",
|
||||
|
||||
@@ -3,10 +3,10 @@
|
||||
"name": "Ім'я",
|
||||
"emailAddress": "Емейл",
|
||||
"title": "Посада",
|
||||
"account": "Контраґент",
|
||||
"accounts": "Контраґенти",
|
||||
"account": "Контрагент",
|
||||
"accounts": "Контрагенти",
|
||||
"phoneNumber": "Телефон",
|
||||
"accountType": "Тип контраґента",
|
||||
"accountType": "Тип контрагента",
|
||||
"doNotCall": "Не дзвонити",
|
||||
"address": "Адреса",
|
||||
"opportunityRole": "Роль угоди",
|
||||
@@ -18,7 +18,7 @@
|
||||
},
|
||||
"links": {
|
||||
"opportunities": "Угоди",
|
||||
"cases": "Кейси",
|
||||
"cases": "Звернення",
|
||||
"targetLists": "Цільові списки",
|
||||
"campaignLogRecords": "Журнал кампанія"
|
||||
},
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
},
|
||||
"fields": {
|
||||
"name": "Ім'я",
|
||||
"status": "статус",
|
||||
"status": "Статус",
|
||||
"file": "Файл",
|
||||
"type": "Тип",
|
||||
"source": "Джерело",
|
||||
@@ -14,7 +14,7 @@
|
||||
"description": "Опис"
|
||||
},
|
||||
"links": {
|
||||
"accounts": "Контраґенти",
|
||||
"accounts": "Контрагенти",
|
||||
"opportunities": "Угоди"
|
||||
},
|
||||
"options": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"scopeNames": {
|
||||
"Account": "Контраґент",
|
||||
"Account": "Контрагент",
|
||||
"Contact": "Контакт",
|
||||
"Lead": "Лід",
|
||||
"Target": "Ціль",
|
||||
@@ -15,7 +15,7 @@
|
||||
"TargetList": "Цільовий список"
|
||||
},
|
||||
"scopeNamesPlural": {
|
||||
"Account": "Контраґенти",
|
||||
"Account": "Контрагенти",
|
||||
"Contact": "Контакти",
|
||||
"Lead": "Ліди",
|
||||
"Target": "Цілі",
|
||||
@@ -24,7 +24,7 @@
|
||||
"Calendar": "Календар",
|
||||
"Call": "Дзвінки",
|
||||
"Task": "Завдання",
|
||||
"Case": "Кейси",
|
||||
"Case": "Звернення",
|
||||
"Document": "Документи",
|
||||
"Campaign": "Кампанії",
|
||||
"TargetList": "Цільові списки"
|
||||
@@ -33,40 +33,40 @@
|
||||
"Leads": "Мої ліди",
|
||||
"Opportunities": "Мої угоди",
|
||||
"Tasks": "Мої завдання",
|
||||
"Cases": "Мої кейси",
|
||||
"Cases": "Мої звернення",
|
||||
"Calendar": "Календар",
|
||||
"Calls": "Мої дзвінки",
|
||||
"Meetings": "Мої зустрічі",
|
||||
"OpportunitiesByStage": "Угоди на стадії",
|
||||
"OpportunitiesByLeadSource": "Угоди за джерелом лідів",
|
||||
"SalesByMonth": "Продажі по місяцях",
|
||||
"SalesPipeline": "Джерела продажу"
|
||||
"SalesPipeline": "Джерела продажу"
|
||||
},
|
||||
"labels": {
|
||||
"Create InboundEmail": "Створити вхідну пошту",
|
||||
"Activities": "Заходи",
|
||||
"History": "Історія",
|
||||
"Attendees": "Учасники",
|
||||
"Schedule Meeting": "Розклад зустрічей",
|
||||
"Schedule Call": "Розклад дзвінків",
|
||||
"Schedule Meeting": "Запланувати зустріч",
|
||||
"Schedule Call": "Запланувати дзвінок",
|
||||
"Compose Email": "Написати емейл",
|
||||
"Log Meeting": "Протокол зустрічей",
|
||||
"Log Call": "Протокол дзвінків",
|
||||
"Archive Email": "Архівувати листа",
|
||||
"Log Meeting": "Записати зустріч",
|
||||
"Log Call": "Записати дзвінок",
|
||||
"Archive Email": "Записати емейл",
|
||||
"Create Task": "Створити завдання",
|
||||
"Tasks": "Завдання"
|
||||
"Tasks": "Завдання"
|
||||
},
|
||||
"fields": {
|
||||
"billingAddressCity": "Місто",
|
||||
"billingAddressCountry": "Країна",
|
||||
"billingAddressPostalCode": "Поштовий індекс",
|
||||
"billingAddressState": "Реґіон",
|
||||
"billingAddressState": "Регіон",
|
||||
"billingAddressStreet": "Вулиця",
|
||||
"addressCity": "Місто",
|
||||
"addressStreet": "Вулиця",
|
||||
"addressCountry": "Країна",
|
||||
"addressState": "Реґіон",
|
||||
"addressPostalCode": "Поштовий індекс",
|
||||
"addressState": "Регіон",
|
||||
"addressPostalCode": "Поштовий індекс",
|
||||
"shippingAddressCity": "Місто доставки",
|
||||
"shippingAddressStreet": "Вулиця доставки",
|
||||
"shippingAddressCountry": "Країна доставки",
|
||||
@@ -76,16 +76,16 @@
|
||||
"links": {
|
||||
"contacts": "Контакти",
|
||||
"opportunities": "Угоди",
|
||||
"leads": "Ліди",
|
||||
"leads": "Ліди",
|
||||
"meetings": "Зустрічі",
|
||||
"calls": "Дзвінки",
|
||||
"tasks": "Завдання",
|
||||
"emails": "Листи",
|
||||
"accounts": "Контраґенти",
|
||||
"cases": "Кейси",
|
||||
"accounts": "Контрагенти",
|
||||
"cases": "Звернення",
|
||||
"documents": "Документи",
|
||||
"account": "Контраґент",
|
||||
"opportunity": "Угода",
|
||||
"account": "Контрагент",
|
||||
"opportunity": "Угода",
|
||||
"contact": "Контакт",
|
||||
"parent": "Джерело"
|
||||
},
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"fields": {
|
||||
"name": "Ім'я",
|
||||
"team": "Команда",
|
||||
"status": "статус",
|
||||
"status": "Статус",
|
||||
"assignToUser": "Зв'язатися з користувачем",
|
||||
"host": "Хост",
|
||||
"username": "Ім'я користувача",
|
||||
|
||||
@@ -10,15 +10,15 @@
|
||||
"title": "Посада",
|
||||
"website": "Вебсайт",
|
||||
"phoneNumber": "Телефон",
|
||||
"accountName": "Ім'я контраґента",
|
||||
"accountName": "Ім'я контрагента",
|
||||
"doNotCall": "Не дзвонити",
|
||||
"address": "Адреса",
|
||||
"status": "статус",
|
||||
"status": "Статус",
|
||||
"source": "Джерело",
|
||||
"opportunityAmount": "Сума угоди",
|
||||
"opportunityAmountConverted": "Сума угоди (конвертована)",
|
||||
"description": "Опис",
|
||||
"createdAccount": "Контраґент",
|
||||
"createdAccount": "Контрагент",
|
||||
"createdContact": "Контакт",
|
||||
"createdOpportunity": "Угода",
|
||||
"campaign": "Кампанія",
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"fields": {
|
||||
"name": "Ім'я",
|
||||
"parent": "Джерело",
|
||||
"status": "статус",
|
||||
"status": "Статус",
|
||||
"dateStart": "Дата початку",
|
||||
"dateEnd": "Дата закінчення",
|
||||
"duration": "Тривалість",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"fields": {
|
||||
"name": "Ім'я",
|
||||
"account": "Контраґент",
|
||||
"account": "Контрагент",
|
||||
"stage": "Стадія",
|
||||
"amount": "Сума",
|
||||
"probability": "Імовірність, %",
|
||||
@@ -20,7 +20,7 @@
|
||||
},
|
||||
"options": {
|
||||
"stage": {
|
||||
"Prospecting": "Проспектінґ",
|
||||
"Prospecting": "Проспектінг",
|
||||
"Qualification": "Кваліфікація",
|
||||
"Needs Analysis": "Вимагає аналізу",
|
||||
"Value Proposition": "Цінова пропозиція",
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"emailAddress": "Емейл",
|
||||
"title": "Посада",
|
||||
"website": "Вебсайт",
|
||||
"accountName": "Ім'я контраґента",
|
||||
"accountName": "Ім'я контрагента",
|
||||
"phoneNumber": "Телефон",
|
||||
"doNotCall": "Не дзвонити",
|
||||
"address": "Адреса",
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
"targetLists": "Цільові списки"
|
||||
},
|
||||
"links": {
|
||||
"accounts": "Контраґенти",
|
||||
"accounts": "Контрагенти",
|
||||
"contacts": "Контакти",
|
||||
"leads": "Ліди",
|
||||
"campaigns": "Кампанії"
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"fields": {
|
||||
"name": "Ім'я",
|
||||
"parent": "Джерело",
|
||||
"status": "статус",
|
||||
"status": "Статус",
|
||||
"dateStart": "Дата початку",
|
||||
"dateEnd": "Належна дата",
|
||||
"dateStartDate": "Date Start (all day)",
|
||||
@@ -10,7 +10,7 @@
|
||||
"priority": "Пріоритет",
|
||||
"description": "Опис",
|
||||
"isOverdue": "Прострочено",
|
||||
"account": "Контраґент",
|
||||
"account": "Контрагент",
|
||||
"dateCompleted": "Date Completed",
|
||||
"attachments": "Вкладення"
|
||||
},
|
||||
|
||||
@@ -3,14 +3,14 @@
|
||||
"label":false,
|
||||
"rows":[
|
||||
[{"name":"file"}, {"name":"source"}],
|
||||
[{"name":"name"}, {"name":"status"}],
|
||||
[{"name":"type"}, false]
|
||||
[{"name":"name"}, {"name":"folder"}]
|
||||
]
|
||||
},
|
||||
{
|
||||
"label": "Details",
|
||||
"rows": [
|
||||
[{"name":"publishDate"}, {"name":"expirationDate"}],
|
||||
[{"name":"type"}, {"name":"publishDate"}],
|
||||
[{"name":"status"}, {"name":"expirationDate"}],
|
||||
[{"name":"description", "fullWidth": true}]
|
||||
]
|
||||
}
|
||||
|
||||
@@ -5,7 +5,8 @@
|
||||
[{"name":"source"}],
|
||||
[{"name":"file"}],
|
||||
[{"name":"name"}],
|
||||
[{"name":"type"}],
|
||||
[{"name":"folder"}],
|
||||
[{"name":"type"}],
|
||||
[{"name":"status"}]
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
[
|
||||
"accounts",
|
||||
"createdAt",
|
||||
"expirationDate",
|
||||
"type",
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
[
|
||||
{
|
||||
"label": "",
|
||||
"rows": [
|
||||
[
|
||||
{
|
||||
"name": "name"
|
||||
},
|
||||
{
|
||||
"name": "parent"
|
||||
},
|
||||
{
|
||||
"name": "description",
|
||||
"fullWidth": true
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,17 @@
|
||||
[
|
||||
{
|
||||
"label": "",
|
||||
"rows": [
|
||||
[
|
||||
{
|
||||
"name": "name"
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"name": "parent"
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,3 @@
|
||||
[
|
||||
"parent"
|
||||
]
|
||||
@@ -0,0 +1,10 @@
|
||||
[
|
||||
{
|
||||
"name": "name",
|
||||
"width": 50,
|
||||
"link": true
|
||||
},
|
||||
{
|
||||
"name": "parent"
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,10 @@
|
||||
[
|
||||
{
|
||||
"name": "name",
|
||||
"width": 50,
|
||||
"link": true
|
||||
},
|
||||
{
|
||||
"name": "parent"
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,3 @@
|
||||
[
|
||||
|
||||
]
|
||||
@@ -0,0 +1,3 @@
|
||||
[
|
||||
"children"
|
||||
]
|
||||
@@ -8,5 +8,6 @@
|
||||
"source",
|
||||
"opportunityAmountConverted",
|
||||
"targetLists",
|
||||
"teams"
|
||||
"teams",
|
||||
"createdAccount"
|
||||
]
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
{"name":"name","width":25,"link":true},
|
||||
{"name":"account","width":18},
|
||||
{"name":"stage"},
|
||||
{"name":"amount"},
|
||||
{"name":"assignedUser"},
|
||||
{"name":"createdAt"}
|
||||
{"name":"createdAt"},
|
||||
{"name":"amount", "align": "right"}
|
||||
]
|
||||
@@ -1,6 +1,6 @@
|
||||
[
|
||||
{"name":"name", "width": 32, "link": true},
|
||||
{"name":"stage"},
|
||||
{"name":"amount"},
|
||||
{"name":"createdAt"}
|
||||
{"name":"name", "width": 35, "link": true},
|
||||
{"name":"stage", "width": 25},
|
||||
{"name":"createdAt"},
|
||||
{"name":"amount", "align": "right"}
|
||||
]
|
||||
@@ -4,18 +4,8 @@
|
||||
"detail":"Crm:Call.Detail"
|
||||
},
|
||||
"recordViews":{
|
||||
"list":"Crm:Call.Record.List"
|
||||
},
|
||||
"menu": {
|
||||
"detail": {
|
||||
"actions": [
|
||||
{
|
||||
"label": "Duplicate",
|
||||
"action": "duplicate",
|
||||
"acl": "edit"
|
||||
}
|
||||
]
|
||||
}
|
||||
"list":"Crm:Call.Record.List",
|
||||
"detail":"Crm:Meeting.Record.Detail"
|
||||
},
|
||||
"sidePanels":{
|
||||
"detail":[
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
"label": "Target Lists",
|
||||
"link": "#TargetList",
|
||||
"acl": "read",
|
||||
"style": "link",
|
||||
"style": "default",
|
||||
"aclScope": "TargetList"
|
||||
}
|
||||
]
|
||||
@@ -35,15 +35,7 @@
|
||||
}
|
||||
},
|
||||
"filterList": [
|
||||
{
|
||||
"name":"active",
|
||||
"data": {
|
||||
"status": {
|
||||
"type": "in",
|
||||
"value": ["Active"]
|
||||
}
|
||||
}
|
||||
}
|
||||
"active"
|
||||
],
|
||||
"boolFilterList": ["onlyMy"]
|
||||
}
|
||||
|
||||
@@ -1,24 +1,14 @@
|
||||
{
|
||||
"controller": "Controllers.Record",
|
||||
"filterList": [
|
||||
{
|
||||
"name":"active",
|
||||
"data": {
|
||||
"status": {
|
||||
"type": "in",
|
||||
"value": ["Active"]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name":"draft",
|
||||
"data": {
|
||||
"status": {
|
||||
"type": "in",
|
||||
"value": ["Draft"]
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"boolFilterList": ["onlyMy"]
|
||||
"controller": "Controllers.Record",
|
||||
"views": {
|
||||
"list": "Crm:Document.List"
|
||||
},
|
||||
"modalViews": {
|
||||
"select": "Crm:Document.Modals.SelectRecords"
|
||||
},
|
||||
"filterList": [
|
||||
"active",
|
||||
"draft"
|
||||
],
|
||||
"boolFilterList": ["onlyMy"]
|
||||
}
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"controller": "Controllers.RecordTree",
|
||||
"collection": "Collections.Tree",
|
||||
"menu": {
|
||||
"listTree": {
|
||||
"buttons": [
|
||||
{
|
||||
"label": "List View",
|
||||
"link": "#DocumentFolder\/list",
|
||||
"acl": "read",
|
||||
"style": "default"
|
||||
}
|
||||
]
|
||||
},
|
||||
"list": {
|
||||
"buttons": [
|
||||
{
|
||||
"label": "Tree View",
|
||||
"link": "#DocumentFolder",
|
||||
"acl": "read",
|
||||
"style": "default"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,18 +4,8 @@
|
||||
"detail":"Crm:Meeting.Detail"
|
||||
},
|
||||
"recordViews":{
|
||||
"list":"Crm:Meeting.Record.List"
|
||||
},
|
||||
"menu": {
|
||||
"detail": {
|
||||
"actions": [
|
||||
{
|
||||
"label": "Duplicate",
|
||||
"action": "duplicate",
|
||||
"acl": "edit"
|
||||
}
|
||||
]
|
||||
}
|
||||
"list":"Crm:Meeting.Record.List",
|
||||
"detail":"Crm:Meeting.Record.Detail"
|
||||
},
|
||||
"sidePanels":{
|
||||
"detail":[
|
||||
|
||||
@@ -1,4 +1,64 @@
|
||||
{
|
||||
"controller": "Controllers.Record",
|
||||
"boolFilterList": ["onlyMy"]
|
||||
"boolFilterList": ["onlyMy"],
|
||||
"sidePanels":{
|
||||
"detail":[
|
||||
{
|
||||
"name":"optedOut",
|
||||
"label":"Opted Out",
|
||||
"view":"Crm:TargetList.Record.Panels.OptedOut"
|
||||
}
|
||||
]
|
||||
},
|
||||
"relationshipPanels": {
|
||||
"contacts": {
|
||||
"actionList": [
|
||||
{
|
||||
"label": "Unlink All",
|
||||
"action": "unlinkAllRelated",
|
||||
"acl": "edit",
|
||||
"data": {
|
||||
"link": "contacts"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"leads": {
|
||||
"actionList": [
|
||||
{
|
||||
"label": "Unlink All",
|
||||
"action": "unlinkAllRelated",
|
||||
"acl": "edit",
|
||||
"data": {
|
||||
"link": "leads"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"accounts": {
|
||||
"actionList": [
|
||||
{
|
||||
"label": "Unlink All",
|
||||
"action": "unlinkAllRelated",
|
||||
"acl": "edit",
|
||||
"data": {
|
||||
"link": "accounts"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"users": {
|
||||
"create": false,
|
||||
"actionList": [
|
||||
{
|
||||
"label": "Unlink All",
|
||||
"action": "unlinkAllRelated",
|
||||
"acl": "edit",
|
||||
"data": {
|
||||
"link": "users"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
{
|
||||
"controller": "Controllers.Record",
|
||||
"recordViews":{
|
||||
"list":"Crm:Task.Record.List"
|
||||
"list": "Crm:Task.Record.List",
|
||||
"detail": "Crm:Task.Record.Detail"
|
||||
},
|
||||
"views": {
|
||||
"list": "Crm:Task.List",
|
||||
|
||||
@@ -52,73 +52,55 @@
|
||||
"type": "int",
|
||||
"notStorable": true,
|
||||
"readOnly": true,
|
||||
"layoutDetailDisabled": true,
|
||||
"layoutListDisabled": true,
|
||||
"layoutFiltersDisabled": true
|
||||
"disabled": true
|
||||
},
|
||||
"openedCount": {
|
||||
"type": "int",
|
||||
"notStorable": true,
|
||||
"readOnly": true,
|
||||
"layoutDetailDisabled": true,
|
||||
"layoutListDisabled": true,
|
||||
"layoutFiltersDisabled": true
|
||||
"disabled": true
|
||||
},
|
||||
"clickedCount": {
|
||||
"type": "int",
|
||||
"notStorable": true,
|
||||
"readOnly": true,
|
||||
"layoutDetailDisabled": true,
|
||||
"layoutListDisabled": true,
|
||||
"layoutFiltersDisabled": true
|
||||
"disabled": true
|
||||
},
|
||||
"optedOutCount": {
|
||||
"type": "int",
|
||||
"notStorable": true,
|
||||
"readOnly": true,
|
||||
"layoutDetailDisabled": true,
|
||||
"layoutListDisabled": true,
|
||||
"layoutFiltersDisabled": true
|
||||
"disabled": true
|
||||
},
|
||||
"bouncedCount": {
|
||||
"type": "int",
|
||||
"notStorable": true,
|
||||
"readOnly": true,
|
||||
"layoutDetailDisabled": true,
|
||||
"layoutListDisabled": true,
|
||||
"layoutFiltersDisabled": true
|
||||
"disabled": true
|
||||
},
|
||||
"hardBouncedCount": {
|
||||
"type": "int",
|
||||
"notStorable": true,
|
||||
"readOnly": true,
|
||||
"layoutDetailDisabled": true,
|
||||
"layoutListDisabled": true,
|
||||
"layoutFiltersDisabled": true
|
||||
"disabled": true
|
||||
},
|
||||
"softBouncedCount": {
|
||||
"type": "int",
|
||||
"notStorable": true,
|
||||
"readOnly": true,
|
||||
"layoutDetailDisabled": true,
|
||||
"layoutListDisabled": true,
|
||||
"layoutFiltersDisabled": true
|
||||
"disabled": true
|
||||
},
|
||||
"leadCreatedCount": {
|
||||
"type": "int",
|
||||
"notStorable": true,
|
||||
"readOnly": true,
|
||||
"layoutDetailDisabled": true,
|
||||
"layoutListDisabled": true,
|
||||
"layoutFiltersDisabled": true
|
||||
"disabled": true
|
||||
},
|
||||
"revenue": {
|
||||
"type": "currency",
|
||||
"notStorable": true,
|
||||
"readOnly": true,
|
||||
"layoutDetailDisabled": true,
|
||||
"layoutListDisabled": true,
|
||||
"layoutFiltersDisabled": true
|
||||
"disabled": true
|
||||
}
|
||||
},
|
||||
"links": {
|
||||
|
||||
@@ -131,8 +131,7 @@
|
||||
"layoutDetailDisabled": true,
|
||||
"layoutListDisabled": true,
|
||||
"layoutMassUpdateDisabled": true,
|
||||
"noLoad": true,
|
||||
"noSave": true
|
||||
"noLoad": true
|
||||
},
|
||||
"targetList": {
|
||||
"type": "link",
|
||||
|
||||
@@ -62,6 +62,18 @@
|
||||
},
|
||||
"teams": {
|
||||
"type": "linkMultiple"
|
||||
},
|
||||
"accounts": {
|
||||
"type": "linkMultiple",
|
||||
"layoutDetailDisabled": true,
|
||||
"layoutListDisabled": true,
|
||||
"layoutMassUpdateDisabled": true,
|
||||
"importDisabled": true,
|
||||
"noLoad": true
|
||||
},
|
||||
"folder": {
|
||||
"type": "link",
|
||||
"view": "Fields.LinkCategoryTree"
|
||||
}
|
||||
},
|
||||
"links": {
|
||||
@@ -92,6 +104,11 @@
|
||||
"entity": "Team",
|
||||
"relationName": "entityTeam",
|
||||
"layoutRelationshipsDisabled": true
|
||||
},
|
||||
"folder": {
|
||||
"type": "belongsTo",
|
||||
"foreign": "documents",
|
||||
"entity": "DocumentFolder"
|
||||
}
|
||||
},
|
||||
"collection": {
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
{
|
||||
"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
|
||||
},
|
||||
"teams": {
|
||||
"type": "linkMultiple"
|
||||
},
|
||||
"parent": {
|
||||
"type": "link"
|
||||
},
|
||||
"childList": {
|
||||
"type": "jsonArray",
|
||||
"notStorable": true
|
||||
}
|
||||
},
|
||||
"links": {
|
||||
"createdBy": {
|
||||
"type": "belongsTo",
|
||||
"entity": "User"
|
||||
},
|
||||
"modifiedBy": {
|
||||
"type": "belongsTo",
|
||||
"entity": "User"
|
||||
},
|
||||
"teams": {
|
||||
"type": "hasMany",
|
||||
"entity": "Team",
|
||||
"relationName": "entityTeam",
|
||||
"layoutRelationshipsDisabled": true
|
||||
},
|
||||
"parent": {
|
||||
"type": "belongsTo",
|
||||
"foreign": "children",
|
||||
"entity": "DocumentFolder"
|
||||
},
|
||||
"children": {
|
||||
"type": "hasMany",
|
||||
"foreign": "parent",
|
||||
"entity": "DocumentFolder"
|
||||
},
|
||||
"documents": {
|
||||
"type": "hasMany",
|
||||
"foreign": "folders",
|
||||
"entity": "Document"
|
||||
}
|
||||
},
|
||||
"collection": {
|
||||
"sortBy": "parent",
|
||||
"asc": true
|
||||
},
|
||||
"additionalTables": {
|
||||
"DocumentFolderPath": {
|
||||
"fields": {
|
||||
"id": {
|
||||
"type": "id",
|
||||
"dbType": "int",
|
||||
"len": "11",
|
||||
"autoincrement": true,
|
||||
"unique" : true
|
||||
},
|
||||
"ascendorId": {
|
||||
"type": "varchar",
|
||||
"len": "100",
|
||||
"index": true
|
||||
},
|
||||
"descendorId" : {
|
||||
"type": "varchar",
|
||||
"len": "24",
|
||||
"index": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -138,8 +138,7 @@
|
||||
"layoutListDisabled": true,
|
||||
"layoutMassUpdateDisabled": true,
|
||||
"importDisabled": true,
|
||||
"noLoad": true,
|
||||
"noSave": true
|
||||
"noLoad": true
|
||||
},
|
||||
"targetList": {
|
||||
"type": "link",
|
||||
|
||||
@@ -54,7 +54,8 @@
|
||||
},
|
||||
"leadSource": {
|
||||
"type": "varchar",
|
||||
"view": "Crm:Opportunity.Fields.LeadSource"
|
||||
"view": "Crm:Opportunity.Fields.LeadSource",
|
||||
"customizationDisabled": true
|
||||
},
|
||||
"closeDate": {
|
||||
"type": "date",
|
||||
|
||||
@@ -66,22 +66,42 @@
|
||||
"accounts": {
|
||||
"type": "hasMany",
|
||||
"entity": "Account",
|
||||
"foreign": "targetLists"
|
||||
"foreign": "targetLists",
|
||||
"additionalColumns": {
|
||||
"optedOut": {
|
||||
"type": "bool"
|
||||
}
|
||||
}
|
||||
},
|
||||
"contacts": {
|
||||
"type": "hasMany",
|
||||
"entity": "Contact",
|
||||
"foreign": "targetLists"
|
||||
"foreign": "targetLists",
|
||||
"additionalColumns": {
|
||||
"optedOut": {
|
||||
"type": "bool"
|
||||
}
|
||||
}
|
||||
},
|
||||
"leads": {
|
||||
"type": "hasMany",
|
||||
"entity": "Lead",
|
||||
"foreign": "targetLists"
|
||||
"foreign": "targetLists",
|
||||
"additionalColumns": {
|
||||
"optedOut": {
|
||||
"type": "bool"
|
||||
}
|
||||
}
|
||||
},
|
||||
"users": {
|
||||
"type": "hasMany",
|
||||
"entity": "User",
|
||||
"foreign": "targetLists"
|
||||
"foreign": "targetLists",
|
||||
"additionalColumns": {
|
||||
"optedOut": {
|
||||
"type": "bool"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"collection": {
|
||||
|
||||
@@ -47,7 +47,7 @@
|
||||
"readOnly": true,
|
||||
"notStorable": true,
|
||||
"view": "Crm:Task.Fields.IsOverdue",
|
||||
"layoutFiltersDisabled": true
|
||||
"disabled": true
|
||||
},
|
||||
"description": {
|
||||
"type": "text"
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"entity": true,
|
||||
"layouts": false,
|
||||
"tab": false,
|
||||
"acl": "recordAllTeamNo",
|
||||
"module": "Crm",
|
||||
"customizable": false,
|
||||
"importable": false,
|
||||
"type": "CategoryTree",
|
||||
"stream": false,
|
||||
"notifications": false
|
||||
}
|
||||
35
application/Espo/Modules/Crm/SelectManagers/Campaign.php
Normal file
35
application/Espo/Modules/Crm/SelectManagers/Campaign.php
Normal file
@@ -0,0 +1,35 @@
|
||||
<?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\SelectManagers;
|
||||
|
||||
class Campaign extends \Espo\Core\SelectManagers\Base
|
||||
{
|
||||
protected function filterActive(&$result)
|
||||
{
|
||||
$result['whereClause'][] = array(
|
||||
'status' => 'Active'
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
42
application/Espo/Modules/Crm/SelectManagers/Document.php
Normal file
42
application/Espo/Modules/Crm/SelectManagers/Document.php
Normal file
@@ -0,0 +1,42 @@
|
||||
<?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\SelectManagers;
|
||||
|
||||
class Document extends \Espo\Core\SelectManagers\Base
|
||||
{
|
||||
protected function filterActive(&$result)
|
||||
{
|
||||
$result['whereClause'][] = array(
|
||||
'status' => 'Active'
|
||||
);
|
||||
}
|
||||
|
||||
protected function filterDraft(&$result)
|
||||
{
|
||||
$result['whereClause'][] = array(
|
||||
'status' => 'Draft'
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -23,6 +23,9 @@
|
||||
namespace Espo\Modules\Crm\Services;
|
||||
|
||||
use \Espo\Core\Exceptions\Error;
|
||||
use \Espo\Core\Exceptions\NotFound;
|
||||
use \Espo\Core\Exceptions\Forbidden;
|
||||
|
||||
use \PDO;
|
||||
|
||||
class Activities extends \Espo\Core\Services\Base
|
||||
@@ -61,11 +64,72 @@ class Activities extends \Espo\Core\Services\Base
|
||||
|
||||
protected function isPerson($scope)
|
||||
{
|
||||
return in_array($scope, array('Contact', 'Lead', 'User'));
|
||||
return in_array($scope, ['Contact', 'Lead', 'User']);
|
||||
}
|
||||
|
||||
protected function getMeetingQuery($scope, $id, $op = 'IN', $notIn = array())
|
||||
protected function getUserMeetingQuery($id, $op, $notIn)
|
||||
{
|
||||
$sql = "
|
||||
SELECT meeting.id AS 'id', meeting.name AS 'name', meeting.date_start AS 'dateStart', meeting.date_end AS 'dateEnd', 'Meeting' AS '_scope',
|
||||
meeting.assigned_user_id AS assignedUserId, TRIM(CONCAT(assignedUser.first_name, ' ', assignedUser.last_name)) AS assignedUserName,
|
||||
meeting.parent_type AS 'parentType', meeting.parent_id AS 'parentId', meeting.status AS status, meeting.created_at AS createdAt
|
||||
FROM `meeting`
|
||||
LEFT JOIN `user` AS `assignedUser` ON assignedUser.id = meeting.assigned_user_id
|
||||
JOIN `meeting_user` AS `usersMiddle` ON usersMiddle.meeting_id = meeting.id AND usersMiddle.deleted = 0
|
||||
WHERE meeting.deleted = 0 AND usersMiddle.user_id = '".$this->getUser()->id."'
|
||||
";
|
||||
if (!empty($notIn)) {
|
||||
$sql .= "
|
||||
AND meeting.status {$op} ('". implode("', '", $notIn) . "')
|
||||
";
|
||||
}
|
||||
return $sql;
|
||||
}
|
||||
|
||||
protected function getUserCallQuery($id, $op, $notIn)
|
||||
{
|
||||
$sql = "
|
||||
SELECT call.id AS 'id', call.name AS 'name', call.date_start AS 'dateStart', call.date_end AS 'dateEnd', 'Call' AS '_scope',
|
||||
call.assigned_user_id AS assignedUserId, TRIM(CONCAT(assignedUser.first_name, ' ', assignedUser.last_name)) AS assignedUserName,
|
||||
call.parent_type AS 'parentType', call.parent_id AS 'parentId', call.status AS status, call.created_at AS createdAt
|
||||
FROM `call`
|
||||
LEFT JOIN `user` AS `assignedUser` ON assignedUser.id = call.assigned_user_id
|
||||
JOIN `call_user` AS `usersMiddle` ON usersMiddle.call_id = call.id AND usersMiddle.deleted = 0
|
||||
WHERE call.deleted = 0 AND usersMiddle.user_id = '".$this->getUser()->id."'
|
||||
";
|
||||
if (!empty($notIn)) {
|
||||
$sql .= "
|
||||
AND call.status {$op} ('". implode("', '", $notIn) . "')
|
||||
";
|
||||
}
|
||||
return $sql;
|
||||
}
|
||||
|
||||
protected function getUserEmailQuery($id, $op, $notIn)
|
||||
{
|
||||
$sql = "
|
||||
SELECT email.id AS 'id', email.name AS 'name', email.date_sent AS 'dateStart', '' AS 'dateEnd', 'Email' AS '_scope',
|
||||
email.assigned_user_id AS assignedUserId, TRIM(CONCAT(assignedUser.first_name, ' ', assignedUser.last_name)) AS assignedUserName,
|
||||
email.parent_type AS 'parentType', email.parent_id AS 'parentId', email.status AS status, email.created_at AS createdAt
|
||||
FROM `email`
|
||||
LEFT JOIN `user` AS `assignedUser` ON assignedUser.id = email.assigned_user_id
|
||||
JOIN `email_user` AS `usersMiddle` ON usersMiddle.email_id = email.id AND usersMiddle.deleted = 0
|
||||
WHERE email.deleted = 0 AND usersMiddle.user_id = '".$this->getUser()->id."'
|
||||
";
|
||||
if (!empty($notIn)) {
|
||||
$sql .= "
|
||||
AND email.status {$op} ('". implode("', '", $notIn) . "')
|
||||
";
|
||||
}
|
||||
return $sql;
|
||||
}
|
||||
|
||||
protected function getMeetingQuery($scope, $id, $op = 'IN', $notIn = [])
|
||||
{
|
||||
if ($scope == 'User') {
|
||||
return $this->getUserMeetingQuery($id, $op, $notIn);
|
||||
}
|
||||
|
||||
$baseSql = "
|
||||
SELECT meeting.id AS 'id', meeting.name AS 'name', meeting.date_start AS 'dateStart', meeting.date_end AS 'dateEnd', 'Meeting' AS '_scope',
|
||||
meeting.assigned_user_id AS assignedUserId, TRIM(CONCAT(user.first_name, ' ', user.last_name)) AS assignedUserName,
|
||||
@@ -143,8 +207,11 @@ class Activities extends \Espo\Core\Services\Base
|
||||
return $sql;
|
||||
}
|
||||
|
||||
protected function getCallQuery($scope, $id, $op = 'IN', $notIn = array())
|
||||
protected function getCallQuery($scope, $id, $op = 'IN', $notIn = [])
|
||||
{
|
||||
if ($scope == 'User') {
|
||||
return $this->getUserCallQuery($id, $op, $notIn);
|
||||
}
|
||||
$baseSql = "
|
||||
SELECT call.id AS 'id', call.name AS 'name', call.date_start AS 'dateStart', call.date_end AS 'dateEnd', 'Call' AS '_scope',
|
||||
call.assigned_user_id AS assignedUserId, TRIM(CONCAT(user.first_name, ' ', user.last_name)) AS assignedUserName,
|
||||
@@ -222,8 +289,11 @@ class Activities extends \Espo\Core\Services\Base
|
||||
return $sql;
|
||||
}
|
||||
|
||||
protected function getEmailQuery($scope, $id, $op = 'IN', $notIn = array())
|
||||
protected function getEmailQuery($scope, $id, $op = 'IN', $notIn = [])
|
||||
{
|
||||
if ($scope == 'User') {
|
||||
return $this->getUserEmailQuery($id, $op, $notIn);
|
||||
}
|
||||
$baseSql = "
|
||||
SELECT DISTINCT
|
||||
email.id AS 'id', email.name AS 'name', email.date_sent AS 'dateStart', '' AS 'dateEnd', 'Email' AS '_scope',
|
||||
@@ -379,26 +449,76 @@ class Activities extends \Espo\Core\Services\Base
|
||||
);
|
||||
}
|
||||
|
||||
public function getActivities($scope, $id, $params = array())
|
||||
protected function accessCheck($entity)
|
||||
{
|
||||
if ($entity->getEntityType() == 'User') {
|
||||
if ($this->getUser()->isAdmin()) {
|
||||
return;
|
||||
}
|
||||
$e = $this->getAcl()->get('userPermission');
|
||||
|
||||
if ($this->getAcl()->get('userPermission') === 'no') {
|
||||
if ($entity->id != $this->getUser()->id) {
|
||||
throw new Forbidden();
|
||||
}
|
||||
} else if ($this->getAcl()->get('userPermission') === 'team') {
|
||||
if ($entity->id != $this->getUser()->id) {
|
||||
if (!$this->getUser()->has('teamsIds')) {
|
||||
$this->getUser()->loadLinkMultipleField('teams');
|
||||
}
|
||||
$entity->loadLinkMultipleField('teams');
|
||||
$teamIdList1 = $this->getUser()->get('teamsIds');
|
||||
$teamIdList2 = $entity->get('teamsIds');
|
||||
|
||||
$inTeam = false;
|
||||
foreach ($teamIdList1 as $id) {
|
||||
if (in_array($id, $teamIdList2)) {
|
||||
$inTeam = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!$inTeam) {
|
||||
throw new Forbidden();
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!$this->getAcl()->check($entity, 'read')) {
|
||||
throw new Forbidden();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function getActivities($scope, $id, $params = [])
|
||||
{
|
||||
$entity = $this->getEntityManager()->getEntity($scope, $id);
|
||||
if (!$entity) {
|
||||
throw new NotFound();
|
||||
}
|
||||
|
||||
$this->accessCheck($entity);
|
||||
|
||||
$fetchAll = empty($params['scope']);
|
||||
|
||||
$parts = array(
|
||||
'Meeting' => ($fetchAll || $params['scope'] == 'Meeting') ? $this->getMeetingQuery($scope, $id, 'NOT IN', array('Held', 'Not Held')) : array(),
|
||||
'Call' => ($fetchAll || $params['scope'] == 'Call') ? $this->getCallQuery($scope, $id, 'NOT IN', array('Held', 'Not Held')) : array(),
|
||||
'Meeting' => ($fetchAll || $params['scope'] == 'Meeting') ? $this->getMeetingQuery($scope, $id, 'NOT IN', ['Held', 'Not Held']) : [],
|
||||
'Call' => ($fetchAll || $params['scope'] == 'Call') ? $this->getCallQuery($scope, $id, 'NOT IN', ['Held', 'Not Held']) : [],
|
||||
);
|
||||
return $this->getResult($parts, $scope, $id, $params);
|
||||
}
|
||||
|
||||
public function getHistory($scope, $id, $params)
|
||||
{
|
||||
$entity = $this->getEntityManager()->getEntity($scope, $id);
|
||||
|
||||
$this->accessCheck($entity);
|
||||
|
||||
$fetchAll = empty($params['scope']);
|
||||
|
||||
$parts = array(
|
||||
'Meeting' => ($fetchAll || $params['scope'] == 'Meeting') ? $this->getMeetingQuery($scope, $id, 'IN', array('Held', 'Not Held')) : array(),
|
||||
'Call' => ($fetchAll || $params['scope'] == 'Call') ? $this->getCallQuery($scope, $id, 'IN', array('Held', 'Not Held')) : array(),
|
||||
'Email' => ($fetchAll || $params['scope'] == 'Email') ? $this->getEmailQuery($scope, $id, 'IN', array('Archived', 'Sent')) : array(),
|
||||
'Meeting' => ($fetchAll || $params['scope'] == 'Meeting') ? $this->getMeetingQuery($scope, $id, 'IN', ['Held', 'Not Held']) : [],
|
||||
'Call' => ($fetchAll || $params['scope'] == 'Call') ? $this->getCallQuery($scope, $id, 'IN', ['Held', 'Not Held']) : [],
|
||||
'Email' => ($fetchAll || $params['scope'] == 'Email') ? $this->getEmailQuery($scope, $id, 'IN', ['Archived', 'Sent']) : [],
|
||||
);
|
||||
$result = $this->getResult($parts, $scope, $id, $params);
|
||||
|
||||
|
||||
@@ -33,5 +33,20 @@ class CaseObj extends \Espo\Services\Record
|
||||
'emails'
|
||||
);
|
||||
|
||||
public function afterCreate($entity, array $data)
|
||||
{
|
||||
parent::afterCreate($entity, $data);
|
||||
if (!empty($data['emailId'])) {
|
||||
$email = $this->getEntityManager()->getEntity('Email', $data['emailId']);
|
||||
if ($email && !$email->get('parentId')) {
|
||||
$email->set(array(
|
||||
'parentType' => 'Case',
|
||||
'parentId' => $entity->id
|
||||
));
|
||||
$this->getEntityManager()->saveEntity($email);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -47,5 +47,116 @@ class TargetList extends \Espo\Services\Record
|
||||
$count += $this->getEntityManager()->getRepository('TargetList')->countRelated($entity, 'accounts');
|
||||
$entity->set('entryCount', $count);
|
||||
}
|
||||
|
||||
public function unlinkAll($id, $link)
|
||||
{
|
||||
$entity = $this->getRepository()->get($id);
|
||||
|
||||
$foreignEntityType = $entity->relations[$link]['entity'];
|
||||
|
||||
if (!$this->getAcl()->check($entity, 'edit')) {
|
||||
throw new Forbidden();
|
||||
}
|
||||
|
||||
if (empty($foreignEntityType)) {
|
||||
throw new Error();
|
||||
}
|
||||
|
||||
$pdo = $this->getEntityManager()->getPDO();
|
||||
$query = $this->getEntityManager()->getQuery();
|
||||
$sql = null;
|
||||
|
||||
switch ($link) {
|
||||
case 'contacts':
|
||||
$sql = "UPDATE contact_target_list SET deleted = 1 WHERE target_list_id = " . $query->quote($entity->id);
|
||||
break;
|
||||
case 'leads':
|
||||
$sql = "UPDATE lead_target_list SET deleted = 1 WHERE target_list_id = " . $query->quote($entity->id);
|
||||
break;
|
||||
case 'accounts':
|
||||
$sql = "UPDATE account_target_list SET deleted = 1 WHERE target_list_id = " . $query->quote($entity->id);
|
||||
break;
|
||||
case 'users':
|
||||
$sql = "UPDATE target_list_user SET deleted = 1 WHERE target_list_id = " . $query->quote($entity->id);
|
||||
break;
|
||||
}
|
||||
if ($sql) {
|
||||
if ($pdo->query($sql)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function findLinkedEntitiesOptedOut($id, $link, $params)
|
||||
{
|
||||
$collection = new \Espo\ORM\EntityCollection;
|
||||
|
||||
$pdo = $this->getEntityManager()->getPDO();
|
||||
$query = $this->getEntityManager()->getQuery();
|
||||
|
||||
$sqlContact = $query->createSelectQuery('Contact', array(
|
||||
'select' => ['id', 'name', 'createdAt', ['VALUE:Contact', '_scope']],
|
||||
'customJoin' => 'JOIN contact_target_list AS j ON j.contact_id = contact.id AND j.deleted = 0 AND j.opted_out = 1',
|
||||
'whereClause' => array(
|
||||
'j.targetListId' => $id
|
||||
)
|
||||
));
|
||||
|
||||
$sqlLead = $query->createSelectQuery('Lead', array(
|
||||
'select' => ['id', 'name', 'createdAt', ['VALUE:Lead', '_scope']],
|
||||
'customJoin' => 'JOIN lead_target_list AS j ON j.lead_id = lead.id AND j.deleted = 0 AND j.opted_out = 1',
|
||||
'whereClause' => array(
|
||||
'j.targetListId' => $id
|
||||
)
|
||||
));
|
||||
|
||||
$sqlUser = $query->createSelectQuery('User', array(
|
||||
'select' => ['id', 'name', 'createdAt', ['VALUE:User', '_scope']],
|
||||
'customJoin' => 'JOIN target_list_user AS j ON j.user_id = user.id AND j.deleted = 0 AND j.opted_out = 1',
|
||||
'whereClause' => array(
|
||||
'j.targetListId' => $id
|
||||
)
|
||||
));
|
||||
|
||||
$sqlAccount = $query->createSelectQuery('Account', array(
|
||||
'select' => ['id', 'name', 'createdAt', ['VALUE:Account', '_scope']],
|
||||
'customJoin' => 'JOIN account_target_list AS j ON j.account_id = account.id AND j.deleted = 0 AND j.opted_out = 1',
|
||||
'whereClause' => array(
|
||||
'j.targetListId' => $id
|
||||
)
|
||||
));
|
||||
|
||||
$sql = "
|
||||
{$sqlContact}
|
||||
UNION
|
||||
{$sqlLead}
|
||||
UNION
|
||||
{$sqlUser}
|
||||
UNION
|
||||
{$sqlAccount}
|
||||
ORDER BY createdAt DESC
|
||||
";
|
||||
|
||||
$sql = $query->limit($sql, $params['offset'], $params['maxSize']);
|
||||
|
||||
$sth = $pdo->prepare($sql);
|
||||
$sth->execute();
|
||||
$arr = [];
|
||||
while ($row = $sth->fetch(\PDO::FETCH_ASSOC)) {
|
||||
$arr[] = $row;
|
||||
}
|
||||
|
||||
$sqlCount = "SELECT COUNT(*) AS 'count' FROM ({$sql}) AS c";
|
||||
$sth = $pdo->prepare($sqlCount);
|
||||
$sth->execute();
|
||||
|
||||
$row = $sth->fetch(\PDO::FETCH_ASSOC);
|
||||
$count = $row['count'];
|
||||
|
||||
return array(
|
||||
'total' => $count,
|
||||
'list' => $arr
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -60,7 +60,7 @@ class Email extends \Espo\Core\Notificators\Base
|
||||
|
||||
$userIdList = [];
|
||||
foreach ($emailUserIdList as $userId) {
|
||||
if (!in_array($userId, $userIdList) && $userId != $this->getUser()->id) {
|
||||
if (!in_array($userId, $userIdList) && !in_array($userId, $previousUserIdList) && $userId != $this->getUser()->id) {
|
||||
$userIdList[] = $userId;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -251,9 +251,24 @@ abstract class Base
|
||||
$fieldList = array_keys($entity->fields);
|
||||
} else {
|
||||
$fieldList = $fields;
|
||||
foreach ($fieldList as $i => $field) {
|
||||
if (!is_array($field)) {
|
||||
$fieldList[$i] = $this->sanitizeAlias($field);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($fieldList as $field) {
|
||||
if (is_array($field) && count($field) == 2) {
|
||||
if (stripos($field[0], 'VALUE:') === 0) {
|
||||
$part = substr($field[0], 6);
|
||||
$part = $this->quote($part);
|
||||
} else {
|
||||
$part = $this->convertComplexExpression($entity, $field[0], $distinct);
|
||||
}
|
||||
$arr[] = $part . ' AS `' . $this->sanitizeAlias($field[1]) . '`';
|
||||
continue;
|
||||
}
|
||||
if (array_key_exists($field, $entity->fields)) {
|
||||
$fieldDefs = $entity->fields[$field];
|
||||
} else {
|
||||
@@ -359,12 +374,20 @@ abstract class Base
|
||||
if (strpos($orderBy, 'LIST:') === 0) {
|
||||
list($l, $field, $list) = explode(':', $orderBy);
|
||||
$fieldPath = $this->getFieldPathForOrderBy($entity, $field);
|
||||
return "FIELD(" . $fieldPath . ", '" . implode("', '", explode(",", $list)) . "')";
|
||||
$part = "FIELD(" . $fieldPath . ", '" . implode("', '", explode(",", $list)) . "')";
|
||||
if (!is_null($order)) {
|
||||
$order = strtoupper($order);
|
||||
if (!in_array($order, ['ASC', 'DESC'])) {
|
||||
$order = 'ASC';
|
||||
}
|
||||
$part .= " " . $order;
|
||||
}
|
||||
return $part;
|
||||
}
|
||||
|
||||
if (!is_null($order)) {
|
||||
$order = strtoupper($order);
|
||||
if (!in_array($order, array('ASC', 'DESC'))) {
|
||||
if (!in_array($order, ['ASC', 'DESC'])) {
|
||||
$order = 'ASC';
|
||||
}
|
||||
} else {
|
||||
@@ -643,6 +666,11 @@ abstract class Base
|
||||
return preg_replace('/[^A-Za-z0-9_]+/', '', $string);
|
||||
}
|
||||
|
||||
public function sanitizeAlias($string)
|
||||
{
|
||||
return preg_replace('/[^A-Za-z0-9_:.]+/', '', $string);
|
||||
}
|
||||
|
||||
protected function getJoins(IEntity $entity, array $joins, $left = false, $joinConditions = array())
|
||||
{
|
||||
$joinsArr = array();
|
||||
@@ -679,6 +707,8 @@ abstract class Base
|
||||
|
||||
$alias = $this->sanitize($relationName);
|
||||
|
||||
$midAlias = $alias . 'Middle';
|
||||
|
||||
$join =
|
||||
"{$pre}JOIN `{$relTable}` AS `{$midAlias}` ON {$this->toDb($entity->getEntityType())}." . $this->toDb($key) . " = {$midAlias}." . $this->toDb($nearKey)
|
||||
. " AND "
|
||||
|
||||
@@ -329,14 +329,12 @@ class RDB extends \Espo\ORM\Repository
|
||||
return $this->getMapper()->sum($this->seed, $params, $field);
|
||||
}
|
||||
|
||||
// @TODO use abstract class for list params
|
||||
// @TODO join conditions
|
||||
public function join()
|
||||
{
|
||||
$args = func_get_args();
|
||||
|
||||
if (empty($this->listParams['joins'])) {
|
||||
$this->listParams['joins'] = array();
|
||||
$this->listParams['joins'] = [];
|
||||
}
|
||||
|
||||
foreach ($args as &$param) {
|
||||
@@ -352,6 +350,27 @@ class RDB extends \Espo\ORM\Repository
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function leftJoin()
|
||||
{
|
||||
$args = func_get_args();
|
||||
|
||||
if (empty($this->listParams['leftJoins'])) {
|
||||
$this->listParams['leftJoins'] = [];
|
||||
}
|
||||
|
||||
foreach ($args as &$param) {
|
||||
if (is_array($param)) {
|
||||
foreach ($param as $k => $v) {
|
||||
$this->listParams['leftJoins'][] = $v;
|
||||
}
|
||||
} else {
|
||||
$this->listParams['leftJoins'][] = $param;
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function distinct()
|
||||
{
|
||||
$this->listParams['distinct'] = true;
|
||||
|
||||
@@ -26,12 +26,10 @@ use Espo\ORM\Entity;
|
||||
|
||||
class Import extends \Espo\Core\ORM\Repositories\RDB
|
||||
{
|
||||
public function findRelated(Entity $entity, $link, $selectParams)
|
||||
public function findRelated(Entity $entity, $link, $selectParams = array())
|
||||
{
|
||||
$entityType = $entity->get('entityType');
|
||||
|
||||
|
||||
|
||||
$selectParams['customJoin'] .= $this->getRelatedJoin($entity, $link);
|
||||
|
||||
return $this->getEntityManager()->getRepository($entityType)->find($selectParams);
|
||||
@@ -68,17 +66,16 @@ class Import extends \Espo\Core\ORM\Repositories\RDB
|
||||
return $sql;
|
||||
}
|
||||
|
||||
public function countRelated(Entity $entity, $link, $selectParams)
|
||||
public function countRelated(Entity $entity, $link, $selectParams = array())
|
||||
{
|
||||
$entityType = $entity->get('entityType');
|
||||
|
||||
|
||||
$selectParams['customJoin'] .= $this->getRelatedJoin($entity, $link);
|
||||
|
||||
return $this->getEntityManager()->getRepository($entityType)->count($selectParams);
|
||||
}
|
||||
|
||||
protected function afterRemove(Entity $entity, array $options)
|
||||
protected function afterRemove(Entity $entity, array $options = array())
|
||||
{
|
||||
if ($entity->get('fileId')) {
|
||||
$attachment = $this->getEntityManager()->getEntity('Attachment', $entity->get('fileId'));
|
||||
|
||||
@@ -151,10 +151,5 @@
|
||||
"medium": "Medium",
|
||||
"large": "Large"
|
||||
}
|
||||
},
|
||||
"layoutManagerDataAttributes": {
|
||||
"width": "Width (%)",
|
||||
"link": "Link",
|
||||
"notSortable": "Not Sortable"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,6 +53,7 @@
|
||||
"Posted": "Posted",
|
||||
"Linked": "Linked",
|
||||
"Unlinked": "Unlinked",
|
||||
"Done": "Done",
|
||||
"Access denied": "Access denied",
|
||||
"Access": "Access",
|
||||
"Are you sure?": "Are you sure?",
|
||||
@@ -101,6 +102,7 @@
|
||||
"PostalCode": "Postal Code",
|
||||
"Followed": "Followed",
|
||||
"Follow": "Follow",
|
||||
"Followers": "Followers",
|
||||
"Clear Local Cache": "Clear Local Cache",
|
||||
"Actions": "Actions",
|
||||
"Delete": "Delete",
|
||||
@@ -109,6 +111,7 @@
|
||||
"Edit": "Edit",
|
||||
"View": "View",
|
||||
"Cancel": "Cancel",
|
||||
"Apply": "Apply",
|
||||
"Unlink": "Unlink",
|
||||
"Mass Update": "Mass Update",
|
||||
"Export": "Export",
|
||||
@@ -152,7 +155,9 @@
|
||||
"Value": "Value",
|
||||
"Current version": "Current version",
|
||||
"List View": "List View",
|
||||
"Tree View": "Tree View"
|
||||
"Tree View": "Tree View",
|
||||
"Unlink All": "Unlink All",
|
||||
"Total": "Total"
|
||||
},
|
||||
"messages": {
|
||||
"pleaseWait": "Please wait...",
|
||||
@@ -175,9 +180,10 @@
|
||||
"assignmentEmailNotificationSubject": "EspoCRM {entityType}: {Entity.name}",
|
||||
"assignmentEmailNotificationBody": "{assignerUserName} has assigned {entityType} '{Entity.name}' to you.\n\n{recordUrl}",
|
||||
"confirmation": "Are you sure?",
|
||||
"unlinkAllConfirmation": "Are you sure you want to unlink all related records?",
|
||||
"resetPreferencesConfirmation": "Are you sure you want to reset preferences to defaults?",
|
||||
"removeRecordConfirmation": "Are you sure you want to remove the record?",
|
||||
"unlinkRecordConfirmation": "Are you sure you want to unlink relationship?",
|
||||
"unlinkRecordConfirmation": "Are you sure you want to unlink the related record?",
|
||||
"removeSelectedRecordsConfirmation": "Are you sure you want to remove selected records?",
|
||||
"massUpdateResult": "{count} records have been updated",
|
||||
"massUpdateResultSingle": "{count} record has been updated",
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
"targetUserPosition": "Target User Position"
|
||||
},
|
||||
"tooltips": {
|
||||
"reply": "Notify email senders that their emails has been received.",
|
||||
"reply": "Notify email senders that their emails has been received.\n\n Only one email will be sent to a particular recipient during some period of time to prevent looping.",
|
||||
"createCase": "Automatically create case from incoming emails.",
|
||||
"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.",
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user