Compare commits

..

31 Commits
4.3.0 ... 4.3.1

Author SHA1 Message Date
yuri
3df9c7eb02 fix stream panel 2016-12-23 16:55:22 +02:00
yuri
5951ac930a fix stream post 2016-12-23 16:31:53 +02:00
yuri
7160c0a65b fix stream panel 2016-12-23 16:30:19 +02:00
yuri
19a9b5c91e fix language.js 2016-12-23 15:56:58 +02:00
yuri
b6d1f26af0 fix cache 2016-12-23 15:52:19 +02:00
yuri
0c10657351 fix import 2016-12-22 17:23:39 +02:00
yuri
469e35d8a0 industries 2016-12-22 11:18:32 +02:00
yuri
977b8fa8e9 fix enum none 2016-12-21 17:40:17 +02:00
yuri
7e0604409a varchar/text none 2016-12-21 17:37:10 +02:00
yuri
c21ec8d5f2 template fix 2016-12-21 16:06:16 +02:00
yuri
41c566e227 template ui improve 2016-12-21 15:56:15 +02:00
yuri
ae82ebbf74 htmlizer file helper 2016-12-21 15:29:12 +02:00
yuri
177018daae entity manager: check reserved names 2016-12-21 15:05:17 +02:00
yuri
5f481fa2dd fix 2016-12-21 12:20:29 +02:00
yuri
8c1142481d campaign log fix 2016-12-21 12:08:19 +02:00
yuri
d20a26680a fix markdown 2016-12-21 12:00:29 +02:00
yuri
84c56256f7 css 2016-12-20 17:52:30 +02:00
yuri
632467a9a6 close modal on location change 2016-12-20 16:11:20 +02:00
yuri
2989181723 fix calendar create acl check 2016-12-19 11:45:01 +02:00
yuri
5df8d833d5 fix lang 2016-12-19 11:26:41 +02:00
yuri
449fc4cddc fix css 2016-12-16 17:24:21 +02:00
yuri
4e61bd6048 fix user field list view tpl 2016-12-16 15:00:41 +02:00
yuri
252ae4e18f v 2016-12-16 14:53:06 +02:00
yuri
3def2ef0da enum: show none 2016-12-16 12:14:16 +02:00
yuri
cb0321aa20 lead layout change 2016-12-16 11:56:28 +02:00
yuri
1096f719ae panels fixes 2016-12-16 11:53:55 +02:00
yuri
ef317064b4 fix modified and created fields 2016-12-15 16:42:53 +02:00
yuri
fa3b4f3ac2 export fix 2016-12-15 10:53:55 +02:00
yuri
052c489bf7 fix export error 2016-12-15 10:45:36 +02:00
yuri
f3402e2c4e dont send invitation for self if accepted 2016-12-14 12:23:15 +02:00
yuri
34a3cd9ce5 fix list js error 2016-12-12 11:17:09 +02:00
60 changed files with 664 additions and 167 deletions

View File

@@ -159,7 +159,15 @@ class Htmlizer
public function render(Entity $entity, $template, $id = null, $additionalData = array(), $skipLinks = false)
{
$code = \LightnCandy::compile($template);
$code = \LightnCandy::compile($template, [
'helpers' => [
'file' => function ($context, $options) {
if (count($context) && $context[0]) {
return 'data/upload/'.$context[0];
}
}
]
]);
$toRemove = false;
if ($id === null) {

View File

@@ -273,18 +273,6 @@ class RDB extends \Espo\ORM\Repositories\RDB implements Injectable
$entity->set('createdById', $this->getEntityManager()->getUser()->id);
}
}
if ($entity->has('modifiedById')) {
$restoreData['modifiedById'] = $entity->get('modifiedById');
}
if ($entity->has('modifiedByName')) {
$restoreData['modifiedByName'] = $entity->get('modifiedByName');
}
if ($entity->has('modifiedAt')) {
$restoreData['modifiedAt'] = $entity->get('modifiedAt');
}
$entity->clear('modifiedById');
$entity->clear('modifiedByName');
} else {
if (empty($options['silent'])) {
if ($entity->hasAttribute('modifiedAt')) {
@@ -295,20 +283,6 @@ class RDB extends \Espo\ORM\Repositories\RDB implements Injectable
$entity->set('modifiedByName', $this->getEntityManager()->getUser()->get('name'));
}
}
if ($entity->has('createdById')) {
if (empty($options['import'])) {
$restoreData['createdById'] = $entity->get('createdById');
$entity->clear('createdById');
}
}
if ($entity->has('createdAt')) {
if (empty($options['import'])) {
$restoreData['createdAt'] = $entity->get('createdAt');
$entity->clear('createdAt');
}
}
}
$this->restoreData = $restoreData;

View File

