Compare commits
273 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 0daeed49c1 | |||
| 34540e12f3 | |||
| b954a81630 | |||
| 56472d6746 | |||
| 2721649313 | |||
| 24d7de60ab | |||
| 441f9296ad | |||
| 3116a152e7 | |||
| 77784c4224 | |||
| bf13c7ca8c | |||
| 26c779a2c5 | |||
| 1bfaf6c3ad | |||
| 69229ff6af | |||
| 719164524b | |||
| 5a67b17916 | |||
| db4968b1ca | |||
| e50f46e38a | |||
| adc8988f6c | |||
| a480ad9c11 | |||
| d9be685f89 | |||
| bd9db140de | |||
| 74c0204590 | |||
| ce9d3299b1 | |||
| 8b27bc194d | |||
| 1a6b4a6683 | |||
| 2ae4d6e090 | |||
| 9467171c84 | |||
| 402db729b7 | |||
| e16be12438 | |||
| 605912443d | |||
| 62acc14f4a | |||
| 017066a311 | |||
| 273b122ca8 | |||
| ce2ec3dd6c | |||
| fda22605cc | |||
| 1076fa6230 | |||
| 019af59c8a | |||
| 08bb644b58 | |||
| 6ec1273e61 | |||
| ba5df9a4ae | |||
| 050d77ef12 | |||
| f8ccaeab08 | |||
| 3c4c464e4a | |||
| 2a5730def9 | |||
| 48fe3e6b86 | |||
| eecbc3aa2c | |||
| e177f14446 | |||
| 493ab0396a | |||
| c776150ce0 | |||
| b6bca982bb | |||
| aa8a243bf1 | |||
| 75da037656 | |||
| 56a06a5581 | |||
| 0af67bea3a | |||
| 08b8d899b7 | |||
| cf07cb39c4 | |||
| 810d10c1c2 | |||
| 342bdcfaac | |||
| d933dbb31a | |||
| fe61364178 | |||
| 88c3c03f99 | |||
| c21b16f5c7 | |||
| 2481a2504f | |||
| dcb1e8040a | |||
| 0e1c611034 | |||
| fccd9d642c | |||
| 849f1becc7 | |||
| be6c4a63ed | |||
| 4e48483276 | |||
| 6817a54aa0 | |||
| 482f235dcc | |||
| 90b863c4ae | |||
| 8541a87d1f | |||
| 002adeb00e | |||
| 30e7d1ba7c | |||
| 0923f2e120 | |||
| 38a0c8e19f | |||
| facf90d964 | |||
| cca3482bc0 | |||
| 6178ca550b | |||
| dc51e467d4 | |||
| 300827c1f0 | |||
| aacf09a444 | |||
| 0123488828 | |||
| a43b188f36 | |||
| f0285ef8f2 | |||
| 70a0ffbf3d | |||
| f2e6a4fc55 | |||
| df5fd76ae2 | |||
| f34d258b45 | |||
| 28c52763c8 | |||
| ebe22fa7f0 | |||
| 160327e471 | |||
| dd080dbff2 | |||
| d509886cd1 | |||
| 239a8f0af8 | |||
| c0565306c3 | |||
| 79d159511d | |||
| e123fc7e6f | |||
| 52fa30f389 | |||
| 1d744d5abc | |||
| 2629c12ade | |||
| 8fb6b1a5a9 | |||
| d4a23c9a4f | |||
| 05d0f982a7 | |||
| e3c8a3fcda | |||
| 8cd56d2705 | |||
| f94d1f3cd8 | |||
| 5f64b74fad | |||
| b422cd6f52 | |||
| 17698409b9 | |||
| fb0dabebe1 | |||
| f81805fbf4 | |||
| 596c9d4996 | |||
| a7530dcce9 | |||
| 55d59eea80 | |||
| 0c66a1fb7b | |||
| ad7ca5d287 | |||
| b34be4730d | |||
| 1c264055eb | |||
| 12f41baff5 | |||
| 9a9ad1baa7 | |||
| 46c31278e7 | |||
| a08dc3d422 | |||
| 158a74f79b | |||
| bacf85ccf8 | |||
| ef683bd7fa | |||
| 20c233fb8c | |||
| ac21313190 | |||
| 8ba6918139 | |||
| 5e357b460d | |||
| d660abe1f6 | |||
| fd9617ec8f | |||
| 48ccdaab5e | |||
| 52d9548333 | |||
| 4706d28560 | |||
| 033f5de85d | |||
| 6760f6f03d | |||
| e1a7862194 | |||
| 8ef53d229d | |||
| c6d215f87c | |||
| dccaed124c | |||
| ccfca4a61a | |||
| 417a05f24f | |||
| 7a85f7576d | |||
| 8407ab3a74 | |||
| c120ebd3ca | |||
| d0635484a2 | |||
| d3c487a28a | |||
| 14f46a5d5d | |||
| 00a8ca8273 | |||
| deec457728 | |||
| 5a5508a973 | |||
| 357b285346 | |||
| f8444b9e67 | |||
| e0a224e414 | |||
| 9dfb128e1d | |||
| 462d1f0274 | |||
| 2e3803eef1 | |||
| 42b186eb94 | |||
| ccdee9ec09 | |||
| 05eeb82efd | |||
| 6eefe64f4c | |||
| 5b96b19735 | |||
| 3764d877ce | |||
| 30e713c161 | |||
| da3a76e441 | |||
| adff467b50 | |||
| d59f05ad8b | |||
| 9c3d124f48 | |||
| 4404b0f02e | |||
| 77533fba72 | |||
| 62d36ed0c2 | |||
| 94ad0123c7 | |||
| 109842f0fe | |||
| d931d65cd8 | |||
| b7861a7cb1 | |||
| a49ad3bb48 | |||
| d94acf5b78 | |||
| fd0e1d1460 | |||
| 2ed9d5487e | |||
| 5dc44cb701 | |||
| 9e07c383fe | |||
| 9e1ec2768b | |||
| 39674e8c7f | |||
| 5dca4b7061 | |||
| 5d3cb25d14 | |||
| 6e337affbb | |||
| 87ab8cc15f | |||
| b654e5cebd | |||
| 11958f2abf | |||
| 2a329ba92d | |||
| b22487ef70 | |||
| bf03a2f540 | |||
| ece51a6f9f | |||
| 915dbffe72 | |||
| fda0d6df1f | |||
| 25891f9dc0 | |||
| 35902adbdc | |||
| 981d15ee55 | |||
| adfec3441b | |||
| a208aa051f | |||
| 9068302357 | |||
| 1bb0439f19 | |||
| f28585e245 | |||
| 99cfeb25ee | |||
| 6b7da9a39e | |||
| 18bba7baec | |||
| ec8ffbc090 | |||
| 60eb1ca236 | |||
| 5e3756313c | |||
| d303d5a423 | |||
| 938415aa79 | |||
| 9033710e56 | |||
| 2fd77e4034 | |||
| b733d8205e | |||
| f725ad09d7 | |||
| e9a53c5b25 | |||
| 43e94cfe68 | |||
| 07d713734f | |||
| 4f004b8402 | |||
| b1f4131af9 | |||
| 1adb19f427 | |||
| fce4a4b1d9 | |||
| 61f35bcb5e | |||
| 0db68917a1 | |||
| 9da8ab9bdb | |||
| 8d1724df84 | |||
| cd88d59937 | |||
| cab4f5ce64 | |||
| 62ab1f8d00 | |||
| f0e1439c79 | |||
| 5060918087 | |||
| bf6b058152 | |||
| 40b607a73f | |||
| 2bdbba0eff | |||
| 058d821a7c | |||
| 4575ff89ad | |||
| 608454fcf2 | |||
| 58c1bec2da | |||
| e1ce16c399 | |||
| 2d3a748286 | |||
| 06834dd8bf | |||
| e4c0f19b1c | |||
| 84faf7a782 | |||
| 61db675ab1 | |||
| 59e72796e3 | |||
| 76e61a6358 | |||
| 455eceefa1 | |||
| e8c171be92 | |||
| 2c8094bf28 | |||
| 2b5eed3a38 | |||
| aa3d2f8787 | |||
| d0cdefd972 | |||
| 1b5346e86d | |||
| 6deb336115 | |||
| 115ae82b17 | |||
| 16579ff1a8 | |||
| a030c50780 | |||
| e60d46b2ec | |||
| 28c4651599 | |||
| 6b458f3b55 | |||
| 874885671b | |||
| a75f4dacab | |||
| 1b12cd46b2 | |||
| 7389acd87f | |||
| a290f7aba9 | |||
| e9c0cd6d53 | |||
| 7d847cf179 | |||
| 74093976db | |||
| 004dfc1687 | |||
| e429bde5b8 | |||
| 3d8bbdd01d |
@@ -9,7 +9,7 @@ Download the latest release from our [website](http://www.espocrm.com).
|
||||
### Requirements
|
||||
|
||||
* PHP 5.6 or above (with pdo, json, gd, openssl, zip, imap, mbstring, curl extensions);
|
||||
* MySQL 5.1 or above.
|
||||
* MySQL 5.5.3 or above.
|
||||
|
||||
For more information about server configuration see [this article](https://www.espocrm.com/documentation/administration/server-configuration/).
|
||||
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM - Open Source CRM application.
|
||||
* Copyright (C) 2014-2018 Yuri Kuznetsov, Taras Machyshyn, Oleksiy Avramenko
|
||||
* Website: http://www.espocrm.com
|
||||
*
|
||||
* EspoCRM is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* EspoCRM is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Acl;
|
||||
|
||||
use \Espo\Entities\User as EntityUser;
|
||||
use \Espo\ORM\Entity;
|
||||
|
||||
class Note extends \Espo\Core\Acl\Base
|
||||
{
|
||||
public function checkIsOwner(EntityUser $user, Entity $entity)
|
||||
{
|
||||
if ($entity->get('type') === 'Post' && $user->id === $entity->get('createdById')) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,11 +39,22 @@ class User extends \Espo\Core\Acl\Base
|
||||
return $user->id === $entity->id;
|
||||
}
|
||||
|
||||
public function checkEntityCreate(EntityUser $user, Entity $entity, $data)
|
||||
{
|
||||
if (!$user->isAdmin()) {
|
||||
return false;
|
||||
}
|
||||
return $this->checkEntity($user, $entity, $data, 'create');
|
||||
}
|
||||
|
||||
public function checkEntityDelete(EntityUser $user, Entity $entity, $data)
|
||||
{
|
||||
if ($entity->id === 'system') {
|
||||
return false;
|
||||
}
|
||||
if (!$user->isAdmin()) {
|
||||
return false;
|
||||
}
|
||||
return parent::checkEntityDelete($user, $entity, $data);
|
||||
}
|
||||
|
||||
@@ -52,6 +63,11 @@ class User extends \Espo\Core\Acl\Base
|
||||
if ($entity->id === 'system') {
|
||||
return false;
|
||||
}
|
||||
if (!$user->isAdmin()) {
|
||||
if ($user->id !== $entity->id) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return $this->checkEntity($user, $entity, $data, 'edit');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM - Open Source CRM application.
|
||||
* Copyright (C) 2014-2018 Yuri Kuznetsov, Taras Machyshyn, Oleksiy Avramenko
|
||||
* Website: http://www.espocrm.com
|
||||
*
|
||||
* EspoCRM is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* EspoCRM is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\AclPortal;
|
||||
|
||||
use \Espo\Entities\User as EntityUser;
|
||||
use \Espo\ORM\Entity;
|
||||
|
||||
class Note extends \Espo\Core\AclPortal\Base
|
||||
{
|
||||
public function checkIsOwner(EntityUser $user, Entity $entity)
|
||||
{
|
||||
if ($entity->get('type') === 'Post' && $user->id === $entity->get('createdById')) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -245,7 +245,9 @@ class EntityManager extends \Espo\Core\Controllers\Base
|
||||
|
||||
$params = array();
|
||||
foreach ($paramList as $item) {
|
||||
$params[$item] = filter_var($data[$item], \FILTER_SANITIZE_STRING);
|
||||
if (array_key_exists($item, $data)) {
|
||||
$params[$item] = filter_var($data[$item], \FILTER_SANITIZE_STRING);
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($additionalParamList as $item) {
|
||||
|
||||
@@ -77,6 +77,11 @@ class FieldManager extends \Espo\Core\Controllers\Base
|
||||
return $fieldManager->read($params['scope'], $data->name);
|
||||
}
|
||||
|
||||
public function patchActionUpdate($params, $data)
|
||||
{
|
||||
return $this->putActionUpdate($params, $data);
|
||||
}
|
||||
|
||||
public function putActionUpdate($params, $data)
|
||||
{
|
||||
if (empty($params['scope']) || empty($params['name'])) {
|
||||
|
||||
@@ -90,11 +90,9 @@ class Settings extends \Espo\Core\Controllers\Base
|
||||
throw new Error('Cannot save settings');
|
||||
}
|
||||
|
||||
/** Rebuild for Currency Settings */
|
||||
if (isset($data->baseCurrency) || isset($data->currencyRates)) {
|
||||
if (isset($data->defaultCurrency) || isset($data->baseCurrency) || isset($data->currencyRates)) {
|
||||
$this->getContainer()->get('dataManager')->rebuildDatabase([]);
|
||||
}
|
||||
/** END Rebuild for Currency Settings */
|
||||
|
||||
return $this->getConfigData();
|
||||
}
|
||||
|
||||
@@ -713,25 +713,5 @@ class Table
|
||||
private function buildCache()
|
||||
{
|
||||
$this->fileManager->putPhpContents($this->cacheFilePath, $this->data, true);
|
||||
/*$contents = '<' . '?'. 'php return ' . $this->varExport($this->data) . ';';
|
||||
$this->fileManager->putContents($this->cacheFilePath, $contents);*/
|
||||
}
|
||||
|
||||
/*private function varExport($variable)
|
||||
{
|
||||
if ($variable instanceof \StdClass) {
|
||||
$result = '(object) ' . $this->varExport(get_object_vars($variable), true);
|
||||
} else if (is_array($variable)) {
|
||||
$array = array();
|
||||
foreach ($variable as $key => $value) {
|
||||
$array[] = var_export($key, true).' => ' . $this->varExport($value, true);
|
||||
}
|
||||
$result = '['.implode(', ', $array).']';
|
||||
} else {
|
||||
$result = var_export($variable, true);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}*/
|
||||
}
|
||||
|
||||
|
||||
@@ -47,6 +47,8 @@ class AclManager
|
||||
|
||||
protected $tableClassName = '\\Espo\\Core\\Acl\\Table';
|
||||
|
||||
protected $userAclClassName = '\\Espo\\Core\\Acl';
|
||||
|
||||
public function __construct(Container $container)
|
||||
{
|
||||
$this->container = $container;
|
||||
@@ -279,5 +281,11 @@ class AclManager
|
||||
{
|
||||
return $this->checkUserPermission($user, $target, 'assignmentPermission');
|
||||
}
|
||||
}
|
||||
|
||||
public function createUserAcl(User $user)
|
||||
{
|
||||
$className = $this->userAclClassName;
|
||||
$acl = new $className($this, $user);
|
||||
return $acl;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -217,7 +217,8 @@ class Container
|
||||
$this->get('acl'),
|
||||
$this->get('aclManager'),
|
||||
$this->get('metadata'),
|
||||
$this->get('config')
|
||||
$this->get('config'),
|
||||
$this->get('injectableFactory')
|
||||
);
|
||||
}
|
||||
|
||||
@@ -295,6 +296,16 @@ class Container
|
||||
);
|
||||
}
|
||||
|
||||
protected function loadBaseLanguage()
|
||||
{
|
||||
return new \Espo\Core\Utils\Language(
|
||||
'en_US',
|
||||
$this->get('fileManager'),
|
||||
$this->get('metadata'),
|
||||
$this->get('useCache')
|
||||
);
|
||||
}
|
||||
|
||||
protected function loadDefaultLanguage()
|
||||
{
|
||||
return new \Espo\Core\Utils\Language(
|
||||
@@ -329,8 +340,6 @@ class Container
|
||||
protected function loadFieldManager()
|
||||
{
|
||||
return new \Espo\Core\Utils\FieldManager(
|
||||
$this->get('metadata'),
|
||||
$this->get('language'),
|
||||
$this
|
||||
);
|
||||
}
|
||||
|
||||
@@ -87,6 +87,20 @@ class Xlsx extends \Espo\Core\Injectable
|
||||
}
|
||||
}
|
||||
|
||||
public function filterFieldList($entityType, $fieldList, $exportAllFields)
|
||||
{
|
||||
if ($exportAllFields) {
|
||||
foreach ($fieldList as $i => $field) {
|
||||
$type = $this->getMetadata()->get(['entityDefs', $entityType, 'fields', $field, 'type']);
|
||||
if (in_array($type, ['linkMultiple', 'attachmentMultiple'])) {
|
||||
unset($fieldList[$i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return array_values($fieldList);
|
||||
}
|
||||
|
||||
public function addAdditionalAttributes($entityType, &$attributeList, $fieldList)
|
||||
{
|
||||
$linkList = [];
|
||||
@@ -130,7 +144,7 @@ class Xlsx extends \Espo\Core\Injectable
|
||||
throw new Error();
|
||||
}
|
||||
|
||||
$phpExcel = new \PHPExcel();
|
||||
$phpExcel = new \PhpOffice\PhpSpreadsheet\Spreadsheet();
|
||||
$sheet = $phpExcel->setActiveSheetIndex(0);
|
||||
|
||||
if (isset($params['exportName'])) {
|
||||
@@ -162,10 +176,11 @@ class Xlsx extends \Espo\Core\Injectable
|
||||
)
|
||||
);
|
||||
|
||||
$now = new \DateTime();
|
||||
$now->setTimezone(new \DateTimeZone($this->getInjection('config')->get('timeZone', 'UTC')));
|
||||
|
||||
$sheet->setCellValue('A1', $exportName);
|
||||
|
||||
$sheet->setCellValue('B1', \PHPExcel_Shared_Date::PHPToExcel(strtotime(date('Y-m-d H:i:s'))));
|
||||
|
||||
$sheet->setCellValue('B1', \PhpOffice\PhpSpreadsheet\Shared\Date::PHPToExcel(strtotime($now->format('Y-m-d H:i:s'))));
|
||||
|
||||
$sheet->getStyle('A1')->applyFromArray($titleStyle);
|
||||
$sheet->getStyle('B1')->applyFromArray($dateStyle);
|
||||
@@ -289,7 +304,7 @@ class Xlsx extends \Espo\Core\Injectable
|
||||
}
|
||||
} else if ($type == 'currencyConverted') {
|
||||
if (array_key_exists($name, $row)) {
|
||||
$currency = $this->getConfig()->get('baseCurrency');
|
||||
$currency = $this->getConfig()->get('defaultCurrency');
|
||||
$currencySymbol = $this->getMetadata()->get(['app', 'currency', 'symbolMap', $currency], '');
|
||||
|
||||
$sheet->getStyle("$col$rowNumber")
|
||||
@@ -316,7 +331,7 @@ class Xlsx extends \Espo\Core\Injectable
|
||||
}
|
||||
} else if ($type == 'date') {
|
||||
if (isset($row[$name])) {
|
||||
$sheet->setCellValue("$col$rowNumber", \PHPExcel_Shared_Date::PHPToExcel(strtotime($row[$name])));
|
||||
$sheet->setCellValue("$col$rowNumber", \PhpOffice\PhpSpreadsheet\Shared\Date::PHPToExcel(strtotime($row[$name])));
|
||||
}
|
||||
} else if ($type == 'datetime' || $type == 'datetimeOptional') {
|
||||
$value = null;
|
||||
@@ -341,14 +356,14 @@ class Xlsx extends \Espo\Core\Injectable
|
||||
}
|
||||
}
|
||||
if ($value) {
|
||||
$sheet->setCellValue("$col$rowNumber", \PHPExcel_Shared_Date::PHPToExcel(strtotime($value)));
|
||||
$sheet->setCellValue("$col$rowNumber", \PhpOffice\PhpSpreadsheet\Shared\Date::PHPToExcel(strtotime($value)));
|
||||
}
|
||||
} else if ($type == 'image') {
|
||||
if (isset($row[$name . 'Id']) && $row[$name . 'Id']) {
|
||||
$attachment = $this->getEntityManager()->getEntity('Attachment', $row[$name . 'Id']);
|
||||
|
||||
if ($attachment) {
|
||||
$objDrawing = new \PHPExcel_Worksheet_Drawing();
|
||||
$objDrawing = new \PhpOffice\PhpSpreadsheet\Worksheet\Drawing();
|
||||
$filePath = $this->getInjection('fileStorageManager')->getLocalFilePath($attachment);
|
||||
|
||||
if ($filePath && file_exists($filePath)) {
|
||||
@@ -420,6 +435,36 @@ class Xlsx extends \Espo\Core\Injectable
|
||||
$value .= $row[$name.'Country'];
|
||||
}
|
||||
$sheet->setCellValue("$col$rowNumber", $value);
|
||||
} else if ($type == 'duration') {
|
||||
if (!empty($row[$name])) {
|
||||
$seconds = intval($row[$name]);
|
||||
|
||||
$days = intval(floor($seconds / 86400));
|
||||
$seconds = $seconds - $days * 86400;
|
||||
$hours = intval(floor($seconds / 3600));
|
||||
$seconds = $seconds - $hours * 3600;
|
||||
$minutes = intval(floor($seconds / 60));
|
||||
|
||||
$value = '';
|
||||
if ($days) {
|
||||
$value .= (string) $days . $this->getInjection('language')->translate('d', 'durationUnits');
|
||||
if ($minutes || $hours) {
|
||||
$value .= ' ';
|
||||
}
|
||||
}
|
||||
if ($hours) {
|
||||
$value .= (string) $hours . $this->getInjection('language')->translate('h', 'durationUnits');
|
||||
if ($minutes) {
|
||||
$value .= ' ';
|
||||
}
|
||||
}
|
||||
if ($minutes) {
|
||||
$value .= (string) $minutes . $this->getInjection('language')->translate('m', 'durationUnits');
|
||||
}
|
||||
|
||||
$sheet->setCellValue("$col$rowNumber", $value);
|
||||
}
|
||||
|
||||
} else {
|
||||
if (array_key_exists($name, $row)) {
|
||||
$sheet->setCellValue("$col$rowNumber", $row[$name]);
|
||||
@@ -428,6 +473,13 @@ class Xlsx extends \Espo\Core\Injectable
|
||||
|
||||
$link = false;
|
||||
|
||||
$foreignLink = null;
|
||||
$isForeign = false;
|
||||
if (strpos($name, '_')) {
|
||||
$isForeign = true;
|
||||
list($foreignLink, $foreignField) = explode('_', $name);
|
||||
}
|
||||
|
||||
if ($name == 'name') {
|
||||
if (array_key_exists('id', $row)) {
|
||||
$link = $this->getConfig()->getSiteUrl() . "/#".$entityType . "/view/" . $row['id'];
|
||||
@@ -438,7 +490,13 @@ class Xlsx extends \Espo\Core\Injectable
|
||||
}
|
||||
} else if ($type == 'link') {
|
||||
if (array_key_exists($name.'Id', $row)) {
|
||||
$foreignEntity = $this->getMetadata()->get(['entityDefs', $entityType, 'links', $name, 'entity']);
|
||||
$foreignEntity = null;
|
||||
if (!$isForeign) {
|
||||
$foreignEntity = $this->getMetadata()->get(['entityDefs', $entityType, 'links', $name, 'entity']);
|
||||
} else {
|
||||
$foreignEntity1 = $this->getMetadata()->get(['entityDefs', $entityType, 'links', $foreignLink, 'entity']);
|
||||
$foreignEntity = $this->getMetadata()->get(['entityDefs', $foreignEntity1, 'links', $foreignField, 'entity']);
|
||||
}
|
||||
if ($foreignEntity) {
|
||||
$link = $this->getConfig()->getSiteUrl() . "/#" . $foreignEntity. "/view/". $row[$name.'Id'];
|
||||
}
|
||||
@@ -470,7 +528,7 @@ class Xlsx extends \Espo\Core\Injectable
|
||||
|
||||
$sheet->getStyle("A2:A$rowNumber")
|
||||
->getNumberFormat()
|
||||
->setFormatCode(\PHPExcel_Style_NumberFormat::FORMAT_TEXT);
|
||||
->setFormatCode(\PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_TEXT);
|
||||
|
||||
$startingRowNumber = 4;
|
||||
|
||||
@@ -521,7 +579,7 @@ class Xlsx extends \Espo\Core\Injectable
|
||||
$sheet->getStyle($linkColumn.$startingRowNumber.':'.$linkColumn.$rowNumber)->applyFromArray($linkStyle);
|
||||
}
|
||||
|
||||
$objWriter = \PHPExcel_IOFactory::createWriter($phpExcel, 'Excel2007');
|
||||
$objWriter = \PhpOffice\PhpSpreadsheet\IOFactory::createWriter($phpExcel, 'Xlsx');
|
||||
|
||||
if (!$this->getInjection('fileManager')->isDir('data/cache/')) {
|
||||
$this->getInjection('fileManager')->mkdir('data/cache/');
|
||||
|
||||
@@ -36,10 +36,10 @@ class IncludesType extends \Espo\Core\Formula\Functions\Base
|
||||
public function process(\StdClass $item)
|
||||
{
|
||||
if (!property_exists($item, 'value') || !is_array($item->value)) {
|
||||
throw new Error('Value for \'Array\\Includses\' item is not array.');
|
||||
throw new Error('Value for \'Array\\Includes\' item is not array.');
|
||||
}
|
||||
if (count($item->value) < 2) {
|
||||
throw new Error('Bad arguments passed to \'Array\\Includses\'.');
|
||||
throw new Error('Bad arguments passed to \'Array\\Includes\'.');
|
||||
}
|
||||
$list = $this->evaluate($item->value[0]);
|
||||
$needle = $this->evaluate($item->value[1]);
|
||||
|
||||
@@ -50,13 +50,19 @@ class Htmlizer
|
||||
|
||||
protected $entityManager;
|
||||
|
||||
public function __construct(FileManager $fileManager, DateTime $dateTime, NumberUtil $number, $acl = null, $entityManager = null)
|
||||
protected $metadata;
|
||||
|
||||
protected $language;
|
||||
|
||||
public function __construct(FileManager $fileManager, DateTime $dateTime, NumberUtil $number, $acl = null, $entityManager = null, $metadata = null, $language = null)
|
||||
{
|
||||
$this->fileManager = $fileManager;
|
||||
$this->dateTime = $dateTime;
|
||||
$this->number = $number;
|
||||
$this->acl = $acl;
|
||||
$this->entityManager = $entityManager;
|
||||
$this->metadata = $metadata;
|
||||
$this->language = $language;
|
||||
}
|
||||
|
||||
protected function getAcl()
|
||||
@@ -117,14 +123,14 @@ class Htmlizer
|
||||
if ($item instanceof \StdClass) {
|
||||
$v = json_decode(json_encode($v), true);
|
||||
}
|
||||
if (!is_array($v)) {
|
||||
$v = [];
|
||||
}
|
||||
foreach ($v as $k => $w) {
|
||||
$keyRaw = $k . '_RAW';
|
||||
$v[$keyRaw] = $v[$k];
|
||||
$v[$k] = $this->format($v[$k]);
|
||||
if (is_array($v)) {
|
||||
foreach ($v as $k => $w) {
|
||||
$keyRaw = $k . '_RAW';
|
||||
$v[$keyRaw] = $v[$k];
|
||||
$v[$k] = $this->format($v[$k]);
|
||||
}
|
||||
}
|
||||
|
||||
$newList[] = $v;
|
||||
}
|
||||
$data[$field] = $newList;
|
||||
@@ -148,6 +154,14 @@ class Htmlizer
|
||||
if (array_key_exists($field, $data)) {
|
||||
$keyRaw = $field . '_RAW';
|
||||
$data[$keyRaw] = $data[$field];
|
||||
|
||||
$fieldType = $this->getFieldType($entity->getEntityType(), $field);
|
||||
if ($fieldType === 'enum') {
|
||||
if ($this->language) {
|
||||
$data[$field] = $this->language->translateOption($data[$field], $field, $entity->getEntityType());
|
||||
}
|
||||
}
|
||||
|
||||
$data[$field] = $this->format($data[$field]);
|
||||
}
|
||||
}
|
||||
@@ -269,4 +283,9 @@ class Htmlizer
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
protected function getFieldType($entityType, $field) {
|
||||
if (!$this->metadata) return;
|
||||
return $this->metadata->get(['entityDefs', $entityType, 'fields', $field, 'type']);
|
||||
}
|
||||
}
|
||||
@@ -351,7 +351,7 @@ class Importer
|
||||
if ($email->get('messageId')) {
|
||||
$duplicate = $this->getEntityManager()->getRepository('Email')->select(['id'])->where(array(
|
||||
'messageId' => $email->get('messageId')
|
||||
))->findOne();
|
||||
))->findOne(['skipAdditionalSelectParams' => true]);
|
||||
if ($duplicate) {
|
||||
return $duplicate;
|
||||
}
|
||||
|
||||
@@ -93,7 +93,7 @@ class Base implements Injectable
|
||||
public function process(Entity $entity)
|
||||
{
|
||||
if (!$entity->get('assignedUserId')) return;
|
||||
if (!$entity->isFieldChanged('assignedUserId')) return;
|
||||
if (!$entity->isAttributeChanged('assignedUserId')) return;
|
||||
|
||||
$assignedUserId = $entity->get('assignedUserId');
|
||||
|
||||
|
||||
@@ -52,7 +52,13 @@ class Entity extends \Espo\ORM\Entity
|
||||
if (!$this->entityManager->hasRepository($parentType)) return;
|
||||
$repository = $this->entityManager->getRepository($parentType);
|
||||
|
||||
$foreignEntity = $repository->select(['id', 'name'])->where(['id' => $parentId])->findOne();
|
||||
$select = ['id', 'name'];
|
||||
if ($parentType === 'Lead') {
|
||||
$select[] = 'accountName';
|
||||
$select[] = 'emailAddress';
|
||||
$select[] = 'phoneNumber';
|
||||
}
|
||||
$foreignEntity = $repository->select($select)->where(['id' => $parentId])->findOne();
|
||||
if ($foreignEntity) {
|
||||
$this->set($field . 'Name', $foreignEntity->get('name'));
|
||||
}
|
||||
@@ -68,26 +74,42 @@ class Entity extends \Espo\ORM\Entity
|
||||
$defs['additionalColumns'] = $columns;
|
||||
}
|
||||
|
||||
$idsAttribute = $field . 'Ids';
|
||||
|
||||
$foreignEntityType = $this->getRelationParam($field, 'entity');
|
||||
if ($foreignEntityType && $this->entityManager) {
|
||||
$foreignEntityDefs = $this->entityManager->getMetadata()->get($foreignEntityType);
|
||||
if ($foreignEntityDefs && !empty($foreignEntityDefs['collection'])) {
|
||||
$collectionDefs = $foreignEntityDefs['collection'];
|
||||
if (!empty($foreignEntityDefs['collection']['orderBy'])) {
|
||||
$orderBy = $foreignEntityDefs['collection']['orderBy'];
|
||||
$order = 'ASC';
|
||||
if (array_key_exists('order', $foreignEntityDefs['collection'])) {
|
||||
$order = $foreignEntityDefs['collection']['order'];
|
||||
}
|
||||
if (array_key_exists($orderBy, $foreignEntityDefs['fields'])) {
|
||||
$defs['orderBy'] = $orderBy;
|
||||
$defs['order'] = $order;
|
||||
|
||||
if ($this->getAttributeParam($idsAttribute, 'orderBy')) {
|
||||
$defs['orderBy'] = $this->getAttributeParam($idsAttribute, 'orderBy');
|
||||
$defs['order'] = 'ASC';
|
||||
if ($this->getAttributeParam($idsAttribute, 'orderDirection')) {
|
||||
$defs['order'] = $this->getAttributeParam($idsAttribute, 'orderDirection');
|
||||
}
|
||||
} else {
|
||||
if ($foreignEntityType && $this->entityManager) {
|
||||
$foreignEntityDefs = $this->entityManager->getMetadata()->get($foreignEntityType);
|
||||
if ($foreignEntityDefs && !empty($foreignEntityDefs['collection'])) {
|
||||
$collectionDefs = $foreignEntityDefs['collection'];
|
||||
if (!empty($foreignEntityDefs['collection']['orderBy'])) {
|
||||
$orderBy = $foreignEntityDefs['collection']['orderBy'];
|
||||
$order = 'ASC';
|
||||
if (array_key_exists('order', $foreignEntityDefs['collection'])) {
|
||||
$order = $foreignEntityDefs['collection']['order'];
|
||||
}
|
||||
if (array_key_exists($orderBy, $foreignEntityDefs['fields'])) {
|
||||
$defs['orderBy'] = $orderBy;
|
||||
$defs['order'] = $order;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$defs['select'] = ['id', 'name'];
|
||||
if ($foreignEntityType === 'Lead') {
|
||||
$defs['select'][] = 'accountName';
|
||||
$defs['select'][] = 'emailAddress';
|
||||
$defs['select'][] = 'phoneNumber';
|
||||
}
|
||||
|
||||
$hasType = false;
|
||||
if ($this->hasField($field . 'Types')) {
|
||||
@@ -120,7 +142,7 @@ class Entity extends \Espo\ORM\Entity
|
||||
}
|
||||
}
|
||||
|
||||
$this->set($field . 'Ids', $ids);
|
||||
$this->set($idsAttribute, $ids);
|
||||
$this->set($field . 'Names', $names);
|
||||
if ($hasType) {
|
||||
$this->set($field . 'Types', $types);
|
||||
|
||||
@@ -40,7 +40,9 @@ use \Espo\Core\Interfaces\Injectable;
|
||||
class RDB extends \Espo\ORM\Repositories\RDB implements Injectable
|
||||
{
|
||||
protected $dependencies = array(
|
||||
'metadata'
|
||||
'metadata',
|
||||
'config',
|
||||
'fieldManagerUtil'
|
||||
);
|
||||
|
||||
protected $injections = array();
|
||||
@@ -51,6 +53,8 @@ class RDB extends \Espo\ORM\Repositories\RDB implements Injectable
|
||||
|
||||
protected $processFieldsAfterSaveDisabled = false;
|
||||
|
||||
protected $processFieldsBeforeSaveDisabled = false;
|
||||
|
||||
protected function addDependency($name)
|
||||
{
|
||||
$this->dependencies[] = $name;
|
||||
@@ -83,6 +87,16 @@ class RDB extends \Espo\ORM\Repositories\RDB implements Injectable
|
||||
return $this->getInjection('metadata');
|
||||
}
|
||||
|
||||
protected function getConfig()
|
||||
{
|
||||
return $this->getInjection('config');
|
||||
}
|
||||
|
||||
protected function getFieldManagerUtil()
|
||||
{
|
||||
return $this->getInjection('fieldManagerUtil');
|
||||
}
|
||||
|
||||
public function __construct($entityType, EntityManager $entityManager, EntityFactory $entityFactory)
|
||||
{
|
||||
parent::__construct($entityType, $entityManager, $entityFactory);
|
||||
@@ -254,6 +268,10 @@ class RDB extends \Espo\ORM\Repositories\RDB implements Injectable
|
||||
if (!$this->hooksDisabled && empty($options['skipHooks'])) {
|
||||
$this->getEntityManager()->getHookManager()->process($this->entityType, 'beforeSave', $entity, $options);
|
||||
}
|
||||
|
||||
if (!$this->processFieldsBeforeSaveDisabled) {
|
||||
$this->processCurrencyFieldsBeforeSave($entity);
|
||||
}
|
||||
}
|
||||
|
||||
protected function afterSave(Entity $entity, array $options = array())
|
||||
@@ -326,6 +344,28 @@ class RDB extends \Espo\ORM\Repositories\RDB implements Injectable
|
||||
return $result;
|
||||
}
|
||||
|
||||
protected function getFieldByTypeList($type)
|
||||
{
|
||||
return $this->getFieldManagerUtil()->getFieldByTypeList($this->entityType, $type);
|
||||
}
|
||||
|
||||
protected function processCurrencyFieldsBeforeSave(Entity $entity)
|
||||
{
|
||||
foreach ($this->getFieldByTypeList('currency') as $field) {
|
||||
$currencyAttribute = $field . 'Currency';
|
||||
$defaultCurrency = $this->getConfig()->get('defaultCurrency');
|
||||
if ($entity->isNew()) {
|
||||
if ($entity->get($field) && !$entity->get($currencyAttribute)) {
|
||||
$entity->set($currencyAttribute, $defaultCurrency);
|
||||
}
|
||||
} else {
|
||||
if ($entity->isAttributeChanged($field) && $entity->has($currencyAttribute) && !$entity->get($currencyAttribute)) {
|
||||
$entity->set($currencyAttribute, $defaultCurrency);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function processFileFieldsSave(Entity $entity)
|
||||
{
|
||||
foreach ($entity->getRelations() as $name => $defs) {
|
||||
@@ -368,14 +408,14 @@ class RDB extends \Espo\ORM\Repositories\RDB implements Injectable
|
||||
protected function processEmailAddressSave(Entity $entity)
|
||||
{
|
||||
if ($entity->hasRelation('emailAddresses') && $entity->hasAttribute('emailAddress')) {
|
||||
$emailAddressRepository = $this->getEntityManager()->getRepository('EmailAddress')->storeEntityEmailAddress($entity);
|
||||
$this->getEntityManager()->getRepository('EmailAddress')->storeEntityEmailAddress($entity);
|
||||
}
|
||||
}
|
||||
|
||||
protected function processPhoneNumberSave(Entity $entity)
|
||||
{
|
||||
if ($entity->hasRelation('phoneNumbers') && $entity->hasAttribute('phoneNumber')) {
|
||||
$emailAddressRepository = $this->getEntityManager()->getRepository('PhoneNumber')->storeEntityPhoneNumber($entity);
|
||||
$this->getEntityManager()->getRepository('PhoneNumber')->storeEntityPhoneNumber($entity);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -41,6 +41,8 @@ class AclManager extends \Espo\Core\AclManager
|
||||
|
||||
private $portal = null;
|
||||
|
||||
protected $userAclClassName = '\\Espo\\Core\\Portal\\Acl';
|
||||
|
||||
public function getImplementation($scope)
|
||||
{
|
||||
if (empty($this->implementationHashMap[$scope])) {
|
||||
|
||||
@@ -41,7 +41,7 @@ class CategoryTree extends \Espo\Core\ORM\Repositories\RDB
|
||||
$query = $this->getEntityManager()->getQuery();
|
||||
|
||||
$parentId = $entity->get('parentId');
|
||||
$pathsTableName = $query->toDb($entity->getEntityType() . 'Path');
|
||||
$pathsTableName = $query->toDb($query->sanitize($entity->getEntityType()) . 'Path');
|
||||
|
||||
if ($entity->isNew()) {
|
||||
if ($parentId) {
|
||||
@@ -62,7 +62,7 @@ class CategoryTree extends \Espo\Core\ORM\Repositories\RDB
|
||||
}
|
||||
$pdo->query($sql);
|
||||
} else {
|
||||
if ($entity->isFieldChanged('parentId')) {
|
||||
if ($entity->isAttributeChanged('parentId')) {
|
||||
$sql = "
|
||||
DELETE a FROM `".$pathsTableName."` AS a
|
||||
JOIN `".$pathsTableName."` AS d ON a.descendor_id = d.descendor_id
|
||||
@@ -93,7 +93,7 @@ class CategoryTree extends \Espo\Core\ORM\Repositories\RDB
|
||||
$pdo = $this->getEntityManager()->getPDO();
|
||||
$query = $this->getEntityManager()->getQuery();
|
||||
|
||||
$pathsTableName = $query->toDb($entity->getEntityType() . 'Path');
|
||||
$pathsTableName = $query->toDb($query->sanitize($entity->getEntityType()) . 'Path');
|
||||
|
||||
$sql = "DELETE FROM `".$pathsTableName."` WHERE descendor_id = ".$pdo->quote($entity->id)."";
|
||||
$pdo->query($sql);
|
||||
|
||||
@@ -32,6 +32,7 @@ namespace Espo\Core;
|
||||
use \Espo\Core\Exceptions\Error;
|
||||
|
||||
use \Espo\Core\Utils\Util;
|
||||
use \Espo\Core\InjectableFactory;
|
||||
|
||||
class SelectManagerFactory
|
||||
{
|
||||
@@ -43,7 +44,9 @@ class SelectManagerFactory
|
||||
|
||||
private $metadata;
|
||||
|
||||
public function __construct($entityManager, \Espo\Entities\User $user, Acl $acl, AclManager $aclManager, Utils\Metadata $metadata, Utils\Config $config)
|
||||
private $injectableFactory;
|
||||
|
||||
public function __construct($entityManager, \Espo\Entities\User $user, Acl $acl, AclManager $aclManager, Utils\Metadata $metadata, Utils\Config $config, InjectableFactory $injectableFactory)
|
||||
{
|
||||
$this->entityManager = $entityManager;
|
||||
$this->user = $user;
|
||||
@@ -51,9 +54,10 @@ class SelectManagerFactory
|
||||
$this->aclManager = $aclManager;
|
||||
$this->metadata = $metadata;
|
||||
$this->config = $config;
|
||||
$this->injectableFactory = $injectableFactory;
|
||||
}
|
||||
|
||||
public function create($entityType)
|
||||
public function create($entityType, $user = null)
|
||||
{
|
||||
$normalizedName = Util::normilizeClassName($entityType);
|
||||
|
||||
@@ -70,7 +74,14 @@ class SelectManagerFactory
|
||||
}
|
||||
}
|
||||
|
||||
$selectManager = new $className($this->entityManager, $this->user, $this->acl, $this->aclManager, $this->metadata, $this->config);
|
||||
if ($user) {
|
||||
$acl = $this->aclManager->createUserAcl($user);
|
||||
} else {
|
||||
$acl = $this->acl;
|
||||
$user = $this->user;
|
||||
}
|
||||
|
||||
$selectManager = new $className($this->entityManager, $user, $acl, $this->aclManager, $this->metadata, $this->config, $this->injectableFactory);
|
||||
$selectManager->setEntityType($entityType);
|
||||
|
||||
return $selectManager;
|
||||
|
||||
@@ -36,6 +36,7 @@ use \Espo\Core\Acl;
|
||||
use \Espo\Core\AclManager;
|
||||
use \Espo\Core\Utils\Metadata;
|
||||
use \Espo\Core\Utils\Config;
|
||||
use \Espo\Core\InjectableFactory;
|
||||
|
||||
class Base
|
||||
{
|
||||
@@ -63,15 +64,15 @@ class Base
|
||||
|
||||
const MIN_LENGTH_FOR_CONTENT_SEARCH = 4;
|
||||
|
||||
public function __construct($entityManager, \Espo\Entities\User $user, Acl $acl, AclManager $aclManager, Metadata $metadata, Config $config)
|
||||
public function __construct($entityManager, \Espo\Entities\User $user, Acl $acl, AclManager $aclManager, Metadata $metadata, Config $config, InjectableFactory $injectableFactory)
|
||||
{
|
||||
$this->entityManager = $entityManager;
|
||||
$this->user = $user;
|
||||
$this->acl = $acl;
|
||||
$this->aclManager = $aclManager;
|
||||
|
||||
$this->metadata = $metadata;
|
||||
$this->config = $config;
|
||||
$this->injectableFactory = $injectableFactory;
|
||||
}
|
||||
|
||||
protected function getEntityManager()
|
||||
@@ -104,6 +105,11 @@ class Base
|
||||
return $this->aclManager;
|
||||
}
|
||||
|
||||
protected function getInjectableFactory()
|
||||
{
|
||||
return $this->injectableFactory;
|
||||
}
|
||||
|
||||
public function setEntityType($entityType)
|
||||
{
|
||||
$this->entityType = $entityType;
|
||||
@@ -186,7 +192,6 @@ class Base
|
||||
{
|
||||
$this->prepareResult($result);
|
||||
|
||||
$whereClause = array();
|
||||
foreach ($where as $item) {
|
||||
if (!isset($item['type'])) continue;
|
||||
|
||||
@@ -207,10 +212,17 @@ class Base
|
||||
}
|
||||
}
|
||||
|
||||
$whereClause = $this->convertWhere($where, false, $result);
|
||||
|
||||
$result['whereClause'] = array_merge($result['whereClause'], $whereClause);
|
||||
}
|
||||
|
||||
public function convertWhere(array $where, $ignoreAdditionaFilterTypes = false, &$result = null)
|
||||
{
|
||||
$whereClause = [];
|
||||
|
||||
$ignoreTypeList = array_merge(['bool', 'primary'], $this->additionalFilterTypeList);
|
||||
|
||||
$additionalFilters = array();
|
||||
foreach ($where as $item) {
|
||||
if (!isset($item['type'])) continue;
|
||||
|
||||
@@ -221,7 +233,7 @@ class Base
|
||||
$whereClause[] = $part;
|
||||
}
|
||||
} else {
|
||||
if (in_array($type, $this->additionalFilterTypeList)) {
|
||||
if (!$ignoreAdditionaFilterTypes && in_array($type, $this->additionalFilterTypeList)) {
|
||||
if (!empty($item['value'])) {
|
||||
$methodName = 'apply' . ucfirst($type);
|
||||
|
||||
@@ -242,7 +254,7 @@ class Base
|
||||
}
|
||||
}
|
||||
|
||||
$result['whereClause'] = array_merge($result['whereClause'], $whereClause);
|
||||
return $whereClause;
|
||||
}
|
||||
|
||||
protected function applyLinkedWith($link, $idsValue, &$result)
|
||||
@@ -1327,6 +1339,20 @@ class Base
|
||||
$method = 'filter' . ucfirst($filterName);
|
||||
if (method_exists($this, $method)) {
|
||||
$this->$method($result);
|
||||
} else {
|
||||
$className = $this->getMetadata()->get(['entityDefs', $this->entityType, 'collection', 'filters', $filterName, 'className']);
|
||||
if ($className) {
|
||||
if (!class_exists($className)) {
|
||||
$GLOBALS['log']->error("Could find class for filter {$filterName}.");
|
||||
return;
|
||||
}
|
||||
$impl = $this->getInjectableFactory()->createByClassName($className);
|
||||
if (!$impl) {
|
||||
$GLOBALS['log']->error("Could not create filter {$filterName} implementation.");
|
||||
return;
|
||||
}
|
||||
$impl->applyFilter($this->entityType, $filterName, $result, $this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1480,10 +1506,11 @@ class Base
|
||||
$fieldList = $this->getTextFilterFieldList();
|
||||
$d = array();
|
||||
|
||||
$textFilterContainsMinLength = $this->getConfig()->get('textFilterContainsMinLength', self::MIN_LENGTH_FOR_CONTENT_SEARCH);
|
||||
|
||||
foreach ($fieldList as $field) {
|
||||
$expression = $textFilter . '%';
|
||||
if (
|
||||
strlen($textFilter) >= self::MIN_LENGTH_FOR_CONTENT_SEARCH
|
||||
strlen($textFilter) >= $textFilterContainsMinLength
|
||||
&&
|
||||
(
|
||||
!empty($fieldDefs[$field]['type']) && $fieldDefs[$field]['type'] == 'text'
|
||||
@@ -1563,4 +1590,3 @@ class Base
|
||||
$this->filterFollowed($result);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@ class Person extends \Espo\Services\Record
|
||||
if (
|
||||
($entity->get('emailAddress') || $entity->get('emailAddressData'))
|
||||
&&
|
||||
($entity->isNew() || $entity->isFieldChanged('emailAddress') || $entity->isFieldChanged('emailAddressData'))
|
||||
($entity->isNew() || $entity->isAttributeChanged('emailAddress') || $entity->isAttributeChanged('emailAddressData'))
|
||||
) {
|
||||
if ($entity->get('emailAddress')) {
|
||||
$list = [$entity->get('emailAddress')];
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"labels": {
|
||||
"Create {entityType}": "Creare {entityTypeTranslated}"
|
||||
}
|
||||
}
|
||||
"labels": {
|
||||
"Create {entityType}": "Creare {entityTypeTranslated}"
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"labels": {
|
||||
"Create {entityType}": "Creare {entityTypeTranslated}"
|
||||
}
|
||||
}
|
||||
"labels": {
|
||||
"Create {entityType}": "Creare {entityTypeTranslated}"
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"labels": {
|
||||
"Create {entityType}": "Creare {entityTypeTranslated}"
|
||||
}
|
||||
}
|
||||
"labels": {
|
||||
"Create {entityType}": "Creare {entityTypeTranslated}"
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"labels": {
|
||||
"Create {entityType}": "Creare {entityTypeTranslated}"
|
||||
}
|
||||
}
|
||||
"labels": {
|
||||
"Create {entityType}": "Creare {entityTypeTranslated}"
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"labels": {
|
||||
"Create {entityType}": "Creare {entityTypeTranslated}"
|
||||
}
|
||||
}
|
||||
"labels": {
|
||||
"Create {entityType}": "Creare {entityTypeTranslated}"
|
||||
}
|
||||
}
|
||||
@@ -47,6 +47,7 @@ class UpgradeManager extends Upgrades\Base
|
||||
'customDirNames' => array(
|
||||
'before' => 'beforeUpgradeFiles',
|
||||
'after' => 'afterUpgradeFiles',
|
||||
'vendor' => 'vendorFiles',
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -28,6 +28,7 @@
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Core\Upgrades\Actions;
|
||||
|
||||
use Espo\Core\Utils\Util;
|
||||
use Espo\Core\Utils\System;
|
||||
use Espo\Core\Utils\Json;
|
||||
@@ -83,6 +84,7 @@ abstract class Base
|
||||
*/
|
||||
protected $defaultPackageType = 'extension';
|
||||
|
||||
protected $vendorDirName = 'vendor';
|
||||
|
||||
public function __construct(\Espo\Core\Container $container, \Espo\Core\Upgrades\ActionManager $actionManager)
|
||||
{
|
||||
@@ -315,8 +317,17 @@ abstract class Base
|
||||
{
|
||||
$manifest = $this->getManifest();
|
||||
|
||||
if (isset($manifest[$type])) {
|
||||
return $manifest[$type];
|
||||
switch ($type) {
|
||||
case 'delete':
|
||||
case 'deleteBeforeCopy':
|
||||
if (isset($manifest[$type])) {
|
||||
return $manifest[$type];
|
||||
}
|
||||
break;
|
||||
|
||||
case 'vendor':
|
||||
return $this->getVendorFileList('delete');
|
||||
break;
|
||||
}
|
||||
|
||||
return array();
|
||||
@@ -332,7 +343,7 @@ abstract class Base
|
||||
if (!isset($this->data['deleteFileList'])) {
|
||||
$deleteFileList = array();
|
||||
|
||||
$deleteList = array_merge($this->getDeleteList('delete'), $this->getDeleteList('deleteBeforeCopy'));
|
||||
$deleteList = array_merge($this->getDeleteList('delete'), $this->getDeleteList('deleteBeforeCopy'), $this->getDeleteList('vendor'));
|
||||
foreach ($deleteList as $key => $itemPath) {
|
||||
if (is_dir($itemPath)) {
|
||||
$fileList = $this->getFileManager()->getFileList($itemPath, true, '', true, true);
|
||||
@@ -356,9 +367,9 @@ abstract class Base
|
||||
*
|
||||
* @return boolen
|
||||
*/
|
||||
protected function deleteFiles($withEmptyDirs = false)
|
||||
protected function deleteFiles($type = 'delete', $withEmptyDirs = false)
|
||||
{
|
||||
$deleteList = $this->getDeleteList('delete');
|
||||
$deleteList = $this->getDeleteList($type);
|
||||
|
||||
if (!empty($deleteList)) {
|
||||
return $this->getFileManager()->remove($deleteList, null, $withEmptyDirs);
|
||||
@@ -367,24 +378,6 @@ abstract class Base
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deleted file/forder list before coppy the upgrade files
|
||||
*
|
||||
* @param boolean $withEmptyDirs
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
protected function deleteBeforeCopy($withEmptyDirs = false)
|
||||
{
|
||||
$deleteList = $this->getDeleteList('deleteBeforeCopy');
|
||||
|
||||
if (!empty($deleteList)) {
|
||||
$this->getFileManager()->remove($deleteList, null, $withEmptyDirs);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function getCopyFileList()
|
||||
{
|
||||
if (!isset($this->data['fileList'])) {
|
||||
@@ -445,6 +438,12 @@ abstract class Base
|
||||
}
|
||||
}
|
||||
|
||||
//vendor file list
|
||||
$vendorFileList = $this->getVendorFileList('copy');
|
||||
if (!empty($vendorFileList)) {
|
||||
$fileList = array_merge($fileList, $vendorFileList);
|
||||
}
|
||||
|
||||
return $fileList;
|
||||
}
|
||||
|
||||
@@ -466,7 +465,7 @@ abstract class Base
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
protected function copyFiles($type = null)
|
||||
protected function copyFiles($type = null, $dest = '')
|
||||
{
|
||||
switch ($type) {
|
||||
case 'before':
|
||||
@@ -475,21 +474,63 @@ abstract class Base
|
||||
$dirPath = $dirNames[$type];
|
||||
break;
|
||||
|
||||
case 'vendor':
|
||||
$dirNames = $this->getParams('customDirNames');
|
||||
if (isset($dirNames['vendor'])) {
|
||||
$dirPath = $dirNames['vendor'];
|
||||
$dest = $this->vendorDirName;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
$dirPath = self::FILES;
|
||||
break;
|
||||
}
|
||||
|
||||
$packagePath = $this->getPackagePath();
|
||||
$filesPath = Util::concatPath($packagePath, $dirPath);
|
||||
if (isset($dirPath)) {
|
||||
$packagePath = $this->getPackagePath();
|
||||
$filesPath = Util::concatPath($packagePath, $dirPath);
|
||||
|
||||
if (file_exists($filesPath)) {
|
||||
return $this->copy($filesPath, '', true);
|
||||
if (file_exists($filesPath)) {
|
||||
return $this->copy($filesPath, $dest, true);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function getVendorFileList($type = 'copy')
|
||||
{
|
||||
$list = [];
|
||||
|
||||
$packagePath = $this->getPackagePath();
|
||||
$dirNames = $this->getParams('customDirNames');
|
||||
if (!isset($dirNames['vendor'])) {
|
||||
return $list;
|
||||
}
|
||||
|
||||
$filesPath = Util::concatPath($packagePath, $dirNames['vendor']);
|
||||
if (!file_exists($filesPath)) {
|
||||
return $list;
|
||||
}
|
||||
|
||||
switch ($type) {
|
||||
case 'copy':
|
||||
$list = $this->getFileManager()->getFileList($filesPath, true, '', true, true);
|
||||
break;
|
||||
|
||||
case 'delete':
|
||||
$list = $this->getFileManager()->getFileList($filesPath, false, '', null, true);
|
||||
break;
|
||||
}
|
||||
|
||||
foreach ($list as &$path) {
|
||||
$path = Util::concatPath($this->vendorDirName, $path);
|
||||
}
|
||||
|
||||
return $list;
|
||||
}
|
||||
|
||||
public function getManifest()
|
||||
{
|
||||
if (!isset($this->data['manifest'])) {
|
||||
@@ -515,6 +556,11 @@ abstract class Base
|
||||
return $this->data['manifest'];
|
||||
}
|
||||
|
||||
protected function setManifest()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the manifest is correct
|
||||
*
|
||||
|
||||
@@ -76,7 +76,7 @@ class Install extends \Espo\Core\Upgrades\Actions\Base
|
||||
$this->runScript('before');
|
||||
|
||||
/* remove files defined in a manifest "deleteBeforeCopy" */
|
||||
$this->deleteBeforeCopy(true);
|
||||
$this->deleteFiles('deleteBeforeCopy', true);
|
||||
|
||||
/* copy files from directory "Files" to EspoCRM files */
|
||||
if (!$this->copyFiles()) {
|
||||
@@ -84,7 +84,10 @@ class Install extends \Espo\Core\Upgrades\Actions\Base
|
||||
}
|
||||
|
||||
/* remove files defined in a manifest */
|
||||
$this->deleteFiles(true);
|
||||
$this->deleteFiles('delete', true);
|
||||
|
||||
$this->deleteFiles('vendor');
|
||||
$this->copyFiles('vendor');
|
||||
|
||||
if (!$this->systemRebuild()) {
|
||||
$this->throwErrorAndRemovePackage('Error occurred while EspoCRM rebuild.');
|
||||
|
||||
@@ -66,7 +66,7 @@ class Uninstall extends \Espo\Core\Upgrades\Actions\Base
|
||||
}
|
||||
|
||||
/* remove extension files, saved in fileList */
|
||||
if (!$this->deleteFiles(true)) {
|
||||
if (!$this->deleteFiles('delete', true)) {
|
||||
$this->throwErrorAndRemovePackage('Permission denied to delete files.');
|
||||
}
|
||||
}
|
||||
@@ -119,10 +119,10 @@ class Uninstall extends \Espo\Core\Upgrades\Actions\Base
|
||||
return $res;
|
||||
}
|
||||
|
||||
protected function copyFiles($type = null)
|
||||
protected function copyFiles($type = null, $dest = '')
|
||||
{
|
||||
$backupPath = $this->getPath('backupPath');
|
||||
$res = $this->copy(array($backupPath, self::FILES), '', true);
|
||||
$res = $this->copy(array($backupPath, self::FILES), $dest, true);
|
||||
|
||||
return $res;
|
||||
}
|
||||
|
||||
@@ -158,8 +158,8 @@ class AdminNotificationManager
|
||||
|
||||
$query = "
|
||||
SELECT version FROM extension
|
||||
WHERE name='". $extensionName ."'
|
||||
AND deleted=0
|
||||
WHERE name = ". $pdo->quote($extensionName) ."
|
||||
AND deleted = 0
|
||||
AND is_installed = 1
|
||||
ORDER BY created_at DESC
|
||||
";
|
||||
|
||||
@@ -133,7 +133,6 @@ class MySqlPlatform extends \Doctrine\DBAL\Platforms\MySqlPlatform
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
return array_merge($sql, $tableSql, $columnSql);
|
||||
}
|
||||
|
||||
@@ -305,10 +304,23 @@ class MySqlPlatform extends \Doctrine\DBAL\Platforms\MySqlPlatform
|
||||
protected function _getCreateTableSQL($tableName, array $columns, array $options = array())
|
||||
{
|
||||
if (!isset($options['engine'])) {
|
||||
$options['engine'] = 'MyISAM';
|
||||
$options['engine'] = 'InnoDB';
|
||||
}
|
||||
|
||||
if (!isset($options['charset'])) {
|
||||
$options['charset'] = 'utf8mb4';
|
||||
}
|
||||
|
||||
if (!isset($options['collate'])) {
|
||||
$options['collate'] = 'utf8mb4_unicode_ci';
|
||||
}
|
||||
|
||||
return parent::_getCreateTableSQL($tableName, $columns, $options);
|
||||
}
|
||||
|
||||
public function getColumnCollationDeclarationSQL($collation)
|
||||
{
|
||||
return $this->getCollationFieldDeclaration($collation);
|
||||
}
|
||||
//end: ESPO
|
||||
}
|
||||
@@ -275,7 +275,8 @@ class Converter
|
||||
)
|
||||
);
|
||||
|
||||
foreach($entityMetadata['fields'] as $fieldName => $fieldParams) {
|
||||
foreach ($entityMetadata['fields'] as $fieldName => $fieldParams) {
|
||||
if (empty($fieldParams['type'])) continue;
|
||||
|
||||
/** check if "fields" option exists in $fieldMeta */
|
||||
$fieldTypeMetadata = $this->getMetadataHelper()->getFieldDefsByType($fieldParams);
|
||||
|
||||
@@ -44,17 +44,26 @@ class LinkMultiple extends Base
|
||||
),
|
||||
$fieldName.'Names' => array(
|
||||
'type' => 'varchar',
|
||||
'notStorable' => true,
|
||||
),
|
||||
),
|
||||
'notStorable' => true
|
||||
)
|
||||
)
|
||||
),
|
||||
'unset' => array(
|
||||
$entityName => array(
|
||||
'fields.'.$fieldName,
|
||||
),
|
||||
),
|
||||
'fields.'.$fieldName
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
$fieldParams = $this->getFieldParams();
|
||||
|
||||
if (array_key_exists('orderBy', $fieldParams)) {
|
||||
$data[$entityName]['fields'][$fieldName . 'Ids']['orderBy'] = $fieldParams['orderBy'];
|
||||
if (array_key_exists('orderDirection', $fieldParams)) {
|
||||
$data[$entityName]['fields'][$fieldName . 'Ids']['orderDirection'] = $fieldParams['orderDirection'];
|
||||
}
|
||||
}
|
||||
|
||||
$columns = $this->getMetadata()->get("entityDefs.{$entityName}.fields.{$fieldName}.columns");
|
||||
if (!empty($columns)) {
|
||||
$data[$entityName]['fields'][$fieldName . 'Columns'] = array(
|
||||
|
||||
@@ -28,15 +28,18 @@
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Core\Utils\Database\Schema;
|
||||
use Espo\Core\Utils\Util,
|
||||
Espo\ORM\Entity,
|
||||
Espo\Core\Exceptions\Error;
|
||||
|
||||
use Espo\Core\Utils\Util;
|
||||
use Espo\ORM\Entity;
|
||||
use Espo\Core\Exceptions\Error;
|
||||
use Espo\Core\Utils\Database\Schema\Utils as SchemaUtils;
|
||||
|
||||
class Converter
|
||||
{
|
||||
private $dbalSchema;
|
||||
|
||||
private $databaseSchema;
|
||||
|
||||
private $fileManager;
|
||||
|
||||
private $metadata;
|
||||
@@ -76,10 +79,11 @@ class Converter
|
||||
'foreign'
|
||||
);
|
||||
|
||||
public function __construct(\Espo\Core\Utils\Metadata $metadata, \Espo\Core\Utils\File\Manager $fileManager)
|
||||
public function __construct(\Espo\Core\Utils\Metadata $metadata, \Espo\Core\Utils\File\Manager $fileManager, \Espo\Core\Utils\Database\Schema\Schema $databaseSchema)
|
||||
{
|
||||
$this->metadata = $metadata;
|
||||
$this->fileManager = $fileManager;
|
||||
$this->databaseSchema = $databaseSchema;
|
||||
|
||||
$this->typeList = array_keys(\Doctrine\DBAL\Types\Type::getTypesMap());
|
||||
}
|
||||
@@ -110,6 +114,11 @@ class Converter
|
||||
return $this->dbalSchema;
|
||||
}
|
||||
|
||||
protected function getDatabaseSchema()
|
||||
{
|
||||
return $this->databaseSchema;
|
||||
}
|
||||
|
||||
/**
|
||||
* Schema convertation process
|
||||
*
|
||||
@@ -154,6 +163,9 @@ class Converter
|
||||
|
||||
$schema = $this->getSchema(true);
|
||||
|
||||
$indexList = SchemaUtils::getIndexList($ormMeta);
|
||||
$fieldListExceededIndexMaxLength = SchemaUtils::getFieldListExceededIndexMaxLength($ormMeta, $this->getDatabaseSchema()->getMaxIndexLength());
|
||||
|
||||
$tables = array();
|
||||
foreach ($ormMeta as $entityName => $entityParams) {
|
||||
|
||||
@@ -177,7 +189,7 @@ class Converter
|
||||
|
||||
$primaryColumns = array();
|
||||
$uniqueColumns = array();
|
||||
$indexList = array(); //list of indexes like array( array(comlumn1, column2), array(column3))
|
||||
|
||||
foreach ($entityParams['fields'] as $fieldName => $fieldParams) {
|
||||
|
||||
if ((isset($fieldParams['notStorable']) && $fieldParams['notStorable']) || in_array($fieldParams['type'], $this->notStorableTypes)) {
|
||||
@@ -197,37 +209,26 @@ class Converter
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isset($fieldListExceededIndexMaxLength[$entityName]) && in_array($fieldName, $fieldListExceededIndexMaxLength[$entityName])) {
|
||||
$fieldParams['utf8mb3'] = true;
|
||||
}
|
||||
|
||||
$columnName = Util::toUnderScore($fieldName);
|
||||
if (!$tables[$entityName]->hasColumn($columnName)) {
|
||||
$tables[$entityName]->addColumn($columnName, $fieldType, $this->getDbFieldParams($fieldParams));
|
||||
}
|
||||
|
||||
//add unique
|
||||
if ($fieldParams['type']!= 'id' && isset($fieldParams['unique'])) {
|
||||
if ($fieldParams['type'] != 'id' && isset($fieldParams['unique'])) {
|
||||
$uniqueColumns = $this->getKeyList($columnName, $fieldParams['unique'], $uniqueColumns);
|
||||
} //END: add unique
|
||||
|
||||
//add index. It can be defined in entityDefs as "index"
|
||||
if (isset($fieldParams['index'])) {
|
||||
$indexList = $this->getKeyList($columnName, $fieldParams['index'], $indexList);
|
||||
} //END: add index
|
||||
}
|
||||
|
||||
$tables[$entityName]->setPrimaryKey($primaryColumns);
|
||||
|
||||
//add indexes
|
||||
if (isset($entityParams['indexes']) && is_array($entityParams['indexes'])) {
|
||||
foreach ($entityParams['indexes'] as $indexName => $indexParams) {
|
||||
if (is_array($indexParams['columns'])) {
|
||||
$tableIndexName = $this->generateIndexName($indexName, $entityName);
|
||||
$indexList[$tableIndexName] = Util::toUnderScore($indexParams['columns']);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!empty($indexList)) {
|
||||
foreach($indexList as $indexName => $indexItem) {
|
||||
$tableIndexName = is_string($indexName) ? $indexName : null;
|
||||
$tables[$entityName]->addIndex($indexItem, $tableIndexName);
|
||||
if (!empty($indexList[$entityName])) {
|
||||
foreach($indexList[$entityName] as $indexName => $indexColumnList) {
|
||||
$tables[$entityName]->addIndex($indexColumnList, $indexName);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -285,14 +286,21 @@ class Converter
|
||||
}
|
||||
|
||||
$table = $this->getSchema()->createTable($tableName);
|
||||
$table->addColumn('id', 'int', array('length'=>$this->defaultLength['int'], 'autoincrement' => true, 'notnull' => true,)); //'unique' => true,
|
||||
$table->addColumn('id', 'int', $this->getDbFieldParams(array(
|
||||
'type' => 'int',
|
||||
'len' => $this->defaultLength['int'],
|
||||
'autoincrement' => true,
|
||||
)));
|
||||
|
||||
//add midKeys to a schema
|
||||
$uniqueIndex = array();
|
||||
foreach($relationParams['midKeys'] as $index => $midKey) {
|
||||
|
||||
$columnName = Util::toUnderScore($midKey);
|
||||
$table->addColumn($columnName, $this->idParams['dbType'], array('length'=>$this->idParams['len']));
|
||||
$table->addColumn($columnName, $this->idParams['dbType'], $this->getDbFieldParams(array(
|
||||
'type' => 'foreignId',
|
||||
'len' => $this->idParams['len'],
|
||||
)));
|
||||
$table->addIndex(array($columnName));
|
||||
|
||||
$uniqueIndex[] = $columnName;
|
||||
@@ -306,7 +314,7 @@ class Converter
|
||||
if (!isset($fieldParams['type'])) {
|
||||
$fieldParams = array_merge($fieldParams, array(
|
||||
'type' => 'varchar',
|
||||
'length' => $this->defaultLength['varchar'],
|
||||
'len' => $this->defaultLength['varchar'],
|
||||
));
|
||||
}
|
||||
|
||||
@@ -326,7 +334,11 @@ class Converter
|
||||
}
|
||||
//END: add unique indexes
|
||||
|
||||
$table->addColumn('deleted', 'bool', array('default' => 0));
|
||||
$table->addColumn('deleted', 'bool', $this->getDbFieldParams(array(
|
||||
'type' => 'bool',
|
||||
'default' => false,
|
||||
)));
|
||||
|
||||
$table->setPrimaryKey(array("id"));
|
||||
|
||||
return $table;
|
||||
@@ -337,13 +349,18 @@ class Converter
|
||||
$dbFieldParams = array();
|
||||
|
||||
foreach($this->allowedDbFieldParams as $espoName => $dbalName) {
|
||||
|
||||
if (isset($fieldParams[$espoName])) {
|
||||
$dbFieldParams[$dbalName] = $fieldParams[$espoName];
|
||||
}
|
||||
}
|
||||
|
||||
switch ($fieldParams['type']) {
|
||||
case 'id':
|
||||
case 'foreignId':
|
||||
case 'foreignType':
|
||||
$fieldParams['utf8mb3'] = true;
|
||||
break;
|
||||
|
||||
case 'array':
|
||||
case 'jsonArray':
|
||||
case 'text':
|
||||
@@ -360,12 +377,17 @@ class Converter
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
if ( isset($fieldParams['autoincrement']) && $fieldParams['autoincrement'] ) {
|
||||
if (isset($fieldParams['autoincrement']) && $fieldParams['autoincrement']) {
|
||||
$dbFieldParams['unique'] = true;
|
||||
$dbFieldParams['notnull'] = true;
|
||||
}
|
||||
|
||||
if (isset($fieldParams['utf8mb3']) && $fieldParams['utf8mb3']) {
|
||||
$dbFieldParams['platformOptions'] = array(
|
||||
'collation' => 'utf8_unicode_ci',
|
||||
);
|
||||
}
|
||||
|
||||
return $dbFieldParams;
|
||||
}
|
||||
|
||||
@@ -378,9 +400,11 @@ class Converter
|
||||
protected function getKeyList($columnName, $keyValue, array $keyList)
|
||||
{
|
||||
if ($keyValue === true) {
|
||||
$keyList[] = array($columnName);
|
||||
$tableIndexName = SchemaUtils::generateIndexName($columnName);
|
||||
$keyList[$tableIndexName] = array($columnName);
|
||||
} else if (is_string($keyValue)) {
|
||||
$keyList[$keyValue][] = $columnName;
|
||||
$tableIndexName = SchemaUtils::generateIndexName($keyValue);
|
||||
$keyList[$tableIndexName][] = $columnName;
|
||||
}
|
||||
|
||||
return $keyList;
|
||||
@@ -451,23 +475,6 @@ class Converter
|
||||
return $dependentEntities;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate index name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function generateIndexName($name, $entityName)
|
||||
{
|
||||
$names = array(
|
||||
'IDX',
|
||||
);
|
||||
|
||||
$names[] = strtoupper( Util::toUnderScore($entityName) );
|
||||
$names[] = strtoupper( Util::toUnderScore($name) );
|
||||
|
||||
return implode('_', $names);
|
||||
}
|
||||
|
||||
protected function loadData($path)
|
||||
{
|
||||
$tables = array();
|
||||
@@ -487,4 +494,4 @@ class Converter
|
||||
|
||||
return $tables;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -94,7 +94,7 @@ class Schema
|
||||
|
||||
$this->converter = new \Espo\Core\Utils\Database\Converter($this->metadata, $this->fileManager);
|
||||
|
||||
$this->schemaConverter = new Converter($this->metadata, $this->fileManager);
|
||||
$this->schemaConverter = new Converter($this->metadata, $this->fileManager, $this);
|
||||
|
||||
$this->ormMetadata = $ormMetadata;
|
||||
}
|
||||
@@ -312,5 +312,45 @@ class Schema
|
||||
}
|
||||
}
|
||||
|
||||
public function getMaxIndexLength()
|
||||
{
|
||||
$connection = $this->getConnection();
|
||||
$mysqlEngine = $this->getMysqlEngine();
|
||||
|
||||
switch ($mysqlEngine) {
|
||||
case 'InnoDB':
|
||||
$mysqlVersion = $this->getMysqlVersion();
|
||||
|
||||
if (version_compare($mysqlVersion, '10.0.0') >= 0) {
|
||||
return 767; //InnoDB, MariaDB
|
||||
}
|
||||
|
||||
if (version_compare($mysqlVersion, '5.7.0') >= 0) {
|
||||
return 3072; //InnoDB, MySQL 5.7+
|
||||
}
|
||||
|
||||
return 767; //InnoDB
|
||||
break;
|
||||
}
|
||||
|
||||
return 1000; //MyISAM
|
||||
}
|
||||
|
||||
protected function getMysqlVersion()
|
||||
{
|
||||
$connection = $this->getConnection();
|
||||
return $connection->fetchColumn("select version()");
|
||||
}
|
||||
|
||||
protected function getMysqlEngine()
|
||||
{
|
||||
$connection = $this->getConnection();
|
||||
$result = $connection->fetchColumn("SHOW TABLE STATUS WHERE Engine = 'MyISAM'");
|
||||
|
||||
if (!empty($result)) {
|
||||
return 'MyISAM';
|
||||
}
|
||||
|
||||
return 'InnoDB';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,166 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM - Open Source CRM application.
|
||||
* Copyright (C) 2014-2018 Yuri Kuznetsov, Taras Machyshyn, Oleksiy Avramenko
|
||||
* Website: http://www.espocrm.com
|
||||
*
|
||||
* EspoCRM is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* EspoCRM is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Core\Utils\Database\Schema;
|
||||
|
||||
use Espo\Core\Utils\Util;
|
||||
|
||||
class Utils
|
||||
{
|
||||
public static function getIndexList(array $ormMeta)
|
||||
{
|
||||
$indexList = array();
|
||||
|
||||
foreach ($ormMeta as $entityName => $entityParams) {
|
||||
foreach ($entityParams['fields'] as $fieldName => $fieldParams) {
|
||||
if (isset($fieldParams['notStorable']) && $fieldParams['notStorable']) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isset($fieldParams['index'])) {
|
||||
$keyValue = $fieldParams['index'];
|
||||
$columnName = Util::toUnderScore($fieldName);
|
||||
|
||||
if (!isset($indexList[$entityName])) {
|
||||
$indexList[$entityName] = [];
|
||||
}
|
||||
|
||||
if ($keyValue === true) {
|
||||
$tableIndexName = static::generateIndexName($columnName);
|
||||
$indexList[$entityName][$tableIndexName] = array($columnName);
|
||||
} else if (is_string($keyValue)) {
|
||||
$tableIndexName = static::generateIndexName($keyValue);
|
||||
$indexList[$entityName][$tableIndexName][] = $columnName;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($entityParams['indexes']) && is_array($entityParams['indexes'])) {
|
||||
foreach ($entityParams['indexes'] as $indexName => $indexParams) {
|
||||
if (is_array($indexParams['columns'])) {
|
||||
$tableIndexName = static::generateIndexName($indexName);
|
||||
$indexList[$entityName][$tableIndexName] = Util::toUnderScore($indexParams['columns']);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $indexList;
|
||||
}
|
||||
|
||||
public static function generateIndexName($name, $prefix = 'IDX', $maxLength = 30)
|
||||
{
|
||||
$nameList = [];
|
||||
$nameList[] = strtoupper($prefix);
|
||||
$nameList[] = strtoupper( Util::toUnderScore($name) );
|
||||
|
||||
return substr(implode('_', $nameList), 0, $maxLength);
|
||||
}
|
||||
|
||||
public static function getFieldListExceededIndexMaxLength(array $ormMeta, $indexMaxLength = 1000, $characterLength = 4)
|
||||
{
|
||||
$permittedFieldTypeList = [
|
||||
'varchar',
|
||||
];
|
||||
|
||||
$fields = array();
|
||||
|
||||
$indexList = static::getIndexList($ormMeta);
|
||||
|
||||
foreach ($indexList as $entityName => $indexes) {
|
||||
foreach ($indexes as $indexName => $columnList) {
|
||||
$indexLength = 0;
|
||||
foreach ($columnList as $columnName) {
|
||||
$fieldName = Util::toCamelCase($columnName);
|
||||
|
||||
if (!isset($ormMeta[$entityName]['fields'][$fieldName])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$indexLength += static::getFieldLength($ormMeta[$entityName]['fields'][$fieldName], $characterLength);
|
||||
}
|
||||
|
||||
if ($indexLength > $indexMaxLength) {
|
||||
foreach ($columnList as $columnName) {
|
||||
$fieldName = Util::toCamelCase($columnName);
|
||||
if (!isset($ormMeta[$entityName]['fields'][$fieldName])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$fieldType = static::getFieldType($ormMeta[$entityName]['fields'][$fieldName]);
|
||||
|
||||
if (in_array($fieldType, $permittedFieldTypeList)) {
|
||||
if (!isset($fields[$entityName]) || !in_array($fieldName, $fields[$entityName])) {
|
||||
$fields[$entityName][] = $fieldName;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $fields;
|
||||
}
|
||||
|
||||
protected static function getFieldLength(array $ormFieldDefs, $characterLength = 4)
|
||||
{
|
||||
$length = 0;
|
||||
|
||||
if (isset($ormFieldDefs['notStorable']) && $ormFieldDefs['notStorable']) {
|
||||
return $length;
|
||||
}
|
||||
|
||||
$defaultLength = array(
|
||||
'datetime' => 8,
|
||||
'time' => 4,
|
||||
'int' => 4,
|
||||
'bool' => 1,
|
||||
'float' => 4,
|
||||
'varchar' => 255,
|
||||
);
|
||||
|
||||
$type = static::getFieldType($ormFieldDefs);
|
||||
|
||||
$length = isset($defaultLength[$type]) ? $defaultLength[$type] : $length;
|
||||
$length = isset($ormFieldDefs['len']) ? $ormFieldDefs['len'] : $length;
|
||||
|
||||
switch ($type) {
|
||||
case 'varchar':
|
||||
$length = $length * $characterLength;
|
||||
break;
|
||||
}
|
||||
|
||||
return $length;
|
||||
}
|
||||
|
||||
protected static function getFieldType(array $ormFieldDefs)
|
||||
{
|
||||
return isset($ormFieldDefs['dbType']) ? $ormFieldDefs['dbType'] : $ormFieldDefs['type'];
|
||||
}
|
||||
}
|
||||
@@ -28,6 +28,7 @@
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Core\Utils\Database\Schema\rebuildActions;
|
||||
|
||||
class Currency extends \Espo\Core\Utils\Database\Schema\BaseRebuildActions
|
||||
{
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
namespace Espo\Core\Utils;
|
||||
|
||||
use \Espo\Core\Exceptions\Error;
|
||||
use \Espo\Core\Exceptions\BadRequest;
|
||||
use \Espo\Core\Exceptions\Forbidden;
|
||||
use \Espo\Core\Exceptions\Conflict;
|
||||
use \Espo\Core\Utils\Json;
|
||||
@@ -80,6 +81,11 @@ class EntityManager
|
||||
return $this->language;
|
||||
}
|
||||
|
||||
protected function getBaseLanguage()
|
||||
{
|
||||
return $this->container->get('baseLanguage');
|
||||
}
|
||||
|
||||
protected function getFileManager()
|
||||
{
|
||||
return $this->fileManager;
|
||||
@@ -122,17 +128,36 @@ class EntityManager
|
||||
return false;
|
||||
}
|
||||
|
||||
public function create($name, $type, $params = array())
|
||||
public function create($name, $type, $params = [], $replaceData = [])
|
||||
{
|
||||
$name = ucfirst($name);
|
||||
$name = trim($name);
|
||||
|
||||
if (empty($name) || empty($type)) {
|
||||
throw new BadRequest();
|
||||
}
|
||||
|
||||
if (strlen($name) > 100) {
|
||||
throw new Error('Entity name should not be longer than 100.');
|
||||
}
|
||||
|
||||
if (is_numeric($name[0])) {
|
||||
throw new Error('Bad entity name.');
|
||||
}
|
||||
|
||||
if (!in_array($type, $this->getMetadata()->get(['app', 'entityTemplateList'], []))) {
|
||||
throw new Error('Type \''.$type.'\' does not exist.');
|
||||
}
|
||||
|
||||
$templateDefs = $this->getMetadata()->get(['app', 'entityTemplates', $type], []);
|
||||
|
||||
if (!empty($templateDefs['isNotCreatable']) && empty($params['forceCreate'])) {
|
||||
throw new Error('Type \''.$type.'\' is not creatable.');
|
||||
}
|
||||
|
||||
if ($this->getMetadata()->get('scopes.' . $name)) {
|
||||
throw new Conflict('Entity \''.$name.'\' already exists.');
|
||||
}
|
||||
if (empty($name) || empty($type)) {
|
||||
throw new Error();
|
||||
}
|
||||
|
||||
if ($this->checkControllerExists($name)) {
|
||||
throw new Conflict('Entity name \''.$name.'\' is not allowed.');
|
||||
@@ -149,9 +174,20 @@ class EntityManager
|
||||
|
||||
$normalizedName = Util::normilizeClassName($name);
|
||||
|
||||
$templateNamespace = "\Espo\Core\Templates";
|
||||
$templatePath = "application/Espo/Core/Templates";
|
||||
|
||||
$templateModuleName = null;
|
||||
if (!empty($templateDefs['module'])) {
|
||||
$templateModuleName = $templateDefs['module'];
|
||||
$normalizedTemplateModuleName = Util::normilizeClassName($templateModuleName);
|
||||
$templateNamespace = "\Espo\Modules\\{$normalizedTemplateModuleName}\Core\Templates";
|
||||
$templatePath = "application/Espo/Modules/".$normalizedTemplateModuleName."/Core/Templates";
|
||||
}
|
||||
|
||||
$contents = "<" . "?" . "php\n\n".
|
||||
"namespace Espo\Custom\Entities;\n\n".
|
||||
"class {$normalizedName} extends \Espo\Core\Templates\Entities\\{$type}\n".
|
||||
"class {$normalizedName} extends {$templateNamespace}\Entities\\{$type}\n".
|
||||
"{\n".
|
||||
" protected \$entityType = \"$name\";\n".
|
||||
"}\n";
|
||||
@@ -161,7 +197,7 @@ class EntityManager
|
||||
|
||||
$contents = "<" . "?" . "php\n\n".
|
||||
"namespace Espo\Custom\Controllers;\n\n".
|
||||
"class {$normalizedName} extends \Espo\Core\Templates\Controllers\\{$type}\n".
|
||||
"class {$normalizedName} extends {$templateNamespace}\Controllers\\{$type}\n".
|
||||
"{\n".
|
||||
"}\n";
|
||||
$filePath = "custom/Espo/Custom/Controllers/{$normalizedName}.php";
|
||||
@@ -169,7 +205,7 @@ class EntityManager
|
||||
|
||||
$contents = "<" . "?" . "php\n\n".
|
||||
"namespace Espo\Custom\Services;\n\n".
|
||||
"class {$normalizedName} extends \Espo\Core\Templates\Services\\{$type}\n".
|
||||
"class {$normalizedName} extends {$templateNamespace}\Services\\{$type}\n".
|
||||
"{\n".
|
||||
"}\n";
|
||||
$filePath = "custom/Espo/Custom/Services/{$normalizedName}.php";
|
||||
@@ -177,17 +213,17 @@ class EntityManager
|
||||
|
||||
$contents = "<" . "?" . "php\n\n".
|
||||
"namespace Espo\Custom\Repositories;\n\n".
|
||||
"class {$normalizedName} extends \Espo\Core\Templates\Repositories\\{$type}\n".
|
||||
"class {$normalizedName} extends {$templateNamespace}\Repositories\\{$type}\n".
|
||||
"{\n".
|
||||
"}\n";
|
||||
|
||||
$filePath = "custom/Espo/Custom/Repositories/{$normalizedName}.php";
|
||||
$this->getFileManager()->putContents($filePath, $contents);
|
||||
|
||||
if (file_exists('application/Espo/Core/Templates/SelectManagers/' . $type . '.php')) {
|
||||
if (file_exists($templatePath . '/SelectManagers/' . $type . '.php')) {
|
||||
$contents = "<" . "?" . "php\n\n".
|
||||
"namespace Espo\Custom\SelectManagers;\n\n".
|
||||
"class {$normalizedName} extends \Espo\Core\Templates\SelectManagers\\{$type}\n".
|
||||
"class {$normalizedName} extends {$templateNamespace}\SelectManagers\\{$type}\n".
|
||||
"{\n".
|
||||
"}\n";
|
||||
|
||||
@@ -214,19 +250,26 @@ class EntityManager
|
||||
|
||||
$languageList = $this->getConfig()->get('languageList', []);
|
||||
foreach ($languageList as $language) {
|
||||
$filePath = 'application/Espo/Core/Templates/i18n/' . $language . '/' . $type . '.json';
|
||||
$filePath = $templatePath . '/i18n/' . $language . '/' . $type . '.json';
|
||||
if (!file_exists($filePath)) continue;
|
||||
$languageContents = $this->getFileManager()->getContents($filePath);
|
||||
$languageContents = str_replace('{entityType}', $name, $languageContents);
|
||||
$languageContents = str_replace('{entityTypeTranslated}', $labelSingular, $languageContents);
|
||||
foreach ($replaceData as $key => $value) {
|
||||
$languageContents = str_replace('{'.$key.'}', $value, $languageContents);
|
||||
}
|
||||
|
||||
$destinationFilePath = 'custom/Espo/Custom/Resources/i18n/' . $language . '/' . $name . '.json';
|
||||
$this->getFileManager()->putContents($destinationFilePath, $languageContents);
|
||||
}
|
||||
|
||||
$filePath = "application/Espo/Core/Templates/Metadata/{$type}/scopes.json";
|
||||
$filePath = $templatePath . "/Metadata/{$type}/scopes.json";
|
||||
$scopesDataContents = $this->getFileManager()->getContents($filePath);
|
||||
$scopesDataContents = str_replace('{entityType}', $name, $scopesDataContents);
|
||||
foreach ($replaceData as $key => $value) {
|
||||
$scopesDataContents = str_replace('{'.$key.'}', $value, $scopesDataContents);
|
||||
}
|
||||
|
||||
$scopesData = Json::decode($scopesDataContents, true);
|
||||
|
||||
$scopesData['stream'] = $stream;
|
||||
@@ -236,28 +279,38 @@ class EntityManager
|
||||
$scopesData['object'] = true;
|
||||
$scopesData['isCustom'] = true;
|
||||
|
||||
if (!empty($templateDefs['isNotRemovable']) ||!empty($params['isNotRemovable'])) {
|
||||
$scopesData['isNotRemovable'] = true;
|
||||
}
|
||||
|
||||
$this->getMetadata()->set('scopes', $name, $scopesData);
|
||||
|
||||
$filePath = "application/Espo/Core/Templates/Metadata/{$type}/entityDefs.json";
|
||||
$filePath = $templatePath . "/Metadata/{$type}/entityDefs.json";
|
||||
$entityDefsDataContents = $this->getFileManager()->getContents($filePath);
|
||||
$entityDefsDataContents = str_replace('{entityType}', $name, $entityDefsDataContents);
|
||||
$entityDefsDataContents = str_replace('{tableName}', $this->getEntityManager()->getQuery()->toDb($name), $entityDefsDataContents);
|
||||
foreach ($replaceData as $key => $value) {
|
||||
$entityDefsDataContents = str_replace('{'.$key.'}', $value, $entityDefsDataContents);
|
||||
}
|
||||
$entityDefsData = Json::decode($entityDefsDataContents, true);
|
||||
$this->getMetadata()->set('entityDefs', $name, $entityDefsData);
|
||||
|
||||
$filePath = "application/Espo/Core/Templates/Metadata/{$type}/clientDefs.json";
|
||||
$filePath = $templatePath . "/Metadata/{$type}/clientDefs.json";
|
||||
$clientDefsContents = $this->getFileManager()->getContents($filePath);
|
||||
$clientDefsContents = str_replace('{entityType}', $name, $clientDefsContents);
|
||||
foreach ($replaceData as $key => $value) {
|
||||
$clientDefsContents = str_replace('{'.$key.'}', $value, $clientDefsContents);
|
||||
}
|
||||
$clientDefsData = Json::decode($clientDefsContents, true);
|
||||
$this->getMetadata()->set('clientDefs', $name, $clientDefsData);
|
||||
|
||||
$this->getLanguage()->set('Global', 'scopeNames', $name, $labelSingular);
|
||||
$this->getLanguage()->set('Global', 'scopeNamesPlural', $name, $labelPlural);
|
||||
$this->getBaseLanguage()->set('Global', 'scopeNames', $name, $labelSingular);
|
||||
$this->getBaseLanguage()->set('Global', 'scopeNamesPlural', $name, $labelPlural);
|
||||
|
||||
$this->getMetadata()->save();
|
||||
$this->getLanguage()->save();
|
||||
$this->getBaseLanguage()->save();
|
||||
|
||||
$layoutsPath = "application/Espo/Core/Templates/Layouts/{$type}";
|
||||
$layoutsPath = $templatePath . "/Layouts/{$type}";
|
||||
if ($this->getFileManager()->isDir($layoutsPath)) {
|
||||
$this->getFileManager()->copy($layoutsPath, 'custom/Espo/Custom/Resources/layouts/' . $name);
|
||||
}
|
||||
@@ -284,6 +337,11 @@ class EntityManager
|
||||
$this->getMetadata()->set('scopes', $name, $scopeData);
|
||||
}
|
||||
|
||||
$isCustom = false;
|
||||
if (!empty($scopeData['isCustom'])) {
|
||||
$isCustom = true;
|
||||
}
|
||||
|
||||
if (array_key_exists('statusField', $data)) {
|
||||
$scopeData['statusField'] = $data['statusField'];
|
||||
$this->getMetadata()->set('scopes', $name, $scopeData);
|
||||
@@ -291,14 +349,23 @@ class EntityManager
|
||||
|
||||
if (!empty($data['labelSingular'])) {
|
||||
$labelSingular = $data['labelSingular'];
|
||||
$this->getLanguage()->set('Global', 'scopeNames', $name, $labelSingular);
|
||||
$labelCreate = $this->getLanguage()->translate('Create') . ' ' . $labelSingular;
|
||||
|
||||
$this->getLanguage()->set('Global', 'scopeNames', $name, $labelSingular);
|
||||
$this->getLanguage()->set($name, 'labels', 'Create ' . $name, $labelCreate);
|
||||
|
||||
if ($isCustom) {
|
||||
$this->getBaseLanguage()->set('Global', 'scopeNames', $name, $labelSingular);
|
||||
$this->getBaseLanguage()->set($name, 'labels', 'Create ' . $name, $labelCreate);
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($data['labelPlural'])) {
|
||||
$labelPlural = $data['labelPlural'];
|
||||
$this->getLanguage()->set('Global', 'scopeNamesPlural', $name, $labelPlural);
|
||||
if ($isCustom) {
|
||||
$this->getBaseLanguage()->set('Global', 'scopeNamesPlural', $name, $labelPlural);
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($data['sortBy'])) {
|
||||
@@ -322,11 +389,16 @@ class EntityManager
|
||||
|
||||
$this->getMetadata()->save();
|
||||
$this->getLanguage()->save();
|
||||
if ($isCustom) {
|
||||
if ($this->getLanguage()->getLanguage() !== $this->getBaseLanguage()->getLanguage()) {
|
||||
$this->getBaseLanguage()->save();
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function delete($name)
|
||||
public function delete($name, $params = [])
|
||||
{
|
||||
if (!$this->isCustom($name)) {
|
||||
throw new Forbidden;
|
||||
@@ -336,6 +408,19 @@ class EntityManager
|
||||
|
||||
$type = $this->getMetadata()->get(['scopes', $name, 'type']);
|
||||
|
||||
$isNotRemovable = $this->getMetadata()->get(['scopes', $name, 'isNotRemovable']);
|
||||
|
||||
$templateDefs = $this->getMetadata()->get(['app', 'entityTemplates', $type], []);
|
||||
|
||||
$templateModuleName = null;
|
||||
if (!empty($templateDefs['module'])) {
|
||||
$templateModuleName = $templateDefs['module'];
|
||||
}
|
||||
|
||||
if ((!empty($templateDefs['isNotRemovable']) || $isNotRemovable) && empty($params['forceRemove'])) {
|
||||
throw new Error('Type \''.$type.'\' is not removable.');
|
||||
}
|
||||
|
||||
$unsets = array(
|
||||
'entityDefs',
|
||||
'clientDefs',
|
||||
@@ -377,11 +462,18 @@ class EntityManager
|
||||
try {
|
||||
$this->getLanguage()->delete('Global', 'scopeNames', $name);
|
||||
$this->getLanguage()->delete('Global', 'scopeNamesPlural', $name);
|
||||
|
||||
$this->getBaseLanguage()->delete('Global', 'scopeNames', $name);
|
||||
$this->getBaseLanguage()->delete('Global', 'scopeNamesPlural', $name);
|
||||
} catch (\Exception $e) {}
|
||||
|
||||
$this->getMetadata()->save();
|
||||
$this->getLanguage()->save();
|
||||
|
||||
if ($this->getLanguage()->getLanguage() !== $this->getBaseLanguage()->getLanguage()) {
|
||||
$this->getBaseLanguage()->save();
|
||||
}
|
||||
|
||||
if ($type) {
|
||||
$this->processHook('afterRemove', $type, $name);
|
||||
}
|
||||
@@ -414,6 +506,18 @@ class EntityManager
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($link) || empty($linkForeign)) {
|
||||
throw new BadRequest();
|
||||
}
|
||||
|
||||
if (strlen($link) > 255 || strlen($linkForeign) > 255) {
|
||||
throw new Error('Link name should not be longer than 255.');
|
||||
}
|
||||
|
||||
if (is_numeric($link[0]) || is_numeric($linkForeign[0])) {
|
||||
throw new Error('Bad link name.');
|
||||
}
|
||||
|
||||
$linkMultipleField = false;
|
||||
if (!empty($params['linkMultipleField'])) {
|
||||
$linkMultipleField = true;
|
||||
@@ -619,9 +723,16 @@ class EntityManager
|
||||
$this->getLanguage()->set($entity, 'links', $link, $label);
|
||||
$this->getLanguage()->set($entityForeign, 'fields', $linkForeign, $labelForeign);
|
||||
$this->getLanguage()->set($entityForeign, 'links', $linkForeign, $labelForeign);
|
||||
|
||||
$this->getLanguage()->save();
|
||||
|
||||
if ($this->getLanguage()->getLanguage() !== $this->getBaseLanguage()->getLanguage()) {
|
||||
$this->getBaseLanguage()->set($entity, 'fields', $link, $label);
|
||||
$this->getBaseLanguage()->set($entity, 'links', $link, $label);
|
||||
$this->getBaseLanguage()->set($entityForeign, 'fields', $linkForeign, $labelForeign);
|
||||
$this->getBaseLanguage()->set($entityForeign, 'links', $linkForeign, $labelForeign);
|
||||
$this->getBaseLanguage()->save();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -632,9 +743,6 @@ class EntityManager
|
||||
$entityForeign = $params['entityForeign'];
|
||||
$linkForeign = $params['linkForeign'];
|
||||
|
||||
$label = $params['label'];
|
||||
$labelForeign = $params['labelForeign'];
|
||||
|
||||
if (empty($entity) || empty($entityForeign)) {
|
||||
throw new Error();
|
||||
}
|
||||
@@ -642,6 +750,8 @@ class EntityManager
|
||||
throw new Error();
|
||||
}
|
||||
|
||||
$isCustom = $this->getMetadata()->get("entityDefs.{$entity}.links.{$link}.isCustom");
|
||||
|
||||
if (
|
||||
$this->getMetadata()->get("entityDefs.{$entity}.links.{$link}.type") == 'hasMany'
|
||||
&&
|
||||
@@ -726,13 +836,41 @@ class EntityManager
|
||||
}
|
||||
}
|
||||
|
||||
$this->getLanguage()->set($entity, 'fields', $link, $label);
|
||||
$this->getLanguage()->set($entity, 'links', $link, $label);
|
||||
$this->getLanguage()->set($entityForeign, 'fields', $linkForeign, $labelForeign);
|
||||
$this->getLanguage()->set($entityForeign, 'links', $linkForeign, $labelForeign);
|
||||
$label = null;
|
||||
if (isset($params['label'])) {
|
||||
$label = $params['label'];
|
||||
}
|
||||
$labelForeign = null;
|
||||
if (isset($params['labelForeign'])) {
|
||||
$labelForeign = $params['labelForeign'];
|
||||
}
|
||||
|
||||
if ($label) {
|
||||
$this->getLanguage()->set($entity, 'fields', $link, $label);
|
||||
$this->getLanguage()->set($entity, 'links', $link, $label);
|
||||
}
|
||||
|
||||
if ($labelForeign) {
|
||||
$this->getLanguage()->set($entityForeign, 'fields', $linkForeign, $labelForeign);
|
||||
$this->getLanguage()->set($entityForeign, 'links', $linkForeign, $labelForeign);
|
||||
}
|
||||
|
||||
$this->getLanguage()->save();
|
||||
|
||||
if ($isCustom) {
|
||||
if ($this->getBaseLanguage()->getLanguage() !== $this->getBaseLanguage()->getLanguage()) {
|
||||
if ($label) {
|
||||
$this->getBaseLanguage()->set($entity, 'fields', $link, $label);
|
||||
$this->getBaseLanguage()->set($entity, 'links', $link, $label);
|
||||
}
|
||||
if ($labelForeign) {
|
||||
$this->getBaseLanguage()->set($entityForeign, 'fields', $linkForeign, $labelForeign);
|
||||
$this->getBaseLanguage()->set($entityForeign, 'links', $linkForeign, $labelForeign);
|
||||
}
|
||||
$this->getBaseLanguage()->save();
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -786,8 +924,20 @@ class EntityManager
|
||||
|
||||
protected function getHook($type)
|
||||
{
|
||||
$templateDefs = $this->getMetadata()->get(['app', 'entityTemplates', $type], []);
|
||||
|
||||
$className = '\\Espo\\Core\\Utils\\EntityManager\\Hooks\\' . $type . 'Type';
|
||||
$className = $this->getMetadata()->get(['entityTemplates', $type, 'hookClassName'], $className);
|
||||
|
||||
$templateModuleName = null;
|
||||
if (!empty($templateDefs['module'])) {
|
||||
$templateModuleName = $templateDefs['module'];
|
||||
$normalizedTemplateModuleName = Util::normilizeClassName($templateModuleName);
|
||||
$className = '\\Espo\\Modules\\'.$normalizedTemplateModuleName.'\\Core\\Utils\\EntityManager\\Hooks\\' . $type . 'Type';
|
||||
}
|
||||
|
||||
|
||||
|
||||
$className = $this->getMetadata()->get(['app', 'entityTemplates', $type, 'hookClassName'], $className);
|
||||
|
||||
if (class_exists($className)) {
|
||||
$hook = new $className();
|
||||
|
||||
@@ -45,29 +45,28 @@ class FieldManager
|
||||
|
||||
private $container;
|
||||
|
||||
protected $metadataType = 'entityDefs';
|
||||
|
||||
protected $customOptionName = 'isCustom';
|
||||
|
||||
protected $forbiddenFieldNameList = ['id', 'deleted'];
|
||||
|
||||
public function __construct(Metadata $metadata, Language $language, Container $container = null)
|
||||
public function __construct(Container $container = null)
|
||||
{
|
||||
$this->metadata = $metadata;
|
||||
$this->language = $language;
|
||||
$this->container = $container;
|
||||
|
||||
$this->metadataHelper = new \Espo\Core\Utils\Metadata\Helper($this->metadata);
|
||||
$this->metadataHelper = new \Espo\Core\Utils\Metadata\Helper($this->getMetadata());
|
||||
}
|
||||
|
||||
protected function getMetadata()
|
||||
{
|
||||
return $this->metadata;
|
||||
return $this->container->get('metadata');
|
||||
}
|
||||
|
||||
protected function getLanguage()
|
||||
{
|
||||
return $this->language;
|
||||
return $this->container->get('language');
|
||||
}
|
||||
|
||||
protected function getBaseLanguage()
|
||||
{
|
||||
return $this->container->get('baseLanguage');
|
||||
}
|
||||
|
||||
protected function getMetadataHelper()
|
||||
@@ -95,6 +94,18 @@ class FieldManager
|
||||
|
||||
public function create($scope, $name, $fieldDefs)
|
||||
{
|
||||
if (empty($name)) {
|
||||
throw new BadRequest();
|
||||
}
|
||||
|
||||
if (strlen($name) > 255) {
|
||||
throw new Error('Field name should not be longer than 255.');
|
||||
}
|
||||
|
||||
if (is_numeric($name[0])) {
|
||||
throw new Error('Bad field name.');
|
||||
}
|
||||
|
||||
$existingField = $this->getFieldDefs($scope, $name);
|
||||
if (isset($existingField)) {
|
||||
throw new Conflict('Field ['.$name.'] exists in '.$scope);
|
||||
@@ -119,18 +130,25 @@ class FieldManager
|
||||
$name = trim($name);
|
||||
$this->isChanged = false;
|
||||
|
||||
/*Add option to metadata to identify the custom field*/
|
||||
if (!$this->isCore($scope, $name)) {
|
||||
$fieldDefs[$this->customOptionName] = true;
|
||||
$fieldDefs['isCustom'] = true;
|
||||
}
|
||||
|
||||
$isCustom = false;
|
||||
if (!empty($fieldDefs['isCustom'])) {
|
||||
$isCustom = true;
|
||||
}
|
||||
|
||||
$result = true;
|
||||
$isLabelChanged = false;
|
||||
|
||||
if (isset($fieldDefs['label'])) {
|
||||
$isLabelChanged |= $this->setLabel($scope, $name, $fieldDefs['label']);
|
||||
$this->setLabel($scope, $name, $fieldDefs['label'], $isNew, $isCustom);
|
||||
$isLabelChanged = true;
|
||||
}
|
||||
if (isset($fieldDefs['tooltipText'])) {
|
||||
$isLabelChanged |= $this->setTooltipText($scope, $name, $fieldDefs['tooltipText']);
|
||||
$this->setTooltipText($scope, $name, $fieldDefs['tooltipText'], $isNew, $isCustom);
|
||||
$isLabelChanged = true;
|
||||
}
|
||||
|
||||
$type = isset($fieldDefs['type']) ? $fieldDefs['type'] : $type = $this->getMetadata()->get(['entityDefs', $scope, 'fields', $name, 'type']);
|
||||
@@ -146,7 +164,8 @@ class FieldManager
|
||||
unset($translatedOptions['_empty_']);
|
||||
}
|
||||
|
||||
$isLabelChanged |= $this->setTranslatedOptions($scope, $name, $translatedOptions);
|
||||
$this->setTranslatedOptions($scope, $name, $translatedOptions, $isNew, $isCustom);
|
||||
$isLabelChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -163,17 +182,19 @@ class FieldManager
|
||||
$subFieldName = $name . ucfirst($partField);
|
||||
$subFieldLabel = $fieldDefs['label'] . ' ' . $partLabel;
|
||||
}
|
||||
$isLabelChanged |= $this->setLabel($scope, $subFieldName, $subFieldLabel);
|
||||
$this->setLabel($scope, $subFieldName, $subFieldLabel, $isNew, $isCustom);
|
||||
$isLabelChanged = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($isLabelChanged) {
|
||||
$result &= $this->getLanguage()->save();
|
||||
|
||||
if (isset($fieldDefs['tooltipText'])) {
|
||||
$this->getDefaultLanguage()->save();
|
||||
$this->getLanguage()->save();
|
||||
if ($isNew || $isCustom) {
|
||||
if ($this->getBaseLanguage()->getLanguage() !== $this->getLanguage()->getLanguage()) {
|
||||
$this->getBaseLanguage()->save();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -185,9 +206,7 @@ class FieldManager
|
||||
if (array_key_exists('dynamicLogicVisible', $fieldDefs)) {
|
||||
if (!is_null($fieldDefs['dynamicLogicVisible'])) {
|
||||
$this->prepareClientDefsFieldsDynamicLogic($clientDefs, $name);
|
||||
$clientDefs['dynamicLogic']['fields'][$name]['visible'] = array(
|
||||
'conditionGroup' => $fieldDefs['dynamicLogicVisible']
|
||||
);
|
||||
$clientDefs['dynamicLogic']['fields'][$name]['visible'] = $fieldDefs['dynamicLogicVisible'];
|
||||
$metadataToBeSaved = true;
|
||||
$clientDefsToBeSet = true;
|
||||
} else {
|
||||
@@ -198,15 +217,12 @@ class FieldManager
|
||||
$clientDefsToBeSet = true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (array_key_exists('dynamicLogicReadOnly', $fieldDefs)) {
|
||||
if (!is_null($fieldDefs['dynamicLogicReadOnly'])) {
|
||||
$this->prepareClientDefsFieldsDynamicLogic($clientDefs, $name);
|
||||
$clientDefs['dynamicLogic']['fields'][$name]['readOnly'] = array(
|
||||
'conditionGroup' => $fieldDefs['dynamicLogicReadOnly']
|
||||
);
|
||||
$clientDefs['dynamicLogic']['fields'][$name]['readOnly'] = $fieldDefs['dynamicLogicReadOnly'];
|
||||
$metadataToBeSaved = true;
|
||||
$clientDefsToBeSet = true;
|
||||
} else {
|
||||
@@ -222,9 +238,7 @@ class FieldManager
|
||||
if (array_key_exists('dynamicLogicRequired', $fieldDefs)) {
|
||||
if (!is_null($fieldDefs['dynamicLogicRequired'])) {
|
||||
$this->prepareClientDefsFieldsDynamicLogic($clientDefs, $name);
|
||||
$clientDefs['dynamicLogic']['fields'][$name]['required'] = array(
|
||||
'conditionGroup' => $fieldDefs['dynamicLogicRequired']
|
||||
);
|
||||
$clientDefs['dynamicLogic']['fields'][$name]['required'] = $fieldDefs['dynamicLogicRequired'];
|
||||
$metadataToBeSaved = true;
|
||||
$clientDefsToBeSet = true;
|
||||
} else {
|
||||
@@ -337,6 +351,11 @@ class FieldManager
|
||||
}
|
||||
}
|
||||
|
||||
$this->getLanguage()->save();
|
||||
if ($this->getBaseLanguage()->getLanguage() !== $this->getLanguage()->getLanguage()) {
|
||||
$this->getBaseLanguage()->save();
|
||||
}
|
||||
|
||||
$this->processHook('afterRemove', $type, $scope, $name);
|
||||
|
||||
return (bool) $res;
|
||||
@@ -362,47 +381,37 @@ class FieldManager
|
||||
$this->getLanguage()->delete($scope, 'fields', $name);
|
||||
$this->getLanguage()->delete($scope, 'options', $name);
|
||||
$this->getLanguage()->delete($scope, 'tooltips', $name);
|
||||
$this->getDefaultLanguage()->delete($scope, 'tooltips', $name);
|
||||
|
||||
$this->getLanguage()->save();
|
||||
$this->getDefaultLanguage()->save();
|
||||
}
|
||||
|
||||
protected function setTranslatedOptions($scope, $name, $value)
|
||||
protected function setTranslatedOptions($scope, $name, $value, $isNew, $isCustom)
|
||||
{
|
||||
if (!$this->isLabelChanged($scope, 'options', $name, $value)) {
|
||||
return false;
|
||||
if ($isNew || $isCustom) {
|
||||
$this->getBaseLanguage()->set($scope, 'options', $name, $value);
|
||||
}
|
||||
|
||||
$this->getLanguage()->set($scope, 'options', $name, $value);
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function setLabel($scope, $name, $value)
|
||||
protected function setLabel($scope, $name, $value, $isNew, $isCustom)
|
||||
{
|
||||
if (!$this->isLabelChanged($scope, 'fields', $name, $value)) {
|
||||
return false;
|
||||
if ($isNew || $isCustom) {
|
||||
$this->getBaseLanguage()->set($scope, 'fields', $name, $value);
|
||||
}
|
||||
|
||||
$this->getLanguage()->set($scope, 'fields', $name, $value);
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function setTooltipText($scope, $name, $value)
|
||||
protected function setTooltipText($scope, $name, $value, $isNew, $isCustom)
|
||||
{
|
||||
if (!$this->isLabelChanged($scope, 'tooltips', $name, $value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($value && $value !== '') {
|
||||
$this->getLanguage()->set($scope, 'tooltips', $name, $value);
|
||||
$this->getDefaultLanguage()->set($scope, 'tooltips', $name, $value);
|
||||
$this->getBaseLanguage()->set($scope, 'tooltips', $name, $value);
|
||||
} else {
|
||||
$this->getLanguage()->delete($scope, 'tooltips', $name);
|
||||
$this->getDefaultLanguage()->delete($scope, 'tooltips', $name);
|
||||
$this->getBaseLanguage()->delete($scope, 'tooltips', $name);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function deleteLabel($scope, $name)
|
||||
@@ -410,10 +419,10 @@ class FieldManager
|
||||
$this->getLanguage()->delete($scope, 'fields', $name);
|
||||
$this->getLanguage()->delete($scope, 'tooltips', $name);
|
||||
$this->getLanguage()->delete($scope, 'options', $name);
|
||||
$this->getDefaultLanguage()->delete($scope, 'tooltips', $name);
|
||||
|
||||
$this->getLanguage()->save();
|
||||
$this->getDefaultLanguage()->save();
|
||||
$this->getBaseLanguage()->delete($scope, 'fields', $name);
|
||||
$this->getBaseLanguage()->delete($scope, 'tooltips', $name);
|
||||
$this->getBaseLanguage()->delete($scope, 'options', $name);
|
||||
}
|
||||
|
||||
protected function getFieldDefs($scope, $name)
|
||||
@@ -557,16 +566,7 @@ class FieldManager
|
||||
return $this->isChanged;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if label is changed
|
||||
*
|
||||
* @param string $scope
|
||||
* @param string $category
|
||||
* @param string $name
|
||||
* @param mixed $newLabel
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
|
||||
protected function isLabelChanged($scope, $category, $name, $newLabel)
|
||||
{
|
||||
$currentLabel = $this->getLanguage()->get([$scope, $category, $name]);
|
||||
@@ -578,11 +578,7 @@ class FieldManager
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Only for update method
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
|
||||
public function isChanged()
|
||||
{
|
||||
return $this->isChanged;
|
||||
@@ -598,7 +594,7 @@ class FieldManager
|
||||
protected function isCore($scope, $name)
|
||||
{
|
||||
$existingField = $this->getFieldDefs($scope, $name);
|
||||
if (isset($existingField) && (!isset($existingField[$this->customOptionName]) || !$existingField[$this->customOptionName])) {
|
||||
if (isset($existingField) && (!isset($existingField['isCustom']) || !$existingField['isCustom'])) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -33,6 +33,8 @@ class FieldManagerUtil
|
||||
{
|
||||
private $metadata;
|
||||
|
||||
private $fieldByTypeListCache = [];
|
||||
|
||||
public function __construct(Metadata $metadata)
|
||||
{
|
||||
$this->metadata = $metadata;
|
||||
@@ -95,4 +97,23 @@ class FieldManagerUtil
|
||||
return array_merge($this->getActualAttributeList($scope, $name), $this->getNotActualAttributeList($scope, $name));
|
||||
}
|
||||
|
||||
}
|
||||
public function getFieldByTypeList($scope, $type)
|
||||
{
|
||||
if (!array_key_exists($scope, $this->fieldByTypeListCache)) {
|
||||
$this->fieldByTypeListCache[$scope] = [];
|
||||
}
|
||||
|
||||
if (!array_key_exists($type, $this->fieldByTypeListCache[$scope])) {
|
||||
$fieldDefs = $this->getMetadata()->get(['entityDefs', $scope, 'fields'], []);
|
||||
$list = [];
|
||||
foreach ($fieldDefs as $field => $defs) {
|
||||
if (isset($defs['type']) && $defs['type'] === $type) {
|
||||
$list[] = $field;
|
||||
}
|
||||
}
|
||||
$this->fieldByTypeListCache[$scope][$type] = $list;
|
||||
}
|
||||
|
||||
return $this->fieldByTypeListCache[$scope][$type];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,8 +28,9 @@
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Core\Utils\File;
|
||||
use Espo\Core\Utils,
|
||||
Espo\Core\Exceptions\Error;
|
||||
|
||||
use Espo\Core\Utils;
|
||||
use Espo\Core\Exceptions\Error;
|
||||
|
||||
class Manager
|
||||
{
|
||||
@@ -110,7 +111,7 @@ class Manager
|
||||
}
|
||||
|
||||
if ($isReturnSingleArray) {
|
||||
return $this->getSingeFileList($result, $onlyFileType);
|
||||
return $this->getSingeFileList($result, $onlyFileType, $path);
|
||||
}
|
||||
|
||||
return $result;
|
||||
@@ -125,7 +126,7 @@ class Manager
|
||||
*
|
||||
* @return aray
|
||||
*/
|
||||
protected function getSingeFileList(array $fileList, $onlyFileType = null, $parentDirName = '')
|
||||
protected function getSingeFileList(array $fileList, $onlyFileType = null, $basePath = null, $parentDirName = '')
|
||||
{
|
||||
$singleFileList = array();
|
||||
foreach($fileList as $dirName => $fileName) {
|
||||
@@ -133,16 +134,16 @@ class Manager
|
||||
if (is_array($fileName)) {
|
||||
$currentDir = Utils\Util::concatPath($parentDirName, $dirName);
|
||||
|
||||
if (!isset($onlyFileType) || $onlyFileType == $this->isFile($currentDir)) {
|
||||
if (!isset($onlyFileType) || $onlyFileType == $this->isFile($currentDir, $basePath)) {
|
||||
$singleFileList[] = $currentDir;
|
||||
}
|
||||
|
||||
$singleFileList = array_merge($singleFileList, $this->getSingeFileList($fileName, $onlyFileType, $currentDir));
|
||||
$singleFileList = array_merge($singleFileList, $this->getSingeFileList($fileName, $onlyFileType, $basePath, $currentDir));
|
||||
|
||||
} else {
|
||||
$currentFileName = Utils\Util::concatPath($parentDirName, $fileName);
|
||||
|
||||
if (!isset($onlyFileType) || $onlyFileType == $this->isFile($currentFileName)) {
|
||||
if (!isset($onlyFileType) || $onlyFileType == $this->isFile($currentFileName, $basePath)) {
|
||||
$singleFileList[] = $currentFileName;
|
||||
}
|
||||
}
|
||||
@@ -221,7 +222,6 @@ class Manager
|
||||
*/
|
||||
public function putPhpContents($path, $data, $withObjects = false)
|
||||
{
|
||||
|
||||
return $this->putContents($path, $this->wrapForDataExport($data, $withObjects), LOCK_EX);
|
||||
}
|
||||
|
||||
@@ -425,13 +425,7 @@ class Manager
|
||||
$sourcePath = $this->concatPaths($sourcePath);
|
||||
$destPath = $this->concatPaths($destPath);
|
||||
|
||||
if (isset($fileList)) {
|
||||
if (!empty($sourcePath)) {
|
||||
foreach ($fileList as &$fileName) {
|
||||
$fileName = $this->concatPaths(array($sourcePath, $fileName));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!isset($fileList)) {
|
||||
$fileList = is_file($sourcePath) ? (array) $sourcePath : $this->getFileList($sourcePath, $recursively, '', true, true);
|
||||
}
|
||||
|
||||
@@ -489,8 +483,7 @@ class Manager
|
||||
$defaultPermissions = $this->getPermissionUtils()->getDefaultPermissions();
|
||||
|
||||
if (file_exists($filePath)) {
|
||||
|
||||
if (!in_array($this->getPermissionUtils()->getCurrentPermission($filePath), array($defaultPermissions['file'], $defaultPermissions['dir']))) {
|
||||
if (!is_writable($filePath) && !in_array($this->getPermissionUtils()->getCurrentPermission($filePath), array($defaultPermissions['file'], $defaultPermissions['dir']))) {
|
||||
return $this->getPermissionUtils()->setDefaultPermissions($filePath, true);
|
||||
}
|
||||
return true;
|
||||
@@ -679,10 +672,16 @@ class Manager
|
||||
* Check if $dirname is directory.
|
||||
*
|
||||
* @param string $dirname
|
||||
* @param string $basePath
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function isDir($dirname)
|
||||
public function isDir($dirname, $basePath = null)
|
||||
{
|
||||
if (!empty($basePath)) {
|
||||
$dirname = $this->concatPaths([$basePath, $dirname]);
|
||||
}
|
||||
|
||||
return is_dir($dirname);
|
||||
}
|
||||
|
||||
@@ -690,10 +689,16 @@ class Manager
|
||||
* Check if $filename is file. If $filename doesn'ot exist, check by pathinfo
|
||||
*
|
||||
* @param string $filename
|
||||
* @param string $basePath
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function isFile($filename)
|
||||
public function isFile($filename, $basePath = null)
|
||||
{
|
||||
if (!empty($basePath)) {
|
||||
$filename = $this->concatPaths([$basePath, $filename]);
|
||||
}
|
||||
|
||||
if (file_exists($filename)) {
|
||||
return is_file($filename);
|
||||
}
|
||||
|
||||
@@ -236,17 +236,39 @@ class Metadata
|
||||
}
|
||||
}
|
||||
|
||||
public function getAllObjects($isJSON = false, $reload = false)
|
||||
protected function getObjData($reload = false)
|
||||
{
|
||||
if (!isset($this->objData) || $reload) {
|
||||
$this->objInit($reload);
|
||||
}
|
||||
|
||||
return $this->objData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Object Metadata
|
||||
*
|
||||
* @param mixed string|array $key
|
||||
* @param mixed $default
|
||||
*
|
||||
* @return object
|
||||
*/
|
||||
public function getObjects($key = null, $default = null)
|
||||
{
|
||||
$objData = $this->getObjData();
|
||||
|
||||
return Util::getValueByKey($objData, $key, $default);
|
||||
}
|
||||
|
||||
public function getAllObjects($isJSON = false, $reload = false)
|
||||
{
|
||||
$objData = $this->getObjData($reload);
|
||||
|
||||
if ($isJSON) {
|
||||
return Json::encode($this->objData);
|
||||
return Json::encode($objData);
|
||||
}
|
||||
|
||||
return $this->objData;
|
||||
return $objData;
|
||||
}
|
||||
|
||||
public function getAllForFrontend()
|
||||
@@ -305,6 +327,47 @@ class Metadata
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get metadata definition in custom directory
|
||||
*
|
||||
* @param string|array $key
|
||||
* @param mixed $default
|
||||
*
|
||||
* @return object|mixed
|
||||
*/
|
||||
public function getCustom($key1, $key2, $default = null)
|
||||
{
|
||||
$filePath = array($this->paths['customPath'], $key1, $key2.'.json');
|
||||
$fileContent = $this->getFileManager()->getContents($filePath);
|
||||
|
||||
if ($fileContent) {
|
||||
return Json::decode($fileContent);
|
||||
}
|
||||
|
||||
return $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set and save metadata in custom directory. The data is not merging with existing data. Use getCustom() to get existing data.
|
||||
*
|
||||
* @param string $key1
|
||||
* @param string $key2
|
||||
* @param array $data
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function saveCustom($key1, $key2, $data)
|
||||
{
|
||||
$filePath = array($this->paths['customPath'], $key1, $key2.'.json');
|
||||
$changedData = Json::encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
||||
|
||||
$result = $this->getFileManager()->putContents($filePath, $changedData);
|
||||
|
||||
$this->init(true);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Metadata data
|
||||
* Ex. $key1 = menu, $key2 = Account then will be created a file metadataFolder/menu/Account.json
|
||||
|
||||
@@ -32,7 +32,7 @@ return array (
|
||||
'driver' => 'pdo_mysql',
|
||||
'host' => 'localhost',
|
||||
'port' => '',
|
||||
'charset' => 'utf8',
|
||||
'charset' => 'utf8mb4',
|
||||
'dbname' => '',
|
||||
'user' => '',
|
||||
'password' => '',
|
||||
@@ -139,7 +139,7 @@ return array (
|
||||
'x' => 2,
|
||||
'y' => 2,
|
||||
'width' => 2,
|
||||
'height' => 2
|
||||
'height' => 4
|
||||
],
|
||||
(object) [
|
||||
'id' => 'default-stream',
|
||||
@@ -148,14 +148,6 @@ return array (
|
||||
'y' => 0,
|
||||
'width' => 2,
|
||||
'height' => 4
|
||||
],
|
||||
(object) [
|
||||
'id' => 'default-tasks',
|
||||
'name' => 'Tasks',
|
||||
'x' => 2,
|
||||
'y' => 0,
|
||||
'width' => 2,
|
||||
'height' => 2
|
||||
]
|
||||
]
|
||||
]
|
||||
@@ -172,6 +164,7 @@ return array (
|
||||
'aclStrictMode' => false,
|
||||
'aclAllowDeleteCreated' => false,
|
||||
'inlineAttachmentUploadMaxSize' => 20,
|
||||
'textFilterUseContainsForVarchar' => false,
|
||||
'isInstalled' => false
|
||||
);
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@ class AssignmentEmailNotification extends \Espo\Core\Hooks\Base
|
||||
) {
|
||||
|
||||
$userId = $entity->get('assignedUserId');
|
||||
if (!empty($userId) && $userId != $this->getUser()->id && $entity->isFieldChanged('assignedUserId')) {
|
||||
if (!empty($userId) && $userId != $this->getUser()->id && $entity->isAttributeChanged('assignedUserId')) {
|
||||
$job = $this->getEntityManager()->getEntity('Job');
|
||||
$job->set(array(
|
||||
'serviceName' => 'EmailNotification',
|
||||
|
||||
@@ -249,7 +249,7 @@ class Stream extends \Espo\Core\Hooks\Base
|
||||
}
|
||||
} else {
|
||||
if (empty($options['noStream']) && empty($options['silent'])) {
|
||||
if ($entity->isFieldChanged('assignedUserId')) {
|
||||
if ($entity->isAttributeChanged('assignedUserId')) {
|
||||
$assignedUserId = $entity->get('assignedUserId');
|
||||
if (!empty($assignedUserId)) {
|
||||
$this->getStreamService()->followEntity($entity, $assignedUserId);
|
||||
|
||||
@@ -62,13 +62,25 @@ class Cleanup extends \Espo\Core\Jobs\Base
|
||||
$this->cleanupActionHistory();
|
||||
$this->cleanupAuthToken();
|
||||
$this->cleanupUpgradeBackups();
|
||||
$this->cleanupUniqueIds();
|
||||
}
|
||||
|
||||
protected function cleanupJobs()
|
||||
{
|
||||
$query = "DELETE FROM `job` WHERE DATE(modified_at) < '".$this->getCleanupJobFromDate()."' AND status <> 'Pending'";
|
||||
|
||||
$pdo = $this->getEntityManager()->getPDO();
|
||||
|
||||
$query = "DELETE FROM `job` WHERE DATE(modified_at) < ".$pdo->quote($this->getCleanupJobFromDate())." AND status <> 'Pending'";
|
||||
|
||||
$sth = $pdo->prepare($query);
|
||||
$sth->execute();
|
||||
}
|
||||
|
||||
protected function cleanupUniqueIds()
|
||||
{
|
||||
$pdo = $this->getEntityManager()->getPDO();
|
||||
|
||||
$query = "DELETE FROM `unique_id` WHERE terminate_at IS NOT NULL AND terminate_at < ".$pdo->quote(date('Y-m-d H:i:s'))."";
|
||||
|
||||
$sth = $pdo->prepare($query);
|
||||
$sth->execute();
|
||||
}
|
||||
@@ -83,29 +95,35 @@ class Cleanup extends \Espo\Core\Jobs\Base
|
||||
while ($row = $sth->fetch(\PDO::FETCH_ASSOC)) {
|
||||
$id = $row['id'];
|
||||
|
||||
$lastRowsSql = "SELECT id FROM scheduled_job_log_record WHERE scheduled_job_id = '".$id."' ORDER BY created_at DESC LIMIT 0,10";
|
||||
$lastRowsSql = "SELECT id FROM scheduled_job_log_record WHERE scheduled_job_id = ".$pdo->quote($id)." ORDER BY created_at DESC LIMIT 0,10";
|
||||
$lastRowsSth = $pdo->prepare($lastRowsSql);
|
||||
$lastRowsSth->execute();
|
||||
$lastRowIds = $lastRowsSth->fetchAll(\PDO::FETCH_COLUMN, 0);
|
||||
|
||||
$delSql = "DELETE FROM `scheduled_job_log_record`
|
||||
WHERE scheduled_job_id = '".$id."'
|
||||
AND DATE(created_at) < '".$this->getCleanupJobFromDate()."'
|
||||
AND id NOT IN ('".implode("', '", $lastRowIds)."')
|
||||
";
|
||||
$pdo->query($delSql);
|
||||
if (count($lastRowIds)) {
|
||||
foreach ($lastRowIds as $i => $v) {
|
||||
$lastRowIds[$i] = $pdo->quote($v);
|
||||
}
|
||||
$delSql = "DELETE FROM `scheduled_job_log_record`
|
||||
WHERE scheduled_job_id = ".$pdo->quote($id)."
|
||||
AND DATE(created_at) < ".$pdo->quote($this->getCleanupJobFromDate())."
|
||||
AND id NOT IN (".implode(',', $lastRowIds).")
|
||||
";
|
||||
$pdo->query($delSql);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function cleanupActionHistory()
|
||||
{
|
||||
$pdo = $this->getEntityManager()->getPDO();
|
||||
|
||||
$period = '-' . $this->getConfig()->get('cleanupActionHistoryPeriod', $this->cleanupActionHistoryPeriod);
|
||||
$datetime = new \DateTime();
|
||||
$datetime->modify($period);
|
||||
|
||||
$query = "DELETE FROM `action_history_record` WHERE DATE(created_at) < '" . $datetime->format('Y-m-d') . "'";
|
||||
$query = "DELETE FROM `action_history_record` WHERE DATE(created_at) < " . $pdo->quote($datetime->format('Y-m-d')) . "";
|
||||
|
||||
$pdo = $this->getEntityManager()->getPDO();
|
||||
$sth = $pdo->prepare($query);
|
||||
$sth->execute();
|
||||
}
|
||||
@@ -116,7 +134,7 @@ class Cleanup extends \Espo\Core\Jobs\Base
|
||||
$datetime = new \DateTime();
|
||||
$datetime->modify($period);
|
||||
|
||||
$query = "DELETE FROM `reminder` WHERE DATE(remind_at) < '" . $datetime->format('Y-m-d') . "'";
|
||||
$query = "DELETE FROM `reminder` WHERE DATE(remind_at) < " . $pdo->quote($datetime->format('Y-m-d')) . "";
|
||||
|
||||
$pdo = $this->getEntityManager()->getPDO();
|
||||
$sth = $pdo->prepare($query);
|
||||
@@ -125,13 +143,14 @@ class Cleanup extends \Espo\Core\Jobs\Base
|
||||
|
||||
protected function cleanupAuthToken()
|
||||
{
|
||||
$pdo = $this->getEntityManager()->getPDO();
|
||||
|
||||
$period = '-' . $this->getConfig()->get('cleanupAuthTokenPeriod', $this->cleanupAuthTokenPeriod);
|
||||
$datetime = new \DateTime();
|
||||
$datetime->modify($period);
|
||||
|
||||
$query = "DELETE FROM `auth_token` WHERE DATE(modified_at) < '" . $datetime->format('Y-m-d') . "' AND is_active = 0";
|
||||
$query = "DELETE FROM `auth_token` WHERE DATE(modified_at) < " . $pdo->quote($datetime->format('Y-m-d')) . " AND is_active = 0";
|
||||
|
||||
$pdo = $this->getEntityManager()->getPDO();
|
||||
$sth = $pdo->prepare($query);
|
||||
$sth->execute();
|
||||
}
|
||||
@@ -334,4 +353,3 @@ class Cleanup extends \Espo\Core\Jobs\Base
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -97,6 +97,21 @@ class Invitations
|
||||
'inviteeType' => $invitee->getEntityType(),
|
||||
'link' => $link
|
||||
));
|
||||
|
||||
if ($entity->get('dateEnd')) {
|
||||
$terminateAt = $entity->get('dateEnd');
|
||||
} else {
|
||||
$dt = new \DateTime();
|
||||
$dt->modify('+1 month');
|
||||
$terminateAt = $dt->format('Y-m-d H:i:s');
|
||||
}
|
||||
|
||||
$uid->set([
|
||||
'targetId' => $entity->id,
|
||||
'targetType' => $entity->getEntityType(),
|
||||
'terminateAt' => $terminateAt
|
||||
]);
|
||||
|
||||
$this->getEntityManager()->saveEntity($uid);
|
||||
|
||||
$emailAddress = $invitee->get('emailAddress');
|
||||
|
||||
@@ -37,7 +37,7 @@ class Opportunity extends \Espo\Core\Controllers\Record
|
||||
public function actionReportByLeadSource($params, $data, $request)
|
||||
{
|
||||
$level = $this->getAcl()->getLevel('Opportunity', 'read');
|
||||
if (!$level || $level == 'own' || $level == 'no') {
|
||||
if (!$level || $level == 'no') {
|
||||
throw new Forbidden();
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ class Opportunity extends \Espo\Core\Controllers\Record
|
||||
public function actionReportByStage($params, $data, $request)
|
||||
{
|
||||
$level = $this->getAcl()->getLevel('Opportunity', 'read');
|
||||
if (!$level || $level == 'own' || $level == 'no') {
|
||||
if (!$level || $level == 'no') {
|
||||
throw new Forbidden();
|
||||
}
|
||||
|
||||
@@ -65,7 +65,7 @@ class Opportunity extends \Espo\Core\Controllers\Record
|
||||
public function actionReportSalesByMonth($params, $data, $request)
|
||||
{
|
||||
$level = $this->getAcl()->getLevel('Opportunity', 'read');
|
||||
if (!$level || $level == 'own' || $level == 'no') {
|
||||
if (!$level || $level == 'no') {
|
||||
throw new Forbidden();
|
||||
}
|
||||
|
||||
@@ -79,7 +79,7 @@ class Opportunity extends \Espo\Core\Controllers\Record
|
||||
public function actionReportSalesPipeline($params, $data, $request)
|
||||
{
|
||||
$level = $this->getAcl()->getLevel('Opportunity', 'read');
|
||||
if (!$level || $level == 'own' || $level == 'no') {
|
||||
if (!$level || $level == 'no') {
|
||||
throw new Forbidden();
|
||||
}
|
||||
|
||||
@@ -90,4 +90,3 @@ class Opportunity extends \Espo\Core\Controllers\Record
|
||||
return $this->getService('Opportunity')->reportSalesPipeline($dateFilter, $dateFrom, $dateTo);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -56,7 +56,6 @@ class EventConfirmation extends \Espo\Core\EntryPoints\Base
|
||||
|
||||
if (!$uniqueId) {
|
||||
throw new NotFound();
|
||||
return;
|
||||
}
|
||||
|
||||
$data = $uniqueId->get('data');
|
||||
@@ -70,36 +69,55 @@ class EventConfirmation extends \Espo\Core\EntryPoints\Base
|
||||
if (!empty($eventType) && !empty($eventId) && !empty($inviteeType) && !empty($inviteeId) && !empty($link)) {
|
||||
$event = $this->getEntityManager()->getEntity($eventType, $eventId);
|
||||
$invitee = $this->getEntityManager()->getEntity($inviteeType, $inviteeId);
|
||||
if ($event && $invitee) {
|
||||
$relDefs = $event->getRelations();
|
||||
$tableName = Util::toUnderscore($relDefs[$link]['relationName']);
|
||||
|
||||
$status = 'None';
|
||||
if ($action == 'accept') {
|
||||
$status = 'Accepted';
|
||||
} else if ($action == 'decline') {
|
||||
$status = 'Declined';
|
||||
} else if ($action == 'tentative') {
|
||||
$status = 'Tentative';
|
||||
}
|
||||
|
||||
$pdo = $this->getEntityManager()->getPDO();
|
||||
$sql = "
|
||||
UPDATE `{$tableName}` SET status = '{$status}'
|
||||
WHERE ".strtolower($eventType)."_id = '{$eventId}' AND ".strtolower($inviteeType)."_id = '{$inviteeId}'
|
||||
";
|
||||
|
||||
$sth = $pdo->prepare($sql);
|
||||
$sth->execute();
|
||||
|
||||
$this->getEntityManager()->getRepository('UniqueId')->remove($uniqueId);
|
||||
|
||||
echo $status;
|
||||
return;
|
||||
if (!$event || !$invitee) {
|
||||
throw new NotFound();
|
||||
}
|
||||
|
||||
if ($event->get('status') === 'Held' || $event->get('status') === 'Not Held') {
|
||||
throw new NotFound();
|
||||
}
|
||||
|
||||
$status = 'None';
|
||||
$hookMethodName = 'afterConfirmation';
|
||||
if ($action == 'accept') {
|
||||
$status = 'Accepted';
|
||||
} else if ($action == 'decline') {
|
||||
$status = 'Declined';
|
||||
} else if ($action == 'tentative') {
|
||||
$status = 'Tentative';
|
||||
}
|
||||
|
||||
$data = (object) [
|
||||
'status' => $status
|
||||
];
|
||||
$this->getEntityManager()->getRepository($eventType)->updateRelation($event, $link, $invitee->id, $data);
|
||||
|
||||
$actionData = [
|
||||
'eventName' => $event->get('name'),
|
||||
'eventType' => $event->getEntityType(),
|
||||
'eventId' => $event->id,
|
||||
'dateStart' => $event->get('dateStart'),
|
||||
'action' => $action,
|
||||
'status' => $status,
|
||||
'link' => $link,
|
||||
'inviteeType' => $invitee->getEntityType(),
|
||||
'inviteeId' => $invitee->id
|
||||
];
|
||||
|
||||
$this->getEntityManager()->getHookManager()->process($event->getEntityType(), $hookMethodName, $event, [], $actionData);
|
||||
|
||||
$runScript = "
|
||||
Espo.require('crm:controllers/event-confirmation', function (Controller) {
|
||||
var controller = new Controller(app.baseController.params, app.getControllerInjection());
|
||||
controller.masterView = app.masterView;
|
||||
controller.doAction('confirmEvent', ".json_encode($actionData).");
|
||||
});
|
||||
";
|
||||
|
||||
$this->getClientManager()->display($runScript);
|
||||
}
|
||||
|
||||
throw new Error();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -113,9 +113,19 @@ class SubscribeAgain extends \Espo\Core\EntryPoints\Base
|
||||
$this->getHookManager()->process('TargetList', 'afterCancelOptOut', $targetList, [], $hookData);
|
||||
}
|
||||
}
|
||||
echo $this->getLanguage()->translate('subscribedAgain', 'messages', 'Campaign');
|
||||
echo '<br><br>';
|
||||
echo '<a href="?entryPoint=unsubscribe&id='.$queueItemId.'">' . $this->getLanguage()->translate('Unsubscribe again', 'labels', 'Campaign') . '</a>';
|
||||
|
||||
$data = [
|
||||
'queueItemId' => $queueItemId
|
||||
];
|
||||
|
||||
$runScript = "
|
||||
Espo.require('crm:controllers/unsubscribe', function (Controller) {
|
||||
var controller = new Controller(app.baseController.params, app.getControllerInjection());
|
||||
controller.masterView = app.masterView;
|
||||
controller.doAction('subscribeAgain', ".json_encode($data).");
|
||||
});
|
||||
";
|
||||
$this->getClientManager()->display($runScript);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,9 +113,19 @@ class Unsubscribe extends \Espo\Core\EntryPoints\Base
|
||||
$this->getHookManager()->process('TargetList', 'afterOptOut', $targetList, [], $hookData);
|
||||
}
|
||||
}
|
||||
echo $this->getLanguage()->translate('unsubscribed', 'messages', 'Campaign');
|
||||
echo '<br><br>';
|
||||
echo '<a href="?entryPoint=subscribeAgain&id='.$queueItemId.'">' . $this->getLanguage()->translate('Subscribe again', 'labels', 'Campaign') . '</a>';
|
||||
|
||||
$data = [
|
||||
'queueItemId' => $queueItemId
|
||||
];
|
||||
|
||||
$runScript = "
|
||||
Espo.require('crm:controllers/unsubscribe', function (Controller) {
|
||||
var controller = new Controller(app.baseController.params, app.getControllerInjection());
|
||||
controller.masterView = app.masterView;
|
||||
controller.doAction('unsubscribe', ".json_encode($data).");
|
||||
});
|
||||
";
|
||||
$this->getClientManager()->display($runScript);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,16 +36,18 @@ class Meeting extends \Espo\Core\Repositories\Event
|
||||
{
|
||||
protected function beforeSave(Entity $entity, array $options = array())
|
||||
{
|
||||
if (!$entity->isNew() && $entity->isAttributeChanged('parentId')) {
|
||||
$entity->set('accountId', null);
|
||||
}
|
||||
|
||||
$parentId = $entity->get('parentId');
|
||||
$parentType = $entity->get('parentType');
|
||||
if (!empty($parentId) || !empty($parentType)) {
|
||||
if ($parentId && $parentType) {
|
||||
$parent = $this->getEntityManager()->getEntity($parentType, $parentId);
|
||||
if (!empty($parent)) {
|
||||
if ($parent) {
|
||||
$accountId = null;
|
||||
if ($parent->getEntityType() == 'Account') {
|
||||
$accountId = $parent->id;
|
||||
} else if ($parent->get('accountId')) {
|
||||
$accountId = $parent->get('accountId');
|
||||
} else if ($parent->getEntityType() == 'Lead') {
|
||||
if ($parent->get('status') == 'Converted') {
|
||||
if ($parent->get('createdAccountId')) {
|
||||
@@ -53,14 +55,17 @@ class Meeting extends \Espo\Core\Repositories\Event
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!empty($accountId)) {
|
||||
if (!$accountId && $parent->get('accountId') && $parent->getRelationParam('account', 'entity') == 'Account') {
|
||||
$accountId = $parent->get('accountId');
|
||||
}
|
||||
if ($accountId) {
|
||||
$entity->set('accountId', $accountId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!$entity->isNew()) {
|
||||
if ($entity->isFieldChanged('dateStart') && $entity->isFieldChanged('dateStart') && !$entity->isFieldChanged('dateEnd')) {
|
||||
if ($entity->isAttributeChanged('dateStart') && $entity->isAttributeChanged('dateStart') && !$entity->isAttributeChanged('dateEnd')) {
|
||||
$dateEndPrevious = $entity->getFetched('dateEnd');
|
||||
$dateStartPrevious = $entity->getFetched('dateStart');
|
||||
if ($dateStartPrevious && $dateEndPrevious) {
|
||||
|
||||
@@ -71,7 +71,7 @@ class Task extends \Espo\Core\Repositories\Event
|
||||
|
||||
protected function beforeSave(Entity $entity, array $options = array())
|
||||
{
|
||||
if ($entity->isFieldChanged('status')) {
|
||||
if ($entity->isAttributeChanged('status')) {
|
||||
if ($entity->get('status') == 'Completed') {
|
||||
$entity->set('dateCompleted', date('Y-m-d H:i:s'));
|
||||
} else {
|
||||
@@ -103,26 +103,46 @@ class Task extends \Espo\Core\Repositories\Event
|
||||
}
|
||||
}
|
||||
|
||||
if (!$entity->isNew() && $entity->isAttributeChanged('parentId')) {
|
||||
$entity->set('accountId', null);
|
||||
$entity->set('contactId', null);
|
||||
}
|
||||
|
||||
$parentId = $entity->get('parentId');
|
||||
$parentType = $entity->get('parentType');
|
||||
if (!empty($parentId) || !empty($parentType)) {
|
||||
if ($parentId && $parentType) {
|
||||
$parent = $this->getEntityManager()->getEntity($parentType, $parentId);
|
||||
if (!empty($parent)) {
|
||||
if ($parent) {
|
||||
$accountId = null;
|
||||
$contactId = null;
|
||||
if ($parent->getEntityType() == 'Account') {
|
||||
$accountId = $parent->id;
|
||||
} else if ($parent->get('accountId')) {
|
||||
$accountId = $parent->get('accountId');
|
||||
} else if ($parent->getEntityType() == 'Lead') {
|
||||
if ($parent->get('status') == 'Converted') {
|
||||
if ($parent->get('createdAccountId')) {
|
||||
$accountId = $parent->get('createdAccountId');
|
||||
}
|
||||
if ($parent->get('createdContactId')) {
|
||||
$contactId = $parent->get('createdContactId');
|
||||
}
|
||||
}
|
||||
} else if ($parent->getEntityType() == 'Contact') {
|
||||
$contactId = $parent->id;
|
||||
}
|
||||
if (!empty($accountId)) {
|
||||
|
||||
if (!$accountId && $parent->get('accountId') && $parent->getRelationParam('account', 'entity') == 'Account') {
|
||||
$accountId = $parent->get('accountId');
|
||||
}
|
||||
if (!$contactId && $parent->get('contactId') && $parent->getRelationParam('contact', 'entity') == 'Contact') {
|
||||
$contactId = $parent->get('contactId');
|
||||
}
|
||||
|
||||
if ($accountId) {
|
||||
$entity->set('accountId', $accountId);
|
||||
}
|
||||
if ($contactId) {
|
||||
$entity->set('contactId', $contactId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,19 +19,6 @@
|
||||
"documents": "Dokumenty",
|
||||
"campaign": "Kampaň"
|
||||
},
|
||||
"options": {
|
||||
"stage": {
|
||||
"Request": "Poptávka",
|
||||
"Quote": "Nabídka",
|
||||
"Order": "Objednávka",
|
||||
"ToProduction": "Čeká na výrobu",
|
||||
"InProduction": "Vyrábí se...",
|
||||
"Dispatch": "Expedice",
|
||||
"Delivered": "Dodáno",
|
||||
"Closed Won": "VYFAKTUROVÁNO",
|
||||
"Closed Lost": "ODMÍTNUTO"
|
||||
}
|
||||
},
|
||||
"labels": {
|
||||
"Create Opportunity": "Vytvořit případ"
|
||||
},
|
||||
|
||||
@@ -26,6 +26,8 @@
|
||||
"stage": {
|
||||
"Prospecting": "Undersøges",
|
||||
"Qualification": "Kvalifikation",
|
||||
"Proposal": "Tilbud",
|
||||
"Negotiation": "Forhandler",
|
||||
"Needs Analysis": "Behovsanalyse",
|
||||
"Value Proposition": "Vejledende Tilbud",
|
||||
"Id. Decision Makers": "Identifikation af Beslutningstagere",
|
||||
|
||||
@@ -25,6 +25,8 @@
|
||||
"stage": {
|
||||
"Prospecting": "Identifikation",
|
||||
"Qualification": "Qualifikation",
|
||||
"Proposal": "Angebot",
|
||||
"Negotiation": "Verhandlung",
|
||||
"Needs Analysis": "Bedarfserhebung",
|
||||
"Value Proposition": "Richtangebot",
|
||||
"Id. Decision Makers": "Identifikation der Entscheider",
|
||||
|
||||
@@ -9,12 +9,12 @@
|
||||
"priority": "Priority",
|
||||
"type": "Type",
|
||||
"description": "Description",
|
||||
"inboundEmail": "Inbound Email",
|
||||
"inboundEmail": "Group Email Account",
|
||||
"lead": "Lead",
|
||||
"attachments": "Attachments"
|
||||
},
|
||||
"links": {
|
||||
"inboundEmail": "Inbound Email",
|
||||
"inboundEmail": "Group Email Account",
|
||||
"account": "Account",
|
||||
"contact": "Contact (Primary)",
|
||||
"Contacts": "Contacts",
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
"account": "Account (Primary)",
|
||||
"accounts": "Accounts",
|
||||
"casesPrimary": "Cases (Primary)",
|
||||
"tasksPrimary": "Tasks (expanded)",
|
||||
"portalUser": "Portal User",
|
||||
"originalLead": "Original Lead",
|
||||
"documents": "Documents"
|
||||
|
||||
@@ -27,6 +27,8 @@
|
||||
"stage": {
|
||||
"Prospecting": "Prospecting",
|
||||
"Qualification": "Qualification",
|
||||
"Proposal": "Proposal",
|
||||
"Negotiation": "Negotiation",
|
||||
"Needs Analysis": "Needs Analysis",
|
||||
"Value Proposition": "Value Proposition",
|
||||
"Id. Decision Makers": "Id. Decision Makers",
|
||||
|
||||
@@ -13,10 +13,13 @@
|
||||
"account": "Account",
|
||||
"dateCompleted": "Date Completed",
|
||||
"attachments": "Attachments",
|
||||
"reminders": "Reminders"
|
||||
"reminders": "Reminders",
|
||||
"contact": "Contact"
|
||||
},
|
||||
"links": {
|
||||
"attachments": "Attachments"
|
||||
"attachments": "Attachments",
|
||||
"account": "Account",
|
||||
"contact": "Contact"
|
||||
},
|
||||
"options": {
|
||||
"status": {
|
||||
|
||||
@@ -27,6 +27,8 @@
|
||||
"stage": {
|
||||
"Prospecting": "Prospección",
|
||||
"Qualification": "Calificación",
|
||||
"Proposal": "Propuesta",
|
||||
"Negotiation": "Negociación",
|
||||
"Needs Analysis": "Análisis de necesidades",
|
||||
"Value Proposition": "Propuesta de valor",
|
||||
"Id. Decision Makers": "Identificar tomador de decisiones",
|
||||
|
||||
@@ -27,6 +27,8 @@
|
||||
"stage": {
|
||||
"Prospecting": "Prospección",
|
||||
"Qualification": "Calificación",
|
||||
"Proposal": "Cotización con Propuesta",
|
||||
"Negotiation": "Negociación",
|
||||
"Needs Analysis": "Análisis de Necesidades",
|
||||
"Value Proposition": "Propuesta de Valor",
|
||||
"Id. Decision Makers": "Id. Tomadores de Decisiones",
|
||||
|
||||
@@ -27,6 +27,8 @@
|
||||
"stage": {
|
||||
"Prospecting": "Prospection",
|
||||
"Qualification": "Qualification",
|
||||
"Proposal": "Proposition",
|
||||
"Negotiation": "Négociation",
|
||||
"Needs Analysis": "Analyse des besoins",
|
||||
"Value Proposition": "Valeur de la proposition",
|
||||
"Id. Decision Makers": "Id. Decisionnaire",
|
||||
|
||||
@@ -24,6 +24,8 @@
|
||||
"Prospecting": "Pencarian",
|
||||
"Qualification": "Kualifikasi",
|
||||
"Needs Analysis": "Butuh analisa",
|
||||
"Proposal": "Proposal",
|
||||
"Negotiation": "Negosiasi",
|
||||
"Value Proposition": "Proposisi nilai",
|
||||
"Id. Decision Makers": "Id. Pembuat keputusan",
|
||||
"Perception Analysis": "Analisis persepsi",
|
||||
|
||||
@@ -27,6 +27,8 @@
|
||||
"stage": {
|
||||
"Prospecting": "Prospecting",
|
||||
"Qualification": "Qualifica",
|
||||
"Proposal": "Proposta",
|
||||
"Negotiation": "Negoziazione",
|
||||
"Needs Analysis": "Necessita di analisi",
|
||||
"Value Proposition": "Proposta di Valore",
|
||||
"Id. Decision Makers": "Id. Responsabile",
|
||||
|
||||
@@ -24,6 +24,8 @@
|
||||
"stage": {
|
||||
"Prospecting": "Potencialių klientų paieška",
|
||||
"Qualification": "Kvalifikacija",
|
||||
"Proposal": "Pasiūlymas",
|
||||
"Negotiation": "Derybos",
|
||||
"Needs Analysis": "Reikalinga analizė",
|
||||
"Value Proposition": "Pasiūlymo vertė",
|
||||
"Id. Decision Makers": "Id. Sprendimus priimantys žmonės",
|
||||
|
||||
@@ -25,6 +25,8 @@
|
||||
"stage": {
|
||||
"Prospecting": "Undersøkes",
|
||||
"Qualification": "Kvalitetskontrolleres",
|
||||
"Proposal": "Prisforslag",
|
||||
"Negotiation": "Forhandler",
|
||||
"Needs Analysis": "Trenger analyse",
|
||||
"Value Proposition": "Verdivurdering",
|
||||
"Id. Decision Makers": "Ser etter beslutningstagere",
|
||||
|
||||
@@ -23,6 +23,8 @@
|
||||
"stage": {
|
||||
"Prospecting": "Prospectie",
|
||||
"Qualification": "Kwalificatie",
|
||||
"Proposal": "Voorstel",
|
||||
"Negotiation": "Onderhandeling",
|
||||
"Needs Analysis": "Analyse behoefte",
|
||||
"Value Proposition": "Waarde voorstel",
|
||||
"Id. Decision Makers": "Id. Beslissers",
|
||||
|
||||
@@ -23,12 +23,14 @@
|
||||
"stage": {
|
||||
"Prospecting": "I Spotkanie",
|
||||
"Qualification": "Kwalifikacja",
|
||||
"Proposal": "Oferta Cenowa",
|
||||
"Negotiation": "Negocjacje",
|
||||
"Needs Analysis": "Dodatkowa Analiza",
|
||||
"Value Proposition": "Oferta Produktowa",
|
||||
"Id. Decision Makers": "Oferta przekazna do osoby decyzyjnej",
|
||||
"Perception Analysis": "Perception Analysis",
|
||||
"Proposal/Price Quote": "Oferta Cenowa",
|
||||
"Negotiation/Review": "Negocjacje ",
|
||||
"Negotiation/Review": "Negocjacje",
|
||||
"Closed Won": "Zakończone Wygrane",
|
||||
"Closed Lost": "Zakończone Przegrane"
|
||||
}
|
||||
|
||||
@@ -22,6 +22,8 @@
|
||||
"stage": {
|
||||
"Prospecting": "Prospectando",
|
||||
"Qualification": "Qualificação",
|
||||
"Proposal": "Proposta",
|
||||
"Negotiation": "Negociação",
|
||||
"Needs Analysis": "Necessita de Análise",
|
||||
"Value Proposition": "Proposta de Valor",
|
||||
"Id. Decision Makers": "Identificação dos Decisores",
|
||||
|
||||
@@ -1,40 +1,51 @@
|
||||
{
|
||||
"fields": {
|
||||
"name": "Nume",
|
||||
"emailAddress": "Email",
|
||||
"website": "Site Web",
|
||||
"phoneNumber": "Telefon",
|
||||
"billingAddress": "Adresa de Facturare",
|
||||
"shippingAddress": "Adresa de Transport",
|
||||
"description": "Descriere",
|
||||
"sicCode": "Cod Sic",
|
||||
"industry": "Industrie",
|
||||
"type": "Tip",
|
||||
"contactRole": "Rol"
|
||||
"fields": {
|
||||
"name": "Nume",
|
||||
"website": "Site Web",
|
||||
"phoneNumber": "Telefon",
|
||||
"billingAddress": "Adresa de Facturare",
|
||||
"shippingAddress": "Adresa de Transport",
|
||||
"description": "Descriere",
|
||||
"sicCode": "Cod Sic",
|
||||
"industry": "Industrie",
|
||||
"type": "Tip",
|
||||
"contactRole": "Rol"
|
||||
},
|
||||
"links": {
|
||||
"contacts": "Contacte",
|
||||
"opportunities": "Oportunitati",
|
||||
"cases": "Cazuri"
|
||||
},
|
||||
"options": {
|
||||
"type": {
|
||||
"Customer": "Client",
|
||||
"Investor": "Investitor",
|
||||
"Partner": "Partener"
|
||||
},
|
||||
"links": {
|
||||
"contacts": "Contacte",
|
||||
"opportunities": "Oportunitati",
|
||||
"cases": "Cazuri"
|
||||
},
|
||||
"options": {
|
||||
"type": {
|
||||
"Customer": "Client",
|
||||
"Investor": "Investitor",
|
||||
"Partner": "Partener",
|
||||
"Reseller": "Reseller"
|
||||
},
|
||||
"industry": {
|
||||
"Apparel": "Haine",
|
||||
"Banking": "Banking",
|
||||
"Computer Software": "Software Computer",
|
||||
"Education": "Educatie",
|
||||
"Electronics": "Electronice",
|
||||
"Finance": "Finante",
|
||||
"Insurance": "Asigurari"
|
||||
}
|
||||
},
|
||||
"labels": {
|
||||
"Create Account": "Creare cont"
|
||||
"industry": {
|
||||
"Education": "Educatie",
|
||||
"Electronics": "Electronice",
|
||||
"Finance": "Finante",
|
||||
"Insurance": "Asigurari",
|
||||
"Technology": "Tehnologie",
|
||||
"Telecommunications": "Telecomunicații",
|
||||
"Television": "Televiziune",
|
||||
"Testing, Inspection & Certification": "Testare, Inspecție & Certificări",
|
||||
"Transportation": "Transport",
|
||||
"Travel": "Călătorit",
|
||||
"Venture Capital": "Capital de risc",
|
||||
"Wholesale": "Angro",
|
||||
"Water": "Apă"
|
||||
}
|
||||
}
|
||||
},
|
||||
"labels": {
|
||||
"Create Account": "Creare cont",
|
||||
"Copy Billing": "Copiați facturarea",
|
||||
"Set Primary": "Setați primar"
|
||||
},
|
||||
"presetFilters": {
|
||||
"customers": "Clienți",
|
||||
"partners": "Parteneri",
|
||||
"recentlyCreated": "Create recent"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"layouts": {
|
||||
"listForAccount": "Listă (pentru Cont)"
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,21 @@
|
||||
{
|
||||
"modes": {
|
||||
"month": "Luna",
|
||||
"week": "Saptamana",
|
||||
"day": "Zi",
|
||||
"agendaWeek": "Saptamana",
|
||||
"agendaDay": "Zi"
|
||||
},
|
||||
"labels": {
|
||||
"Today": "Astazi",
|
||||
"Create": "Creaza"
|
||||
}
|
||||
}
|
||||
"modes": {
|
||||
"month": "Lună",
|
||||
"week": "Săptămână",
|
||||
"agendaWeek": "Săptămână",
|
||||
"day": "Zi",
|
||||
"agendaDay": "Zi",
|
||||
"timeline": "Cronologie"
|
||||
},
|
||||
"labels": {
|
||||
"Today": "Astăzi",
|
||||
"Create": "Crează",
|
||||
"Shared": "Partajat",
|
||||
"Add User": "Adăugare utilizator",
|
||||
"current": "actual",
|
||||
"time": "timp",
|
||||
"User List": "Listă Utiliziator",
|
||||
"Manage Users": "Gestionare Utilizatori",
|
||||
"View Calendar": "Vizualizare Calendar"
|
||||
}
|
||||
}
|
||||
@@ -1,39 +1,50 @@
|
||||
{
|
||||
"fields": {
|
||||
"name": "Nume",
|
||||
"parent": "Parinte",
|
||||
"status": "Status",
|
||||
"dateStart": "Data inceput",
|
||||
"dateEnd": "Data terminare",
|
||||
"direction": "Directie",
|
||||
"duration": "Durata",
|
||||
"description": "Descriere",
|
||||
"users": "Utilizatori",
|
||||
"contacts": "Contacte",
|
||||
"leads": "Lead-uri"
|
||||
"fields": {
|
||||
"name": "Nume",
|
||||
"parent": "Părinte",
|
||||
"status": "Stare",
|
||||
"dateStart": "Dată început",
|
||||
"dateEnd": "Dată terminare",
|
||||
"direction": "Direcție",
|
||||
"duration": "Durată",
|
||||
"description": "Descriere",
|
||||
"users": "Utilizatori",
|
||||
"contacts": "Contacte",
|
||||
"leads": "Lead-uri",
|
||||
"reminders": "Mementouri",
|
||||
"account": "Cont",
|
||||
"acceptanceStatus": "Starea de acceptare"
|
||||
},
|
||||
"options": {
|
||||
"status": {
|
||||
"Planned": "Planificat",
|
||||
"Held": "Reținut",
|
||||
"Not Held": "Nereșinut"
|
||||
},
|
||||
"links": {
|
||||
"direction": {
|
||||
"Outbound": "Ieșire",
|
||||
"Inbound": "Intrare"
|
||||
},
|
||||
"options": {
|
||||
"status": {
|
||||
"Planned": "Planificat",
|
||||
"Held": "Detinut",
|
||||
"Not Held": "Nedetinut"
|
||||
},
|
||||
"direction": {
|
||||
"Outbound": "Iesire",
|
||||
"Inbound": "Intrare"
|
||||
}
|
||||
},
|
||||
"labels": {
|
||||
"Create Call": "Creare Apelare",
|
||||
"Set Held": "Setare detinere",
|
||||
"Set Not Held": "Setare nedetinere",
|
||||
"Send Invitations": "Trimite invitatii"
|
||||
},
|
||||
"presetFilters": {
|
||||
"planned": "Planificat",
|
||||
"held": "Detinut",
|
||||
"todays": "Today's"
|
||||
"acceptanceStatus": {
|
||||
"None": "Nici unul",
|
||||
"Accepted": "Acceptat",
|
||||
"Declined": "Respins",
|
||||
"Tentative": "Tentativă"
|
||||
}
|
||||
}
|
||||
},
|
||||
"massActions": {
|
||||
"setHeld": "Setează ca reținut",
|
||||
"setNotHeld": "Setează ca nereținut"
|
||||
},
|
||||
"labels": {
|
||||
"Create Call": "Creare Apelare",
|
||||
"Set Held": "Setează ca reținut",
|
||||
"Set Not Held": "Setează ca nereținut",
|
||||
"Send Invitations": "Trimite invitații"
|
||||
},
|
||||
"presetFilters": {
|
||||
"planned": "Planificat",
|
||||
"held": "Reținut",
|
||||
"todays": "Azi"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
{
|
||||
"fields": {
|
||||
"name": "Nume",
|
||||
"description": "Descriere",
|
||||
"status": "Stare",
|
||||
"type": "Tip",
|
||||
"startDate": "Dată începere",
|
||||
"endDate": "Dată terminare",
|
||||
"targetLists": "Liste țintă",
|
||||
"excludingTargetLists": "Excluderea listelor țintă",
|
||||
"sentCount": "Trimis",
|
||||
"openedCount": "Deschis",
|
||||
"clickedCount": "Apăsat",
|
||||
"optedOutCount": "Ales să nu",
|
||||
"revenue": "Venituri",
|
||||
"revenueConverted": "Venituri (convertit)",
|
||||
"budget": "Buget",
|
||||
"budgetConverted": "Buget (convertit)"
|
||||
},
|
||||
"links": {
|
||||
"targetLists": "Liste Țintă",
|
||||
"excludingTargetLists": "Excluderea listelor țintă",
|
||||
"accounts": "Conturi",
|
||||
"contacts": "Contacte",
|
||||
"leads": "Lead-uri",
|
||||
"opportunities": "Oportunități",
|
||||
"campaignLogRecords": "Jurnal",
|
||||
"massEmails": "Email-uri în Masă",
|
||||
"trackingUrls": "Urmărire URL-uri"
|
||||
},
|
||||
"options": {
|
||||
"type": {
|
||||
"Television": "Televiziune"
|
||||
},
|
||||
"status": {
|
||||
"Planning": "Planificare",
|
||||
"Active": "Activ",
|
||||
"Inactive": "Inactiv",
|
||||
"Complete": "Complet"
|
||||
}
|
||||
},
|
||||
"labels": {
|
||||
"Create Campaign": "Crează Campanie",
|
||||
"Target Lists": "Liste Țintă",
|
||||
"Statistics": "Statistici",
|
||||
"hard": "tare",
|
||||
"soft": "ușor",
|
||||
"Unsubscribe": "Dezabonare",
|
||||
"Mass Emails": "Email-uri în Masă",
|
||||
"Email Templates": "Șabloane Email",
|
||||
"Unsubscribe again": "Dezabonare încă o dată",
|
||||
"Subscribe again": "Abonare încă o dată",
|
||||
"Create Target List": "Creați Listă Țintă"
|
||||
},
|
||||
"presetFilters": {
|
||||
"active": "Activ"
|
||||
},
|
||||
"messages": {
|
||||
"unsubscribed": "V-ați dezabonat din lista noastră de mail-uri",
|
||||
"subscribedAgain": "V-ați abonat încă o dată."
|
||||
},
|
||||
"tooltips": {
|
||||
"targetLists": "Țintele care ar trebui să primească mesaje.",
|
||||
"excludingTargetLists": "Țintele care nu ar trebui să primească mesaje."
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
{
|
||||
"fields": {
|
||||
"action": "Acțiune",
|
||||
"actionDate": "Dată",
|
||||
"data": "Date",
|
||||
"campaign": "Campanie",
|
||||
"parent": "Țintă",
|
||||
"object": "Obiect",
|
||||
"application": "Aplicație",
|
||||
"queueItem": "Elementul de așteptare",
|
||||
"stringData": "Șir Date",
|
||||
"stringAdditionalData": "Șir de Date Adiționale",
|
||||
"isTest": "Este Test"
|
||||
},
|
||||
"links": {
|
||||
"queueItem": "Elementul de așteptare",
|
||||
"parent": "Părinte",
|
||||
"object": "Obiect",
|
||||
"campaign": "Campanie"
|
||||
},
|
||||
"options": {
|
||||
"action": {
|
||||
"Sent": "Trimis",
|
||||
"Opened": "Deschis",
|
||||
"Opted Out": "Ales să nu",
|
||||
"Clicked": "Apăsat",
|
||||
"Lead Created": "Lead-ul a fost creat"
|
||||
}
|
||||
},
|
||||
"labels": {
|
||||
"All": "Tot"
|
||||
},
|
||||
"presetFilters": {
|
||||
"sent": "Trimis",
|
||||
"opened": "Deschis",
|
||||
"optedOut": "Ales să nu",
|
||||
"clicked": "Apăsat",
|
||||
"leadCreated": "Lead-ul a fost creat"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"fields": {
|
||||
"urlToUse": "Cod pentru inserare, în locul URL",
|
||||
"campaign": "Campanie"
|
||||
},
|
||||
"links": {
|
||||
"campaign": "Campanie"
|
||||
},
|
||||
"labels": {
|
||||
"Create CampaignTrackingUrl": "Creați Urmărire URL"
|
||||
}
|
||||
}
|
||||
@@ -1,42 +1,55 @@
|
||||
{
|
||||
"fields": {
|
||||
"name": "Nume",
|
||||
"number": "Numar",
|
||||
"status": "Status",
|
||||
"account": "Cont",
|
||||
"contact": "Contact",
|
||||
"priority": "Prioritate",
|
||||
"type": "Tip",
|
||||
"description": "Descriere"
|
||||
"fields": {
|
||||
"name": "Nume",
|
||||
"number": "Număr",
|
||||
"status": "Stare",
|
||||
"account": "Cont",
|
||||
"contacts": "Contacte",
|
||||
"priority": "Prioritate",
|
||||
"type": "Tip",
|
||||
"description": "Descriere",
|
||||
"inboundEmail": "Grup Cont Email",
|
||||
"attachments": "Atașamente"
|
||||
},
|
||||
"links": {
|
||||
"inboundEmail": "Grup Cont Email",
|
||||
"account": "Cont",
|
||||
"contact": "Contact (Principal)",
|
||||
"Contacts": "Contacte",
|
||||
"meetings": "Întâlniri",
|
||||
"calls": "Apeluri",
|
||||
"tasks": "Sarcini",
|
||||
"emails": "Email-uri",
|
||||
"articles": "Articole Bază de Cunoștință",
|
||||
"attachments": "Atașamente"
|
||||
},
|
||||
"options": {
|
||||
"status": {
|
||||
"New": "Nou",
|
||||
"Assigned": "Alocat",
|
||||
"Pending": "În așteptare",
|
||||
"Closed": "Închis",
|
||||
"Rejected": "Respins",
|
||||
"Duplicate": "Duplicat"
|
||||
},
|
||||
"links": {
|
||||
"priority": {
|
||||
"Low": "Scăzut",
|
||||
"High": "Înalt"
|
||||
},
|
||||
"options": {
|
||||
"status": {
|
||||
"New": "Nou",
|
||||
"Assigned": "Alocat",
|
||||
"Pending": "In asteptare",
|
||||
"Closed": "Inchis",
|
||||
"Rejected": "Respins",
|
||||
"Duplicate": "Duplicat"
|
||||
},
|
||||
"priority" : {
|
||||
"Low": "Scazut",
|
||||
"Normal": "Normal",
|
||||
"High": "Inalt",
|
||||
"Urgent": "Urgent"
|
||||
},
|
||||
"type": {
|
||||
"Question": "Intrebare",
|
||||
"Incident": "Incident",
|
||||
"Problem": "Problema"
|
||||
}
|
||||
},
|
||||
"labels": {
|
||||
"Create Case": "Creare Caz"
|
||||
},
|
||||
"presetFilters": {
|
||||
"open": "Deschide",
|
||||
"closed": "Inchis"
|
||||
"type": {
|
||||
"Question": "Întrebare",
|
||||
"Problem": "Problemă"
|
||||
}
|
||||
}
|
||||
},
|
||||
"labels": {
|
||||
"Create Case": "Creare Caz",
|
||||
"Close": "Închis",
|
||||
"Reject": "Respins",
|
||||
"Closed": "Închis",
|
||||
"Rejected": "Respins"
|
||||
},
|
||||
"presetFilters": {
|
||||
"open": "Deschis",
|
||||
"closed": "Închis"
|
||||
}
|
||||
}
|
||||
@@ -1,31 +1,52 @@
|
||||
{
|
||||
"fields": {
|
||||
"name": "Nume",
|
||||
"emailAddress": "Email",
|
||||
"title": "Titlu",
|
||||
"account": "Cont",
|
||||
"accounts": "Conturi",
|
||||
"phoneNumber": "Telefon",
|
||||
"accountType": "Tip cont",
|
||||
"doNotCall": "Nu sunati",
|
||||
"address": "Adresa",
|
||||
"opportunityRole": "Opportunity Role",
|
||||
"accountRole": "Rol",
|
||||
"description": "Descriere"
|
||||
},
|
||||
"links": {
|
||||
"opportunities": "Oportunitati",
|
||||
"cases": "Cazuri"
|
||||
},
|
||||
"labels": {
|
||||
"Create Contact": "Creare Contact"
|
||||
},
|
||||
"options": {
|
||||
"opportunityRole": {
|
||||
"": "--None--",
|
||||
"Decision Maker": "Decision Maker",
|
||||
"Evaluator": "Evaluator",
|
||||
"Influencer": "Influencer"
|
||||
}
|
||||
"fields": {
|
||||
"name": "Nume",
|
||||
"title": "Rol",
|
||||
"accountRole": "Rol",
|
||||
"account": "Cont",
|
||||
"accounts": "Conturi",
|
||||
"phoneNumber": "Telefon",
|
||||
"accountType": "Tip cont",
|
||||
"doNotCall": "Nu sunați",
|
||||
"address": "Adresă",
|
||||
"opportunityRole": "Rol Oportunitate",
|
||||
"description": "Descriere",
|
||||
"campaign": "Campanie",
|
||||
"targetLists": "Liste Țintă",
|
||||
"targetList": "Listă țintă",
|
||||
"portalUser": "Utilizator Portal",
|
||||
"originalLead": "Lead Original",
|
||||
"acceptanceStatus": "Stare Acceptare",
|
||||
"accountIsInactive": "Cont Inactiv",
|
||||
"acceptanceStatusMeetings": "Stare Acceptare (Întâlniri)",
|
||||
"acceptanceStatusCalls": "Stare Acceptare (Apeluri)"
|
||||
},
|
||||
"links": {
|
||||
"opportunities": "Oportunități",
|
||||
"cases": "Cazuri",
|
||||
"targetLists": "Liste Țintă",
|
||||
"campaignLogRecords": "Jurnal Campanie",
|
||||
"campaign": "Campanie",
|
||||
"account": "Cont (Principal)",
|
||||
"accounts": "Conturi",
|
||||
"casesPrimary": "Cazuri (Principal)",
|
||||
"tasksPrimary": "Sarcini (extins)",
|
||||
"portalUser": "Utilizator Portal",
|
||||
"originalLead": "Lead Original",
|
||||
"documents": "Documente"
|
||||
},
|
||||
"labels": {
|
||||
"Create Contact": "Creare Contact"
|
||||
},
|
||||
"options": {
|
||||
"opportunityRole": {
|
||||
"": "--Nici unul--",
|
||||
"Decision Maker": "Factor de decizie"
|
||||
}
|
||||
}
|
||||
},
|
||||
"presetFilters": {
|
||||
"portalUsers": "Utilizatori Portal",
|
||||
"notPortalUsers": "Nu sunt Utilizatori ai Portalului",
|
||||
"accountActive": "Activ"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
{
|
||||
"labels": {
|
||||
"Create Document": "Creați Document",
|
||||
"Details": "Detalii"
|
||||
},
|
||||
"fields": {
|
||||
"name": "Nume",
|
||||
"status": "Stare",
|
||||
"file": "Fișier",
|
||||
"type": "Tip",
|
||||
"publishDate": "Publică Dată",
|
||||
"expirationDate": "Data expirării",
|
||||
"description": "Descriere",
|
||||
"accounts": "Conturi",
|
||||
"folder": "Dosar"
|
||||
},
|
||||
"links": {
|
||||
"accounts": "Conturi",
|
||||
"opportunities": "Oportunități",
|
||||
"folder": "Dosar",
|
||||
"leads": "Lead-uri",
|
||||
"contacts": "Contacte"
|
||||
},
|
||||
"options": {
|
||||
"status": {
|
||||
"Active": "Activ",
|
||||
"Draft": "Schiță",
|
||||
"Expired": "Expirat",
|
||||
"Canceled": "Anulat"
|
||||
},
|
||||
"type": {
|
||||
"": "Nici unul",
|
||||
"License Agreement": "Acord de licențiere"
|
||||
}
|
||||
},
|
||||
"presetFilters": {
|
||||
"active": "Activ",
|
||||
"draft": "Schiță"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"labels": {
|
||||
"Create DocumentFolder": "Creați Dosarul Documentului",
|
||||
"Manage Categories": "Gestionare Dosare",
|
||||
"Documents": "Documente"
|
||||
},
|
||||
"links": {
|
||||
"documents": "Documente"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"labels": {
|
||||
"Create Lead": "Creați Lead",
|
||||
"Create Contact": "Creați Contact",
|
||||
"Add to Contact": "Adăugați la Contacte",
|
||||
"Add to Lead": "Adăugați la Lead",
|
||||
"Create Task": "Creați Sarcină",
|
||||
"Create Case": "Creați Caz"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"fields": {
|
||||
"name": "Nume",
|
||||
"status": "Stare",
|
||||
"target": "Țintă",
|
||||
"sentAt": "Dată Trimitere",
|
||||
"attemptCount": "Încercări",
|
||||
"emailAddress": "Adresă Email",
|
||||
"massEmail": "Email în Masă",
|
||||
"isTest": "Este test"
|
||||
},
|
||||
"links": {
|
||||
"target": "Țintă",
|
||||
"massEmail": "Email în Masă"
|
||||
},
|
||||
"options": {
|
||||
"status": {
|
||||
"Pending": "În așteptare",
|
||||
"Sent": "Trimis",
|
||||
"Failed": "Eșuat",
|
||||
"Sending": "Se trimite"
|
||||
}
|
||||
},
|
||||
"presetFilters": {
|
||||
"pending": "În așteptare",
|
||||
"sent": "Trimis",
|
||||
"failed": "Eșuat"
|
||||
}
|
||||
}
|
||||
@@ -1,79 +1,105 @@
|
||||
{
|
||||
"scopeNames": {
|
||||
"Account": "Cont",
|
||||
"Contact": "Contact",
|
||||
"Lead": "Lead",
|
||||
"Target": "Tinta",
|
||||
"Opportunity": "Oportunitate",
|
||||
"Meeting": "Intalnire",
|
||||
"Calendar": "Calendar",
|
||||
"Call": "Apel",
|
||||
"Task": "Activitate",
|
||||
"Case": "Caz",
|
||||
"InboundEmail": "Intrari Email"
|
||||
},
|
||||
"scopeNamesPlural": {
|
||||
"Account": "Conturi",
|
||||
"Contact": "Contacte",
|
||||
"Lead": "Lead-uri",
|
||||
"Target": "Tinte",
|
||||
"Opportunity": "Oportunitati",
|
||||
"Meeting": "Intalniri",
|
||||
"Calendar": "Calendar",
|
||||
"Call": "Apeluri",
|
||||
"Task": "Activitati",
|
||||
"Case": "Cazuri",
|
||||
"InboundEmail": "Email-uri intrate"
|
||||
},
|
||||
"dashlets": {
|
||||
"Leads": "Lead-urile mele",
|
||||
"Opportunities": "Oportunitatile mele",
|
||||
"Tasks": "Activitatile mele",
|
||||
"Cases": "Cazurile mele",
|
||||
"Calendar": "Calendar",
|
||||
"OpportunitiesByStage": "Oportunitati in functie de stadiu",
|
||||
"OpportunitiesByLeadSource": "Oportunitati in functie de Sursal Lead-ului",
|
||||
"SalesByMonth": "Vanzari pe luna",
|
||||
"SalesPipeline": "Flux Vanzari"
|
||||
},
|
||||
"labels": {
|
||||
"Create InboundEmail": "Creare Email intrare",
|
||||
"Activities": "Activitati",
|
||||
"History": "Istoric",
|
||||
"Attendees": "Participanti",
|
||||
"Schedule Meeting": "Programeaza Intalnire",
|
||||
"Schedule Call": "Programeaza Apel",
|
||||
"Compose Email": "Compune Email",
|
||||
"Log Meeting": "Log Intalnire",
|
||||
"Log Call": "Log Apel",
|
||||
"Archive Email": "Arhiveaza Email",
|
||||
"Create Task": "Creaza Activitate",
|
||||
"Tasks": "Activitati"
|
||||
},
|
||||
"fields": {
|
||||
"billingAddressCity": "Oras",
|
||||
"billingAddressCountry": "Tara",
|
||||
"billingAddressPostalCode": "Code Postal ",
|
||||
"billingAddressState": "Stat",
|
||||
"billingAddressStreet": "Strada",
|
||||
"addressCity": "Oras",
|
||||
"addressStreet": "Strada",
|
||||
"addressCountry": "Tara",
|
||||
"addressState": "Stat",
|
||||
"addressPostalCode": "Code Postal ",
|
||||
"shippingAddressCity": "City (Shipping)",
|
||||
"shippingAddressStreet": "Street (Shipping)",
|
||||
"shippingAddressCountry": "Country (Shipping)",
|
||||
"shippingAddressState": "State (Shipping)",
|
||||
"shippingAddressPostalCode": "Postal Code (Shipping)"
|
||||
},
|
||||
"links": {
|
||||
"contacts": "Contacte",
|
||||
"opportunities": "Oportunitati",
|
||||
"leads": "Lead-uri",
|
||||
"meetings": "Intalniri",
|
||||
"calls": "Apeluri",
|
||||
"tasks": "Activitati",
|
||||
"emails": "Email-uri"
|
||||
}
|
||||
}
|
||||
"links": {
|
||||
"parent": "Părinte",
|
||||
"contacts": "Contacte",
|
||||
"opportunities": "Oportunități",
|
||||
"leads": "Lead-uri",
|
||||
"meetings": "Întâlniri",
|
||||
"calls": "Apeluri",
|
||||
"tasks": "Sarcini",
|
||||
"emails": "Email-uri",
|
||||
"accounts": "Conturi",
|
||||
"cases": "Cazuri",
|
||||
"documents": "Documente",
|
||||
"account": "Cont",
|
||||
"opportunity": "Oportunitate"
|
||||
},
|
||||
"scopeNames": {
|
||||
"Account": "Cont",
|
||||
"Target": "Țintă",
|
||||
"Opportunity": "Oportunitate",
|
||||
"Meeting": "Întâlnire",
|
||||
"Call": "Apel",
|
||||
"Task": "Sarcină",
|
||||
"Case": "Caz",
|
||||
"DocumentFolder": "Dosar Document",
|
||||
"Campaign": "Campanie",
|
||||
"TargetList": "Listă Țintă",
|
||||
"MassEmail": "Email în Masă",
|
||||
"EmailQueueItem": "Email Element de așteptare",
|
||||
"CampaignTrackingUrl": "Urmărire URL",
|
||||
"Activities": "Activități",
|
||||
"KnowledgeBaseArticle": "Atricol Bază de Cunoștințe",
|
||||
"KnowledgeBaseCategory": "Categorie Bază de Cunoștințe",
|
||||
"CampaignLogRecord": "Înregistrare Jurnal Campanie"
|
||||
},
|
||||
"scopeNamesPlural": {
|
||||
"Account": "Conturi",
|
||||
"Contact": "Contacte",
|
||||
"Lead": "Lead-uri",
|
||||
"Target": "Ținte",
|
||||
"Opportunity": "Oportunități",
|
||||
"Meeting": "Întâlniri",
|
||||
"Call": "Apeluri",
|
||||
"Task": "Sarcini",
|
||||
"Case": "Cazuri",
|
||||
"Document": "Documente",
|
||||
"DocumentFolder": "Dosare Document",
|
||||
"Campaign": "Campanii",
|
||||
"TargetList": "Liste Țintă",
|
||||
"MassEmail": "Email-uri în Masă",
|
||||
"EmailQueueItem": "Email Element de Așteptare",
|
||||
"CampaignTrackingUrl": "Urmărire URL-uri",
|
||||
"Activities": "Activității",
|
||||
"KnowledgeBaseArticle": "Bază de Cunoștințe",
|
||||
"KnowledgeBaseCategory": "Categorii Bază de Cunoștințe",
|
||||
"CampaignLogRecord": "Înregistrări Jurnal Campanie"
|
||||
},
|
||||
"dashlets": {
|
||||
"Leads": "Lead-urile mele",
|
||||
"Opportunities": "Oportunitățile mele",
|
||||
"Tasks": "Sarcinile mele",
|
||||
"Cases": "Cazurile mele",
|
||||
"Calls": "Apelurile mele",
|
||||
"Meetings": "Întâlnirile mele",
|
||||
"OpportunitiesByStage": "Oportunități în funcție de stadiu",
|
||||
"OpportunitiesByLeadSource": "Oportunități în funcție de Sursa Lead-ului",
|
||||
"SalesByMonth": "Vânzări pe luna",
|
||||
"SalesPipeline": "Linie de Vanzari",
|
||||
"Activities": "Activitățiile mele"
|
||||
},
|
||||
"labels": {
|
||||
"Create InboundEmail": "Creare Email Intrare",
|
||||
"Activities": "Activități",
|
||||
"History": "Istoric",
|
||||
"Attendees": "Participanți",
|
||||
"Schedule Meeting": "Planifică Întâlnire",
|
||||
"Schedule Call": "Planifică Apel",
|
||||
"Compose Email": "Compune Email",
|
||||
"Log Meeting": "Jurnal Intalnire",
|
||||
"Log Call": "Jurnal Apel",
|
||||
"Archive Email": "Arhivează Email",
|
||||
"Create Task": "Crează Sarcină",
|
||||
"Tasks": "Activități"
|
||||
},
|
||||
"fields": {
|
||||
"billingAddressCity": "Oraș",
|
||||
"addressCity": "Oraș",
|
||||
"billingAddressCountry": "Țara",
|
||||
"addressCountry": "Țara",
|
||||
"billingAddressPostalCode": "Cod Poștal",
|
||||
"addressPostalCode": "Cod Poștal",
|
||||
"billingAddressState": "Stat",
|
||||
"addressState": "Stat",
|
||||
"billingAddressStreet": "Strada",
|
||||
"addressStreet": "Strada",
|
||||
"billingAddressMap": "Hartă",
|
||||
"addressMap": "Hartă",
|
||||
"shippingAddressCity": "Oraș (Livrare)",
|
||||
"shippingAddressStreet": "Stradă (Livrare)",
|
||||
"shippingAddressCountry": "Țară (Livrare)",
|
||||
"shippingAddressState": "Stat (Livrare)",
|
||||
"shippingAddressPostalCode": "Cod Poștal (Livrare)",
|
||||
"shippingAddressMap": "Hartă (Livrare)"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
{
|
||||
"labels": {
|
||||
"Create KnowledgeBaseArticle": "Creați Articol",
|
||||
"Any": "Oricare",
|
||||
"Send in Email": "Trimite în Email",
|
||||
"Move Up": "Mută Sus",
|
||||
"Move Down": "Mută Jos",
|
||||
"Move to Top": "Mută la Început",
|
||||
"Move to Bottom": "Mută la Sfârșit"
|
||||
},
|
||||
"fields": {
|
||||
"name": "Nume",
|
||||
"status": "Stare",
|
||||
"type": "Tip",
|
||||
"attachments": "Atașamente",
|
||||
"publishDate": "Publică Dată",
|
||||
"expirationDate": "Data Expirării",
|
||||
"description": "Descriere",
|
||||
"body": "Conținut",
|
||||
"categories": "Categorii",
|
||||
"language": "Limbă",
|
||||
"portals": "Portaluri"
|
||||
},
|
||||
"links": {
|
||||
"cases": "Cazuri",
|
||||
"opportunities": "Oportunități",
|
||||
"categories": "Ctegorii",
|
||||
"portals": "Portaluri"
|
||||
},
|
||||
"options": {
|
||||
"status": {
|
||||
"In Review": "În revizuire",
|
||||
"Draft": "Schiță",
|
||||
"Archived": "Arhivat",
|
||||
"Published": "Publicat"
|
||||
},
|
||||
"type": {
|
||||
"Article": "Articol"
|
||||
}
|
||||
},
|
||||
"tooltips": {
|
||||
"portals": "Articolul o să fie disponibil doar în portalurile specificate."
|
||||
},
|
||||
"presetFilters": {
|
||||
"published": "Publicat"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"labels": {
|
||||
"Create KnowledgeBaseCategory": "Creați Categorie",
|
||||
"Manage Categories": "Gestionare Categorii",
|
||||
"Articles": "Articole"
|
||||
},
|
||||
"links": {
|
||||
"articles": "Articole"
|
||||
}
|
||||
}
|
||||
@@ -1,50 +1,65 @@
|
||||
{
|
||||
"labels": {
|
||||
"Converted To": "Convertit la",
|
||||
"Create Lead": "Creare Lead",
|
||||
"Convert": "Converteste",
|
||||
"convert": "converteste"
|
||||
"labels": {
|
||||
"Converted To": "Convertit la",
|
||||
"Create Lead": "Creare Lead",
|
||||
"Convert": "Convertește",
|
||||
"convert": "convertește"
|
||||
},
|
||||
"fields": {
|
||||
"name": "Nume",
|
||||
"title": "Titlu",
|
||||
"website": "Site Web",
|
||||
"phoneNumber": "Telefon",
|
||||
"accountName": "Nume Cont",
|
||||
"doNotCall": "Nu sunați",
|
||||
"address": "Adresa",
|
||||
"status": "Stare",
|
||||
"source": "Sursa",
|
||||
"opportunityAmount": "Sumă Oportunitate",
|
||||
"opportunityAmountConverted": "Sumă Oportunitate (convertită)",
|
||||
"description": "Descriere",
|
||||
"createdAccount": "Cont",
|
||||
"createdOpportunity": "Oportunitate",
|
||||
"campaign": "Campanie",
|
||||
"targetLists": "Liste Țintă",
|
||||
"targetList": "Listă Țintă",
|
||||
"industry": "Industrie",
|
||||
"acceptanceStatus": "Stare Acceptare",
|
||||
"opportunityAmountCurrency": "Valută Sumă Oportunitate",
|
||||
"acceptanceStatusMeetings": "Stare Acceptare (Întâlniri)",
|
||||
"acceptanceStatusCalls": "Stare Acceptare (Apeluri)"
|
||||
},
|
||||
"links": {
|
||||
"targetLists": "Liste Țintă",
|
||||
"campaignLogRecords": "Jurnal Campanie",
|
||||
"campaign": "Campanie",
|
||||
"createdAccount": "Cont",
|
||||
"createdOpportunity": "Oportunitate",
|
||||
"cases": "Cazuri",
|
||||
"documents": "Documente"
|
||||
},
|
||||
"options": {
|
||||
"status": {
|
||||
"New": "Nou",
|
||||
"Assigned": "Atribuit",
|
||||
"In Process": "În Proces",
|
||||
"Converted": "Convertit",
|
||||
"Recycled": "Reciclat",
|
||||
"Dead": "Inactiv"
|
||||
},
|
||||
"fields": {
|
||||
"name": "Nume",
|
||||
"emailAddress": "Email",
|
||||
"title": "Titlu",
|
||||
"website": "Site Web",
|
||||
"phoneNumber": "Telefon",
|
||||
"accountName": "Nume Cont",
|
||||
"doNotCall": "Nu sunati",
|
||||
"address": "Adresa",
|
||||
"status": "Status",
|
||||
"source": "Sursa",
|
||||
"opportunityAmount": "Suma Oportunitate",
|
||||
"description": "Descriere",
|
||||
"createdAccount": "Cont",
|
||||
"createdContact": "Contact",
|
||||
"createdOpportunity": "Oportunitate"
|
||||
},
|
||||
"links": {
|
||||
},
|
||||
"options": {
|
||||
"status": {
|
||||
"New": "Nou",
|
||||
"Assigned": "Alocat",
|
||||
"In Process": "In Proces",
|
||||
"Converted": "Convertit",
|
||||
"Recycled": "Reciclat",
|
||||
"Dead": "Inactiv"
|
||||
},
|
||||
"source": {
|
||||
"Call": "Apel",
|
||||
"Email": "Email",
|
||||
"Existing Customer": "Client Existent",
|
||||
"Partner": "Partener",
|
||||
"Public Relations": "Relatii Publice",
|
||||
"Web Site": "Site Web",
|
||||
"Campaign": "Campanie",
|
||||
"Other": "Altele"
|
||||
}
|
||||
},
|
||||
"presetFilters": {
|
||||
"active": "Activ"
|
||||
"source": {
|
||||
"": "Nici unul",
|
||||
"Call": "Apel",
|
||||
"Existing Customer": "Client Existent",
|
||||
"Partner": "Partener",
|
||||
"Public Relations": "Relații Publice",
|
||||
"Web Site": "Site Web",
|
||||
"Campaign": "Campanie",
|
||||
"Other": "Altele"
|
||||
}
|
||||
}
|
||||
},
|
||||
"presetFilters": {
|
||||
"active": "Activ",
|
||||
"converted": "Convertit"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
{
|
||||
"fields": {
|
||||
"name": "Nume",
|
||||
"status": "Stare",
|
||||
"storeSentEmails": "Stocați email-urile trimise",
|
||||
"startAt": "Data Începerii",
|
||||
"fromAddress": "Din Adresă",
|
||||
"fromName": "Din Nume",
|
||||
"replyToAddress": "Răspundeți la Adresă",
|
||||
"replyToName": "Răspundeți la Nume",
|
||||
"campaign": "Campanie",
|
||||
"emailTemplate": "Șablon Email",
|
||||
"inboundEmail": "Cont Email",
|
||||
"targetLists": "Liste Țintă",
|
||||
"excludingTargetLists": "Excluderea listelor țintă",
|
||||
"smtpAccount": "Cont SMTP"
|
||||
},
|
||||
"links": {
|
||||
"targetLists": "Liste Țintă",
|
||||
"excludingTargetLists": "Excluderea listelor țintă",
|
||||
"queueItems": "Elemente de Așteptare",
|
||||
"campaign": "Campanie",
|
||||
"emailTemplate": "Șablon Email",
|
||||
"inboundEmail": "Cont Email"
|
||||
},
|
||||
"options": {
|
||||
"status": {
|
||||
"Draft": "Schiță",
|
||||
"Pending": "În așteptare",
|
||||
"In Process": "În proces",
|
||||
"Complete": "Terminat",
|
||||
"Canceled": "Anulat",
|
||||
"Failed": "Eșuat"
|
||||
}
|
||||
},
|
||||
"labels": {
|
||||
"Create MassEmail": "Creați Email în Masă",
|
||||
"Send Test": "Trimite Test",
|
||||
"System SMTP": "Sistem SMTP",
|
||||
"system": "sistem",
|
||||
"group": "grup"
|
||||
},
|
||||
"messages": {
|
||||
"selectAtLeastOneTarget": "Selectați cel puțin o țintă.",
|
||||
"testSent": "Email(uri) test care ar trebui să fie trimise"
|
||||
},
|
||||
"tooltips": {
|
||||
"optOutEntirely": "Adresele de email ale destinatarilor care s-au dezabonat, vor fi marcate ca fiind oprite și nu vor mai primi email-uri în masă ",
|
||||
"targetLists": "Țintele care ar trebui să primească mesaje.",
|
||||
"excludingTargetLists": "Țintele care nu ar trebui să primească mesaje.",
|
||||
"storeSentEmails": "Email-urile o să fie stocate în CRM."
|
||||
},
|
||||
"presetFilters": {
|
||||
"complete": "Terminat"
|
||||
}
|
||||
}
|
||||
@@ -1,36 +1,50 @@
|
||||
{
|
||||
"fields": {
|
||||
"name": "Nume",
|
||||
"parent": "Parinte",
|
||||
"status": "Status",
|
||||
"dateStart": "Data inceput",
|
||||
"dateEnd": "Data terminare",
|
||||
"duration": "Durata",
|
||||
"description": "Descriere",
|
||||
"users": "Utilizatori",
|
||||
"contacts": "Contacte",
|
||||
"leads": "Lead-uri"
|
||||
"fields": {
|
||||
"name": "Nume",
|
||||
"parent": "Părinte",
|
||||
"status": "Stare",
|
||||
"dateStart": "Dată Început",
|
||||
"dateEnd": "Dată Terminare",
|
||||
"duration": "Durată",
|
||||
"description": "Descriere",
|
||||
"users": "Utilizatori",
|
||||
"contacts": "Contacte",
|
||||
"leads": "Lead-uri",
|
||||
"reminders": "Mementouri",
|
||||
"account": "Cont",
|
||||
"acceptanceStatus": "Stare Acceptare"
|
||||
},
|
||||
"options": {
|
||||
"status": {
|
||||
"Planned": "Planificat",
|
||||
"Held": "Reținut",
|
||||
"Not Held": "Nereținut"
|
||||
},
|
||||
"links": {
|
||||
},
|
||||
"options": {
|
||||
"status": {
|
||||
"Planned": "Planificat",
|
||||
"Held": "Detinut",
|
||||
"Not Held": "Nedetinut"
|
||||
}
|
||||
},
|
||||
"labels": {
|
||||
"Create Meeting": "Creare Intalnire",
|
||||
"Set Held": "Setare detinere",
|
||||
"Set Not Held": "Setare nedetinere",
|
||||
"Send Invitations": "Trimite invitatii",
|
||||
"Saved as Held": "Saved as Held",
|
||||
"Saved as Not Held": "Saved as Not Held"
|
||||
},
|
||||
"presetFilters": {
|
||||
"planned": "Planificat",
|
||||
"held": "Detinut",
|
||||
"todays": "Today's"
|
||||
"acceptanceStatus": {
|
||||
"None": "Nici unul",
|
||||
"Accepted": "Acceptat",
|
||||
"Declined": "Respins",
|
||||
"Tentative": "Tentativă"
|
||||
}
|
||||
}
|
||||
},
|
||||
"massActions": {
|
||||
"setHeld": "Setat ca reținut",
|
||||
"setNotHeld": "Setat ca nereținut"
|
||||
},
|
||||
"labels": {
|
||||
"Create Meeting": "Creare Întâlnire",
|
||||
"Set Held": "Setat ca reținut",
|
||||
"Set Not Held": "Setat ca nereținut",
|
||||
"Send Invitations": "Trimite Invitații",
|
||||
"on time": "la timp",
|
||||
"before": "înainte"
|
||||
},
|
||||
"presetFilters": {
|
||||
"planned": "Planificat",
|
||||
"held": "Reținut",
|
||||
"todays": "Astăzi"
|
||||
},
|
||||
"messages": {
|
||||
"nothingHasBeenSent": "Nu a fost trimis nimic"
|
||||
}
|
||||
}
|
||||
@@ -1,38 +1,50 @@
|
||||
{
|
||||
"fields": {
|
||||
"name": "Nume",
|
||||
"account": "Cont",
|
||||
"stage": "Stadiu",
|
||||
"amount": "Suma",
|
||||
"probability": "Probabilitate, %",
|
||||
"leadSource": "Sursa Lead",
|
||||
"doNotCall": "Nu sunati",
|
||||
"closeDate": "Data Inchiderii",
|
||||
"contacts": "Contacte",
|
||||
"description": "Descriere"
|
||||
},
|
||||
"links": {
|
||||
"contacts": "Contacte"
|
||||
},
|
||||
"options": {
|
||||
"stage": {
|
||||
"Prospecting": "Prospectare",
|
||||
"Qualification": "Calificari",
|
||||
"Needs Analysis": "Necesita Analiza",
|
||||
"Value Proposition": "Valoare Propunere",
|
||||
"Id. Decision Makers": "Id. Factori de Decizie",
|
||||
"Perception Analysis": "Analiza Perceptiei",
|
||||
"Proposal/Price Quote": "Propunere/Oferta Pret",
|
||||
"Negotiation/Review": "Negociere/Revizuire",
|
||||
"Closed Won": "Inchide ca Castigat",
|
||||
"Closed Lost": "Inchide ca Pierdut"
|
||||
}
|
||||
},
|
||||
"labels": {
|
||||
"Create Opportunity": "Creare Oportunitate"
|
||||
},
|
||||
"presetFilters": {
|
||||
"open": "Deschide",
|
||||
"won": "Won"
|
||||
"fields": {
|
||||
"name": "Nume",
|
||||
"account": "Cont",
|
||||
"stage": "Stadiu",
|
||||
"amount": "Sumă",
|
||||
"probability": "Probabilitate, %",
|
||||
"leadSource": "Sursă Lead",
|
||||
"doNotCall": "Nu sunați",
|
||||
"closeDate": "Data Închiderii",
|
||||
"contacts": "Contacte",
|
||||
"description": "Descriere",
|
||||
"amountConverted": "Sumă (convertit)",
|
||||
"amountWeightedConverted": "Suma ponderată",
|
||||
"campaign": "Campanie",
|
||||
"originalLead": "Lead Original",
|
||||
"amountCurrency": "Sumă Valută",
|
||||
"contactRole": "Rol Contact"
|
||||
},
|
||||
"links": {
|
||||
"contacts": "Contacte",
|
||||
"documents": "Documente",
|
||||
"campaign": "Campanie",
|
||||
"originalLead": "Lead Original"
|
||||
},
|
||||
"options": {
|
||||
"stage": {
|
||||
"Prospecting": "Prospectare",
|
||||
"Qualification": "Calificări",
|
||||
"Proposal": "Propunere",
|
||||
"Negotiation": "Negociere",
|
||||
"Needs Analysis": "Necesită Analiză",
|
||||
"Value Proposition": "Propunere Valoare",
|
||||
"Id. Decision Makers": "Id. Factori de Decizie",
|
||||
"Perception Analysis": "Analiză Percepției",
|
||||
"Proposal/Price Quote": "Propunere/Preț Ofertă",
|
||||
"Negotiation/Review": "Negociere/Revizuire",
|
||||
"Closed Won": "Închide Câștigat",
|
||||
"Closed Lost": "Închide Pierdut"
|
||||
}
|
||||
}
|
||||
},
|
||||
"labels": {
|
||||
"Create Opportunity": "Creare Oportunitate"
|
||||
},
|
||||
"presetFilters": {
|
||||
"open": "Deschide",
|
||||
"won": "Câștigat",
|
||||
"lost": "Pierdut"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"links": {
|
||||
"articles": "Articole Bază de Cunoștință"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"options": {
|
||||
"job": {
|
||||
"ProcessMassEmail": "Trimite Email-uri în Masă",
|
||||
"ControlKnowledgeBaseArticleStatus": "Controlează Starea Articolelor Bază de Cunoștințe"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"fields": {
|
||||
"name": "Nume",
|
||||
"description": "Descriere",
|
||||
"entryCount": "Numără Intrări",
|
||||
"campaigns": "Campanii",
|
||||
"endDate": "Dată Terminare",
|
||||
"targetLists": "Liste Țintă",
|
||||
"includingActionList": "Include",
|
||||
"excludingActionList": "Exclude"
|
||||
},
|
||||
"links": {
|
||||
"accounts": "Conturi",
|
||||
"contacts": "Contacte",
|
||||
"leads": "Lead-uri",
|
||||
"campaigns": "Campanii",
|
||||
"massEmails": "Email-uri în Masă"
|
||||
},
|
||||
"options": {
|
||||
"type": {
|
||||
"Television": "Televiziune"
|
||||
}
|
||||
},
|
||||
"labels": {
|
||||
"Create TargetList": "Creați Listă Țintă",
|
||||
"Opted Out": "Ales să nu",
|
||||
"Cancel Opt-Out": "Anulează alegerea de ieșire",
|
||||
"Opt-Out": "Aleg să nu"
|
||||
}
|
||||
}
|
||||
@@ -1,37 +1,45 @@
|
||||
{
|
||||
"fields": {
|
||||
"name": "Nume",
|
||||
"parent": "Parinte",
|
||||
"status": "Status",
|
||||
"dateStart": "Data inceput",
|
||||
"dateEnd": "Data scadenta",
|
||||
"priority": "Prioritate",
|
||||
"description": "Descriere",
|
||||
"isOverdue": "Este Restant"
|
||||
"fields": {
|
||||
"name": "Nume",
|
||||
"parent": "Părinte",
|
||||
"status": "Stare",
|
||||
"dateStart": "Dată Începere",
|
||||
"dateEnd": "Dată Scadență",
|
||||
"dateStartDate": "Dată Începere (toată ziua)",
|
||||
"dateEndDate": "Dată Terminare (toată ziua)",
|
||||
"priority": "Prioritate",
|
||||
"description": "Descriere",
|
||||
"isOverdue": "Este Restant",
|
||||
"account": "Cont",
|
||||
"dateCompleted": "Dată Terminare",
|
||||
"attachments": "Atașamente",
|
||||
"reminders": "Mementouri"
|
||||
},
|
||||
"links": {
|
||||
"attachments": "Atașamente",
|
||||
"account": "Cont"
|
||||
},
|
||||
"options": {
|
||||
"status": {
|
||||
"Not Started": "Nu a început",
|
||||
"Started": "Început",
|
||||
"Completed": "Terminat",
|
||||
"Canceled": "Anulat",
|
||||
"Deferred": "Amânat"
|
||||
},
|
||||
"links": {
|
||||
},
|
||||
"options": {
|
||||
"status": {
|
||||
"Not Started": "Ne inceput",
|
||||
"Started": "Inceput",
|
||||
"Completed": "Completat",
|
||||
"Canceled": "Anulat"
|
||||
},
|
||||
"priority" : {
|
||||
"Low": "Scazut",
|
||||
"Normal": "Normal",
|
||||
"High": "Inalt",
|
||||
"Urgent": "Urgent"
|
||||
}
|
||||
},
|
||||
"labels": {
|
||||
"Create Task": "Creaza Activitate"
|
||||
},
|
||||
"presetFilters": {
|
||||
"active": "Activ",
|
||||
"completed": "Completat",
|
||||
"todays": "Today's",
|
||||
"overdue": "Overdue"
|
||||
"priority": {
|
||||
"Low": "Scăzut",
|
||||
"High": "Înalt"
|
||||
}
|
||||
}
|
||||
},
|
||||
"labels": {
|
||||
"Create Task": "Crează Sarcine",
|
||||
"Complete": "Terminat"
|
||||
},
|
||||
"presetFilters": {
|
||||
"completed": "Terminat",
|
||||
"deferred": "Amânat",
|
||||
"todays": "Astăzi",
|
||||
"overdue": "Restant"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"fields": {
|
||||
"acceptanceStatus": "Stare Acceptare",
|
||||
"acceptanceStatusMeetings": "Stare Acceptare (Întâlniri)",
|
||||
"acceptanceStatusCalls": "Stare Acceptare (Apeluri)"
|
||||
},
|
||||
"links": {
|
||||
"targetLists": "Liste Țintă"
|
||||
}
|
||||
}
|
||||
@@ -26,6 +26,8 @@
|
||||
"stage": {
|
||||
"Prospecting": "Привлечение клиента",
|
||||
"Qualification": "Оценка возможности",
|
||||
"Proposal": "Предложение",
|
||||
"Negotiation": "Согласование",
|
||||
"Needs Analysis": "Требует анализа",
|
||||
"Value Proposition": "Выбор предложения/оферты",
|
||||
"Id. Decision Makers": "Определение ответственного лица",
|
||||
|
||||
@@ -27,6 +27,8 @@
|
||||
"stage": {
|
||||
"Prospecting": "Izviđanje",
|
||||
"Qualification": "Kvalifikacija",
|
||||
"Proposal": "Predlog",
|
||||
"Negotiation": "Pregovori",
|
||||
"Needs Analysis": "Analiza potrebna",
|
||||
"Value Proposition": "Dobitak",
|
||||
"Id. Decision Makers": "Id. Donosioci odluka",
|
||||
|
||||
@@ -26,6 +26,8 @@
|
||||
"stage": {
|
||||
"Prospecting": "Araştırma",
|
||||
"Qualification": "Yeterlilik",
|
||||
"Proposal": "Proposal",
|
||||
"Negotiation": "Görüşme",
|
||||
"Needs Analysis": "Analiz Yapılmalı",
|
||||
"Value Proposition": "Değer Önerisi",
|
||||
"Id. Decision Makers": "Id. Karar Vericiler",
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user