mirror of
https://github.com/espocrm/espocrm.git
synced 2026-03-05 15:17:01 +00:00
Compare commits
32 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f896a2d71a | ||
|
|
dd6704ace5 | ||
|
|
1dc4d44a65 | ||
|
|
bfb28ea178 | ||
|
|
ec6f3a22f2 | ||
|
|
5a0c7c330c | ||
|
|
f64df5af87 | ||
|
|
d4dc7a4051 | ||
|
|
d676f85c8f | ||
|
|
ab382f2387 | ||
|
|
206219c738 | ||
|
|
37d1c707cb | ||
|
|
93af1c9bfc | ||
|
|
a021c4c8d5 | ||
|
|
a125244cdf | ||
|
|
1cfd251c4c | ||
|
|
d2f4f312e5 | ||
|
|
c468b061d9 | ||
|
|
7bf945f0b6 | ||
|
|
fecbb26cbf | ||
|
|
a5ae33ab81 | ||
|
|
c6fa0e464e | ||
|
|
38bae6238a | ||
|
|
79de4c874f | ||
|
|
814748ec61 | ||
|
|
1f0ad0cbec | ||
|
|
7224f566d6 | ||
|
|
eefb01ec4f | ||
|
|
24d46ed81d | ||
|
|
37c749faf8 | ||
|
|
4306a3131e | ||
|
|
8fa95fcce3 |
@@ -76,5 +76,46 @@ class Email extends \Espo\Core\Acl\Base
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function checkEntityDelete(EntityUser $user, Entity $entity, $data)
|
||||
{
|
||||
if ($user->isAdmin()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($data === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($data->delete === 'own') {
|
||||
if ($user->id === $entity->get('assignedUserId')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($user->id === $entity->get('createdById')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$assignedUserIdList = $entity->getLinkMultipleIdList('assignedUsers');
|
||||
if (count($assignedUserIdList) === 1 && $entity->hasLinkMultipleId('assignedUsers', $user->id)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($this->checkEntity($user, $entity, $data, 'delete')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($data->edit !== 'no' || $data->create !== 'no') {
|
||||
if ($entity->get('createdById') === $user->id) {
|
||||
if ($entity->get('status') !== 'Sent' && $entity->get('status') !== 'Archived') {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -423,5 +423,21 @@ class Record extends Base
|
||||
|
||||
return $this->getRecordService()->merge($targetId, $sourceIds, $attributes);
|
||||
}
|
||||
|
||||
public function postActionGetDuplicateAttributes($params, $data, $request)
|
||||
{
|
||||
if (empty($data['id'])) {
|
||||
throw new BadRequest();
|
||||
}
|
||||
|
||||
if (!$this->getAcl()->check($this->name, 'create')) {
|
||||
throw new Forbidden();
|
||||
}
|
||||
if (!$this->getAcl()->check($this->name, 'read')) {
|
||||
throw new Forbidden();
|
||||
}
|
||||
|
||||
return $this->getRecordService()->getDuplicateAttributes($data['id']);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -72,6 +72,9 @@ class Importer
|
||||
$email->set('isBeingImported', true);
|
||||
|
||||
$subject = $message->subject;
|
||||
if (!empty($subject) && is_string($subject)) {
|
||||
$subject = trim($subject);
|
||||
}
|
||||
if ($subject !== '0' && empty($subject)) {
|
||||
$subject = '(No Subject)';
|
||||
}
|
||||
|
||||
@@ -47,9 +47,9 @@ class LDAP extends Base
|
||||
*/
|
||||
protected $ldapFieldMap = array(
|
||||
'userName' => 'userNameAttribute',
|
||||
'firstName' => 'userTitleAttribute',
|
||||
'lastName' => 'userFirstNameAttribute',
|
||||
'title' => 'userLastNameAttribute',
|
||||
'firstName' => 'userFirstNameAttribute',
|
||||
'lastName' => 'userLastNameAttribute',
|
||||
'title' => 'userTitleAttribute',
|
||||
'emailAddress' => 'userEmailAddressAttribute',
|
||||
'phoneNumber' => 'userPhoneNumberAttribute',
|
||||
);
|
||||
@@ -108,33 +108,34 @@ class LDAP extends Base
|
||||
|
||||
$ldapClient = $this->getLdapClient();
|
||||
|
||||
//login LDAP admin user (ldapUsername, ldapPassword)
|
||||
//login LDAP system user (ldapUsername, ldapPassword)
|
||||
try {
|
||||
$ldapClient->bind();
|
||||
} catch (\Exception $e) {
|
||||
$options = $this->getUtils()->getLdapClientOptions();
|
||||
$GLOBALS['log']->error('LDAP: Authentication failed for user ['.$options['username'].'], details: ' . $e->getMessage());
|
||||
return;
|
||||
$GLOBALS['log']->error('LDAP: Could not connect to LDAP server ['.$options['host'].'], details: ' . $e->getMessage());
|
||||
|
||||
$adminUser = $this->adminLogin($username, $password);
|
||||
if (!isset($adminUser)) {
|
||||
return null;
|
||||
}
|
||||
$GLOBALS['log']->info('LDAP: Administrator ['.$username.'] was logged in by Espo method.');
|
||||
}
|
||||
|
||||
$userDn = $this->findLdapUserDnByUsername($username);
|
||||
$GLOBALS['log']->debug('Found DN for ['.$username.']: ['.$userDn.'].');
|
||||
if (!isset($userDn)) {
|
||||
$GLOBALS['log']->error('LDAP: Authentication failed for user ['.$username.'], details: user is not found.');
|
||||
return;
|
||||
}
|
||||
if (!isset($adminUser)) {
|
||||
$userDn = $this->findLdapUserDnByUsername($username);
|
||||
$GLOBALS['log']->debug('Found DN for ['.$username.']: ['.$userDn.'].');
|
||||
if (!isset($userDn)) {
|
||||
$GLOBALS['log']->error('LDAP: Authentication failed for user ['.$username.'], details: user is not found.');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
$ldapClient->bind($userDn, $password);
|
||||
} catch (\Exception $e) {
|
||||
|
||||
$admin = $this->adminLogin($username, $password);
|
||||
if (!isset($admin)) {
|
||||
try {
|
||||
$ldapClient->bind($userDn, $password);
|
||||
} catch (\Exception $e) {
|
||||
$GLOBALS['log']->error('LDAP: Authentication failed for user ['.$username.'], details: ' . $e->getMessage());
|
||||
return null;
|
||||
}
|
||||
|
||||
$GLOBALS['log']->info('LDAP: Administrator ['.$username.'] was logged in by Espo method.');
|
||||
}
|
||||
|
||||
$user = $this->getEntityManager()->getRepository('User')->findOne(array(
|
||||
@@ -262,8 +263,8 @@ class LDAP extends Base
|
||||
$loginFilterString = $this->convertToFilterFormat($options['userLoginFilter']);
|
||||
}
|
||||
|
||||
$searchString = '(&(objectClass=user)('.$options['userNameAttribute'].'='.$username.')'.$loginFilterString.')';
|
||||
$result = $ldapClient->search($searchString, null, LDAP\Client::SEARCH_SCOPE_ONE);
|
||||
$searchString = '(&(objectClass='.$options['userObjectClass'].')('.$options['userNameAttribute'].'='.$username.')'.$loginFilterString.')';
|
||||
$result = $ldapClient->search($searchString, null, LDAP\Client::SEARCH_SCOPE_SUB);
|
||||
$GLOBALS['log']->debug('LDAP: user search string: "' . $searchString . '"');
|
||||
|
||||
foreach ($result as $item) {
|
||||
|
||||
@@ -66,6 +66,7 @@ class Utils
|
||||
'userLoginFilter' => 'ldapUserLoginFilter',
|
||||
'userTeamsIds' => 'ldapUserTeamsIds',
|
||||
'userDefaultTeamId' => 'ldapUserDefaultTeamId',
|
||||
'userObjectClass' => 'ldapUserObjectClass',
|
||||
);
|
||||
|
||||
/**
|
||||
@@ -76,6 +77,7 @@ class Utils
|
||||
protected $permittedEspoOptions = array(
|
||||
'createEspoUser',
|
||||
'userNameAttribute',
|
||||
'userObjectClass',
|
||||
'userTitleAttribute',
|
||||
'userFirstNameAttribute',
|
||||
'userLastNameAttribute',
|
||||
|
||||
@@ -373,7 +373,7 @@ class Permission
|
||||
protected function chmodReal($filename, $mode)
|
||||
{
|
||||
try {
|
||||
$result = chmod($filename, $mode);
|
||||
$result = @chmod($filename, $mode);
|
||||
} catch (\Exception $e) {
|
||||
$result = false;
|
||||
}
|
||||
@@ -383,7 +383,7 @@ class Permission
|
||||
$this->chgrp($filename, $this->getDefaultGroup(true));
|
||||
|
||||
try {
|
||||
$result = chmod($filename, $mode);
|
||||
$result = @chmod($filename, $mode);
|
||||
} catch (\Exception $e) {
|
||||
throw new Error($e->getMessage());
|
||||
}
|
||||
@@ -395,7 +395,7 @@ class Permission
|
||||
protected function chownReal($path, $user)
|
||||
{
|
||||
try {
|
||||
$result = chown($path, $user);
|
||||
$result = @chown($path, $user);
|
||||
} catch (\Exception $e) {
|
||||
throw new Error($e->getMessage());
|
||||
}
|
||||
@@ -406,7 +406,7 @@ class Permission
|
||||
protected function chgrpReal($path, $group)
|
||||
{
|
||||
try {
|
||||
$result = chgrp($path, $group);
|
||||
$result = @chgrp($path, $group);
|
||||
} catch (\Exception $e) {
|
||||
throw new Error($e->getMessage());
|
||||
}
|
||||
|
||||
@@ -131,6 +131,7 @@ return array ( 'defaultPermissions' =>
|
||||
'ldapUserTitleAttribute',
|
||||
'ldapUserEmailAddressAttribute',
|
||||
'ldapUserPhoneNumberAttribute',
|
||||
'ldapUserObjectClass',
|
||||
'maxEmailAccountCount',
|
||||
'massEmailMaxPerHourCount',
|
||||
'personalEmailMaxPortionSize',
|
||||
@@ -145,5 +146,6 @@ return array ( 'defaultPermissions' =>
|
||||
'ldapUserTitleAttribute' => 'title',
|
||||
'ldapUserEmailAddressAttribute' => 'mail',
|
||||
'ldapUserPhoneNumberAttribute' => 'telephoneNumber',
|
||||
'ldapUserObjectClass' => 'person',
|
||||
);
|
||||
|
||||
|
||||
@@ -58,11 +58,11 @@ class Stream extends \Espo\Core\Hooks\Base
|
||||
|
||||
protected function checkHasStream(Entity $entity)
|
||||
{
|
||||
$entityName = $entity->getEntityName();
|
||||
if (!array_key_exists($entityName, $this->hasStreamCache)) {
|
||||
$this->hasStreamCache[$entityName] = $this->getMetadata()->get("scopes.{$entityName}.stream");
|
||||
$entityType = $entity->getEntityType();
|
||||
if (!array_key_exists($entityType, $this->hasStreamCache)) {
|
||||
$this->hasStreamCache[$entityType] = $this->getMetadata()->get("scopes.{$entityType}.stream");
|
||||
}
|
||||
return $this->hasStreamCache[$entityName];
|
||||
return $this->hasStreamCache[$entityType];
|
||||
}
|
||||
|
||||
protected function isLinkObservableInStream($scope, $link)
|
||||
@@ -80,6 +80,8 @@ class Stream extends \Espo\Core\Hooks\Base
|
||||
{
|
||||
if ($this->checkHasStream($entity)) {
|
||||
$this->getStreamService()->unfollowAllUsersFromEntity($entity);
|
||||
echo "---";
|
||||
die;
|
||||
}
|
||||
$query = $this->getEntityManager()->getQuery();
|
||||
$sql = "
|
||||
@@ -177,7 +179,11 @@ class Stream extends \Espo\Core\Hooks\Base
|
||||
$assignedUserId = $entity->get('assignedUserId');
|
||||
$createdById = $entity->get('createdById');
|
||||
|
||||
if ($this->getConfig()->get('followCreatedEntities') && !empty($createdById)) {
|
||||
if (
|
||||
($this->getConfig()->get('followCreatedEntities') || $this->getUser()->get('isPortalUser'))
|
||||
&&
|
||||
!empty($createdById)
|
||||
) {
|
||||
$userIdList[] = $createdById;
|
||||
}
|
||||
if (!empty($assignedUserId) && !in_array($assignedUserId, $userIdList)) {
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
"title": "Título",
|
||||
"accountRole": "Título",
|
||||
"account": "Cuenta",
|
||||
"accounts": "Cuentas",
|
||||
"accounts": "Empresas",
|
||||
"phoneNumber": "Teléfono",
|
||||
"accountType": "Tipo de Cuenta",
|
||||
"doNotCall": "No Llamar",
|
||||
@@ -24,7 +24,7 @@
|
||||
"campaignLogRecords": "Registro de Campaña",
|
||||
"campaign": "Campaña",
|
||||
"account": "Cuentas (Primaria)",
|
||||
"accounts": "Cuentas",
|
||||
"accounts": "Empresas",
|
||||
"casesPrimary": "Casos (Primaria)",
|
||||
"portalUser": "Usuarios de portal"
|
||||
},
|
||||
|
||||
@@ -12,11 +12,11 @@
|
||||
"publishDate": "Publicar Fecha",
|
||||
"expirationDate": "Fecha de Expiración",
|
||||
"description": "Descripción",
|
||||
"accounts": "Cuentas",
|
||||
"accounts": "Empresas",
|
||||
"folder": "Carpeta"
|
||||
},
|
||||
"links": {
|
||||
"accounts": "Cuentas",
|
||||
"accounts": "Empresas",
|
||||
"opportunities": "Oportunidades",
|
||||
"folder": "Carpeta",
|
||||
"leads": "Leads"
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
"calls": "Llamadas",
|
||||
"tasks": "Tareas",
|
||||
"emails": "Correos",
|
||||
"accounts": "Cuentas",
|
||||
"accounts": "Empresas",
|
||||
"cases": "Casos",
|
||||
"documents": "Documentos",
|
||||
"account": "Cuenta",
|
||||
@@ -38,7 +38,7 @@
|
||||
"KnowledgeBaseCategory": "Categoría de base de conocimientos"
|
||||
},
|
||||
"scopeNamesPlural": {
|
||||
"Account": "Cuentas",
|
||||
"Account": "Empresas",
|
||||
"Contact": "Contactos",
|
||||
"Lead": "Potenciales",
|
||||
"Target": "Objetivos",
|
||||
|
||||
@@ -731,8 +731,22 @@ class Activities extends \Espo\Core\Services\Base
|
||||
'leftJoins' => ['users'],
|
||||
'whereClause' => array(
|
||||
'usersMiddle.userId' => $userId,
|
||||
'dateStart>=' => $from,
|
||||
'dateStart<' => $to,
|
||||
array(
|
||||
'OR' => array(
|
||||
array(
|
||||
'dateStart>=' => $from,
|
||||
'dateStart<' => $to
|
||||
),
|
||||
array(
|
||||
'dateEnd>=' => $from,
|
||||
'dateEnd<' => $to
|
||||
),
|
||||
array(
|
||||
'dateStart<=' => $from,
|
||||
'dateEnd>=' => $to
|
||||
)
|
||||
)
|
||||
),
|
||||
'usersMiddle.status!=' => 'Declined'
|
||||
),
|
||||
'customJoin' => ''
|
||||
@@ -762,8 +776,22 @@ class Activities extends \Espo\Core\Services\Base
|
||||
'leftJoins' => ['users'],
|
||||
'whereClause' => array(
|
||||
'usersMiddle.userId' => $userId,
|
||||
'dateStart>=' => $from,
|
||||
'dateStart<' => $to,
|
||||
array(
|
||||
'OR' => array(
|
||||
array(
|
||||
'dateStart>=' => $from,
|
||||
'dateStart<' => $to
|
||||
),
|
||||
array(
|
||||
'dateEnd>=' => $from,
|
||||
'dateEnd<' => $to
|
||||
),
|
||||
array(
|
||||
'dateStart<=' => $from,
|
||||
'dateEnd>=' => $to
|
||||
)
|
||||
)
|
||||
),
|
||||
'usersMiddle.status!=' => 'Declined'
|
||||
),
|
||||
'customJoin' => ''
|
||||
@@ -864,6 +892,10 @@ class Activities extends \Espo\Core\Services\Base
|
||||
'dateEnd>=' => $from,
|
||||
'dateEnd<' => $to,
|
||||
),
|
||||
array(
|
||||
'dateStart<=' => $from,
|
||||
'dateEnd>=' => $to
|
||||
),
|
||||
array(
|
||||
'dateEndDate!=' => null,
|
||||
'dateEndDate>=' => $from,
|
||||
|
||||
@@ -146,7 +146,7 @@ class Opportunity extends \Espo\Services\Record
|
||||
opportunity.stage = 'Closed Won'
|
||||
|
||||
GROUP BY DATE_FORMAT(opportunity.close_date, '%Y-%m')
|
||||
ORDER BY opportunity.close_date
|
||||
ORDER BY `month`
|
||||
";
|
||||
|
||||
$sth = $pdo->prepare($sql);
|
||||
|
||||
@@ -73,7 +73,11 @@ class Email extends \Espo\Core\Notificators\Base
|
||||
|
||||
$dateSent = $entity->get('dateSent');
|
||||
if (!$dateSent) return;
|
||||
$dt = new \DateTime($dateSent);
|
||||
|
||||
$dt = null;
|
||||
try {
|
||||
$dt = new \DateTime($dateSent);
|
||||
} catch (\Exception $e) {}
|
||||
if (!$dt) return;
|
||||
|
||||
if ($dt->diff(new \DateTime())->days > self::DAYS_THRESHOLD) return;
|
||||
|
||||
@@ -46,6 +46,7 @@
|
||||
"ldapAccountDomainNameShort": "Account Domain Name Short",
|
||||
"ldapOptReferrals": "Opt Referrals",
|
||||
"ldapUserNameAttribute": "Username Attribute",
|
||||
"ldapUserObjectClass": "User ObjectClass",
|
||||
"ldapUserTitleAttribute": "User Title Attribute",
|
||||
"ldapUserFirstNameAttribute": "User First Name Attribute",
|
||||
"ldapUserLastNameAttribute": "User Last Name Attribute",
|
||||
@@ -101,7 +102,8 @@
|
||||
"ldapUsername": "The full system user DN which allows to search other users. E.g. \"CN=LDAP System User,OU=users,OU=espocrm, DC=test,DC=lan\".",
|
||||
"ldapPassword": "The password to access to LDAP server.",
|
||||
"ldapAuth": "Access credentials for the LDAP server.",
|
||||
"ldapUserNameAttribute": "The attribute to identify the user. For Active Directory it can be \"userPrincipalName\" or \"sAMAccountName\".",
|
||||
"ldapUserNameAttribute": "The attribute to identify the user. \nE.g. \"userPrincipalName\" or \"sAMAccountName\" for Active Directory, \"uid\" for OpenLDAP.",
|
||||
"ldapUserObjectClass": "ObjectClass attribute for searching users. E.g. \"person\" for AD, \"inetOrgPerson\" for OpenLDAP.",
|
||||
"ldapAccountCanonicalForm": "The type of your account canonical form. There are 4 options:<br>- 'Dn' - the form in the format 'CN=tester,OU=espocrm,DC=test, DC=lan'.<br>- 'Username' - the form 'tester'.<br>- 'Backslash' - the form 'COMPANY\\tester'.<br>- 'Principal' - the form 'tester@company.com'.",
|
||||
"ldapBindRequiresDn": "The option to format the username in the DN form.",
|
||||
"ldapBaseDn": "The default base DN used for searching users. E.g. \"OU=users,OU=espocrm,DC=test, DC=lan\".",
|
||||
|
||||
@@ -100,7 +100,6 @@
|
||||
"ldapUsername": "El sistema de usuario DN completo que permite a los usuarios buscar otros. E.g. \"CN=LDAP usuario del sistema,OU=users,OU=espocrm, DC=test,DC=lan\".",
|
||||
"ldapPassword": "La contraseña para acceder al servidor LDAP.",
|
||||
"ldapAuth": "Credenciales de acceso para el servidor LDAP.",
|
||||
"ldapUserNameAttribute": "El atributo para identificar al usuario. Para Active Directory puede ser \"userPrincipalName\" o \"sAMAccountName\".",
|
||||
"ldapBindRequiresDn": "La opción para formatear el nombre de usuario en el formulario de DN.",
|
||||
"ldapBaseDn": "La base DN predeterminado utilizado para la búsqueda de los usuarios. E.g. \"OU=users,OU=espocrm,DC=test, DC=lan\".",
|
||||
"ldapTryUsernameSplit": "La opción de dividir un nombre de usuario con el dominio.",
|
||||
|
||||
@@ -18,9 +18,9 @@
|
||||
"newPasswordConfirm": "Confirmar Contraseña Nueva",
|
||||
"avatar": "Avatar",
|
||||
"isActive": "Está Activo",
|
||||
"isPortalUser": "Is un usuario del portal",
|
||||
"isPortalUser": "Es un usuario del portal",
|
||||
"contact": "Contacto",
|
||||
"accounts": "Cuentas",
|
||||
"accounts": "Empresas",
|
||||
"account": "Cuenta (Primaria)",
|
||||
"sendAccessInfo": "Enviar correo electrónico con los accesos a la información del usuario",
|
||||
"portal": "Portal",
|
||||
@@ -33,7 +33,7 @@
|
||||
"portals": "Portales",
|
||||
"portalRoles": "Roles del portal",
|
||||
"contact": "Contacto",
|
||||
"accounts": "Cuentas",
|
||||
"accounts": "Empresas",
|
||||
"account": "Cuenta (Primaria)"
|
||||
},
|
||||
"labels": {
|
||||
@@ -58,8 +58,8 @@
|
||||
"isActive": "Si lo desmarca, el usuario no podrá iniciar sesión.",
|
||||
"teams": "Equipos a los que este usuario pertenece. Nivel de control de acceso se hereda de los roles de equipo.",
|
||||
"roles": "Roles de acceso adicionales. Úsalo si el usuario no pertenece a ningún equipo o si necesita ampliar el nivel de control de acceso sólo para este usuario.",
|
||||
"portalRoles": "Los roles de portal adicionales. Lo utilizan para extender el nivel de control de acceso exclusivo para este usuario.",
|
||||
"portals": "Portales donde el usuario tiene acceso a."
|
||||
"portalRoles": "Los roles del portal se utilizan para extender el nivel de control de acceso exclusivo para este usuario.",
|
||||
"portals": "El usuario tiene accesos a los siguientes portales."
|
||||
},
|
||||
"messages": {
|
||||
"passwordWillBeSent": "La Contraseña será enviada al correo electrónico del usuario",
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
[{"name": "ldapAuth"}, {"name": "ldapSecurity"}],
|
||||
[{"name": "ldapUsername", "fullWidth": true}],
|
||||
[{"name": "ldapPassword"}, {"name": "testConnection", "customLabel": null, "view": "views/admin/authentication/fields/test-connection"}],
|
||||
[{"name": "ldapUserNameAttribute"}, false],
|
||||
[{"name": "ldapUserNameAttribute"}, {"name": "ldapUserObjectClass"}],
|
||||
[{"name": "ldapAccountCanonicalForm"}, {"name": "ldapBindRequiresDn"}],
|
||||
[{"name": "ldapBaseDn", "fullWidth": true}],
|
||||
[{"name": "ldapUserLoginFilter", "fullWidth": true}],
|
||||
|
||||
@@ -91,10 +91,7 @@
|
||||
"read": "yes",
|
||||
"edit": "no"
|
||||
},
|
||||
"teams": {
|
||||
"read": "yes",
|
||||
"edit": "no"
|
||||
}
|
||||
"teams": false
|
||||
},
|
||||
"scopeFieldLevel": {
|
||||
"Call": {
|
||||
|
||||
@@ -131,8 +131,7 @@
|
||||
"messageIdInternal": {
|
||||
"type": "varchar",
|
||||
"maxLength": 300,
|
||||
"readOnly": true,
|
||||
"index": true
|
||||
"readOnly": true
|
||||
},
|
||||
"emailAddress": {
|
||||
"type": "base",
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
"type": "varchar",
|
||||
"required": true,
|
||||
"maxLength": 100,
|
||||
"view": "views/email-account/fields/email-address",
|
||||
"view": "views/inbound-email/fields/email-address",
|
||||
"trim": true
|
||||
},
|
||||
"status": {
|
||||
|
||||
@@ -220,6 +220,11 @@
|
||||
"required": true,
|
||||
"tooltip": true
|
||||
},
|
||||
"ldapUserObjectClass": {
|
||||
"type": "varchar",
|
||||
"required": true,
|
||||
"tooltip": true
|
||||
},
|
||||
"ldapUserFirstNameAttribute": {
|
||||
"type": "varchar",
|
||||
"required": true,
|
||||
|
||||
@@ -248,7 +248,11 @@ class EmailAccount extends Record
|
||||
if (!empty($lastUID)) {
|
||||
$ids = $storage->getIdsFromUID($lastUID);
|
||||
} else {
|
||||
$dt = new \DateTime($emailAccount->get('fetchSince'));
|
||||
$dt = null;
|
||||
try {
|
||||
$dt = new \DateTime($emailAccount->get('fetchSince'));
|
||||
} catch (\Exception $e) {}
|
||||
|
||||
if ($dt) {
|
||||
$ids = $storage->getIdsFromDate($dt->format('d-M-Y'));
|
||||
} else {
|
||||
@@ -315,7 +319,11 @@ class EmailAccount extends Record
|
||||
$lastUID = $storage->getUniqueId($id);
|
||||
|
||||
if ($message && isset($message->date)) {
|
||||
$dt = new \DateTime($message->date);
|
||||
$dt = null;
|
||||
try {
|
||||
$dt = new \DateTime($message->date);
|
||||
} catch (\Exception $e) {}
|
||||
|
||||
if ($dt) {
|
||||
$dateSent = $dt->setTimezone(new \DateTimeZone('UTC'))->format('Y-m-d H:i:s');
|
||||
$lastDate = $dateSent;
|
||||
|
||||
@@ -39,6 +39,8 @@ class InboundEmail extends \Espo\Services\Record
|
||||
{
|
||||
protected $internalAttributeList = ['password'];
|
||||
|
||||
protected $readOnlyAttributeList= ['fetchData'];
|
||||
|
||||
private $campaignService = null;
|
||||
|
||||
const PORTION_LIMIT = 20;
|
||||
@@ -250,7 +252,11 @@ class InboundEmail extends \Espo\Services\Record
|
||||
if (!empty($lastUID)) {
|
||||
$ids = $storage->getIdsFromUID($lastUID);
|
||||
} else {
|
||||
$dt = new \DateTime($emailAccount->get('fetchSince'));
|
||||
$dt = null;
|
||||
try {
|
||||
$dt = new \DateTime($emailAccount->get('fetchSince'));
|
||||
} catch (\Exception $e) {}
|
||||
|
||||
if ($dt) {
|
||||
$ids = $storage->getIdsFromDate($dt->format('d-M-Y'));
|
||||
} else {
|
||||
@@ -330,7 +336,11 @@ class InboundEmail extends \Espo\Services\Record
|
||||
|
||||
if ($k == count($ids) - 1) {
|
||||
if ($message && isset($message->date)) {
|
||||
$dt = new \DateTime($message->date);
|
||||
$dt = null;
|
||||
try {
|
||||
$dt = new \DateTime($message->date);
|
||||
} catch (\Exception $e) {}
|
||||
|
||||
if ($dt) {
|
||||
$dateSent = $dt->setTimezone(new \DateTimeZone('UTC'))->format('Y-m-d H:i:s');
|
||||
$lastDate = $dateSent;
|
||||
|
||||
@@ -1422,5 +1422,59 @@ class Record extends \Espo\Core\Services\Base
|
||||
'list' => $list
|
||||
);
|
||||
}
|
||||
|
||||
public function getDuplicateAttributes($id)
|
||||
{
|
||||
if (empty($id)) {
|
||||
throw new BadRequest();
|
||||
}
|
||||
|
||||
$entity = $this->getEntity($id);
|
||||
|
||||
if (!$entity) {
|
||||
throw new NotFound();
|
||||
}
|
||||
|
||||
$attributes = $entity->getValues();
|
||||
unset($attributes['id']);
|
||||
|
||||
$fields = $this->getMetadata()->get(['entityDefs', $this->getEntityType(), 'fields'], array());
|
||||
|
||||
foreach ($fields as $field => $item) {
|
||||
if (empty($item['type'])) continue;
|
||||
$type = $item['type'];
|
||||
|
||||
if (in_array($type, ['file', 'image'])) {
|
||||
$attachment = $entity->get($field);
|
||||
if ($attachment) {
|
||||
$attachment = $this->getEntityManager()->getRepository('Attachment')->getCopiedAttachment($attachment);
|
||||
$idAttribute = $field . 'Id';
|
||||
if ($attachment) {
|
||||
$attributes[$idAttribute] = $attachment->id;
|
||||
}
|
||||
}
|
||||
} else if (in_array($type, ['attachmentMultiple'])) {
|
||||
$attachmentList = $entity->get($field);
|
||||
if (count($attachmentList)) {
|
||||
$idList = [];
|
||||
$nameHash = (object) [];
|
||||
$typeHash = (object) [];
|
||||
foreach ($attachmentList as $attachment) {
|
||||
$attachment = $this->getEntityManager()->getRepository('Attachment')->getCopiedAttachment($attachment);
|
||||
if ($attachment) {
|
||||
$idList[] = $attachment->id;
|
||||
$nameHash->{$attachment->id} = $attachment->get('name');
|
||||
$typeHash->{$attachment->id} = $attachment->get('type');
|
||||
}
|
||||
}
|
||||
$attributes[$field . 'Ids'] = $idList;
|
||||
$attributes[$field . 'Names'] = $nameHash;
|
||||
$attributes[$field . 'Types'] = $typeHash;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $attributes;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -273,7 +273,7 @@ class Stream extends \Espo\Core\Services\Base
|
||||
$sql = "
|
||||
DELETE FROM subscription
|
||||
WHERE
|
||||
entity_id = " . $pdo->quote($entity->id) . " AND entity_type = " . $pdo->quote($entity->getEntityName()) . "
|
||||
entity_id = " . $pdo->quote($entity->id) . " AND entity_type = " . $pdo->quote($entity->getEntityType()) . "
|
||||
";
|
||||
$sth = $pdo->prepare($sql)->execute();
|
||||
}
|
||||
|
||||
@@ -72,6 +72,31 @@ Espo.define('acl/email', 'acl', function (Dep) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
checkModelDelete: function (model, data, precise) {
|
||||
var result = this.checkModel(model, data, 'delete', precise);
|
||||
|
||||
if (result) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (data === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var d = data || {};
|
||||
if (d.read === 'no') {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (model.get('createdById') === this.getUser().id) {
|
||||
if (model.get('status') !== 'Sent' && model.get('status') !== 'Archived') {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -311,15 +311,20 @@ Espo.define('views/detail', 'views/main', function (Dep) {
|
||||
},
|
||||
|
||||
actionDuplicate: function () {
|
||||
var attributes = Espo.Utils.cloneDeep(this.model.attributes);
|
||||
delete attributes.id;
|
||||
Espo.Ui.notify(this.translate('pleaseWait', 'messages'));
|
||||
this.ajaxPostRequest(this.scope + '/action/getDuplicateAttributes', {
|
||||
id: this.model.id
|
||||
}).then(function (attributes) {
|
||||
Espo.Ui.notify(false);
|
||||
var url = '#' + this.scope + '/create';
|
||||
|
||||
this.getRouter().dispatch(this.scope, 'create', {
|
||||
attributes: attributes,
|
||||
});
|
||||
this.getRouter().navigate(url, {trigger: false});
|
||||
}.bind(this));
|
||||
|
||||
var url = '#' + this.scope + '/create';
|
||||
|
||||
this.getRouter().dispatch(this.scope, 'create', {
|
||||
attributes: attributes,
|
||||
});
|
||||
this.getRouter().navigate(url, {trigger: false});
|
||||
},
|
||||
|
||||
});
|
||||
|
||||
@@ -151,7 +151,7 @@ Espo.define('views/email/record/list', 'views/record/list', function (Dep) {
|
||||
|
||||
ids.forEach(function (id) {
|
||||
this.removeRecordFromList(id);
|
||||
this.collection.trigger('moving-to-trash', model);
|
||||
this.collection.trigger('moving-to-trash', id);
|
||||
}, this);
|
||||
},
|
||||
|
||||
|
||||
@@ -419,7 +419,7 @@ Espo.define('views/fields/attachment-multiple', 'views/fields/base', function (D
|
||||
previews.push('<div class="attachment-preview">' + this.getDetailPreview(name, type, id) + '</div>');
|
||||
continue;
|
||||
}
|
||||
var line = '<div class="attachment-block"><span class="glyphicon glyphicon-paperclip small"></span> <a href="' + this.getDownloadUrl(id) + '">' + name + '</a></div>';
|
||||
var line = '<div class="attachment-block"><span class="glyphicon glyphicon-paperclip small"></span> <a href="' + this.getDownloadUrl(id) + '" target="_BLANK">' + name + '</a></div>';
|
||||
names.push(line);
|
||||
}
|
||||
var string = previews.join('') + names.join('');
|
||||
|
||||
@@ -200,7 +200,7 @@ Espo.define('views/fields/file', 'views/fields/link', function (Dep) {
|
||||
if (this.showPreview && ~this.previewTypeList.indexOf(type)) {
|
||||
string = '<div class="attachment-preview">' + this.getDetailPreview(name, type, id) + '</div>';
|
||||
} else {
|
||||
string = '<span class="glyphicon glyphicon-paperclip small"></span> <a href="'+ this.getDownloadUrl(id) +'">' + name + '</a>';
|
||||
string = '<span class="glyphicon glyphicon-paperclip small"></span> <a href="'+ this.getDownloadUrl(id) +'" target="_BLANK">' + name + '</a>';
|
||||
}
|
||||
return string;
|
||||
}
|
||||
|
||||
@@ -117,15 +117,19 @@ Espo.define('views/fields/map', 'views/fields/base', function (Dep) {
|
||||
|
||||
var geocoder = new google.maps.Geocoder();
|
||||
|
||||
var map = new google.maps.Map(this.$el.find('.map').get(0), {
|
||||
zoom: 15,
|
||||
center: {lat: 0, lng: 0},
|
||||
scrollwheel: false
|
||||
});
|
||||
try {
|
||||
var map = new google.maps.Map(this.$el.find('.map').get(0), {
|
||||
zoom: 15,
|
||||
center: {lat: 0, lng: 0},
|
||||
scrollwheel: false
|
||||
});
|
||||
} catch (e) {
|
||||
console.error(e.message);
|
||||
return;
|
||||
}
|
||||
|
||||
var address = '';
|
||||
|
||||
|
||||
if (this.addressData.street) {
|
||||
address += this.addressData.street;
|
||||
}
|
||||
|
||||
46
client/src/views/inbound-email/fields/email-address.js
Normal file
46
client/src/views/inbound-email/fields/email-address.js
Normal file
@@ -0,0 +1,46 @@
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM - Open Source CRM application.
|
||||
* Copyright (C) 2014-2015 Yuri Kuznetsov, Taras Machyshyn, Oleksiy Avramenko
|
||||
* Website: http://www.espocrm.com
|
||||
*
|
||||
* EspoCRM is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* EspoCRM is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
Espo.define('views/inbound-email/fields/email-address', 'views/fields/varchar', function (Dep) {
|
||||
|
||||
return Dep.extend({
|
||||
|
||||
setup: function () {
|
||||
Dep.prototype.setup.call(this);
|
||||
|
||||
this.on('change', function () {
|
||||
var emailAddress = this.model.get('emailAddress');
|
||||
this.model.set('name', emailAddress);
|
||||
if (this.model.isNew() || !this.model.get('replyToAddress')) {
|
||||
this.model.set('replyToAddress', emailAddress);
|
||||
}
|
||||
}, this);
|
||||
},
|
||||
|
||||
});
|
||||
});
|
||||
@@ -1100,7 +1100,7 @@ Espo.define('views/record/list', 'view', function (Dep) {
|
||||
if (!id) return;
|
||||
|
||||
var model = this.collection.get(id);
|
||||
if (!this.getAcl().checkScope(this.scope, 'delete')) {
|
||||
if (!this.getAcl().checkModel(model, 'delete')) {
|
||||
this.notify('Access denied', 'error');
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ Espo.define('views/settings/fields/currency-rates', 'views/fields/base', functio
|
||||
var currencyRates = this.model.get('currencyRates') || {};
|
||||
|
||||
var rateValues = {};
|
||||
this.model.get('currencyList').forEach(function (currency) {
|
||||
(this.model.get('currencyList') || []).forEach(function (currency) {
|
||||
if (currency != baseCurrency) {
|
||||
rateValues[currency] = currencyRates[currency] || 1.00;
|
||||
}
|
||||
|
||||
@@ -205,11 +205,17 @@ Espo.define('views/stream/panel', ['views/record/panels/relationship', 'lib!Text
|
||||
view.render();
|
||||
});
|
||||
|
||||
this.stopListening(this.model, 'all');
|
||||
this.stopListening(this.model, 'destroy');
|
||||
setTimeout(function () {
|
||||
this.listenTo(this.model, 'all', function (event) {
|
||||
if (!~['sync', 'after:relate'].indexOf(event)) return;
|
||||
collection.fetchNew();
|
||||
}, this);
|
||||
|
||||
this.listenTo(this.model, 'destroy', function () {
|
||||
this.stopListening(this.model, 'all');
|
||||
}, this);
|
||||
}.bind(this), 500);
|
||||
|
||||
}, this);
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
/**
|
||||
* Smarty Internal Plugin Templatelexer
|
||||
*
|
||||
* This is the lexer to break the template source into tokens
|
||||
* This is the lexer to break the template source into tokens
|
||||
* @package Smarty
|
||||
* @subpackage Compiler
|
||||
* @author Uwe Tews
|
||||
* @author Uwe Tews
|
||||
*/
|
||||
/**
|
||||
* Smarty Internal Plugin Templatelexer
|
||||
@@ -75,8 +75,8 @@ class Smarty_Internal_Templatelexer
|
||||
'AS' => 'as',
|
||||
'TO' => 'to',
|
||||
);
|
||||
|
||||
|
||||
|
||||
|
||||
function __construct($data,$compiler)
|
||||
{
|
||||
// $this->data = preg_replace("/(\r\n|\r|\n)/", "\n", $data);
|
||||
@@ -85,10 +85,10 @@ class Smarty_Internal_Templatelexer
|
||||
$this->line = 1;
|
||||
$this->smarty = $compiler->smarty;
|
||||
$this->compiler = $compiler;
|
||||
$this->ldel = preg_quote($this->smarty->left_delimiter,'/');
|
||||
$this->ldel_length = strlen($this->smarty->left_delimiter);
|
||||
$this->ldel = preg_quote($this->smarty->left_delimiter,'/');
|
||||
$this->ldel_length = strlen($this->smarty->left_delimiter);
|
||||
$this->rdel = preg_quote($this->smarty->right_delimiter,'/');
|
||||
$this->rdel_length = strlen($this->smarty->right_delimiter);
|
||||
$this->rdel_length = strlen($this->smarty->right_delimiter);
|
||||
$this->smarty_token_names['LDEL'] = $this->smarty->left_delimiter;
|
||||
$this->smarty_token_names['RDEL'] = $this->smarty->right_delimiter;
|
||||
$this->mbstring_overload = ini_get('mbstring.func_overload') & 2;
|
||||
@@ -312,8 +312,7 @@ class Smarty_Internal_Templatelexer
|
||||
}
|
||||
function yy_r1_13($yy_subpatterns)
|
||||
{
|
||||
|
||||
if ($this->smarty->auto_literal && ($this->mbstring_overload ? (mb_strpos(" \n\t\r",mb_substr($this->value,$this->ldel_length,1,'latin1'),0,'latin1') !== false) : (strpos(" \n\t\r",substr($this->value,$this->ldel_length,1)) !== false))) {
|
||||
if ($this->smarty->auto_literal && ($this->mbstring_overload ? (mb_strpos(" \n\t\r",mb_substr($this->value,$this->ldel_length,1,'latin1'),0,'latin1') !== false) : ($this->getStrpos($this->value, $this->ldel_length, 1) /*espocrm: fix a warning for PHP 7*/ !== false))) {
|
||||
$this->token = Smarty_Internal_Templateparser::TP_TEXT;
|
||||
} else {
|
||||
$this->token = Smarty_Internal_Templateparser::TP_LDEL;
|
||||
@@ -693,7 +692,7 @@ class Smarty_Internal_Templatelexer
|
||||
function yy_r2_46($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = Smarty_Internal_Templateparser::TP_PTR;
|
||||
$this->token = Smarty_Internal_Templateparser::TP_PTR;
|
||||
}
|
||||
function yy_r2_47($yy_subpatterns)
|
||||
{
|
||||
@@ -855,7 +854,7 @@ class Smarty_Internal_Templatelexer
|
||||
function yy_r2_75($yy_subpatterns)
|
||||
{
|
||||
|
||||
if ($this->smarty->auto_literal && ($this->mbstring_overload ? (mb_strpos(" \n\t\r",mb_substr($this->value,$this->ldel_length,1,'latin1'),0,'latin1') !== false) : (strpos(" \n\t\r",substr($this->value,$this->ldel_length,1)) !== false))) {
|
||||
if ($this->smarty->auto_literal && ($this->mbstring_overload ? (mb_strpos(" \n\t\r",mb_substr($this->value,$this->ldel_length,1,'latin1'),0,'latin1') !== false) : ($this->getStrpos($this->value, $this->ldel_length, 1) !== false))) {
|
||||
$this->token = Smarty_Internal_Templateparser::TP_TEXT;
|
||||
} else {
|
||||
$this->token = Smarty_Internal_Templateparser::TP_LDEL;
|
||||
@@ -993,7 +992,7 @@ class Smarty_Internal_Templatelexer
|
||||
$to = $match[0][1];
|
||||
} else {
|
||||
$this->compiler->trigger_template_error ("missing or misspelled literal closing tag");
|
||||
}
|
||||
}
|
||||
if ($this->mbstring_overload) {
|
||||
$this->value = mb_substr($this->data,$this->counter,$to-$this->counter,'latin1');
|
||||
} else {
|
||||
@@ -1118,7 +1117,7 @@ class Smarty_Internal_Templatelexer
|
||||
function yy_r4_6($yy_subpatterns)
|
||||
{
|
||||
|
||||
if ($this->smarty->auto_literal && ($this->mbstring_overload ? (mb_strpos(" \n\t\r",mb_substr($this->value,$this->ldel_length,1,'latin1'),0,'latin1') !== false) : (strpos(" \n\t\r",substr($this->value,$this->ldel_length,1)) !== false))) {
|
||||
if ($this->smarty->auto_literal && ($this->mbstring_overload ? (mb_strpos(" \n\t\r",mb_substr($this->value,$this->ldel_length,1,'latin1'),0,'latin1') !== false) : ($this->getStrpos($this->value, $this->ldel_length, 1) /*espocrm: fix a warning for PHP 7*/ !== false))) {
|
||||
$this->token = Smarty_Internal_Templateparser::TP_TEXT;
|
||||
} else {
|
||||
$this->token = Smarty_Internal_Templateparser::TP_LDEL;
|
||||
@@ -1393,4 +1392,12 @@ class Smarty_Internal_Templatelexer
|
||||
$this->token = Smarty_Internal_Templateparser::TP_BLOCKSOURCE;
|
||||
}
|
||||
|
||||
|
||||
//espocrm: fix a warning for PHP 7
|
||||
protected function getStrpos($value, $ldelLength, $offset = 0)
|
||||
{
|
||||
$substrRes = substr($value, $ldelLength, $offset);
|
||||
return !empty($substrRes) ? strpos(" \n\t\r", $substrRes) : false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "espocrm",
|
||||
"version": "4.2.1",
|
||||
"version": "4.2.3",
|
||||
"description": "",
|
||||
"main": "index.php",
|
||||
"repository": {
|
||||
|
||||
Reference in New Issue
Block a user