@@ -49,6 +49,8 @@ class EntityManager
private $container;
private $reservedWordList = ['__halt_compiler', 'abstract', 'and', 'array', 'as', 'break', 'callable', 'case', 'catch', 'class', 'clone', 'const', 'continue', 'declare', 'default', 'die', 'do', 'echo', 'else', 'elseif', 'empty', 'enddeclare', 'endfor', 'endforeach', 'endif', 'endswitch', 'endwhile', 'eval', 'exit', 'extends', 'final', 'for', 'foreach', 'function', 'global', 'goto', 'if', 'implements', 'include', 'include_once', 'instanceof', 'insteadof', 'interface', 'isset', 'list', 'namespace', 'new', 'or', 'print', 'private', 'protected', 'public', 'require', 'require_once', 'return', 'static', 'switch', 'throw', 'trait', 'try', 'unset', 'use', 'var', 'while', 'xor'];
public function __construct(Metadata $metadata, Language $language, File\Manager $fileManager, Config $config, Container $container = null)
{
$this->metadata = $metadata;
@@ -86,16 +88,33 @@ class EntityManager
return $this->metadataHelper;
}
protected function getServiceFactory()
{
if (!$this->container) return;
return $this->container->get('serviceFactory');
}
public function create($name, $type, $params = array())
{
$name = ucfirst($name);
$name = trim($name);
if ($this->getMetadata()->get('scopes.' . $name)) {
throw new Conflict('Entity ['.$name.'] already exists.');
throw new Conflict('Entity \''.$name.'\' already exists.');
}
if (empty($name) || empty($type)) {
throw new Error();
}
$name = trim($name);
$serviceFactory = $this->getServiceFactory();
if ($serviceFactory && $serviceFactory->checKExists($name)) {
throw new Conflict('Entity name \''.$name.'\' is not allowed.');
}
if (in_array(strtolower($name), $this->reservedWordList)) {
throw new Conflict('Entity name \''.$name.'\' is not allowed.');
}
$normalizedName = Util::normilizeClassName($name);

View File

@@ -68,9 +68,11 @@
"Legal": "Legal",
"Manufacturing": "Manufacturing",
"Mass Media": "Mass Media",
"Mining": "Mining",
"Music": "Music",
"Marketing": "Marketing",
"Publishing": "Publishing",
"Petroleum": "Petroleum",
"Real Estate": "Real Estate",
"Retail": "Retail",
"Shipping": "Shipping",

View File

@@ -45,5 +45,8 @@
"planned": "Planned",
"held": "Held",
"todays": "Today's"
},
"messages": {
"nothingHasBeenSent": "Nothing were sent"
}
}

View File

@@ -0,0 +1,11 @@
{
"activities": {
"disabled": true
},
"history": {
"disabled": true
},
"tasks": {
"disabled": true
}
}

View File

@@ -0,0 +1,11 @@
{
"activities": {
"disabled": true
},
"history": {
"disabled": true
},
"tasks": {
"disabled": true
}
}

View File

@@ -0,0 +1,11 @@
{
"activities": {
"disabled": true
},
"history": {
"disabled": true
},
"tasks": {
"disabled": true
}
}

View File

@@ -13,7 +13,7 @@
"rows": [
[{"name":"status"},{"name":"source"}],
[{"name":"opportunityAmount"},{"name":"campaign"}],
[false, {"name":"industry"}],
[{"name":"industry"}, false],
[{"name":"description", "fullWidth": true}]
]
}

View File

@@ -0,0 +1,11 @@
{
"activities": {
"disabled": true
},
"history": {
"disabled": true
},
"tasks": {
"disabled": true
}
}

View File

@@ -0,0 +1,11 @@
{
"activities": {
"disabled": true
},
"history": {
"disabled": true
},
"tasks": {
"disabled": true
}
}

View File

