Compare commits

...

28 Commits

Author SHA1 Message Date
yuri a4c15992a9 fix calendar task 2018-06-08 14:20:35 +03:00
yuri 14d1173a0c calendar prevent drag between allday and hours 2018-06-08 14:01:06 +03:00
yuri 1e7acbdbd2 fix calendar 2018-06-08 12:47:11 +03:00
yuri 35e729b25c Merge branch 'hotfix/5.2.5' of ssh://172.20.0.1/var/git/espo/backend into hotfix/5.2.5 2018-06-08 12:12:18 +03:00
Taras Machyshyn 4ee5ea78e3 LDAP bug fixes 2018-06-08 12:11:32 +03:00
yuri fe971f9f67 outboundEmailBccAddress on ui 2018-06-08 11:30:04 +03:00
yuri a828523f26 external email client for email address field link 2018-06-08 11:25:32 +03:00
yuri af9ca6788e re-render header on name change after sync 2018-06-07 16:28:48 +03:00
yuri 36d1c3af63 version 2018-06-07 13:58:52 +03:00
yuri 1853e98209 wysiwyg text filters support 2018-06-07 12:43:38 +03:00
yuri f526d43798 improve dynamic logic conditions ui 2018-06-07 12:30:38 +03:00
yuri ae8c76cecb fix select manager 2018-06-07 10:48:48 +03:00
yuri 2b32c94543 Merge branch 'hotfix/5.2.5' of ssh://172.20.0.1/var/git/espo/backend into hotfix/5.2.5 2018-06-07 10:45:38 +03:00
Taras Machyshyn b6f5909df1 MySQL 8 bug fixes 2018-06-06 16:44:06 +03:00
yuri ef35bbbb63 import: setting null for wrong date time 2018-06-05 13:11:34 +03:00
yuri 64f2cc6c7e fix import varchar length exceeded 2018-06-05 13:03:00 +03:00
yuri a16635eb26 fix import ui 2018-06-05 13:02:31 +03:00
yuri 6e614f0a7d history has attachment 2018-06-04 16:40:07 +03:00
yuri 859f4eab0a target list panels change order 2018-06-04 14:33:11 +03:00
yuri 104e0b9079 target list changes 2 2018-06-04 14:25:00 +03:00
yuri 612abbf5c0 fix range fields 2018-06-04 12:46:54 +03:00
yuri 2dfbf71806 target list changes 2018-06-04 12:39:57 +03:00
yuri c43f4d129d fix calendar 2018-06-04 10:47:14 +03:00
yuri 8d47a48f62 lang fix 2018-06-04 10:44:55 +03:00
yuri 9a333e6e38 list layout widthPx preserving 2018-06-01 12:07:45 +03:00
yuri f6e0ef8cc6 layout manager data attribute list for custom layouts 2018-06-01 11:37:53 +03:00
yuri 36a45717f1 fix excel export varchar 2018-05-31 11:47:34 +03:00
yuri 86f63d72e1 fix list expanded 2018-05-31 11:41:38 +03:00
56 changed files with 759 additions and 94 deletions
+1 -1
View File
@@ -469,7 +469,7 @@ class Xlsx extends \Espo\Core\Injectable
} else {
if (array_key_exists($name, $row)) {
$sheet->setCellValue("$col$rowNumber", $row[$name]);
$sheet->setCellValueExplicit("$col$rowNumber", $row[$name], \PhpOffice\PhpSpreadsheet\Cell\DataType::TYPE_STRING);
}
}
@@ -1007,7 +1007,7 @@ class Base
foreach ($item['value'] as $i) {
$a = $this->getWherePart($i, $result);
foreach ($a as $left => $right) {
if (!empty($right) || is_null($right) || $right === '') {
if (!empty($right) || is_null($right) || $right === '' || $right === 0 || $right === false) {
$arr[] = array($left => $right);
}
}
@@ -251,6 +251,8 @@ class LDAP extends Base
$data[$fieldName] = $fieldValue;
}
$this->getAuth()->useNoAuth();
$user = $this->getEntityManager()->getEntity('User');
$user->set($data);
@@ -315,12 +315,121 @@ class MySqlPlatform extends \Doctrine\DBAL\Platforms\MySqlPlatform
$options['collate'] = 'utf8mb4_unicode_ci';
}
return parent::_getCreateTableSQL($tableName, $columns, $options);
$queryFields = $this->getColumnDeclarationListSQL($columns);
if (isset($options['uniqueConstraints']) && ! empty($options['uniqueConstraints'])) {
foreach ($options['uniqueConstraints'] as $index => $definition) {
$queryFields .= ', ' . $this->getUniqueConstraintDeclarationSQL($index, $definition);
}
}
// add all indexes
if (isset($options['indexes']) && ! empty($options['indexes'])) {
foreach($options['indexes'] as $index => $definition) {
$queryFields .= ', ' . $this->getIndexDeclarationSQL($index, $definition);
}
}
// attach all primary keys
if (isset($options['primary']) && ! empty($options['primary'])) {
$keyColumns = array_unique(array_values($options['primary']));
$queryFields .= ', PRIMARY KEY(' . implode(', ', $keyColumns) . ')';
}
$query = 'CREATE ';
if (!empty($options['temporary'])) {
$query .= 'TEMPORARY ';
}
$query .= 'TABLE ' . $this->espoQuote($tableName) . ' (' . $queryFields . ') ';
$query .= $this->buildTableOptions($options);
$query .= $this->buildPartitionOptions($options);
$sql[] = $query;
if (isset($options['foreignKeys'])) {
foreach ((array) $options['foreignKeys'] as $definition) {
$sql[] = $this->getCreateForeignKeySQL($definition, $tableName);
}
}
return $sql;
}
public function getColumnCollationDeclarationSQL($collation)
{
return $this->getCollationFieldDeclaration($collation);
}
/**
* Build SQL for table options
*
* @param array $options
*
* @return string
*/
private function buildTableOptions(array $options)
{
if (isset($options['table_options'])) {
return $options['table_options'];
}
$tableOptions = array();
// Charset
if ( ! isset($options['charset'])) {
$options['charset'] = 'utf8';
}
$tableOptions[] = sprintf('DEFAULT CHARACTER SET %s', $options['charset']);
// Collate
if ( ! isset($options['collate'])) {
$options['collate'] = 'utf8_unicode_ci';
}
$tableOptions[] = sprintf('COLLATE %s', $options['collate']);
// Engine
if ( ! isset($options['engine'])) {
$options['engine'] = 'InnoDB';
}
$tableOptions[] = sprintf('ENGINE = %s', $options['engine']);
// Auto increment
if (isset($options['auto_increment'])) {
$tableOptions[] = sprintf('AUTO_INCREMENT = %s', $options['auto_increment']);
}
// Comment
if (isset($options['comment'])) {
$comment = trim($options['comment'], " '");
$tableOptions[] = sprintf("COMMENT = '%s' ", str_replace("'", "''", $comment));
}
// Row format
if (isset($options['row_format'])) {
$tableOptions[] = sprintf('ROW_FORMAT = %s', $options['row_format']);
}
return implode(' ', $tableOptions);
}
/**
* Build SQL for partition options.
*
* @param array $options
*
* @return string
*/
private function buildPartitionOptions(array $options)
{
return (isset($options['partition_options']))
? ' ' . $options['partition_options']
: '';
}
//end: ESPO
}
@@ -63,6 +63,9 @@ class TargetList extends \Espo\Core\Controllers\Record
if (empty($data->targetId)) {
throw new BadRequest();
}
$data->id = strval($data->id);
$data->targetId = strval($data->targetId);
return $this->getRecordService()->optOut($data->id, $data->targetType, $data->targetId);
}
@@ -77,6 +80,9 @@ class TargetList extends \Espo\Core\Controllers\Record
if (empty($data->targetId)) {
throw new BadRequest();
}
$data->id = strval($data->id);
$data->targetId = strval($data->targetId);
return $this->getRecordService()->cancelOptOut($data->id, $data->targetType, $data->targetId);
}
}
@@ -3,11 +3,14 @@
"name": "Name",
"description": "Description",
"entryCount": "Entry Count",
"optedOutCount": "Opted Out Count",
"campaigns": "Campaigns",
"endDate": "End Date",
"targetLists": "Target Lists",
"includingActionList": "Including",
"excludingActionList": "Excluding"
"excludingActionList": "Excluding",
"targetStatus": "Target Status",
"isOptedOut": "Is Opted Out"
},
"links": {
"accounts": "Accounts",
@@ -23,6 +26,9 @@
"Television": "Television",
"Radio": "Radio",
"Newsletter": "Newsletter"
},
"targetStatus": {
"Opted Out": "Opted Out"
}
},
"labels": {
@@ -3,6 +3,7 @@
"label":"Overview",
"rows":[
[{"name":"name"}, {"name":"entryCount"}],
[false, {"name":"optedOutCount"}],
[{"name":"description", "fullWidth": true}]
]
}
@@ -4,6 +4,7 @@
"rows": [
[{"name":"name", "fullWidth": true}],
[{"name":"entryCount", "fullWidth": true}],
[{"name":"optedOutCount", "fullWidth": true}],
[{"name":"description", "fullWidth": true}]
]
}
@@ -1,4 +1,4 @@
[
{"name":"name", "link":true},
{"name":"entryCount", "width": 25, "notSortable": true}
{"name":"targetStatus", "width": 25, "notSortable": true}
]
@@ -1,6 +1,6 @@
[
"contacts",
"leads",
"users",
"accounts"
"accounts",
"users"
]
@@ -61,7 +61,9 @@
"create": false
},
"targetLists": {
"layout": "listForTarget"
"rowActionsView": "crm:views/record/row-actions/relationship-target",
"layout": "listForTarget",
"view": "crm:views/record/panels/target-lists"
}
},
"filterList": [
@@ -85,8 +85,9 @@
},
"targetLists": {
"create": false,
"rowActionsView": "views/record/row-actions/relationship-unlink-only",
"layout": "listForTarget"
"rowActionsView": "crm:views/record/row-actions/relationship-target",
"layout": "listForTarget",
"view": "crm:views/record/panels/target-lists"
}
},
"boolFilterList": [
@@ -112,14 +112,15 @@
},
"relationshipPanels":{
"campaignLogRecords":{
"rowActionsView":"Record.RowActions.Empty",
"rowActionsView":"views/record/row-actions/empty",
"select":false,
"create":false
},
"targetLists":{
"create":false,
"rowActionsView":"views/record/row-actions/relationship-unlink-only",
"layout": "listForTarget"
"create": false,
"rowActionsView": "crm:views/record/row-actions/relationship-target",
"layout": "listForTarget",
"view": "crm:views/record/panels/target-lists"
}
},
"filterList":[
@@ -206,6 +206,12 @@
"layoutListDisabled": true,
"readOnly": true,
"view": "views/fields/link-one"
},
"targetListIsOptedOut": {
"type": "bool",
"notStorable": true,
"readOnly": true,
"disabled": true
}
},
"links": {
@@ -289,6 +289,12 @@
"layoutListDisabled": true,
"readOnly": true,
"view": "views/fields/link-one"
},
"targetListIsOptedOut": {
"type": "bool",
"notStorable": true,
"readOnly": true,
"disabled": true
}
},
"links": {
@@ -194,6 +194,12 @@
"layoutMassUpdateDisabled": true,
"layoutFiltersDisabled": true,
"entity": "TargetList"
},
"targetListIsOptedOut": {
"type": "bool",
"notStorable": true,
"readOnly": true,
"disabled": true
}
},
"links": {
@@ -8,7 +8,17 @@
"entryCount": {
"type": "int",
"readOnly": true,
"notStorable": true
"notStorable": true,
"layoutFiltersDisabled": true,
"layoutMassUpdateDisabled": true
},
"optedOutCount": {
"type": "int",
"readOnly": true,
"notStorable": true,
"layoutListDisabled": true,
"layoutFiltersDisabled": true,
"layoutMassUpdateDisabled": true
},
"description": {
"type": "text"
@@ -60,6 +70,28 @@
"layoutLinkDisabled": true,
"notStorable": true,
"disabled": true
},
"targetStatus": {
"type": "enum",
"options": ["", "Opted Out"],
"notStorable": true,
"readOnly": true,
"layoutListDisabled": true,
"layoutDetailDisabled": true,
"layoutMassUpdateDisabled": true,
"exportDisabled": true,
"importDisabled": true,
"view": "crm:views/target-list/fields/target-status"
},
"isOptedOut": {
"type": "bool",
"notStorable": true,
"readOnly": true,
"layoutListDisabled": true,
"layoutDetailDisabled": true,
"layoutMassUpdateDisabled": true,
"exportDisabled": true,
"importDisabled": true
}
},
"links": {
@@ -4,6 +4,12 @@
"type": "hasMany",
"entity": "TargetList",
"foreign": "users"
},
"targetListIsOptedOut": {
"type": "bool",
"notStorable": true,
"readOnly": true,
"disabled": true
}
}
}
@@ -39,6 +39,11 @@ class Account extends \Espo\Services\Record
'role' => 'accountRole',
'isInactive' => 'accountIsInactive'
)
),
'targetLists' => array(
'additionalColumns' => array(
'optedOut' => 'isOptedOut'
)
)
);
@@ -114,7 +114,8 @@ class Activities extends \Espo\Core\Services\Base
'parentType',
'parentId',
'status',
'createdAt'
'createdAt',
['VALUE:', 'hasAttachment']
],
'leftJoins' => [['users', 'usersLeft']],
'whereClause' => array(
@@ -165,7 +166,8 @@ class Activities extends \Espo\Core\Services\Base
'parentType',
'parentId',
'status',
'createdAt'
'createdAt',
['VALUE:', 'hasAttachment']
],
'leftJoins' => [['users', 'usersLeft']],
'whereClause' => array(
@@ -223,7 +225,8 @@ class Activities extends \Espo\Core\Services\Base
'parentType',
'parentId',
'status',
'createdAt'
'createdAt',
'hasAttachment'
],
'leftJoins' => [['users', 'usersLeft']],
'whereClause' => array(
@@ -269,7 +272,8 @@ class Activities extends \Espo\Core\Services\Base
'parentType',
'parentId',
'status',
'createdAt'
'createdAt',
['VALUE:', 'hasAttachment']
],
'whereClause' => array(),
'customJoin' => ''
@@ -375,7 +379,8 @@ class Activities extends \Espo\Core\Services\Base
'parentType',
'parentId',
'status',
'createdAt'
'createdAt',
['VALUE:', 'hasAttachment']
],
'whereClause' => array()
);
@@ -480,7 +485,8 @@ class Activities extends \Espo\Core\Services\Base
'parentType',
'parentId',
'status',
'createdAt'
'createdAt',
'hasAttachment'
],
'whereClause' => array(),
'customJoin' => ''
@@ -627,15 +633,21 @@ class Activities extends \Espo\Core\Services\Base
$sth->execute();
$rows = $sth->fetchAll(PDO::FETCH_ASSOC);
$rowList = $sth->fetchAll(PDO::FETCH_ASSOC);
$list = array();
foreach ($rows as $row) {
$boolAttributeList = ['hasAttachment'];
$list = [];
foreach ($rowList as $row) {
foreach ($boolAttributeList as $attribute) {
if (!array_key_exists($attribute, $row)) continue;
$row[$attribute] = $row[$attribute] == '1' ? true : false;
}
$list[] = $row;
}
return array(
'list' => $rows,
'list' => $list,
'total' => $totalCount
);
}
@@ -1004,7 +1016,8 @@ class Activities extends \Espo\Core\Services\Base
($seed->hasAttribute('parentType') ? ['parentType', 'parentType'] : ['VALUE:', 'parentType']),
($seed->hasAttribute('parentId') ? ['parentId', 'parentId'] : ['VALUE:', 'parentId']),
'status',
'createdAt'
'createdAt',
['VALUE:', 'hasAttachment']
];
$selectParams = $selectManager->getEmptySelectParams();
@@ -43,6 +43,14 @@ class Contact extends \Espo\Core\Templates\Services\Person
'title'
];
protected $linkSelectParams = array(
'targetLists' => array(
'additionalColumns' => array(
'optedOut' => 'isOptedOut'
)
)
);
protected function afterCreateEntity(Entity $entity, $data)
{
if (!empty($data->emailId)) {
@@ -43,6 +43,14 @@ class Lead extends \Espo\Core\Templates\Services\Person
$this->addDependency('container');
}
protected $linkSelectParams = array(
'targetLists' => array(
'additionalColumns' => array(
'optedOut' => 'isOptedOut'
)
)
);
protected function getFieldManager()
{
return $this->getInjection('container')->get('fieldManager');
@@ -48,6 +48,29 @@ class TargetList extends \Espo\Services\Record
'User' => 'users'
);
protected $linkSelectParams = [
'accounts' => [
'additionalColumns' => [
'optedOut' => 'targetListIsOptedOut'
]
],
'contacts' => [
'additionalColumns' => [
'optedOut' => 'targetListIsOptedOut'
]
],
'leads' => [
'additionalColumns' => [
'optedOut' => 'targetListIsOptedOut'
]
],
'users' => [
'additionalColumns' => [
'optedOut' => 'targetListIsOptedOut'
]
]
];
protected function init()
{
parent::init();
@@ -60,6 +83,7 @@ class TargetList extends \Espo\Services\Record
{
parent::loadAdditionalFields($entity);
$this->loadEntryCountField($entity);
$this->loadOptedOutCountField($entity);
}
public function loadAdditionalFieldsForList(Entity $entity)
@@ -78,6 +102,34 @@ class TargetList extends \Espo\Services\Record
$entity->set('entryCount', $count);
}
protected function loadOptedOutCountField(Entity $entity)
{
$count = 0;
$count += $this->getEntityManager()->getRepository('Contact')->join(['targetLists'])->where([
'targetListsMiddle.targetListId' => $entity->id,
'targetListsMiddle.optedOut' => 1
])->count();
$count += $this->getEntityManager()->getRepository('Lead')->join(['targetLists'])->where([
'targetListsMiddle.targetListId' => $entity->id,
'targetListsMiddle.optedOut' => 1
])->count();
$count += $this->getEntityManager()->getRepository('Account')->join(['targetLists'])->where([
'targetListsMiddle.targetListId' => $entity->id,
'targetListsMiddle.optedOut' => 1
])->count();
$count += $this->getEntityManager()->getRepository('User')->join(['targetLists'])->where([
'targetListsMiddle.targetListId' => $entity->id,
'targetListsMiddle.optedOut' => 1
])->count();
$entity->set('optedOutCount', $count);
}
protected function afterCreate(Entity $entity, array $data = array())
{
if (array_key_exists('sourceCampaignId', $data) && !empty($data['includingActionList'])) {
@@ -52,7 +52,7 @@
"Team": "Teams",
"Role": "Roles",
"EmailTemplate": "Email Templates",
"EmailTemplateCategory": "Email Template Category",
"EmailTemplateCategory": "Email Template Categories",
"EmailAccount": "Personal Email Accounts",
"EmailAccountScope": "Personal Email Accounts",
"OutboundEmail": "Outbound Emails",
@@ -338,7 +338,8 @@
"children": "Children",
"id": "ID",
"ids": "IDs",
"names": "Names"
"names": "Names",
"targetListIsOptedOut": "Is Opted Out (Target List)"
},
"links": {
"assignedUser": "Assigned User",
@@ -98,7 +98,8 @@
"scopeColorsDisabled": "Disable scope colors",
"tabColorsDisabled": "Disable tab colors",
"tabIconsDisabled": "Disable tab icons",
"emailAddressIsOptedOutByDefault": "Mark new email addresses as opted-out"
"emailAddressIsOptedOutByDefault": "Mark new email addresses as opted-out",
"outboundEmailBccAddress": "BCC address for external clients"
},
"options": {
"weekStart": {
@@ -12,7 +12,7 @@
"label": "Configuration",
"rows": [
[{"name": "outboundEmailFromAddress"}, {"name": "outboundEmailIsShared"}],
[{"name": "outboundEmailFromName"}, false]
[{"name": "outboundEmailFromName"}, {"name": "outboundEmailBccAddress"}]
]
},
{
@@ -101,6 +101,14 @@
}
]
},
"relationshipPanels":{
"targetLists":{
"create": false,
"rowActionsView": "crm:views/record/row-actions/relationship-target",
"layout": "listForTarget",
"view": "crm:views/record/panels/target-lists"
}
},
"filterList": [
"active"
],
@@ -469,6 +469,10 @@
"emailAddressIsOptedOutByDefault": {
"type": "bool",
"tooltip": true
},
"outboundEmailBccAddress": {
"type": "varchar",
"trim": true
}
}
}
@@ -15,7 +15,5 @@
"notMergeable": true,
"notCreatable": true,
"filter": false,
"fieldDefs": {
"notStorable": true
}
"skipOrmDefs": true
}
@@ -15,7 +15,5 @@
"notMergeable": true,
"notCreatable": true,
"filter": false,
"fieldDefs": {
"notStorable": true
}
"skipOrmDefs": true
}
@@ -15,7 +15,5 @@
"notMergeable": true,
"notCreatable": true,
"filter": false,
"fieldDefs": {
"notStorable": true
}
"skipOrmDefs": true
}
+25 -6
View File
@@ -523,16 +523,18 @@ class Import extends \Espo\Services\Record
if ($value !== '') {
$type = $this->getMetadata()->get("entityDefs.{$scope}.fields.{$field}.type");
if ($type == 'personName') {
$lastNameField = 'last' . ucfirst($field);
$firstNameField = 'first' . ucfirst($field);
$firstNameAttribute = 'first' . ucfirst($field);
$lastNameAttribute = 'last' . ucfirst($field);
$personName = $this->parsePersonName($value, $params['personNameFormat']);
if (!$entity->get($firstNameField)) {
$entity->set($firstNameField, $personName['firstName']);
if (!$entity->get($firstNameAttribute)) {
$personName['firstName'] = $this->prepareAttributeValue($entity, $firstNameAttribute, $personName['firstName']);
$entity->set($firstNameAttribute, $personName['firstName']);
}
if (!$entity->get($lastNameField)) {
$entity->set($lastNameField, $personName['lastName']);
if (!$entity->get($lastNameAttribute)) {
$personName['lastName'] = $this->prepareAttributeValue($entity, $lastNameAttribute, $personName['lastName']);
$entity->set($lastNameAttribute, $personName['lastName']);
}
continue;
}
@@ -652,6 +654,19 @@ class Import extends \Espo\Services\Record
return $result;
}
protected function prepareAttributeValue($entity, $attribute, $value)
{
if ($entity->getAttributeType($attribute) === $entity::VARCHAR) {
$maxLength = $entity->getAttributeParam($attribute, 'len');
if ($maxLength) {
if (mb_strlen($value) > $maxLength) {
$value = substr($value, 0, $maxLength);
}
}
}
return $value;
}
protected function parsePersonName($value, $format)
{
$firstName = '';
@@ -711,6 +726,7 @@ class Import extends \Espo\Services\Record
if ($dt) {
return $dt->format('Y-m-d');
}
return null;
break;
case Entity::DATETIME:
$timezone = new \DateTimeZone(isset($params['timezone']) ? $params['timezone'] : 'UTC');
@@ -719,6 +735,7 @@ class Import extends \Espo\Services\Record
$dt->setTimezone(new \DateTimeZone('UTC'));
return $dt->format('Y-m-d H:i:s');
}
return null;
break;
case Entity::FLOAT:
$a = explode($decimalMark, $value);
@@ -746,6 +763,8 @@ class Import extends \Espo\Services\Record
}
}
$value = $this->prepareAttributeValue($entity, $attribute, $value);
return $value;
}
+8
View File
@@ -63,6 +63,14 @@ class User extends Record
'accountsIds'
];
protected $linkSelectParams = array(
'targetLists' => array(
'additionalColumns' => array(
'optedOut' => 'isOptedOut'
)
)
);
protected function getMailSender()
{
return $this->getContainer()->get('mailSender');
@@ -393,10 +393,13 @@ Espo.define('crm:views/calendar/calendar', ['view', 'lib!full-calendar'], functi
handleAllDay: function (event, notInitial) {
if (~this.allDayScopeList.indexOf(event.scope)) {
event.allDay = true;
event.allDay = event.allDayCopy = true;
if (!notInitial) {
if (event.end) {
event.start = event.end;
if (event.end.hours() === 0 && event.end.minutes() === 0) {
event.start.add(-1, 'days');
}
}
}
return;
@@ -423,6 +426,8 @@ Espo.define('crm:views/calendar/calendar', ['view', 'lib!full-calendar'], functi
event.allDay = false;
}
}
event.allDayCopy = event.allDay;
},
convertToFcEvents: function (list) {
@@ -579,6 +584,17 @@ Espo.define('crm:views/calendar/calendar', ['view', 'lib!full-calendar'], functi
this.fetchEvents(fromStr, toStr, callback);
}.bind(this),
eventDrop: function (event, delta, revertFunc) {
if (event.start.hasTime()) {
if (event.allDayCopy) {
revertFunc();
return;
}
} else {
if (!event.allDayCopy) {
revertFunc();
return;
}
}
var eventCloned = Espo.Utils.clone(event);
var dateStart = this.convertTime(event.start) || null;
@@ -625,6 +641,7 @@ Espo.define('crm:views/calendar/calendar', ['view', 'lib!full-calendar'], functi
this.getModelFactory().create(event.scope, function (model) {
model.once('sync', function () {
this.notify(false);
this.$calendar.fullCalendar('updateEvent', event);
}, this);
model.id = event.recordId;
model.save(attributes, {patch: true}).fail(function () {
@@ -645,6 +662,7 @@ Espo.define('crm:views/calendar/calendar', ['view', 'lib!full-calendar'], functi
this.getModelFactory().create(event.scope, function (model) {
model.once('sync', function () {
this.notify(false);
this.$calendar.fullCalendar('updateEvent', event);
}.bind(this));
model.id = event.recordId;
model.save(attributes, {patch: true}).fail(function () {
@@ -52,7 +52,8 @@ Espo.define('crm:views/record/panels/history', 'crm:views/record/panels/activiti
],
[
{name: 'status'},
{name: 'dateSent'}
{name: 'dateSent'},
{name: 'hasAttachment', view: 'views/email/fields/has-attachment'}
]
]
},
@@ -0,0 +1,71 @@
/************************************************************************
* 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.
************************************************************************/
Espo.define('crm:views/record/panels/target-lists', 'views/record/panels/relationship', function (Dep) {
return Dep.extend({
actionOptOut: function (data) {
this.confirm(this.translate('confirmation', 'messages'), function () {
$.ajax({
url: 'TargetList/action/optOut',
type: 'POST',
data: JSON.stringify({
id: data.id,
targetId: this.model.id,
targetType: this.model.name
})
}).done(function () {
this.collection.fetch();
Espo.Ui.success(this.translate('Done'));
this.model.trigger('opt-out');
}.bind(this));
}, this);
},
actionCancelOptOut: function (data) {
this.confirm(this.translate('confirmation', 'messages'), function () {
$.ajax({
url: 'TargetList/action/cancelOptOut',
type: 'POST',
data: JSON.stringify({
id: data.id,
targetId: this.model.id,
targetType: this.model.name
})
}).done(function () {
this.collection.fetch();
Espo.Ui.success(this.translate('Done'));
this.collection.fetch();
this.model.trigger('cancel-opt-out');
}.bind(this));
}, this);
}
});
});
@@ -0,0 +1,60 @@
/************************************************************************
* 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.
************************************************************************/
Espo.define('crm:views/record/row-actions/relationship-target', 'views/record/row-actions/relationship-unlink-only', function (Dep) {
return Dep.extend({
getActionList: function () {
var list = Dep.prototype.getActionList.call(this);
if (this.options.acl.edit) {
if (this.model.get('isOptedOut')) {
list.push({
action: 'cancelOptOut',
html: this.translate('Cancel Opt-Out', 'labels', 'TargetList'),
data: {
id: this.model.id
}
});
} else {
list.push({
action: 'optOut',
html: this.translate('Opt-Out', 'labels', 'TargetList'),
data: {
id: this.model.id
}
});
}
}
return list;
}
});
});
@@ -0,0 +1,41 @@
/************************************************************************
* 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.
************************************************************************/
Espo.define('crm:views/target-list/fields/target-status', 'views/fields/base', function (Dep) {
return Dep.extend({
getValueForDisplay: function () {
if (this.model.get('isOptedOut')) {
return this.getLanguage().translateOption('Opted Out', 'targetStatus', 'TargetList');
}
return '';
}
});
});
@@ -108,6 +108,10 @@ Espo.define('crm:views/target-list/record/panels/opted-out', ['views/record/pane
this.listenTo(this.model, 'opt-out', function () {
this.actionRefresh();
}, this);
this.listenTo(this.model, 'cancel-opt-out', function () {
this.actionRefresh();
}, this);
},
afterRender: function () {
@@ -48,8 +48,26 @@ Espo.define('crm:views/target-list/record/panels/relationship', 'views/record/pa
this.model.trigger('opt-out');
}.bind(this));
}, this);
},
actionCancelOptOut: function (data) {
this.confirm(this.translate('confirmation', 'messages'), function () {
$.ajax({
url: 'TargetList/action/cancelOptOut',
type: 'POST',
data: JSON.stringify({
id: this.model.id,
targetId: data.id,
targetType: data.type
})
}).done(function () {
this.collection.fetch();
Espo.Ui.success(this.translate('Done'));
this.collection.fetch();
this.model.trigger('cancel-opt-out');
}.bind(this));
}, this);
}
});
});
@@ -33,17 +33,27 @@ Espo.define('crm:views/target-list/record/row-actions/default', 'views/record/ro
getActionList: function () {
var list = Dep.prototype.getActionList.call(this);
if (this.options.acl.edit) {
list.push({
action: 'optOut',
label: 'Opt-Out',
data: {
id: this.model.id,
type: this.model.name
}
});
if (this.model.get('targetListIsOptedOut')) {
list.push({
action: 'cancelOptOut',
label: 'Cancel Opt-Out',
data: {
id: this.model.id,
type: this.model.name
}
});
} else {
list.push({
action: 'optOut',
label: 'Opt-Out',
data: {
id: this.model.id,
type: this.model.name
}
});
}
}
return list;
}
});
});
@@ -1,21 +1,34 @@
<div class="group-head">
<div class="group-head" data-level="{{level}}">
{{#ifNotEqual level 0}}
<a class="pull-right" href="javascript:" data-action="remove"><span class="glyphicon glyphicon-remove"></span></a>
{{/ifNotEqual}}
<span>{{translate operator category='logicalOperators' scope='Admin'}}</span>
{{#ifNotEqual level 0}}
<div>(</div>
{{else}}
&nbsp;
{{/ifNotEqual}}
</div>
<div class="item-list" data-level="{{level}}">
{{#each viewDataList}}
<div data-view-key="{{key}}">{{{var key ../this}}}</div>
<div class="group-operator" data-view-ref-key="{{key}}">{{translate ../groupOperator category='logicalOperators' scope='Admin'}}</div>
{{/each}}
</div>
<div class="group-bottom" data-level="{{level}}">
<div class="btn-group">
<button class="btn btn-link btn-sm dropdown-toggle" data-toggle="dropdown"><span class="glyphicon glyphicon-plus"></span></button>
<a class="dropdown-toggle small" href="javascript:" data-toggle="dropdown">{{translate groupOperator category='logicalOperators' scope='Admin'}} <span class="glyphicon glyphicon-plus"></span></a>
<ul class="dropdown-menu">
<li><a href="javascript:" data-action="addField">{{translate 'Field' scope='DynamicLogic'}}</a></li>
<li><a href="javascript:" data-action="addAnd">{{translate 'and' category='logicalOperators' scope='Admin'}}</a></li>
<li><a href="javascript:" data-action="addOr">{{translate 'or' category='logicalOperators' scope='Admin'}}</a></li>
<li><a href="javascript:" data-action="addNot">{{translate 'not' category='logicalOperators' scope='Admin'}}</a></li>
<li><a href="javascript:" data-action="addAnd">(... {{translate 'and' category='logicalOperators' scope='Admin'}} ...)</a></li>
<li><a href="javascript:" data-action="addOr">(... {{translate 'or' category='logicalOperators' scope='Admin'}} ...)</a></li>
<li><a href="javascript:" data-action="addNot">{{translate 'not' category='logicalOperators' scope='Admin'}} (...)</a></li>
</ul>
</div>
</div>
<div class="item-list">
{{#each viewDataList}}
<div data-view-key="{{key}}">{{{var key ../this}}}</div>
{{/each}}
</div>
{{#ifNotEqual level 0}}
<div>)</div>
{{/ifNotEqual}}
@@ -1,17 +1,23 @@
<div class="group-head">
<div class="group-head" data-level="{{level}}">
<a class="pull-right" href="javascript:" data-action="remove"><span class="glyphicon glyphicon-remove"></span></a>
<span>{{translate 'not' category='logicalOperators' scope='Admin'}}</span>
<div>{{translate 'not' category='logicalOperators' scope='Admin'}} (</div>
</div>
<div class="item-list" data-level="{{level}}">
<div data-view-key="{{viewKey}}">{{#if hasItem}}{{{var viewKey this}}}{{/if}}</div>
</div>
<div class="group-bottom" data-level="{{level}}">
<div class="btn-group">
<button class="btn btn-link btn-sm dropdown-toggle" data-toggle="dropdown"><span class="glyphicon glyphicon-plus"></span></button>
<a class="dropdown-toggle small" href="javascript:" data-toggle="dropdown"><span class="glyphicon glyphicon-plus"></span></a>
<ul class="dropdown-menu">
<li><a href="javascript:" data-action="addField">{{translate 'Field' scope='DynamicLogic'}}</a></li>
<li><a href="javascript:" data-action="addAnd">{{translate 'and' category='logicalOperators' scope='Admin'}}</a></li>
<li><a href="javascript:" data-action="addOr">{{translate 'or' category='logicalOperators' scope='Admin'}}</a></li>
<li><a href="javascript:" data-action="addNot">{{translate 'not' category='logicalOperators' scope='Admin'}}</a></li>
<li><a href="javascript:" data-action="addAnd">(... {{translate 'and' category='logicalOperators' scope='Admin'}} ...)</a></li>
<li><a href="javascript:" data-action="addOr">(... {{translate 'or' category='logicalOperators' scope='Admin'}} ...)</a></li>
<li><a href="javascript:" data-action="addNot">{{translate 'not' category='logicalOperators' scope='Admin'}} (...)</a></li>
</ul>
</div>
</div>
<div class="item-list">
<div data-view-key="{{viewKey}}">{{#if hasItem}}{{{var viewKey this}}}{{/if}}</div>
</div>
<div>)</div>
@@ -0,0 +1 @@
{{#if value}}<span class="glyphicon glyphicon-paperclip small"></span>{{/if}}
@@ -36,7 +36,8 @@ Espo.define('views/admin/dynamic-logic/conditions/group-base', 'view', function
return {
viewDataList: this.viewDataList,
operator: this.operator,
level: this.level
level: this.level,
groupOperator: this.getGroupOperator()
};
},
@@ -45,16 +46,16 @@ Espo.define('views/admin/dynamic-logic/conditions/group-base', 'view', function
e.stopPropagation();
this.trigger('remove-item');
},
'click > div.group-head [data-action="addField"]': function (e) {
'click > div.group-bottom [data-action="addField"]': function (e) {
this.actionAddField();
},
'click > div.group-head [data-action="addAnd"]': function (e) {
'click > div.group-bottom [data-action="addAnd"]': function (e) {
this.actionAddGroup('and');
},
'click > div.group-head [data-action="addOr"]': function (e) {
'click > div.group-bottom [data-action="addOr"]': function (e) {
this.actionAddGroup('or');
},
'click > div.group-head [data-action="addNot"]': function (e) {
'click > div.group-bottom [data-action="addNot"]': function (e) {
this.actionAddGroup('not');
}
},
@@ -79,6 +80,12 @@ Espo.define('views/admin/dynamic-logic/conditions/group-base', 'view', function
}, this);
},
getGroupOperator: function () {
if (this.operator === 'or') return 'or';
return 'and';
},
getKey: function (i) {
return 'view-' + this.level.toString() + '-' + this.number.toString() + '-' + i.toString();
},
@@ -126,6 +133,8 @@ Espo.define('views/admin/dynamic-logic/conditions/group-base', 'view', function
view.render()
}
this.controlAddItemVisibility();
this.listenToOnce(view, 'remove-item', function () {
this.removeItem(number);
}, this);
@@ -151,6 +160,7 @@ Espo.define('views/admin/dynamic-logic/conditions/group-base', 'view', function
this.clearView(key);
this.$el.find('[data-view-key="'+key+'"]').remove();
this.$el.find('[data-view-ref-key="'+key+'"]').remove();
var index = -1;
this.viewDataList.forEach(function (data, i) {
@@ -161,6 +171,8 @@ Espo.define('views/admin/dynamic-logic/conditions/group-base', 'view', function
if (~index) {
this.viewDataList.splice(index, 1);
}
this.controlAddItemVisibility();
},
actionAddField: function () {
@@ -216,6 +228,10 @@ Espo.define('views/admin/dynamic-logic/conditions/group-base', 'view', function
addItemContainer: function (i) {
var $item = $('<div data-view-key="'+this.getKey(i)+'"></div>');
this.$el.find('> .item-list').append($item);
var groupOperatorLabel = this.translate(this.getGroupOperator(), 'logicalOperators', 'Admin');
var $operatorItem = $('<div class="group-operator" data-view-ref-key="'+this.getKey(i)+'">' + groupOperatorLabel +'</div>');
this.$el.find('> .item-list').append($operatorItem);
},
actionAddGroup: function (operator) {
@@ -231,6 +247,12 @@ Espo.define('views/admin/dynamic-logic/conditions/group-base', 'view', function
});
},
afterRender: function () {
this.controlAddItemVisibility();
},
controlAddItemVisibility: function () {}
});
});
@@ -38,7 +38,9 @@ Espo.define('views/admin/dynamic-logic/conditions/not', 'views/admin/dynamic-log
return {
viewKey: this.viewKey,
operator: this.operator,
hasItem: this.hasView(this.viewKey)
hasItem: this.hasView(this.viewKey),
level: this.level,
groupOperator: this.getGroupOperator()
};
},
@@ -60,6 +62,8 @@ Espo.define('views/admin/dynamic-logic/conditions/not', 'views/admin/dynamic-log
removeItem: function () {
var key = this.getKey();
this.clearView(key);
this.controlAddItemVisibility();
},
getKey: function () {
@@ -78,7 +82,15 @@ Espo.define('views/admin/dynamic-logic/conditions/not', 'views/admin/dynamic-log
},
fetch: function () {
var value = this.getView(this.viewKey).fetch();
var view = this.getView(this.viewKey);
if (!view) return {
type: 'and',
value: []
};
var value = view.fetch();
console.log(value);
return {
type: this.operator,
@@ -86,6 +98,14 @@ Espo.define('views/admin/dynamic-logic/conditions/not', 'views/admin/dynamic-log
};
},
controlAddItemVisibility: function () {
if (this.getView(this.getKey())) {
this.$el.find(' > .group-bottom').addClass('hidden');
} else {
this.$el.find(' > .group-bottom').removeClass('hidden');
}
}
});
});
@@ -269,7 +269,7 @@ Espo.define('views/admin/entity-manager/modals/edit-entity', ['views/modal', 'mo
});
var optionList = Object.keys(fieldDefs).filter(function (item) {
if (!~['varchar', 'text', 'phone', 'email', 'personName', 'number'].indexOf(this.getMetadata().get(['entityDefs', scope, 'fields', item, 'type']))) {
if (!~['varchar', 'wysiwyg', 'text', 'phone', 'email', 'personName', 'number'].indexOf(this.getMetadata().get(['entityDefs', scope, 'fields', item, 'type']))) {
return false;
}
if (this.getMetadata().get(['entityDefs', scope, 'fields', item, 'disabled'])) {
+7 -1
View File
@@ -108,11 +108,17 @@ Espo.define('views/admin/layouts/base', 'view', function (Dep) {
fetch: function () {},
setup: function () {
this.dataAttributeList = _.clone(this.dataAttributeList);
this.buttonList = _.clone(this.buttonList);
this.events = _.clone(this.events);
this.scope = this.options.scope;
this.type = this.options.type;
this.dataAttributeList =
this.getMetadata().get(['clientDefs', this.scope, 'additionalLayouts', this.type, 'dataAttributeList'])
||
this.dataAttributeList;
this.dataAttributeList = Espo.Utils.clone(this.dataAttributeList);
},
unescape: function (string) {
+5 -1
View File
@@ -30,7 +30,7 @@ Espo.define('views/admin/layouts/list', 'views/admin/layouts/rows', function (De
return Dep.extend({
dataAttributeList: ['name', 'width', 'link', 'notSortable', 'align', 'view', 'customLabel'],
dataAttributeList: ['name', 'width', 'link', 'notSortable', 'align', 'view', 'customLabel', 'widthPx'],
dataAttributesDefs: {
link: {type: 'bool'},
@@ -48,6 +48,10 @@ Espo.define('views/admin/layouts/list', 'views/admin/layouts/rows', function (De
type: 'varchar',
readOnly: true
},
widthPx: {
type: 'int',
readOnly: true
},
name: {
type: 'varchar',
readOnly: true
+8
View File
@@ -92,6 +92,14 @@ Espo.define('views/detail', 'views/main', function (Dep) {
el: '#main > .header',
scope: this.scope
});
this.listenTo(this.model, 'sync', function (model) {
if (model.hasChanged('name')) {
if (this.getView('header')) {
this.getView('header').reRender();
}
}
}, this);
},
setupRecord: function () {
@@ -0,0 +1,39 @@
/************************************************************************
* 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.
************************************************************************/
Espo.define('views/email/fields/has-attachment', 'views/fields/base', function (Dep) {
return Dep.extend({
listTemplate: 'email/fields/has-attachment/detail',
detailTemplate: 'email/fields/has-attachment/detail',
});
});
+6 -1
View File
@@ -342,7 +342,12 @@ Espo.define('views/fields/email', 'views/fields/varchar', function (Dep) {
}
if (this.getPreferences().get('emailUseExternalClient')) {
document.location.href = 'mailto:' + emailAddress;
require('email-helper', function (EmailHelper) {
var emailHelper = new EmailHelper();
var link = emailHelper.composeMailToLink(attributes, this.getConfig().get('outboundEmailBccAddress'));
document.location.href = link;
}.bind(this));
return;
}
+4 -4
View File
@@ -106,14 +106,14 @@ Espo.define('views/import/step2', 'view', function (Dep) {
afterRender: function () {
$container = $('#mapping-container');
$table = $('<table>').addClass('table').addClass('table-bordered');
$table = $('<table>').addClass('table').addClass('table-bordered').css('table-layout', 'fixed');
$row = $('<tr>');
if (this.formData.headerRow) {
$cell = $('<th>').attr('width', '27%').html(this.translate('Header Row Value', 'labels', 'Import'));
$cell = $('<th>').attr('width', '25%').html(this.translate('Header Row Value', 'labels', 'Import'));
$row.append($cell);
}
$cell = $('<th>').attr('width', '33%').html(this.translate('Field', 'labels', 'Import'));
$cell = $('<th>').attr('width', '25%').html(this.translate('Field', 'labels', 'Import'));
$row.append($cell);
$cell = $('<th>').html(this.translate('First Row Value', 'labels', 'Import'));
$row.append($cell);
@@ -139,7 +139,7 @@ Espo.define('views/import/step2', 'view', function (Dep) {
value = value.substr(0, 200) + '...';
}
$cell = $('<td>').html(value);
$cell = $('<td>').css('overflow', 'hidden').html(value);
$row.append($cell);
if (~['update', 'createAndUpdate'].indexOf(this.formData.action)) {
+1 -1
View File
@@ -134,7 +134,7 @@ Espo.define('views/record/list-expanded', 'views/record/list', function (Dep) {
},
getItemEl: function (model, item) {
return this.options.el + ' li[data-id="' + model.id + '"] .cell[data-name="' + item.name + '"]';
return this.options.el + ' li[data-id="' + model.id + '"] .cell[data-name="' + item.field + '"]';
},
getRowContainerHtml: function (id) {
+12
View File
@@ -1840,6 +1840,9 @@ h5 {
}
.dynamic-logic-expression-container {
div.item-list[data-level="0"] {
margin-left: 0;
}
div.item-list {
margin-left: 30px;
}
@@ -1854,6 +1857,15 @@ h5 {
line-height: 33px;
}
}
.group-operator:last-child {
display: none;
}
.group-bottom[data-level="0"] {
margin-left: 0;
}
.group-bottom {
margin-left: 30px;
}
}
.dynamic-logic-options {
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "espocrm",
"version": "5.2.4",
"version": "5.2.5",
"description": "",
"main": "index.php",
"repository": {