mirror of
https://github.com/espocrm/espocrm.git
synced 2026-03-11 05:37:02 +00:00
Compare commits
42 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9b170483da | ||
|
|
71ecc3c59e | ||
|
|
04039f5bc3 | ||
|
|
94d0bccd96 | ||
|
|
72a12636b3 | ||
|
|
bfbc69fb25 | ||
|
|
206dc36a78 | ||
|
|
65f285e93f | ||
|
|
41f842e581 | ||
|
|
9879f749e4 | ||
|
|
b88686816b | ||
|
|
08da60b1b0 | ||
|
|
987b838856 | ||
|
|
8ba79cce2a | ||
|
|
3a48b155e2 | ||
|
|
c5af04ec2b | ||
|
|
62f0b6ad04 | ||
|
|
e7787b18a3 | ||
|
|
e77127e2d5 | ||
|
|
a44a25f2e7 | ||
|
|
2ec595f8b0 | ||
|
|
9d61f22296 | ||
|
|
69a99667a3 | ||
|
|
f7c5e8773d | ||
|
|
1e7bdaa991 | ||
|
|
fcd40af7c9 | ||
|
|
8d949fa1fc | ||
|
|
d9504360d9 | ||
|
|
09c34ff006 | ||
|
|
dfc3f0a5d3 | ||
|
|
0a3f1dad50 | ||
|
|
3772e1e839 | ||
|
|
c4f5416adf | ||
|
|
fcdb9aca5d | ||
|
|
eaadcca1a9 | ||
|
|
f0bff56146 | ||
|
|
92e2177ed3 | ||
|
|
de72e6d6af | ||
|
|
32a8efad0e | ||
|
|
f8523cf2b6 | ||
|
|
4e1df42f36 | ||
|
|
9a60ee9fe6 |
@@ -70,6 +70,11 @@ class AttributeFetcher
|
||||
if ($parent = $entity->get($relationName)) {
|
||||
return $parent->get('name');
|
||||
}
|
||||
} else if ($entity->getAttributeParam($attribute, 'isLinkMultipleIdList') && $methodName == 'get') {
|
||||
$relationName = $entity->getAttributeParam($attribute, 'relation');
|
||||
if (!$entity->has($attribute)) {
|
||||
$entity->loadLinkMultipleField($relationName);
|
||||
}
|
||||
}
|
||||
|
||||
return $entity->$methodName($attribute);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<?php
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
@@ -54,10 +54,20 @@ class AddLinkMultipleIdType extends \Espo\Core\Formula\Functions\Base
|
||||
if (!is_string($link)) {
|
||||
throw new Error();
|
||||
}
|
||||
if (!is_string($id)) {
|
||||
throw new Error();
|
||||
}
|
||||
|
||||
$this->getEntity()->addLinkMultipleId($link, $id);
|
||||
if (is_array($id)) {
|
||||
$idList = $id;
|
||||
foreach ($idList as $id) {
|
||||
if (!is_string($id)) {
|
||||
throw new Error();
|
||||
}
|
||||
$this->getEntity()->addLinkMultipleId($link, $id);
|
||||
}
|
||||
} else {
|
||||
if (!is_string($id)) {
|
||||
throw new Error();
|
||||
}
|
||||
$this->getEntity()->addLinkMultipleId($link, $id);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
<?php
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
@@ -40,6 +40,12 @@ class ValueType extends Base
|
||||
throw new Error();
|
||||
}
|
||||
|
||||
return $item->value;
|
||||
$value = $item->value;
|
||||
|
||||
if (is_string($value)) {
|
||||
$value = str_replace("\\n", "\n", $value);
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
<?php
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
@@ -47,7 +47,7 @@ class VariableType extends Base
|
||||
}
|
||||
|
||||
if (!property_exists($this->getVariables(), $name)) {
|
||||
throw new Error();
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->getVariables()->$name;
|
||||
|
||||
@@ -121,6 +121,8 @@ class Htmlizer
|
||||
$v = [];
|
||||
}
|
||||
foreach ($v as $k => $w) {
|
||||
$keyRaw = $k . '_RAW';
|
||||
$v[$keyRaw] = $v[$k];
|
||||
$v[$k] = $this->format($v[$k]);
|
||||
}
|
||||
$newList[] = $v;
|
||||
@@ -134,6 +136,8 @@ class Htmlizer
|
||||
$data[$field] = get_object_vars($value);
|
||||
}
|
||||
foreach ($data[$field] as $k => $w) {
|
||||
$keyRaw = $k . '_RAW';
|
||||
$data[$field][$keyRaw] = $data[$field][$k];
|
||||
$data[$field][$k] = $this->format($data[$field][$k]);
|
||||
}
|
||||
}
|
||||
@@ -142,7 +146,9 @@ class Htmlizer
|
||||
}
|
||||
|
||||
if (array_key_exists($field, $data)) {
|
||||
$data[$field] = $this->format($data[$field]);
|
||||
$keyRaw = $field . '_RAW';
|
||||
$data[$keyRaw] = $data[$field];
|
||||
$data[$field] = $this->format($data[$field]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -171,12 +177,54 @@ class Htmlizer
|
||||
public function render(Entity $entity, $template, $id = null, $additionalData = array(), $skipLinks = false)
|
||||
{
|
||||
$code = \LightnCandy::compile($template, [
|
||||
'flags' => \LightnCandy::FLAG_HANDLEBARSJS,
|
||||
'helpers' => [
|
||||
'file' => function ($context, $options) {
|
||||
if (count($context) && $context[0]) {
|
||||
$id = $context[0];
|
||||
return "?entryPoint=attachment&id=" . $id;
|
||||
}
|
||||
},
|
||||
'numberFormat' => function ($context, $options) {
|
||||
if ($context && isset($context[0])) {
|
||||
$number = $context[0];
|
||||
|
||||
$decimals = 0;
|
||||
$decimalPoint = '.';
|
||||
$thousandsSeparator = ',';
|
||||
|
||||
if (isset($options['decimals'])) {
|
||||
$decimals = $options['decimals'];
|
||||
}
|
||||
if (isset($options['decimalPoint'])) {
|
||||
$decimalPoint = $options['decimalPoint'];
|
||||
}
|
||||
if (isset($options['thousandsSeparator'])) {
|
||||
$thousandsSeparator = $options['thousandsSeparator'];
|
||||
}
|
||||
return number_format($number, $decimals, $decimalPoint, $thousandsSeparator);
|
||||
}
|
||||
return '';
|
||||
}
|
||||
],
|
||||
'hbhelpers' => [
|
||||
'ifEqual' => function () {
|
||||
$args = func_get_args();
|
||||
$context = $args[count($args) - 1];
|
||||
if ($args[0] === $args[1]) {
|
||||
return $context['fn']();
|
||||
} else {
|
||||
return $context['inverse'] ? $context['inverse']() : '';
|
||||
}
|
||||
},
|
||||
'ifNotEqual' => function () {
|
||||
$args = func_get_args();
|
||||
$context = $args[count($args) - 1];
|
||||
if ($args[0] !== $args[1]) {
|
||||
return $context['fn']();
|
||||
} else {
|
||||
return $context['inverse'] ? $context['inverse']() : '';
|
||||
}
|
||||
}
|
||||
]
|
||||
]);
|
||||
|
||||
@@ -92,26 +92,35 @@ class Base implements Injectable
|
||||
|
||||
public function process(Entity $entity)
|
||||
{
|
||||
if ($entity->has('assignedUserId') && $entity->get('assignedUserId')) {
|
||||
$assignedUserId = $entity->get('assignedUserId');
|
||||
if ($assignedUserId != $this->getUser()->id && $entity->isFieldChanged('assignedUserId')) {
|
||||
$notification = $this->getEntityManager()->getEntity('Notification');
|
||||
$notification->set(array(
|
||||
'type' => 'Assign',
|
||||
'userId' => $assignedUserId,
|
||||
'data' => array(
|
||||
'entityType' => $entity->getEntityType(),
|
||||
'entityId' => $entity->id,
|
||||
'entityName' => $entity->get('name'),
|
||||
'isNew' => $entity->isNew(),
|
||||
'userId' => $this->getUser()->id,
|
||||
'userName' => $this->getUser()->get('name')
|
||||
)
|
||||
));
|
||||
$this->getEntityManager()->saveEntity($notification);
|
||||
if (!$entity->get('assignedUserId')) return;
|
||||
if (!$entity->isFieldChanged('assignedUserId')) return;
|
||||
|
||||
$assignedUserId = $entity->get('assignedUserId');
|
||||
|
||||
if ($entity->hasAttribute('createdById') && $entity->hasAttribute('modifiedById')) {
|
||||
if ($entity->isNew()) {
|
||||
$isNotSelfAssignment = $assignedUserId !== $entity->get('createdById');
|
||||
} else {
|
||||
$isNotSelfAssignment = $assignedUserId !== $entity->get('modifiedById');
|
||||
}
|
||||
} else {
|
||||
$isNotSelfAssignment = $assignedUserId !== $this->getUser()->id;
|
||||
}
|
||||
if (!$isNotSelfAssignment) return;
|
||||
|
||||
$notification = $this->getEntityManager()->getEntity('Notification');
|
||||
$notification->set(array(
|
||||
'type' => 'Assign',
|
||||
'userId' => $assignedUserId,
|
||||
'data' => array(
|
||||
'entityType' => $entity->getEntityType(),
|
||||
'entityId' => $entity->id,
|
||||
'entityName' => $entity->get('name'),
|
||||
'isNew' => $entity->isNew(),
|
||||
'userId' => $this->getUser()->id,
|
||||
'userName' => $this->getUser()->get('name')
|
||||
)
|
||||
));
|
||||
$this->getEntityManager()->saveEntity($notification);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -50,6 +50,23 @@ class Entity extends \Espo\ORM\Entity
|
||||
$defs['additionalColumns'] = $columns;
|
||||
}
|
||||
|
||||
$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'];
|
||||
}
|
||||
$defs['orderBy'] = $orderBy;
|
||||
$defs['order'] = $order;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$collection = $this->get($field, $defs);
|
||||
$ids = array();
|
||||
$names = new \stdClass();
|
||||
|
||||
@@ -36,13 +36,16 @@ class Person extends \Espo\Services\Record
|
||||
protected function getDuplicateWhereClause(Entity $entity, $data = array())
|
||||
{
|
||||
$data = array(
|
||||
'OR' => array(
|
||||
array(
|
||||
'firstName' => $entity->get('firstName'),
|
||||
'lastName' => $entity->get('lastName'),
|
||||
)
|
||||
)
|
||||
'OR' => []
|
||||
);
|
||||
$toCheck = false;
|
||||
if ($entity->get('firstName') || $entity->get('lastName')) {
|
||||
$part = [];
|
||||
$part['firstName'] = $entity->get('firstName');
|
||||
$part['lastName'] = $entity->get('lastName');
|
||||
$data['OR'][] = $part;
|
||||
$toCheck = true;
|
||||
}
|
||||
if (
|
||||
($entity->get('emailAddress') || $entity->get('emailAddressData'))
|
||||
&&
|
||||
@@ -62,8 +65,12 @@ class Person extends \Espo\Services\Record
|
||||
$data['OR'][] = array(
|
||||
'emailAddress' => $emailAddress
|
||||
);
|
||||
$toCheck = true;
|
||||
}
|
||||
}
|
||||
if (!$toCheck) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
@@ -146,51 +146,64 @@ class Converter
|
||||
{
|
||||
$entityDefs = $this->getEntityDefs(true);
|
||||
|
||||
$ormMeta = array();
|
||||
foreach($entityDefs as $entityName => $entityMeta) {
|
||||
$ormMetadata = array();
|
||||
foreach($entityDefs as $entityName => $entityMetadata) {
|
||||
|
||||
if (empty($entityMeta)) {
|
||||
if (empty($entityMetadata)) {
|
||||
$GLOBALS['log']->critical('Orm\Converter:process(), Entity:'.$entityName.' - metadata cannot be converted into ORM format');
|
||||
continue;
|
||||
}
|
||||
|
||||
$ormMeta = Util::merge($ormMeta, $this->convertEntity($entityName, $entityMeta));
|
||||
$ormMetadata = Util::merge($ormMetadata, $this->convertEntity($entityName, $entityMetadata));
|
||||
}
|
||||
|
||||
$ormMeta = $this->afterProcess($ormMeta);
|
||||
$ormMetadata = $this->afterProcess($ormMetadata);
|
||||
|
||||
return $ormMeta;
|
||||
return $ormMetadata;
|
||||
}
|
||||
|
||||
protected function convertEntity($entityName, $entityMeta)
|
||||
protected function convertEntity($entityName, $entityMetadata)
|
||||
{
|
||||
$ormMeta = array();
|
||||
$ormMeta[$entityName] = array(
|
||||
$ormMetadata = array();
|
||||
$ormMetadata[$entityName] = array(
|
||||
'fields' => array(
|
||||
),
|
||||
'relations' => array(
|
||||
),
|
||||
)
|
||||
);
|
||||
|
||||
foreach ($this->permittedEntityOptions as $optionName) {
|
||||
if (isset($entityMeta[$optionName])) {
|
||||
$ormMeta[$entityName][$optionName] = $entityMeta[$optionName];
|
||||
if (isset($entityMetadata[$optionName])) {
|
||||
$ormMetadata[$entityName][$optionName] = $entityMetadata[$optionName];
|
||||
}
|
||||
}
|
||||
|
||||
$ormMeta[$entityName]['fields'] = $this->convertFields($entityName, $entityMeta);
|
||||
$ormMeta = $this->correctFields($entityName, $ormMeta);
|
||||
$ormMetadata[$entityName]['fields'] = $this->convertFields($entityName, $entityMetadata);
|
||||
$ormMetadata = $this->correctFields($entityName, $ormMetadata);
|
||||
|
||||
$convertedLinks = $this->convertLinks($entityName, $entityMeta, $ormMeta);
|
||||
$convertedLinks = $this->convertLinks($entityName, $entityMetadata, $ormMetadata);
|
||||
|
||||
$ormMeta = Util::merge($ormMeta, $convertedLinks);
|
||||
$ormMetadata = Util::merge($ormMetadata, $convertedLinks);
|
||||
|
||||
return $ormMeta;
|
||||
if (!empty($entityMetadata['collection']) && is_array($entityMetadata['collection'])) {
|
||||
$collectionDefs = $entityMetadata['collection'];
|
||||
$ormMetadata[$entityName]['collection'] = array();
|
||||
if (array_key_exists('sortBy', $collectionDefs)) {
|
||||
$ormMetadata[$entityName]['collection']['orderBy'] = $collectionDefs['sortBy'];
|
||||
}
|
||||
$ormMetadata[$entityName]['collection']['order'] = 'ASC';
|
||||
if (array_key_exists('asc', $collectionDefs)) {
|
||||
$ormMetadata[$entityName]['collection']['order'] = $collectionDefs['asc'] ? 'ASC' : 'DESC';
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return $ormMetadata;
|
||||
}
|
||||
|
||||
public function afterProcess(array $ormMeta)
|
||||
public function afterProcess(array $ormMetadata)
|
||||
{
|
||||
foreach ($ormMeta as $entityName => &$entityParams) {
|
||||
foreach ($ormMetadata as $entityName => &$entityParams) {
|
||||
foreach ($entityParams['fields'] as $fieldName => &$fieldParams) {
|
||||
|
||||
/* remove fields without type */
|
||||
@@ -225,18 +238,18 @@ class Converter
|
||||
}
|
||||
}
|
||||
|
||||
return $ormMeta;
|
||||
return $ormMetadata;
|
||||
}
|
||||
|
||||
/**
|
||||
* Metadata conversion from Espo format into Doctrine
|
||||
*
|
||||
* @param string $entityName
|
||||
* @param array $entityMeta
|
||||
* @param array $entityMetadata
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function convertFields($entityName, &$entityMeta)
|
||||
protected function convertFields($entityName, &$entityMetadata)
|
||||
{
|
||||
//List of unmerged fields with default field defenitions in $outputMeta
|
||||
$unmergedFields = array(
|
||||
@@ -249,7 +262,7 @@ class Converter
|
||||
'dbType' => 'varchar'
|
||||
),
|
||||
'name' => array(
|
||||
'type' => isset($entityMeta['fields']['name']['type']) ? $entityMeta['fields']['name']['type'] : Entity::VARCHAR,
|
||||
'type' => isset($entityMetadata['fields']['name']['type']) ? $entityMetadata['fields']['name']['type'] : Entity::VARCHAR,
|
||||
'notStorable' => true
|
||||
),
|
||||
'deleted' => array(
|
||||
@@ -258,7 +271,7 @@ class Converter
|
||||
)
|
||||
);
|
||||
|
||||
foreach($entityMeta['fields'] as $fieldName => $fieldParams) {
|
||||
foreach($entityMetadata['fields'] as $fieldName => $fieldParams) {
|
||||
|
||||
/** check if "fields" option exists in $fieldMeta */
|
||||
$fieldTypeMeta = $this->getMetadataHelper()->getFieldDefsByType($fieldParams);
|
||||
@@ -278,10 +291,10 @@ class Converter
|
||||
if (isset($fieldTypeMeta['linkDefs'])) {
|
||||
$linkDefs = $this->getMetadataHelper()->getLinkDefsInFieldMeta($entityName, $fieldParams, $fieldTypeMeta['linkDefs']);
|
||||
if (isset($linkDefs)) {
|
||||
if (!isset($entityMeta['links'])) {
|
||||
$entityMeta['links'] = array();
|
||||
if (!isset($entityMetadata['links'])) {
|
||||
$entityMetadata['links'] = array();
|
||||
}
|
||||
$entityMeta['links'] = Util::merge( array($fieldName => $linkDefs), $entityMeta['links'] );
|
||||
$entityMetadata['links'] = Util::merge( array($fieldName => $linkDefs), $entityMetadata['links'] );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -292,17 +305,17 @@ class Converter
|
||||
/**
|
||||
* Correct fields defenitions based on \Espo\Custom\Core\Utils\Database\Orm\Fields
|
||||
*
|
||||
* @param array $ormMeta
|
||||
* @param array $ormMetadata
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function correctFields($entityName, array $ormMeta)
|
||||
protected function correctFields($entityName, array $ormMetadata)
|
||||
{
|
||||
$entityDefs = $this->getEntityDefs();
|
||||
|
||||
$entityMeta = $ormMeta[$entityName];
|
||||
$entityMetadata = $ormMetadata[$entityName];
|
||||
//load custom field definitions and customCodes
|
||||
foreach ($entityMeta['fields'] as $fieldName => $fieldParams) {
|
||||
foreach ($entityMetadata['fields'] as $fieldName => $fieldParams) {
|
||||
if (empty($fieldParams['type'])) continue;
|
||||
|
||||
$fieldType = ucfirst($fieldParams['type']);
|
||||
@@ -312,14 +325,14 @@ class Converter
|
||||
}
|
||||
|
||||
if (class_exists($className) && method_exists($className, 'load')) {
|
||||
$helperClass = new $className($this->metadata, $ormMeta, $entityDefs);
|
||||
$helperClass = new $className($this->metadata, $ormMetadata, $entityDefs);
|
||||
$fieldResult = $helperClass->process($fieldName, $entityName);
|
||||
if (isset($fieldResult['unset'])) {
|
||||
$ormMeta = Util::unsetInArray($ormMeta, $fieldResult['unset']);
|
||||
$ormMetadata = Util::unsetInArray($ormMetadata, $fieldResult['unset']);
|
||||
unset($fieldResult['unset']);
|
||||
}
|
||||
|
||||
$ormMeta = Util::merge($ormMeta, $fieldResult);
|
||||
$ormMetadata = Util::merge($ormMetadata, $fieldResult);
|
||||
}
|
||||
|
||||
$defaultAttributes = $this->metadata->get(['entityDefs', $entityName, 'fields', $fieldName, 'defaultAttributes']);
|
||||
@@ -333,7 +346,7 @@ class Converter
|
||||
)
|
||||
)
|
||||
);
|
||||
$ormMeta = Util::merge($ormMeta, $defaultMetadataPart);
|
||||
$ormMetadata = Util::merge($ormMetadata, $defaultMetadataPart);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -341,24 +354,24 @@ class Converter
|
||||
//add a field 'isFollowed' for scopes with 'stream => true'
|
||||
$scopeDefs = $this->getMetadata()->get('scopes.'.$entityName);
|
||||
if (isset($scopeDefs['stream']) && $scopeDefs['stream']) {
|
||||
if (!isset($entityMeta['fields']['isFollowed'])) {
|
||||
$ormMeta[$entityName]['fields']['isFollowed'] = array(
|
||||
if (!isset($entityMetadata['fields']['isFollowed'])) {
|
||||
$ormMetadata[$entityName]['fields']['isFollowed'] = array(
|
||||
'type' => 'varchar',
|
||||
'notStorable' => true,
|
||||
);
|
||||
|
||||
$ormMeta[$entityName]['fields']['followersIds'] = array(
|
||||
$ormMetadata[$entityName]['fields']['followersIds'] = array(
|
||||
'type' => 'jsonArray',
|
||||
'notStorable' => true,
|
||||
);
|
||||
$ormMeta[$entityName]['fields']['followersNames'] = array(
|
||||
$ormMetadata[$entityName]['fields']['followersNames'] = array(
|
||||
'type' => 'jsonObject',
|
||||
'notStorable' => true,
|
||||
);
|
||||
}
|
||||
} //END: add a field 'isFollowed' for stream => true
|
||||
|
||||
return $ormMeta;
|
||||
return $ormMetadata;
|
||||
}
|
||||
|
||||
protected function convertField($entityName, $fieldName, array $fieldParams, $fieldTypeMeta = null)
|
||||
@@ -397,20 +410,20 @@ class Converter
|
||||
return $fieldDefs;
|
||||
}
|
||||
|
||||
protected function convertLinks($entityName, $entityMeta, $ormMeta)
|
||||
protected function convertLinks($entityName, $entityMetadata, $ormMetadata)
|
||||
{
|
||||
if (!isset($entityMeta['links'])) {
|
||||
if (!isset($entityMetadata['links'])) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$relationships = array();
|
||||
foreach ($entityMeta['links'] as $linkName => $linkParams) {
|
||||
foreach ($entityMetadata['links'] as $linkName => $linkParams) {
|
||||
|
||||
if (isset($linkParams['skipOrmDefs']) && $linkParams['skipOrmDefs'] === true) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$convertedLink = $this->getRelationManager()->convert($linkName, $linkParams, $entityName, $ormMeta);
|
||||
$convertedLink = $this->getRelationManager()->convert($linkName, $linkParams, $entityName, $ormMetadata);
|
||||
|
||||
if (isset($convertedLink)) {
|
||||
$relationships = Util::merge($convertedLink, $relationships);
|
||||
|
||||
@@ -39,6 +39,8 @@ class LinkMultiple extends Base
|
||||
$fieldName.'Ids' => array(
|
||||
'type' => 'varchar',
|
||||
'notStorable' => true,
|
||||
'isLinkMultipleIdList' => true,
|
||||
'relation' => $fieldName
|
||||
),
|
||||
$fieldName.'Names' => array(
|
||||
'type' => 'varchar',
|
||||
|
||||
@@ -103,7 +103,7 @@ class RelationManager
|
||||
return false;
|
||||
}
|
||||
|
||||
public function convert($linkName, $linkParams, $entityName, $ormMeta)
|
||||
public function convert($linkName, $linkParams, $entityName, $ormMetadata)
|
||||
{
|
||||
$entityDefs = $this->getMetadata()->get('entityDefs');
|
||||
|
||||
@@ -132,7 +132,7 @@ class RelationManager
|
||||
}
|
||||
|
||||
if (isset($className) && $className !== false) {
|
||||
$helperClass = new $className($this->metadata, $ormMeta, $entityDefs);
|
||||
$helperClass = new $className($this->metadata, $ormMetadata, $entityDefs);
|
||||
return $helperClass->process($linkName, $entityName, $foreignLink['name'], $foreignEntityName);
|
||||
}
|
||||
//END: relationDefs defined in separate file
|
||||
|
||||
@@ -93,4 +93,9 @@ class OrmMetadata
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
public function get($key = null, $default = null)
|
||||
{
|
||||
$result = Util::getValueByKey($this->getData(), $key, $default);
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
@@ -9,12 +9,14 @@
|
||||
"application": "Application",
|
||||
"queueItem": "Queue Item",
|
||||
"stringData": "String Data",
|
||||
"stringAdditionalData": "String Additional Data"
|
||||
"stringAdditionalData": "String Additional Data",
|
||||
"isTest": "Is Test"
|
||||
},
|
||||
"links": {
|
||||
"queueItem": "Queue Item",
|
||||
"parent": "Parent",
|
||||
"object": "Object"
|
||||
"object": "Object",
|
||||
"campaign": "Campaign"
|
||||
},
|
||||
"options": {
|
||||
"action": {
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
{
|
||||
"name":"statistics",
|
||||
"label":"Statistics",
|
||||
"view":"crm:views/campaign/record/panels/statistics",
|
||||
"view":"crm:views/campaign/record/panels/campaign-stats",
|
||||
"hidden": false
|
||||
}
|
||||
]
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
<p>{{assignerUserName}} te ha asignado {{entityTypeLowerFirst}} a ti.</p>
|
||||
<p><strong>{{name}}</strong></p>
|
||||
<p>Comienzo: {{dateStart}}</p>
|
||||
{{#if description}}
|
||||
<p>{{{description}}}</p>
|
||||
{{/if}}
|
||||
<p><a href="{{recordUrl}}">Ver registro</a></p>
|
||||
@@ -0,0 +1,13 @@
|
||||
<p>Asunto: {{name}}</p>
|
||||
<p>Comienzo: {{dateStart}}</p>
|
||||
{{#if isUser}}
|
||||
{{#if description}}
|
||||
<p>{{{description}}}</p>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
<p>
|
||||
<a href="{{acceptLink}}">Aceptar</a>, <a href="{{declineLink}}">Declinar</a>, <a href="{{tentativeLink}}">Provisional</a>
|
||||
</p>
|
||||
{{#if isUser}}
|
||||
<p><a href="{{recordUrl}}">Ver registro</a></p>
|
||||
{{/if}}
|
||||
@@ -0,0 +1 @@
|
||||
Invitacion a {{{entityTypeLowerFirst}}} '{{{name}}}'
|
||||
@@ -0,0 +1,4 @@
|
||||
<p>Asunto: {{name}}</p>
|
||||
<p>Fecha Final: {{dateEnd}}</p>
|
||||
|
||||
<p><a href="{{recordUrl}}">Ver registro</a></p>
|
||||
@@ -0,0 +1,4 @@
|
||||
<p>Asunto: {{name}}</p>
|
||||
<p>Comienzo: {{dateStart}}</p>
|
||||
|
||||
<p><a href="{{recordUrl}}">Ver registro</a></p>
|
||||
@@ -0,0 +1 @@
|
||||
Recordatorio sobre {{{entityTypeLowerFirst}}} '{{{name}}}'
|
||||
@@ -44,6 +44,9 @@ class Account extends \Espo\Services\Record
|
||||
|
||||
protected function getDuplicateWhereClause(Entity $entity, $data = array())
|
||||
{
|
||||
if (!$entity->get('name')) {
|
||||
return false;
|
||||
}
|
||||
return array(
|
||||
'name' => $entity->get('name')
|
||||
);
|
||||
|
||||
@@ -31,7 +31,7 @@ namespace Espo\Modules\Crm\Services;
|
||||
|
||||
use \Espo\ORM\Entity;
|
||||
|
||||
class Contact extends \Espo\Services\Record
|
||||
class Contact extends \Espo\Core\Templates\Services\Person
|
||||
{
|
||||
|
||||
protected $readOnlyAttributeList = [
|
||||
@@ -43,41 +43,6 @@ class Contact extends \Espo\Services\Record
|
||||
'title'
|
||||
];
|
||||
|
||||
protected function getDuplicateWhereClause(Entity $entity, $data = array())
|
||||
{
|
||||
$data = array(
|
||||
'OR' => array(
|
||||
array(
|
||||
'firstName' => $entity->get('firstName'),
|
||||
'lastName' => $entity->get('lastName'),
|
||||
)
|
||||
)
|
||||
);
|
||||
if (
|
||||
($entity->get('emailAddress') || $entity->get('emailAddressData'))
|
||||
&&
|
||||
($entity->isNew() || $entity->isFieldChanged('emailAddress') || $entity->isFieldChanged('emailAddressData'))
|
||||
) {
|
||||
if ($entity->get('emailAddress')) {
|
||||
$list = [$entity->get('emailAddress')];
|
||||
}
|
||||
if ($entity->get('emailAddressData')) {
|
||||
foreach ($entity->get('emailAddressData') as $row) {
|
||||
if (!in_array($row->emailAddress, $list)) {
|
||||
$list[] = $row->emailAddress;
|
||||
}
|
||||
}
|
||||
}
|
||||
foreach ($list as $emailAddress) {
|
||||
$data['OR'][] = array(
|
||||
'emailAddress' => $emailAddress
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function afterCreate(Entity $entity, array $data = array())
|
||||
{
|
||||
parent::afterCreate($entity, $data);
|
||||
@@ -102,4 +67,3 @@ class Contact extends \Espo\Services\Record
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ use \Espo\Core\Exceptions\Forbidden;
|
||||
|
||||
use \Espo\ORM\Entity;
|
||||
|
||||
class Lead extends \Espo\Services\Record
|
||||
class Lead extends \Espo\Core\Templates\Services\Person
|
||||
{
|
||||
|
||||
protected function init()
|
||||
@@ -48,41 +48,6 @@ class Lead extends \Espo\Services\Record
|
||||
return $this->getInjection('container')->get('fieldManager');
|
||||
}
|
||||
|
||||
protected function getDuplicateWhereClause(Entity $entity, $data = array())
|
||||
{
|
||||
$data = array(
|
||||
'OR' => array(
|
||||
array(
|
||||
'firstName' => $entity->get('firstName'),
|
||||
'lastName' => $entity->get('lastName'),
|
||||
)
|
||||
)
|
||||
);
|
||||
if (
|
||||
($entity->get('emailAddress') || $entity->get('emailAddressData'))
|
||||
&&
|
||||
($entity->isNew() || $entity->isFieldChanged('emailAddress') || $entity->isFieldChanged('emailAddressData'))
|
||||
) {
|
||||
if ($entity->get('emailAddress')) {
|
||||
$list = [$entity->get('emailAddress')];
|
||||
}
|
||||
if ($entity->get('emailAddressData')) {
|
||||
foreach ($entity->get('emailAddressData') as $row) {
|
||||
if (!in_array($row->emailAddress, $list)) {
|
||||
$list[] = $row->emailAddress;
|
||||
}
|
||||
}
|
||||
}
|
||||
foreach ($list as $emailAddress) {
|
||||
$data['OR'][] = array(
|
||||
'emailAddress' => $emailAddress
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function afterCreate(Entity $entity, array $data = array())
|
||||
{
|
||||
parent::afterCreate($entity, $data);
|
||||
|
||||
@@ -563,7 +563,7 @@ abstract class Mapper implements IMapper
|
||||
if (!empty($data) && is_array($data)) {
|
||||
$setArr = array();
|
||||
foreach ($data as $column => $value) {
|
||||
$setArr[] = $this->toDb($column) . " = " . $this->pdo->quote($value);
|
||||
$setArr[] = $this->toDb($column) . " = " . $this->quote($value);
|
||||
}
|
||||
$setPart .= ', ' . implode(', ', $setArr);
|
||||
}
|
||||
|
||||
@@ -110,12 +110,15 @@ abstract class Entity implements IEntity
|
||||
|
||||
public function set($p1, $p2 = null)
|
||||
{
|
||||
if (is_array($p1)) {
|
||||
if (is_array($p1) || is_object($p1)) {
|
||||
if (is_object($p1)) {
|
||||
$p1 = get_object_vars($p1);
|
||||
}
|
||||
if ($p2 === null) {
|
||||
$p2 = false;
|
||||
}
|
||||
$this->populateFromArray($p1, $p2);
|
||||
} else {
|
||||
} else if (is_string($p1)) {
|
||||
$name = $p1;
|
||||
$value = $p2;
|
||||
if ($name == 'id') {
|
||||
|
||||
@@ -1,25 +1,32 @@
|
||||
{
|
||||
"actualFields":[
|
||||
"salutation",
|
||||
"first",
|
||||
"last"
|
||||
],
|
||||
"fields":{
|
||||
"salutation":{
|
||||
"type":"enum"
|
||||
},
|
||||
"first":{
|
||||
"type":"varchar"
|
||||
},
|
||||
"last":{
|
||||
"type":"varchar"
|
||||
}
|
||||
},
|
||||
"naming":"prefix",
|
||||
"notMergeable":true,
|
||||
"notCreatable": true,
|
||||
"filter": true,
|
||||
"fieldDefs":{
|
||||
"skipOrmDefs": true
|
||||
}
|
||||
}
|
||||
"actualFields":[
|
||||
"salutation",
|
||||
"first",
|
||||
"last"
|
||||
],
|
||||
"params":[
|
||||
{
|
||||
"name":"required",
|
||||
"type":"bool",
|
||||
"default":false
|
||||
}
|
||||
],
|
||||
"fields":{
|
||||
"salutation":{
|
||||
"type":"enum"
|
||||
},
|
||||
"first":{
|
||||
"type":"varchar"
|
||||
},
|
||||
"last":{
|
||||
"type":"varchar"
|
||||
}
|
||||
},
|
||||
"naming":"prefix",
|
||||
"notMergeable":true,
|
||||
"notCreatable":true,
|
||||
"filter":true,
|
||||
"fieldDefs":{
|
||||
"skipOrmDefs":true
|
||||
}
|
||||
}
|
||||
@@ -33,7 +33,7 @@ class EmailFilter extends \Espo\Core\SelectManagers\Base
|
||||
{
|
||||
protected function access(&$result)
|
||||
{
|
||||
if (!$this->hetUser()->isAdmin()) {
|
||||
if (!$this->getUser()->isAdmin()) {
|
||||
$this->accessOnlyOwn($result);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -757,6 +757,9 @@ class Record extends \Espo\Core\Services\Base
|
||||
|
||||
foreach ($collection as $e) {
|
||||
$this->loadAdditionalFieldsForList($e);
|
||||
if (!empty($params['loadAdditionalFields'])) {
|
||||
$this->loadAdditionalFields($e);
|
||||
}
|
||||
$this->prepareEntityForOutput($e);
|
||||
}
|
||||
|
||||
@@ -827,6 +830,9 @@ class Record extends \Espo\Core\Services\Base
|
||||
|
||||
foreach ($collection as $e) {
|
||||
$recordService->loadAdditionalFieldsForList($e);
|
||||
if (!empty($params['loadAdditionalFields'])) {
|
||||
$recordService->loadAdditionalFields($e);
|
||||
}
|
||||
$recordService->prepareEntityForOutput($e);
|
||||
}
|
||||
|
||||
|
||||
@@ -268,6 +268,7 @@ var Bull = Bull || {};
|
||||
|
||||
this.init();
|
||||
this.setup();
|
||||
this.setupFinal();
|
||||
|
||||
this.template = this.options.template || this.template;
|
||||
this.layout = this.options.layout || this.layout;
|
||||
@@ -322,6 +323,8 @@ var Bull = Bull || {};
|
||||
*/
|
||||
setup: function () {},
|
||||
|
||||
setupFinal: function () {},
|
||||
|
||||
/**
|
||||
* Set view container element if doesn't exist yet. It will call setElement after render.
|
||||
*/
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
************************************************************************/
|
||||
|
||||
|
||||
Espo.define('Crm:Views.Campaign.Record.Panels.Statistics', 'Views.Record.Panels.Side', function (Dep) {
|
||||
Espo.define('crm:views/campaign/record/panels/campaign-stats', 'views/record/panels/side', function (Dep) {
|
||||
|
||||
return Dep.extend({
|
||||
|
||||
@@ -36,7 +36,7 @@ Espo.define('crm:views/task/detail', 'views/detail', function (Dep) {
|
||||
if (this.getAcl().checkModel(this.model, 'edit')) {
|
||||
this.menu.buttons.push({
|
||||
'label': 'Complete',
|
||||
'action': 'setCompleted',
|
||||
'action': 'setCompletedMain',
|
||||
'iconHtml': '<span class="glyphicon glyphicon-ok"></span>',
|
||||
'acl': 'edit',
|
||||
});
|
||||
@@ -49,7 +49,7 @@ Espo.define('crm:views/task/detail', 'views/detail', function (Dep) {
|
||||
}
|
||||
},
|
||||
|
||||
actionSetCompleted: function (data) {
|
||||
actionSetCompletedMain: function (data) {
|
||||
var id = data.id;
|
||||
|
||||
this.model.save({
|
||||
|
||||
@@ -33,7 +33,6 @@ Espo.define('crm:views/task/record/list-expanded', ['views/record/list-expanded'
|
||||
rowActionsView: 'crm:views/task/record/row-actions/default',
|
||||
|
||||
actionSetCompleted: function (data) {
|
||||
console.log(1);
|
||||
List.prototype.actionSetCompleted.call(this, data);
|
||||
},
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
<a href="#{{model.name}}/view/{{model.id}}" class="link" data-id="{{model.id}}" title="{{value}}">{{value}}</a>
|
||||
<a href="#{{model.name}}/view/{{model.id}}" class="link" data-id="{{model.id}}" title="{{value}}">{{#if value}}{{value}}{{else}}{{translate 'None'}}{{/if}}</a>
|
||||
|
||||
5
client/res/templates/fields/link-parent/detail.tpl
Normal file
5
client/res/templates/fields/link-parent/detail.tpl
Normal file
@@ -0,0 +1,5 @@
|
||||
{{#if idValue}}
|
||||
<a href="#{{foreignScope}}/view/{{idValue}}" title="{{translate foreignScope category='scopeNames'}}">{{nameValue}}</a>
|
||||
{{else}}
|
||||
{{translate 'None'}}
|
||||
{{/if}}
|
||||
@@ -1 +1,3 @@
|
||||
{{translateOption salutationValue field='salutationName' scope=scope}} {{firstValue}} {{lastValue}}
|
||||
{{#if isNotEmpty}}{{translateOption salutationValue field='salutationName' scope=scope}} {{firstValue}} {{lastValue}}{{else}}
|
||||
{{translate 'None'}}
|
||||
{{/if}}
|
||||
@@ -5,9 +5,9 @@
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-sm-4 col-xs-4">
|
||||
<input type="text" class="form-control" name="first{{ucName}}" value="{{firstValue}}" placeholder="{{translate 'First Name'}}">
|
||||
<input type="text" class="form-control" name="first{{ucName}}" value="{{firstValue}}" placeholder="{{translate 'First Name'}}"{{#if firstMaxLength}} maxlength="{{firstMaxLength}}"{{/if}}>
|
||||
</div>
|
||||
<div class="col-sm-5 col-xs-5">
|
||||
<input type="text" class="form-control" name="last{{ucName}}" value="{{lastValue}}" placeholder="{{translate 'Last Name'}}">
|
||||
<input type="text" class="form-control" name="last{{ucName}}" value="{{lastValue}}" placeholder="{{translate 'Last Name'}}"{{#if lastMaxLength}} maxlength="{{lastMaxLength}}"{{/if}}>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -30,7 +30,7 @@ Espo.define('views/admin/field-manager/fields/foreign/field', 'views/fields/enum
|
||||
|
||||
return Dep.extend({
|
||||
|
||||
typeList: ['varchar', 'enum', 'enumInt', 'enumFloat', 'int', 'float', 'website'],
|
||||
typeList: ['varchar', 'enum', 'enumInt', 'enumFloat', 'int', 'float', 'website', 'date', 'datetime'],
|
||||
|
||||
setup: function () {
|
||||
Dep.prototype.setup.call(this);
|
||||
@@ -112,6 +112,10 @@ Espo.define('views/admin/field-manager/fields/foreign/field', 'views/fields/enum
|
||||
this.viewValue = 'views/fields/foreign-int';
|
||||
} else if (type == 'float') {
|
||||
this.viewValue = 'views/fields/foreign-float';
|
||||
} else if (type == 'date') {
|
||||
this.viewValue = 'views/fields/foreign-date';
|
||||
} else if (type == 'datetime') {
|
||||
this.viewValue = 'views/fields/foreign-datetime';
|
||||
} else {
|
||||
this.viewValue = null;
|
||||
}
|
||||
|
||||
@@ -62,6 +62,7 @@ Espo.define('views/admin/layouts/grid', 'views/admin/layouts/base', function (De
|
||||
}
|
||||
});
|
||||
$(e.target).closest('ul.panels > li').remove();
|
||||
this.normilizaDisabledItemList();
|
||||
},
|
||||
'click #layout a[data-action="addRow"]': function (e) {
|
||||
var tpl = this.unescape($("#layout-row-tpl").html());
|
||||
@@ -76,6 +77,7 @@ Espo.define('views/admin/layouts/grid', 'views/admin/layouts/base', function (De
|
||||
}
|
||||
});
|
||||
$(e.target).closest('ul.rows > li').remove();
|
||||
this.normilizaDisabledItemList();
|
||||
},
|
||||
'click #layout a[data-action="removeField"]': function (e) {
|
||||
var el = $(e.target).closest('li');
|
||||
@@ -166,6 +168,12 @@ Espo.define('views/admin/layouts/grid', 'views/admin/layouts/base', function (De
|
||||
}
|
||||
}, Dep.prototype.events),
|
||||
|
||||
normilizaDisabledItemList: function () {
|
||||
$('#layout ul.cells.disabled > li').each(function (i, el) {
|
||||
$(el).removeAttr('data-full-width');
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
addPanel: function (data) {
|
||||
var tpl = this.unescape($("#layout-panel-tpl").html());
|
||||
|
||||
|
||||
@@ -261,12 +261,19 @@ Espo.define('views/fields/array', ['views/fields/base', 'lib!Selectize'], functi
|
||||
|
||||
getValueForDisplay: function () {
|
||||
return this.selected.map(function (item) {
|
||||
var label = null;
|
||||
if (this.translatedOptions != null) {
|
||||
if (item in this.translatedOptions) {
|
||||
return this.getHelper().stripTags(this.translatedOptions[item]);
|
||||
label = this.getHelper().stripTags(this.translatedOptions[item]);
|
||||
}
|
||||
}
|
||||
return this.getHelper().stripTags(item);
|
||||
if (label === null) {
|
||||
label = this.getHelper().stripTags(item);
|
||||
}
|
||||
if (label === '') {
|
||||
label = this.translate('None');
|
||||
}
|
||||
return label;
|
||||
}, this).join(', ');
|
||||
},
|
||||
|
||||
@@ -338,7 +345,7 @@ Espo.define('views/fields/array', ['views/fields/base', 'lib!Selectize'], functi
|
||||
arr.push({
|
||||
type: 'like',
|
||||
field: field,
|
||||
value: "%" + value.replace(/\//, '\\\\/' ) + "%"
|
||||
value: "%" + value.replace(/\//g, '\\\\/' ) + "%"
|
||||
});
|
||||
arrFront.push(value);
|
||||
});
|
||||
|
||||
@@ -184,6 +184,7 @@ Espo.define('views/fields/base', 'view', function (Dep) {
|
||||
data.searchType = this.getSearchType();
|
||||
data.searchTypeList = this.getSearchTypeList();
|
||||
}
|
||||
|
||||
return data;
|
||||
},
|
||||
|
||||
|
||||
@@ -79,26 +79,33 @@ Espo.define('views/fields/enum', ['views/fields/base', 'lib!Selectize'], functio
|
||||
}
|
||||
|
||||
if (this.params.translation) {
|
||||
var translationObj;
|
||||
var data = this.getLanguage().data;
|
||||
var arr = this.params.translation.split('.');
|
||||
var pointer = this.getLanguage().data;
|
||||
arr.forEach(function (key) {
|
||||
if (key in pointer) {
|
||||
pointer = pointer[key];
|
||||
t = pointer;
|
||||
translationObj = pointer;
|
||||
}
|
||||
}, this);
|
||||
|
||||
this.translatedOptions = null;
|
||||
var translatedOptions = {};
|
||||
if (this.params.options) {
|
||||
this.params.options.forEach(function (o) {
|
||||
if (typeof t === 'object' && o in t) {
|
||||
translatedOptions[o] = t[o];
|
||||
this.params.options.forEach(function (item) {
|
||||
if (typeof translationObj === 'object' && item in translationObj) {
|
||||
translatedOptions[item] = translationObj[item];
|
||||
} else {
|
||||
translatedOptions[o] = o;
|
||||
translatedOptions[item] = item;
|
||||
}
|
||||
}, this);
|
||||
var value = this.model.get(this.name);
|
||||
if ((value || value === '') && !(value in translatedOptions)) {
|
||||
if (typeof translationObj === 'object' && value in translationObj) {
|
||||
translatedOptions[value] = translationObj[value];
|
||||
}
|
||||
}
|
||||
this.translatedOptions = translatedOptions;
|
||||
}
|
||||
}
|
||||
|
||||
37
client/src/views/fields/foreign-date.js
Normal file
37
client/src/views/fields/foreign-date.js
Normal file
@@ -0,0 +1,37 @@
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM - Open Source CRM application.
|
||||
* Copyright (C) 2014-2017 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.
|
||||
************************************************************************/
|
||||
|
||||
Espo.define('views/fields/foreign-date', 'views/fields/date', function (Dep) {
|
||||
|
||||
return Dep.extend({
|
||||
|
||||
type: 'foreign'
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
37
client/src/views/fields/foreign-datetime.js
Normal file
37
client/src/views/fields/foreign-datetime.js
Normal file
@@ -0,0 +1,37 @@
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM - Open Source CRM application.
|
||||
* Copyright (C) 2014-2017 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.
|
||||
************************************************************************/
|
||||
|
||||
Espo.define('views/fields/foreign-datetime', 'views/fields/datetime', function (Dep) {
|
||||
|
||||
return Dep.extend({
|
||||
|
||||
type: 'foreign'
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
@@ -34,7 +34,7 @@ Espo.define('views/fields/link-parent', 'views/fields/base', function (Dep) {
|
||||
|
||||
listTemplate: 'fields/link/list',
|
||||
|
||||
detailTemplate: 'fields/link/detail',
|
||||
detailTemplate: 'fields/link-parent/detail',
|
||||
|
||||
editTemplate: 'fields/link-parent/edit',
|
||||
|
||||
@@ -86,7 +86,8 @@ Espo.define('views/fields/link-parent', 'views/fields/base', function (Dep) {
|
||||
this.typeName = this.name + 'Type';
|
||||
this.idName = this.name + 'Id';
|
||||
|
||||
this.foreignScopeList = this.params.entityList || this.model.getLinkParam(this.name, 'entityList') || [];
|
||||
this.foreignScopeList = this.options.foreignScopeList || this.foreignScopeList;
|
||||
this.foreignScopeList = this.foreignScopeList || this.params.entityList || this.model.getLinkParam(this.name, 'entityList') || [];
|
||||
this.foreignScopeList = Espo.Utils.clone(this.foreignScopeList).filter(function (item) {
|
||||
if (!this.getMetadata().get(['scopes', item, 'disabled'])) return true;
|
||||
}, this);
|
||||
|
||||
@@ -65,8 +65,15 @@ Espo.define('views/fields/multi-enum', ['views/fields/array', 'lib!Selectize'],
|
||||
afterRender: function () {
|
||||
if (this.mode == 'edit') {
|
||||
var $element = this.$element = this.$el.find('[name="' + this.name + '"]');
|
||||
this.$element.val(this.selected.join(':,:'));
|
||||
|
||||
var valueList = Espo.Utils.clone(this.selected);
|
||||
for (var i in valueList) {
|
||||
if (valueList[i] === '') {
|
||||
valueList[i] = '__emptystring__';
|
||||
}
|
||||
}
|
||||
|
||||
this.$element.val(valueList.join(':,:'));
|
||||
|
||||
var data = [];
|
||||
(this.params.options || []).forEach(function (value) {
|
||||
@@ -76,6 +83,12 @@ Espo.define('views/fields/multi-enum', ['views/fields/array', 'lib!Selectize'],
|
||||
label = this.translatedOptions[value];
|
||||
}
|
||||
}
|
||||
if (value === '') {
|
||||
value = '__emptystring__';
|
||||
}
|
||||
if (label === '') {
|
||||
label = this.translate('None');
|
||||
}
|
||||
data.push({
|
||||
value: value,
|
||||
label: label
|
||||
@@ -117,6 +130,11 @@ Espo.define('views/fields/multi-enum', ['views/fields/array', 'lib!Selectize'],
|
||||
if (list.length == 1 && list[0] == '') {
|
||||
list = [];
|
||||
}
|
||||
for (var i in list) {
|
||||
if (list[i] === '__emptystring__') {
|
||||
list[i] = '';
|
||||
}
|
||||
}
|
||||
var data = {};
|
||||
data[this.name] = list;
|
||||
return data;
|
||||
|
||||
@@ -43,6 +43,14 @@ Espo.define('views/fields/person-name', 'views/fields/varchar', function (Dep) {
|
||||
data.firstValue = this.model.get(this.firstField);
|
||||
data.lastValue = this.model.get(this.lastField);
|
||||
data.salutationOptions = this.model.getFieldParam(this.salutationField, 'options');
|
||||
data.firstMaxLength = this.model.getFieldParam(this.firstField, 'maxLength');
|
||||
data.lastMaxLength = this.model.getFieldParam(this.lastField, 'maxLength');
|
||||
|
||||
if (this.mode === 'detail') {
|
||||
data.isNotEmpty = !!data.firstValue || !!data.lastValue || !!data.salutationValue;
|
||||
} else if (this.mode === 'list' || this.mode === 'listLink') {
|
||||
data.isNotEmpty = !!data.firstValue || !!data.lastValue;
|
||||
}
|
||||
return data;
|
||||
},
|
||||
|
||||
@@ -74,16 +82,26 @@ Espo.define('views/fields/person-name', 'views/fields/varchar', function (Dep) {
|
||||
},
|
||||
|
||||
validateRequired: function () {
|
||||
var isRequired = this.model.getFieldParam(this.name, 'required');
|
||||
|
||||
var validate = function (name) {
|
||||
if (this.model.isRequired(name)) {
|
||||
if (this.model.get(name) === '') {
|
||||
var msg = this.translate('fieldIsRequired', 'messages').replace('{field}', this.translate(this.name, 'fields', this.model.name));
|
||||
var msg = this.translate('fieldIsRequired', 'messages').replace('{field}', this.translate(name, 'fields', this.model.name));
|
||||
this.showValidationMessage(msg, '[name="'+name+'"]');
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}.bind(this);
|
||||
|
||||
if (isRequired) {
|
||||
if (!this.model.get(this.firstField) && !this.model.get(this.lastField)) {
|
||||
var msg = this.translate('fieldIsRequired', 'messages').replace('{field}', this.translate(this.name, 'fields', this.model.name));
|
||||
this.showValidationMessage(msg, '[name="'+this.lastField+'"]');
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
var result = false;
|
||||
result = validate(this.salutationField) || result;
|
||||
result = validate(this.firstField) || result;
|
||||
@@ -94,7 +112,8 @@ Espo.define('views/fields/person-name', 'views/fields/varchar', function (Dep) {
|
||||
isRequired: function () {
|
||||
return this.model.getFieldParam(this.salutationField, 'required') ||
|
||||
this.model.getFieldParam(this.firstField, 'required') ||
|
||||
this.model.getFieldParam(this.lastField, 'required');
|
||||
this.model.getFieldParam(this.lastField, 'required') ||
|
||||
this.model.getFieldParam(this.name, 'required');
|
||||
},
|
||||
|
||||
fetch: function (form) {
|
||||
|
||||
@@ -274,10 +274,10 @@ Espo.define('views/record/base', ['view', 'view-record-helper', 'dynamic-logic']
|
||||
this.isNew = true;
|
||||
}
|
||||
|
||||
this.setupFinal();
|
||||
this.setupBeforeFinal();
|
||||
},
|
||||
|
||||
setupFinal: function () {
|
||||
setupBeforeFinal: function () {
|
||||
this.attributes = this.model.getClonedAttributes();
|
||||
|
||||
this.listenTo(this.model, 'change', function () {
|
||||
|
||||
@@ -681,8 +681,7 @@ Espo.define('views/record/detail', ['views/record/base', 'view-record-helper'],
|
||||
this.navigateButtonsDisabled = this.options.navigateButtonsDisabled || this.navigateButtonsDisabled;
|
||||
|
||||
this.setupActionItems();
|
||||
|
||||
this.setupFinal();
|
||||
this.setupBeforeFinal();
|
||||
|
||||
this.on('after:render', function () {
|
||||
this.$detailButtonContainer = this.$el.find('.detail-button-container');
|
||||
@@ -690,7 +689,7 @@ Espo.define('views/record/detail', ['views/record/base', 'view-record-helper'],
|
||||
}, this);
|
||||
},
|
||||
|
||||
setupFinal: function () {
|
||||
setupBeforeFinal: function () {
|
||||
this.manageAccess();
|
||||
|
||||
this.attributes = this.model.getClonedAttributes();
|
||||
@@ -716,6 +715,9 @@ Espo.define('views/record/detail', ['views/record/base', 'view-record-helper'],
|
||||
this.initDynamicLogic();
|
||||
|
||||
this.setupFieldLevelSecurity();
|
||||
},
|
||||
|
||||
setupFinal: function () {
|
||||
this.build();
|
||||
},
|
||||
|
||||
|
||||
@@ -76,11 +76,11 @@ Espo.define('views/record/edit', 'views/record/detail', function (Dep) {
|
||||
this.exit('cancel');
|
||||
},
|
||||
|
||||
setupFinal: function () {
|
||||
setupBeforeFinal: function () {
|
||||
if (this.model.isNew()) {
|
||||
this.populateDefaults();
|
||||
}
|
||||
Dep.prototype.setupFinal.call(this);
|
||||
Dep.prototype.setupBeforeFinal.call(this);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
@@ -108,6 +108,18 @@ Espo.define('views/record/search', 'view', function (Dep) {
|
||||
|
||||
this.loadSearchData();
|
||||
|
||||
if (this.presetName) {
|
||||
var hasPresetListed = this.presetFilterList.find(function (item) {
|
||||
var name = (typeof item === 'string') ? item : item.name;
|
||||
if (name === this.presetName) {
|
||||
return true;
|
||||
}
|
||||
}, this);
|
||||
if (!hasPresetListed) {
|
||||
this.presetFilterList.push(this.presetName);
|
||||
}
|
||||
}
|
||||
|
||||
this.model = new this.collection.model();
|
||||
this.model.clear();
|
||||
|
||||
@@ -577,17 +589,20 @@ Espo.define('views/record/search', 'view', function (Dep) {
|
||||
this.presetName = searchData.presetName;
|
||||
}
|
||||
|
||||
var primaryIsSet = false;
|
||||
if ('primary' in searchData) {
|
||||
this.primary = searchData.primary;
|
||||
if (!this.presetName) {
|
||||
this.presetName = this.primary;
|
||||
}
|
||||
primaryIsSet = true;
|
||||
}
|
||||
|
||||
if (this.presetName) {
|
||||
this.advanced = _.extend(Espo.Utils.clone(this.getPresetData()), searchData.advanced);
|
||||
|
||||
this.primary = this.getPrimaryFilterName();
|
||||
if (!primaryIsSet) {
|
||||
this.primary = this.getPrimaryFilterName();
|
||||
}
|
||||
} else {
|
||||
this.advanced = Espo.Utils.clone(searchData.advanced);
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ Espo.define('views/settings/fields/quick-create-list', 'views/fields/array', fun
|
||||
|
||||
this.params.options = Object.keys(this.getMetadata().get('scopes')).filter(function (scope) {
|
||||
if (this.getMetadata().get('scopes.' + scope + '.disabled')) return;
|
||||
return this.getMetadata().get('scopes.' + scope + '.entity') && this.getMetadata().get('scopes.' + scope + '.tab');
|
||||
return this.getMetadata().get('scopes.' + scope + '.entity') && this.getMetadata().get('scopes.' + scope + '.object');
|
||||
}, this).sort(function (v1, v2) {
|
||||
return this.translate(v1, 'scopeNamesPlural').localeCompare(this.translate(v2, 'scopeNamesPlural'));
|
||||
}.bind(this));
|
||||
|
||||
@@ -1316,6 +1316,7 @@ table.table td.cell .complex-text {
|
||||
margin: 0;
|
||||
margin-bottom: 0;
|
||||
border: 0;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
#main > .calendar-container {
|
||||
|
||||
@@ -37,7 +37,7 @@ $fields = array(
|
||||
'default' => $config->get('smtpPort', 25),
|
||||
),
|
||||
'smtpAuth' => array(
|
||||
'default' => $config->get('smtpAuth', ''),
|
||||
'default' => false,
|
||||
),
|
||||
'smtpSecurity' => array(
|
||||
'default' => $config->get('smtpSecurity', ''),
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "espocrm",
|
||||
"version": "4.8.2",
|
||||
"version": "4.8.3",
|
||||
"description": "",
|
||||
"main": "index.php",
|
||||
"repository": {
|
||||
|
||||
@@ -926,6 +926,20 @@ class FormulaTest extends \PHPUnit_Framework_TestCase
|
||||
$this->assertTrue($result);
|
||||
}
|
||||
|
||||
function testStringNewLine()
|
||||
{
|
||||
$item = json_decode('
|
||||
{
|
||||
"type": "value",
|
||||
"value": "test\\ntest"
|
||||
}
|
||||
');
|
||||
|
||||
$result = $this->formula->process($item, $this->entity);
|
||||
|
||||
$this->assertEquals("test\ntest", $result);
|
||||
}
|
||||
|
||||
function testVariable()
|
||||
{
|
||||
$item = json_decode('
|
||||
|
||||
@@ -127,6 +127,45 @@ class HtmlizerTest extends \PHPUnit_Framework_TestCase
|
||||
$html = $this->htmlizer->render($entity, $template);
|
||||
$this->assertEquals('3,000', $html);
|
||||
|
||||
$template = "{{float_RAW}}";
|
||||
$entity->set('float', 10000.50);
|
||||
$html = $this->htmlizer->render($entity, $template);
|
||||
$this->assertEquals('10000.5', $html);
|
||||
|
||||
$template = "{{numberFormat float_RAW}}";
|
||||
$entity->set('float', 10000.60);
|
||||
$html = $this->htmlizer->render($entity, $template);
|
||||
$this->assertEquals('10,001', $html);
|
||||
|
||||
$template = "{{numberFormat float_RAW decimals=2}}";
|
||||
$entity->set('float', 10000.601);
|
||||
$html = $this->htmlizer->render($entity, $template);
|
||||
$this->assertEquals('10,000.60', $html);
|
||||
|
||||
$template = "{{numberFormat float_RAW decimals=0}}";
|
||||
$entity->set('float', 10000.1);
|
||||
$html = $this->htmlizer->render($entity, $template);
|
||||
$this->assertEquals('10,000', $html);
|
||||
|
||||
$template = "{{numberFormat float_RAW decimals=2 decimalPoint='.' thousandsSeparator=' '}}";
|
||||
$entity->set('float', 10000.60);
|
||||
$html = $this->htmlizer->render($entity, $template);
|
||||
$this->assertEquals('10 000.60', $html);
|
||||
|
||||
$template = "{{file name}}";
|
||||
$entity->set('name', '1');
|
||||
$html = $this->htmlizer->render($entity, $template);
|
||||
$this->assertEquals('?entryPoint=attachment&id=1', $html);
|
||||
|
||||
$template = "{{#ifEqual name '1'}}hello{{/ifEqual}}";
|
||||
$entity->set('name', '1');
|
||||
$html = $this->htmlizer->render($entity, $template);
|
||||
$this->assertEquals('hello', $html);
|
||||
|
||||
$template = "{{#ifNotEqual name '1'}}hello{{else}}test{{/ifNotEqual}}";
|
||||
$entity->set('name', '1');
|
||||
$html = $this->htmlizer->render($entity, $template);
|
||||
$this->assertEquals('test', $html);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user