@@ -24,6 +24,26 @@
"view":"crm:views/record/panels/tasks",
"aclScope": "Task"
}
],
"detailSmall": [
{
"name":"activities",
"label":"Activities",
"view":"crm:views/record/panels/activities",
"aclScope": "Activities"
},
{
"name":"history",
"label":"History",
"view":"crm:views/record/panels/history",
"aclScope": "Activities"
},
{
"name":"tasks",
"label":"Tasks",
"view":"crm:views/record/panels/tasks",
"aclScope": "Task"
}
]
},
"relationshipPanels": {

View File

@@ -3,10 +3,6 @@
"recordViews": {
"detail": "crm:views/case/record/detail"
},
"bottomPanels":{
"detail":[
]
},
"sidePanels":{
"detail":[
{
@@ -27,6 +23,26 @@
"view":"crm:views/record/panels/tasks",
"aclScope": "Task"
}
],
"detailSmall":[
{
"name":"activities",
"label":"Activities",
"view":"crm:views/case/record/panels/activities",
"aclScope": "Activities"
},
{
"name":"history",
"label":"History",
"view":"crm:views/record/panels/history",
"aclScope": "Activities"
},
{
"name":"tasks",
"label":"Tasks",
"view":"crm:views/record/panels/tasks",
"aclScope": "Task"
}
]
},
"filterList": [

View File

@@ -52,6 +52,26 @@
"view":"crm:views/record/panels/tasks",
"aclScope": "Task"
}
],
"detailSmall":[
{
"name":"activities",
"label":"Activities",
"view":"crm:views/record/panels/activities",
"aclScope": "Activities"
},
{
"name":"history",
"label":"History",
"view":"crm:views/record/panels/history",
"aclScope": "Activities"
},
{
"name":"tasks",
"label":"Tasks",
"view":"crm:views/record/panels/tasks",
"aclScope": "Task"
}
]
},
"relationshipPanels": {

View File

@@ -71,6 +71,24 @@
"notRefreshable": true,
"hidden": true,
"style": "success"
},
{
"name":"activities",
"label":"Activities",
"view":"crm:views/record/panels/activities",
"aclScope": "Activities"
},
{
"name":"history",
"label":"History",
"view":"crm:views/record/panels/history",
"aclScope": "Activities"
},
{
"name":"tasks",
"label":"Tasks",
"view":"crm:views/record/panels/tasks",
"aclScope": "Task"
}
],
"editSmall": [

View File

@@ -1,7 +1,7 @@
{
"controller": "controllers/record",
"views":{
"detail":"Crm:Opportunity.Detail"
"detail":"crm:views/opportunity/detail"
},
"sidePanels":{
"detail":[
@@ -23,6 +23,26 @@
"view":"crm:views/record/panels/tasks",
"aclScope": "Task"
}
],
"detailSmall":[
{
"name":"activities",
"label":"Activities",
"view":"crm:views/record/panels/activities",
"aclScope": "Activities"
},
{
"name":"history",
"label":"History",
"view":"crm:views/record/panels/history",
"aclScope": "Activities"
},
{
"name":"tasks",
"label":"Tasks",
"view":"crm:views/record/panels/tasks",
"aclScope": "Task"
}
]
},
"filterList": [

View File

@@ -55,8 +55,10 @@
"Manufacturing",
"Mass Media",
"Marketing",
"Mining",
"Music",
"Publishing",
"Petroleum",
"Real Estate",
"Retail",
"Service",

View File

@@ -74,7 +74,7 @@
},
"object": {
"type": "belongsToParent",
"entityList": ["Email"]
"entityList": ["Email", "CampaignTrackingUrl"]
}
},
"collection": {

View File

@@ -110,7 +110,8 @@
},
"assignedUser": {
"type": "belongsTo",
"entity": "User"
"entity": "User",
"foreign": "tasks"
},
"teams": {
"type": "hasMany",

View File

@@ -138,11 +138,19 @@ class Meeting extends \Espo\Services\Record
$emailHash = array();
$sentCount = 0;
$users = $entity->get('users');
foreach ($users as $user) {
if ($user->id === $this->getUser()->id) {
if ($entity->getLinkMultipleColumn('users', 'status', $user->id) === 'Accepted') {
continue;
}
}
if ($user->get('emailAddress') && !array_key_exists($user->get('emailAddress'), $emailHash)) {
$invitationManager->sendInvitation($entity, $user, 'users');
$emailHash[$user->get('emailAddress')] = true;
$sentCount ++;
}
}
@@ -151,6 +159,7 @@ class Meeting extends \Espo\Services\Record
if ($contact->get('emailAddress') && !array_key_exists($contact->get('emailAddress'), $emailHash)) {
$invitationManager->sendInvitation($entity, $contact, 'contacts');
$emailHash[$user->get('emailAddress')] = true;
$sentCount ++;
}
}
@@ -159,9 +168,12 @@ class Meeting extends \Espo\Services\Record
if ($lead->get('emailAddress') && !array_key_exists($lead->get('emailAddress'), $emailHash)) {
$invitationManager->sendInvitation($entity, $lead, 'leads');
$emailHash[$user->get('emailAddress')] = true;
$sentCount ++;
}
}
if (!$sentCount) return false;
return true;
}

View File

@@ -35,7 +35,8 @@
"portalRoles": "Portal Roles",
"contact": "Contact",
"accounts": "Accounts",
"account": "Account (Primary)"
"account": "Account (Primary)",
"tasks": "Tasks"
},
"labels": {
"Create User": "Create User",
@@ -67,7 +68,7 @@
"accountInfoEmailSubject": "EspoCRM User Access Info",
"accountInfoEmailBody": "Your access information:\n\nUsername: {userName}\nPassword: {password}\n\n{siteUrl}",
"passwordChangeLinkEmailSubject": "Change Password Request",
"passwordChangeLinkEmailBody": "You can change your password by this link {link}. This unique url will be exprired soon.",
"passwordChangeLinkEmailBody": "You can change your password by this link {link}. This unique url will be expired soon.",
"passwordChanged": "Password has been changed",
"userCantBeEmpty": "Username can not be empty",
"wrongUsernamePasword": "Wrong username/password",

View File

@@ -0,0 +1,11 @@
{
"activities": {
"disabled": true
},
"history": {
"disabled": true
},
"tasks": {
"disabled": true
}
}

View File

@@ -1,7 +1,8 @@
{
"controller": "controllers/record",
"recordViews": {
"detail": "Template.Record.Detail"
"detail": "views/template/record/detail",
"edit": "views/template/record/edit"
},
"formDependency": {
"printFooter": {

View File

@@ -10,6 +10,62 @@
"editQuick":"views/user/record/edit-quick",
"list":"views/user/record/list"
},
"defaultSidePanelFieldList": {
"detail": [
"avatar"
],
"detailSmall": [
"avatar"
],
"edit": [
"avatar"
],
"editSmall": [
"avatar"
]
},
"sidePanels": {
"detail": [
{
"name":"activities",
"label":"Activities",
"view":"crm:views/record/panels/activities",
"aclScope": "Activities"
},
{
"name":"history",
"label":"History",
"view":"crm:views/record/panels/history",
"aclScope": "Activities"
},
{
"name":"tasks",
"label":"Tasks",
"view":"crm:views/record/panels/tasks",
"aclScope": "Task"
}
],
"detailSmall": [
{
"name":"activities",
"label":"Activities",
"view":"crm:views/record/panels/activities",
"aclScope": "Activities"
},
{
"name":"history",
"label":"History",
"view":"crm:views/record/panels/history",
"aclScope": "Activities"
},
{
"name":"tasks",
"label":"Tasks",
"view":"crm:views/record/panels/tasks",
"aclScope": "Task"
}
]
},
"filterList": [
"active",
"activePortal"

View File

@@ -253,6 +253,11 @@
"entity": "Account",
"foreign": "portalUsers",
"relationName": "AccountPortalUser"
},
"tasks": {
"type": "hasMany",
"entity": "Task",
"foreign": "assignedUser"
}
},
"collection": {

View File

@@ -271,6 +271,7 @@ class Import extends \Espo\Services\Record
public function runIdleImport($data)
{
$entityType = $data['entityType'];
$params = json_decode(json_encode($data['params']), true);
$importFieldList = $data['importFieldList'];
@@ -452,7 +453,11 @@ class Import extends \Espo\Services\Record
if (!empty($params['defaultValues'])) {
$v = get_object_vars($params['defaultValues']);
if (is_object($params['defaultValues'])) {
$v = get_object_vars($params['defaultValues']);
} else {
$v = $params['defaultValues'];
}
$entity->set($v);
}

View File

@@ -530,6 +530,13 @@ class Record extends \Espo\Core\Services\Base
$this->filterInput($data);
$this->handleInput($data);
unset($data['modifiedById']);
unset($data['modifiedByName']);
unset($data['modifiedAt']);
unset($data['createdById']);
unset($data['createdByName']);
unset($data['createdAt']);
$entity->set($data);
$this->beforeCreate($entity, $data);
@@ -565,6 +572,13 @@ class Record extends \Espo\Core\Services\Base
$this->filterInput($data);
$this->handleInput($data);
unset($data['modifiedById']);
unset($data['modifiedByName']);
unset($data['modifiedAt']);
unset($data['createdById']);
unset($data['createdByName']);
unset($data['createdAt']);
if ($this->getEntityBeforeUpdate) {
$entity = $this->getEntity($id);
} else {
@@ -1168,7 +1182,8 @@ class Record extends \Espo\Core\Services\Base
if (in_array($attribute, $attributeListToSkip)) {
continue;
}
if (empty($entity->getAttributeParam($attribute, 'notStorable'))) {
$isNotStorable = $entity->getAttributeParam($attribute, 'notStorable');
if (!$isNotStorable) {
$attributeList[] = $attribute;
} else {
if (in_array($entity->getAttributeParam($attribute, 'type'), ['email', 'phone'])) {

View File

@@ -100,7 +100,7 @@ Espo.define('crm:views/calendar/modals/edit', 'views/modals/edit', function (Dep
if (!this.options.id && !this.options.scope) {
var scopeList = [];
this.scopeList.forEach(function (scope) {
if (this.getAcl().check(scope, 'edit')) {
if (this.getAcl().check(scope, 'create')) {
if (~this.enabledScopeList.indexOf(scope)) {
scopeList.push(scope);
}

View File

@@ -26,26 +26,28 @@
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
Espo.define('crm:views/call/detail', 'views/detail', function (Dep) {
Espo.define('crm:views/call/detail', ['views/detail', 'crm:views/meeting/detail'], function (Dep, MeetingDetail) {
return Dep.extend({
setup: function () {
Dep.prototype.setup.call(this);
if (['Held', 'Not Held'].indexOf(this.model.get('status')) == -1) {
if (this.getAcl().checkModel(this.model, 'edit') && this.getAcl().checkScope('Email', 'create')) {
this.menu.buttons.push({
'label': 'Send Invitations',
'action': 'sendInvitations',
'acl': 'edit',
});
MeetingDetail.prototype.controlSendInvitationsButton.call(this);
this.listenTo(this.model, 'change', function () {
if (
this.model.hasChanged('status')
||
this.model.hasChanged('teamsIds')
) {
MeetingDetail.prototype.controlSendInvitationsButton.call(this);
}
}
}.bind(this));
},
actionSendInvitations: function () {
if (confirm(this.translate('confirmation', 'messages'))) {
this.$el.find('[data-action="sendInvitations"]').addClass('disabled');
this.disableMenuItem('sendInvitations');
this.notify('Sending...');
$.ajax({
url: 'Call/action/sendInvitations',
@@ -53,12 +55,16 @@
data: JSON.stringify({
id: this.model.id
}),
success: function () {
this.notify('Sent', 'success');
this.$el.find('[data-action="sendInvitations"]').removeClass('disabled');
success: function (result) {
if (result) {
this.notify('Sent', 'success');
} else {
Espo.Ui.warning(this.translate('nothingHasBeenSent', 'messages', 'Meeting'));
}
this.enableMenuItem('sendInvitations');
}.bind(this),
error: function () {
this.$el.find('[data-action="sendInvitations"]').removeClass('disabled');
this.enableMenuItem('sendInvitations');
}.bind(this),
});
}

View File

@@ -32,20 +32,55 @@ Espo.define('crm:views/meeting/detail', 'views/detail', function (Dep) {
setup: function () {
Dep.prototype.setup.call(this);
if (['Held', 'Not Held'].indexOf(this.model.get('status')) == -1) {
if (this.getAcl().checkModel(this.model, 'edit') && this.getAcl().checkScope('Email', 'create')) {
this.menu.buttons.push({
'label': 'Send Invitations',
'action': 'sendInvitations',
'acl': 'edit',
});
this.controlSendInvitationsButton();
this.listenTo(this.model, 'change', function () {
if (
this.model.hasChanged('status')
||
this.model.hasChanged('teamsIds')
) {
this.controlSendInvitationsButton();
}
}.bind(this));
},
controlSendInvitationsButton: function () {
var show = true;;
if (
~['Held', 'Not Held'].indexOf(this.model.get('status'))
) {
show = false;
}
if (show && (!this.getAcl().checkModel(this.model, 'edit') || !this.getAcl().checkScope('Email', 'create'))) {
show = false;
}
if (show) {
var userIdList = this.model.getLinkMultipleIdList('users');
var contactIdList = this.model.getLinkMultipleIdList('contacts');
var leadIdList = this.model.getLinkMultipleIdList('leads');
if (!contactIdList.length && !leadIdList.length && !userIdList.length) {
show = false;
}
}
if (show) {
this.addMenuItem('buttons', {
label: 'Send Invitations',
action: 'sendInvitations',
acl: 'edit',
});
} else {
this.removeMenuItem('sendInvitations');
}
},
actionSendInvitations: function () {
if (confirm(this.translate('confirmation', 'messages'))) {
this.$el.find('button[data-action="sendInvitations"]').addClass('disabled');
this.disableMenuItem('sendInvitations');
this.notify('Sending...');
$.ajax({
url: 'Meeting/action/sendInvitations',
@@ -53,12 +88,17 @@ Espo.define('crm:views/meeting/detail', 'views/detail', function (Dep) {
data: JSON.stringify({
id: this.model.id
}),
success: function () {
this.notify('Sent', 'success');
this.$el.find('[data-action="sendInvitations"]').removeClass('disabled');
success: function (result) {
if (result) {
this.notify('Sent', 'success');
} else {
Espo.Ui.warning(this.translate('nothingHasBeenSent', 'messages', 'Meeting'));
}
this.enableMenuItem('sendInvitations');
}.bind(this),
error: function () {
this.$el.find('[data-action="sendInvitations"]').removeClass('disabled');
this.enableMenuItem('sendInvitations');
}.bind(this),
});
}

View File

@@ -1 +1,5 @@
<span class="text-{{style}}">{{translateOption value scope=scope field=name}}</span>
{{#if isNotEmpty}}
<span class="text-{{style}}">{{translateOption value scope=scope field=name}}</span>
{{else}}
{{translate 'None'}}
{{/if}}

View File

@@ -0,0 +1,3 @@
{{#if isNotEmpty}}
<span class="text-{{style}}">{{translateOption value scope=scope field=name}}</span>
{{/if}}

View File

@@ -1 +1,5 @@
{{translateOption value scope=scope field=name translatedOptions=translatedOptions}}
{{#if isNotEmpty}}
{{translateOption value scope=scope field=name translatedOptions=translatedOptions}}
{{else}}
{{translate 'None'}}
{{/if}}

View File

@@ -0,0 +1,3 @@
{{#if isNotEmpty}}
{{translateOption value scope=scope field=name translatedOptions=translatedOptions}}
{{/if}}

View File

@@ -1,3 +1 @@
<span class="complext-text">
{{complexText value}}
</span>
{{#if isNotEmpty}}<span class="complext-text">{{complexText value}}</span>{{else}}{{translate 'None'}}{{/if}}

View File

@@ -0,0 +1,4 @@
{{#if idValue}}
{{{avatar}}}
<a href="#{{foreignScope}}/view/{{idValue}}">{{nameValue}}</a>
{{/if}}

View File

@@ -0,0 +1 @@
{{#if isNotEmpty}}{{value}}{{else}}{{translate 'None'}}{{/if}}

View File

@@ -67,7 +67,7 @@ Espo.define('cache', [], function () {
_checkType: function (type) {
if (typeof type === 'undefined' && toString.call(type) != '[object String]') {
throw new TypeError("Bad type \"" + type + "\" passed to Bull.Cacher().");
throw new TypeError("Bad type \"" + type + "\" passed to Cache().");
}
},
@@ -77,25 +77,26 @@ Espo.define('cache', [], function () {
var key = this._composeKey(type, name);
var stored = localStorage.getItem(key);
if (stored) {
var str = stored;
if (stored[0] == "{" || stored[0] == "[") {
try {
str = JSON.parse(stored);
var result = stored;
if (stored.length > 9 && stored.substr(0, 9) === '__JSON__:') {
var jsonString = stored.substr(9);
try {
result = JSON.parse(jsonString);
} catch (error) {
str = stored;
result = stored;
}
stored = str;
}
return stored;
return result;
}
return null;
},
set: function (type, name, value) {
this._checkType(type);
var key = this._composeKey(type, name);
if (value instanceof Object) {
value = JSON.stringify(value);
var key = this._composeKey(type, name);
if (value instanceof Object || Array.isArray(value)) {
value = '__JSON__:' + JSON.stringify(value);
}
localStorage.setItem(key, value);
},

View File

@@ -117,10 +117,10 @@ Espo.define('language', [], function () {
}
}
this.fetch();
this.fetch(false, disableCache);
},
fetch: function (sync) {
fetch: function (sync, disableCache) {
var self = this;
$.ajax({
url: this.url,
@@ -129,7 +129,9 @@ Espo.define('language', [], function () {
async: !(sync || false),
success: function (data) {
self.data = data;
self.storeToCache();
if (!disableCache) {
self.storeToCache();
}
self.trigger('sync');
}
});

View File

@@ -34,17 +34,23 @@ Espo.define('view-helper', [], function () {
this.mdSearch = [
/\["?(.*?)"?\]\((.*?)\)/g,
/\&\#x60;(([\s\S]*?)\&\#x60;)/g,
/\&\#x60;\&\#x60;\&\#x60;\n?([\s\S]*?)\&\#x60;\&\#x60;\&\#x60;/g,
/\&\#x60;([\s\S]*?)\&\#x60;/g,
/(\*\*)(.*?)\1/g,
/(\*)(.*?)\1/g,
/\~\~(.*?)\~\~/g
];
this.mdReplace = [
'<a href="$2">$1</a>',
'<code>$2</code>',
function (s, string) {
return '<code>' + string.replace(/\*/g, '&#42;').replace(/\~/g, '&#126;') + '</code>';
},
function (s, string) {
return '<code>' + string.replace(/\*/g, '&#42;').replace(/\~/g, '&#126;') + '</code>';
},
'<strong>$2</strong>',
'<em>$2</em>',
'<del>$1</del>',
'<del>$1</del>'
];
}

View File

@@ -45,6 +45,16 @@ Espo.define('views/export/record/record', 'views/record/base', function (Dep) {
return !~forbiddenFieldList.indexOf(item);
}, this);
fieldList = fieldList.filter(function (item) {
var defs = this.getMetadata().get(['entityDefs', this.scope, 'fields', item]) || {};
if (defs.disabled) return;
if (defs.exportDisabled) return;
if (defs.type === 'map') return;
return true;
}, this);
this.getLanguage().sortFieldList(this.scope, fieldList);
fieldList.unshift('id');

View File

@@ -26,13 +26,13 @@
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
Espo.define('Views.Fields.EnumStyled', 'Views.Fields.Enum', function (Dep) {
Espo.define('views/fields/enum-styled', 'views/fields/enum', function (Dep) {
return Dep.extend({
listTemplate: 'fields.enum-styled.detail',
listTemplate: 'fields/enum-styled/list',
detailTemplate: 'fields.enum-styled.detail',
detailTemplate: 'fields/enum-styled/detail',
data: function () {
var value = this.model.get(this.name);
@@ -49,7 +49,7 @@ Espo.define('Views.Fields.EnumStyled', 'Views.Fields.Enum', function (Dep) {
Dep.prototype.setup.call(this);
this.styleHash = this.model.getFieldParam(this.name, 'style') || {};
},
}
});
});

View File

@@ -32,7 +32,7 @@ Espo.define('views/fields/enum', ['views/fields/base', 'lib!Selectize'], functio
type: 'enum',
listTemplate: 'fields/enum/detail',
listTemplate: 'fields/enum/list',
detailTemplate: 'fields/enum/detail',
@@ -45,6 +45,16 @@ Espo.define('views/fields/enum', ['views/fields/base', 'lib!Selectize'], functio
data: function () {
var data = Dep.prototype.data.call(this);
data.translatedOptions = this.translatedOptions;
var value = this.model.get(this.name);
if (
value !== null
&&
value !== ''
||
value === '' && (value in (this.translatedOptions || {}) && (this.translatedOptions || {})[value] !== '')
) {
data.isNotEmpty = true;
}
return data;
},

View File

@@ -72,6 +72,18 @@ Espo.define('views/fields/text', 'views/fields/base', function (Dep) {
}, this.events || {});
},
data: function () {
var data = Dep.prototype.data.call(this);
if (
this.model.get(this.name) !== null
&&
this.model.get(this.name) !== ''
) {
data.isNotEmpty = true;
}
return data;
},
handleSearchType: function (type) {
if (~['isEmpty', 'isNotEmpty'].indexOf(type)) {
this.$el.find('input.main-element').addClass('hidden');

View File

@@ -30,7 +30,7 @@ Espo.define('views/fields/user-with-avatar', 'views/fields/user', function (Dep)
return Dep.extend({
listTemplate: 'fields/user-with-avatar/detail',
listTemplate: 'fields/user-with-avatar/list',
detailTemplate: 'fields/user-with-avatar/detail',

View File

@@ -32,6 +32,8 @@ Espo.define('views/fields/varchar', 'views/fields/base', function (Dep) {
type: 'varchar',
detailTemplate: 'fields/varchar/detail',
searchTemplate: 'fields/varchar/search',
searchTypeList: ['startsWith', 'contains', 'equals', 'endsWith', 'like', 'isEmpty', 'isNotEmpty'],
@@ -45,6 +47,18 @@ Espo.define('views/fields/varchar', 'views/fields/base', function (Dep) {
}, this.events || {});
},
data: function () {
var data = Dep.prototype.data.call(this);
if (
this.model.get(this.name) !== null
&&
this.model.get(this.name) !== ''
) {
data.isNotEmpty = true;
}
return data;
},
handleSearchType: function (type) {
if (~['isEmpty', 'isNotEmpty'].indexOf(type)) {
this.$el.find('input.main-element').addClass('hidden');

View File

@@ -188,6 +188,13 @@ Espo.define('views/list', ['views/main', 'search-manager'], function (Dep, Searc
if (!this.hasParentView()) return;
view.render();
this.listenToOnce(view, 'after:render', function () {
if (!this.hasParentView()) {
this.clearView('list');
}
}, this);
view.notify(false);
if (this.searchPanel) {
this.listenTo(view, 'sort', function (obj) {

View File

@@ -142,6 +142,14 @@ Espo.define('views/main', 'view', function (Dep) {
}
},
disableMenuItem: function (action) {
this.$el.find('.header .header-buttons [data-action="'+action+'"]').addClass('disabled');
},
enableMenuItem: function (action) {
this.$el.find('.header .header-buttons [data-action="'+action+'"]').removeClass('disabled');
},
removeMenuItem: function (name, doNotReRender) {
var index = -1;
var type = false;

View File

@@ -138,6 +138,10 @@ Espo.define('views/modals/detail', 'views/modal', function (Dep) {
this.createRecordView();
}
}, this);
this.listenToOnce(this.getRouter(), 'routed', function () {
this.remove();
}, this);
},
addEditButton: function () {

View File

@@ -44,16 +44,19 @@ Espo.define('views/note/fields/post', ['views/fields/text', 'lib!Textcomplete'],
Dep.prototype.setup.call(this);
},
controlTextareaHeight: function () {
controlTextareaHeight: function (lastHeight) {
var scrollHeight = this.$element.prop('scrollHeight');
var clientHeight = this.$element.prop('clientHeight');
if (this.$element.prop('scrollHeight') > clientHeight) {
this.$element.prop('rows', this.$element.prop('rows') + 1);
this.controlTextareaHeight();
if (clientHeight === lastHeight) return;
if (scrollHeight > clientHeight) {
this.$element.attr('rows', this.$element.prop('rows') + 1);
this.controlTextareaHeight(clientHeight);
}
if (this.$element.val().length === 0) {
this.$element.prop('rows', 1);
this.$element.attr('rows', 1);
}
},

View File

@@ -67,6 +67,8 @@ Espo.define('views/record/detail-bottom', 'view', function (Dep) {
},
showPanel: function (name, callback) {
this.recordHelper.setPanelStateParam(name, 'hidden', false);
var isFound = false;
this.panelList.forEach(function (d) {
if (d.name == name) {
@@ -76,8 +78,6 @@ Espo.define('views/record/detail-bottom', 'view', function (Dep) {
}, this);
if (!isFound) return;
this.recordHelper.setPanelStateParam(name, 'hidden', false);
if (this.isRendered()) {
var view = this.getView(name);
if (view) {
@@ -97,6 +97,8 @@ Espo.define('views/record/detail-bottom', 'view', function (Dep) {
},
hidePanel: function (name, callback) {
this.recordHelper.setPanelStateParam(name, 'hidden', true);
var isFound = false;
this.panelList.forEach(function (d) {
if (d.name == name) {
@@ -106,8 +108,6 @@ Espo.define('views/record/detail-bottom', 'view', function (Dep) {
}, this);
if (!isFound) return;
this.recordHelper.setPanelStateParam(name, 'hidden', true);
if (this.isRendered()) {
var view = this.getView(name);
if (view) {
@@ -129,7 +129,7 @@ Espo.define('views/record/detail-bottom', 'view', function (Dep) {
setupPanels: function () {
var scope = this.scope;
this.panelList = Espo.Utils.clone(this.getMetadata().get('clientDefs.' + scope + '.bottomPanels.' + this.mode) || this.panelList || []);
this.panelList = Espo.Utils.clone(this.getMetadata().get('clientDefs.' + scope + '.bottomPanels.' + this.type) || this.panelList || []);
if (this.streamPanel && this.getMetadata().get('scopes.' + scope + '.stream')) {
this.setupStreamPanel();
@@ -201,6 +201,11 @@ Espo.define('views/record/detail-bottom', 'view', function (Dep) {
},
setup: function () {
this.type = this.mode;
if ('type' in this.options) {
this.type = this.options.type;
}
this.panelList = [];
this.setupPanels();

View File

@@ -140,9 +140,8 @@ Espo.define('views/record/detail-side', 'view', function (Dep) {
return item;
}, this);
this.wait(true);
this.getHelper().layoutManager.get(this.scope, 'sidePanels' + Espo.Utils.upperCaseFirst(this.mode), function (layoutData) {
this.getHelper().layoutManager.get(this.scope, 'sidePanels' + Espo.Utils.upperCaseFirst(this.type), function (layoutData) {
if (layoutData) {
this.alterPanels(layoutData);
}
@@ -267,6 +266,8 @@ Espo.define('views/record/detail-side', 'view', function (Dep) {
},
showPanel: function (name, callback) {
this.recordHelper.setPanelStateParam(name, 'hidden', false);
var isFound = false;
this.panelList.forEach(function (d) {
if (d.name == name) {
@@ -276,8 +277,6 @@ Espo.define('views/record/detail-side', 'view', function (Dep) {
}, this);
if (!isFound) return;
this.recordHelper.setPanelStateParam(name, 'hidden', false);
if (this.isRendered()) {
var view = this.getView(name);
if (view) {
@@ -297,6 +296,8 @@ Espo.define('views/record/detail-side', 'view', function (Dep) {
},
hidePanel: function (name, callback) {
this.recordHelper.setPanelStateParam(name, 'hidden', true);
var isFound = false;
this.panelList.forEach(function (d) {
if (d.name == name) {
@@ -306,8 +307,6 @@ Espo.define('views/record/detail-side', 'view', function (Dep) {
}, this);
if (!isFound) return;
this.recordHelper.setPanelStateParam(name, 'hidden', true);
if (this.isRendered()) {
var view = this.getView(name);
if (view) {

View File

@@ -78,15 +78,18 @@ Espo.define('views/stream/panel', ['views/record/panels/relationship', 'lib!Text
return data;
},
controlTextareaHeight: function () {
controlTextareaHeight: function (lastHeight) {
var scrollHeight = this.$textarea.prop('scrollHeight');
var clientHeight = this.$textarea.prop('clientHeight');
if (this.$textarea.prop('scrollHeight') > clientHeight) {
this.$textarea.prop('rows', this.$textarea.prop('rows') + 1);
this.controlTextareaHeight();
if (clientHeight === lastHeight) return;
if (scrollHeight > clientHeight) {
this.$textarea.attr('rows', this.$textarea.prop('rows') + 1);
this.controlTextareaHeight(clientHeight);
}
if (this.$textarea.val().length === 0) {
this.$textarea.prop('rows', 1);
this.$textarea.attr('rows', 1);
}
},

View File

@@ -30,7 +30,13 @@ Espo.define('views/template/record/detail', 'views/record/detail', function (Dep
return Dep.extend({
duplicateAction: true
setup: function () {
Dep.prototype.setup.call(this);
if (!this.model.isNew()) {
this.setFieldReadOnly('entityType');
}
}
});

View File

@@ -0,0 +1,103 @@
/************************************************************************
* 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/template/record/edit', 'views/record/edit', function (Dep) {
return Dep.extend({
setup: function () {
Dep.prototype.setup.call(this);
if (!this.model.isNew()) {
this.setFieldReadOnly('entityType');
}
if (this.model.isNew()) {
var storedData = {};
this.listenTo(this.model, 'change:entityType', function (model) {
var entityType = this.model.get('entityType');
if (!entityType) {
this.model.set('header', '');
this.model.set('body', '');
this.model.set('footer', '');
return;
}
if (entityType in storedData) {
this.model.set('header', storedData[entityType].header);
this.model.set('body', storedData[entityType].body);
this.model.set('footer', storedData[entityType].footer);
return;
}
var header = this.getMetadata().get(['entityDefs', 'Template', 'defaultTemplates', entityType, 'header']);
var body = this.getMetadata().get(['entityDefs', 'Template', 'defaultTemplates', entityType, 'body']);
var footer = this.getMetadata().get(['entityDefs', 'Template', 'defaultTemplates', entityType, 'footer']);
if (header) {
this.model.set('header', header);
} else {
this.model.set('header', '');
}
if (body) {
this.model.set('body', body);
} else {
this.model.set('body', '');
}
if (footer) {
this.model.set('footer', footer);
} else {
this.model.set('footer', '');
}
}, this);
this.listenTo(this.model, 'change', function (e, o) {
if (!o.ui) return;
if (!this.model.hasChanged('header') && !this.model.hasChanged('body') && !this.model.hasChanged('footer')) {
return;
}
var entityType = this.model.get('entityType');
if (!entityType) return;
storedData[entityType] = {
header: this.model.get('header'),
body: this.model.get('body'),
footer: this.model.get('footer')
};
}, this);
}
}
});
});

View File

@@ -26,21 +26,13 @@
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
Espo.define('views/user/record/detail-quick-side', 'views/record/detail-side', function (Dep) {
Espo.define('views/user/record/detail-quick-side', ['views/record/detail-side', 'views/user/record/detail-side'], function (Dep, UserDetailSide) {
return Dep.extend({
panelList: [
{
name: 'default',
label: false,
view: 'views/record/panels/side',
options: {
fieldList: ['avatar'],
mode: 'detail',
}
}
]
setupPanels: function () {
UserDetailSide.prototype.setupPanels.call(this);
}
});

View File

@@ -30,16 +30,6 @@ Espo.define('views/user/record/detail-side', 'views/record/detail-side', functio
return Dep.extend({
defaultPanelDefs: {
name: 'default',
label: false,
view: 'views/record/panels/side',
options: {
fieldList: ['avatar'],
mode: 'detail',
}
},
setupPanels: function () {
Dep.prototype.setupPanels.call(this);
@@ -55,26 +45,20 @@ Espo.define('views/user/record/detail-side', 'views/record/detail-side', functio
this.showPanel('history', function () {
this.getView('history').actionRefresh();
});
this.showPanel('tasks', function () {
this.getView('tasks').actionRefresh();
});
}
}, this);
}
}
}
this.panelList.push({
"name":"activities",
"label":"Activities",
"view":"crm:views/record/panels/activities",
"hidden": !showActivities,
"aclScope": "Activities"
});
this.panelList.push({
"name":"history",
"label":"History",
"view":"crm:views/record/panels/history",
"hidden": !showActivities,
"aclScope": "Activities"
});
if (!showActivities) {
this.hidePanel('activities');
this.hidePanel('history');
this.hidePanel('tasks');
}
}
});

View File

@@ -30,16 +30,6 @@ Espo.define('views/user/record/edit-side', 'views/record/edit-side', function (D
return Dep.extend({
defaultPanelDefs: {
name: 'default',
label: false,
view: 'views/record/panels/side',
options: {
fieldList: ['avatar'],
mode: 'edit',
}
}
});
});

View File

@@ -488,6 +488,10 @@ select[multiple].input-sm {
}
}
.panel-body .no-bottom-margin {
margin-bottom: -@panel-padding;
}
.table.less-padding > thead > tr > th,
.table.less-padding > tbody >tr > th,
.table.less-padding > tfoot >tr > th,
@@ -498,12 +502,19 @@ select[multiple].input-sm {
}
.panel > .panel-body > .list-container > .list > .list-group {
.list-group-item {
> .list-group-item {
border-width: 0;
border-bottom-width: 1px;
}
> li.list-group-item:first-child {
border-top-width: 1px;
}
> li.list-group-item:last-child {
border-bottom-width: 0;
}
}
.record {
.panel-warning {
> .panel-heading {

View File

@@ -1,6 +1,6 @@
{
"name": "espocrm",
"version": "4.3.0",
"version": "4.3.1",
"description": "",
"main": "index.php",
"repository": {