Compare commits

..

94 Commits

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
yuri 4d5f3bcbfe v 2016-12-08 11:41:55 +02:00
yuri 339e1dc897 defaultSidePanelFieldList 2016-12-07 17:32:43 +02:00
yuri c4e617f18e css 2016-12-07 15:48:35 +02:00
yuri 89bc329ea6 css 2016-12-07 13:21:47 +02:00
yuri eee49b215e portal user field show up 2016-12-07 11:32:22 +02:00
yuri d9a8b83268 remove isToSelf not used field 2016-12-07 10:56:32 +02:00
yuri def7abe3ba fix mention notification 2016-12-07 10:55:33 +02:00
yuri 75e64b3836 Merge branch 'master' of ssh://172.20.0.1/var/git/espo/backend 2016-12-06 15:52:56 +02:00
yuri efb106c597 fix translated options 2016-12-06 15:50:48 +02:00
Taras Machyshyn 5e1087c6b4 Cache in language 2016-12-06 13:18:06 +02:00
yuri 5f8800defa fix css 2016-12-06 13:08:06 +02:00
yuri 445c0b3834 integration check 2016-12-06 12:24:07 +02:00
yuri 61c1e5ab6c fix mass action list 2016-12-06 11:37:40 +02:00
yuri 2502cad0c1 settings activityEntityList no emails 2016-12-05 14:20:50 +02:00
yuri 76032493b9 fix 2016-12-01 12:33:34 +02:00
yuri 0a3a6b3773 load users config params after login 2016-12-01 12:29:26 +02:00
yuri 014d47d017 Merge branch 'master' of ssh://172.20.0.1/var/git/espo/backend 2016-12-01 11:45:00 +02:00
Taras Machyshyn 8bd7734ae3 Merge branch 'master' of ssh://172.20.0.1/var/git/espo/backend 2016-12-01 11:22:48 +02:00
Taras Machyshyn 7aa2ba917f Added 'userItems' 2016-12-01 11:21:20 +02:00
yuri 96f0ce2381 fix template 2016-11-30 17:41:30 +02:00
yuri f1eb5b9862 email notification about email received 2016-11-30 15:54:23 +02:00
yuri 45cb5a6e38 Merge branch 'master' of https://github.com/espocrm/espocrm 2016-11-30 11:13:43 +02:00
ayman-alkom 4131ea54ae remove decleration duplicate (#319) 2016-11-30 11:12:56 +02:00
yuri 3806affe1b add industry 2016-11-30 10:58:17 +02:00
yuri 6ea3a03de3 readme change 2016-11-29 17:55:59 +02:00
yuri 5b25e8b7e6 fix address validate error 2016-11-29 17:04:27 +02:00
yuri 60c96ba5f7 import status colors 2016-11-29 15:29:09 +02:00
yuri 9732852891 job search fix 2016-11-29 15:27:28 +02:00
yuri 326b46a6e6 fix setting max records per page and int max length 2016-11-29 15:18:31 +02:00
yuri 29924ea3b1 css fix 2016-11-29 12:37:47 +02:00
yuri 434506699b fix list width 2016-11-29 12:00:13 +02:00
yuri eeaaaf7af1 deferred filter 2016-11-29 11:19:32 +02:00
yuri 3f5dab0680 fix task date end field 2016-11-29 11:03:51 +02:00
yuri dd69658f2a css change 2016-11-29 10:37:23 +02:00
yuri 3c2d998a30 added industries 2016-11-28 16:54:09 +02:00
yuri a0ead9d774 fix css 2016-11-25 16:24:39 +02:00
yuri 4460a105a8 dynamic logic ID field and mass email duplicate fix 2016-11-25 15:52:05 +02:00
yuri c6b369587d fix css 2016-11-25 12:46:13 +02:00
yuri 8c19ad5f7f fix warning 2016-11-25 12:22:48 +02:00
yuri 1824e0d0a6 version 2016-11-22 14:29:35 +02:00
yuri 404bb0c3cd layout panel colors 2 2016-11-22 12:27:03 +02:00
yuri 7fd6b16cf3 fix color panels 2016-11-22 10:39:28 +02:00
yuri 47abd1ab52 middle panel styles 2016-11-21 15:32:10 +02:00
yuri 5c8d728ac5 cleanup 2016-11-21 15:09:55 +02:00
yuri 2706bc461e fix error 2016-11-21 15:09:36 +02:00
yuri 91f52e4f42 panel color and sidePanels Small layout 2016-11-21 14:58:08 +02:00
yuri 5538dd45d3 preferences: reload page after theme or language changed 2016-11-21 11:00:06 +02:00
yuri 105205a9aa change in record base 2016-11-21 10:59:50 +02:00
yuri e3547717a0 change varchar filter type order 2016-11-21 10:35:09 +02:00
yuri 1714e0e9a0 Merge branch 'master' of ssh://172.20.0.1/var/git/espo/backend 2016-11-21 10:30:50 +02:00
yuri afdd15ac44 text field: contains filter by default 2016-11-21 10:28:54 +02:00
yuri 09097dba4a cleanup 2016-11-21 10:28:23 +02:00
yuri 01a8fbd5b1 fix label 2016-11-21 10:27:21 +02:00
yuri f2d5eed7e3 company entity: check for duplicates 2016-11-21 10:26:27 +02:00
Taras Machyshyn cfae3cde1d Label correction 2016-11-18 18:18:01 +02:00
Taras Machyshyn 87a0c1b293 Installer bug fixes 2016-11-18 18:17:37 +02:00
Taras Machyshyn 486c8c8f37 Merge branch 'master' of ssh://172.20.0.1/var/git/espo/backend 2016-11-18 17:33:45 +02:00
Taras Machyshyn ea99ca3d38 FileManager: bug fixes 2016-11-18 17:33:29 +02:00
yuri 2069a25f30 b2c mode in settings 2016-11-18 11:31:25 +02:00
yuri baf3493044 show up date time on mouse over shortened dates 2016-11-18 11:23:12 +02:00
yuri 543a12a475 note post textarea height 2016-11-18 11:11:00 +02:00
yuri a73f97744a fix email address add 2016-11-18 10:58:59 +02:00
yuri ae135ab678 fix mention text 2016-11-17 17:25:08 +02:00
162 changed files with 1924 additions and 494 deletions
+1 -1
View File
@@ -15,7 +15,7 @@ For more information about server configuration see [this article](http://blog.e
### How to report bug
Create an issue [here](https://github.com/espocrm/espocrm/issues) or post on our [forum](http://forum.espocrm.com/bug-reports?routestring=forum/bug-reports).
Create an issue [here](https://github.com/espocrm/espocrm/issues) or post on our [forum](http://forum.espocrm.com/forum/bug-reports).
### How to get started (for developers)
+7 -1
View File
@@ -60,11 +60,17 @@ class App extends \Espo\Core\Controllers\Base
}
$userData['emailAddressList'] = $emailAddressList;
$settings = (object)[];
foreach ($this->getConfig()->get('userItems') as $item) {
$settings->$item = $this->getConfig()->get($item);
}
return array(
'user' => $userData,
'acl' => $this->getAcl()->getMap(),
'preferences' => $preferences,
'token' => $this->getUser()->get('token')
'token' => $this->getUser()->get('token'),
'settings' => $settings
);
}
+6 -6
View File
@@ -40,32 +40,32 @@ class AuthToken extends \Espo\Core\Controllers\Record
}
}
public function actionUpdate($params, $data)
public function actionUpdate($params, $data, $request)
{
throw new Forbidden();
}
public function actionCreate($params, $data)
public function actionCreate($params, $data, $request)
{
throw new Forbidden();
}
public function actionListLinked($params, $data)
public function actionListLinked($params, $data, $request)
{
throw new Forbidden();
}
public function actionMassUpdate($params, $data)
public function actionMassUpdate($params, $data, $request)
{
throw new Forbidden();
}
public function actionCreateLink($params, $data)
public function actionCreateLink($params, $data, $request)
{
throw new Forbidden();
}
public function actionRemoveLink($params, $data)
public function actionRemoveLink($params, $data, $request)
{
throw new Forbidden();
}
@@ -67,6 +67,19 @@ class Integration extends \Espo\Core\Controllers\Record
$entity->set($data);
$this->getEntityManager()->saveEntity($entity);
$integrationsConfigData = $this->getConfig()->get('integrations');
if (!$integrationsConfigData || !($integrationsConfigData instanceof \StdClass)) {
$integrationsConfigData = (object)[];
}
$integrationName = $params['id'];
$integrationsConfigData->$integrationName = $entity->get('enabled');
$this->getConfig()->set('integrations', $integrationsConfigData);
$this->getConfig()->save();
return $entity->toArray();
}
}
+5 -1
View File
@@ -37,7 +37,11 @@ class Settings extends \Espo\Core\Controllers\Base
{
protected function getConfigData()
{
$data = $this->getConfig()->getData($this->getUser()->isAdmin());
if ($this->getUser()->id == 'system') {
$data = $this->getConfig()->getData();
} else {
$data = $this->getConfig()->getData($this->getUser()->isAdmin());
}
$fieldDefs = $this->getMetadata()->get('entityDefs.Settings.fields');
+1 -1
View File
@@ -283,7 +283,7 @@ class Container
\Espo\Core\Utils\Language::detectLanguage($this->get('config'), $this->get('preferences')),
$this->get('fileManager'),
$this->get('metadata'),
$this->get('useCache')
$this->get('config')->get('useCache')
);
}
+9 -1
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) {
@@ -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;
@@ -29,7 +29,14 @@
namespace Espo\Core\Templates\Services;
use \Espo\ORM\Entity;
class Company extends \Espo\Services\Record
{
protected function getDuplicateWhereClause(Entity $entity, $data = array())
{
return array(
'name' => $entity->get('name')
);
}
}
@@ -29,6 +29,7 @@
namespace Espo\Core\Templates\Services;
use \Espo\ORM\Entity;
class Person extends \Espo\Services\Record
{
+8 -4
View File
@@ -226,7 +226,7 @@ class Config
* @param $isAdmin
* @return array
*/
public function getData($isAdmin = false)
public function getData($isAdmin = null)
{
$data = $this->loadConfig();
@@ -247,7 +247,7 @@ class Config
* @param $isAdmin
* @return bool
*/
public function setData($data, $isAdmin = false)
public function setData($data, $isAdmin = null)
{
$restrictItems = $this->getRestrictItems($isAdmin);
@@ -285,7 +285,7 @@ class Config
*
* @return object
*/
protected function getRestrictItems($onlySystemItems = false)
protected function getRestrictItems($onlySystemItems = null)
{
$data = $this->loadConfig();
@@ -297,7 +297,11 @@ class Config
$this->adminItems = array_merge($data['systemItems'], $data['adminItems']);
}
return $this->adminItems;
if ($onlySystemItems === false) {
return $this->adminItems;
}
return array_merge($this->adminItems, $data['userItems']);
}
+21 -2
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);
+7 -1
View File
@@ -123,7 +123,13 @@ class FieldManager
if ($this->getMetadata()->get(['fields', $type, 'translatedOptions'])) {
if (isset($fieldDefs['translatedOptions'])) {
$this->setTranslatedOptions($name, $fieldDefs['translatedOptions'], $scope);
$translatedOptions = $fieldDefs['translatedOptions'];
$translatedOptions = json_decode(json_encode($fieldDefs['translatedOptions']), true);
if (isset($translatedOptions['_empty_'])) {
$translatedOptions[''] = $translatedOptions['_empty_'];
}
$this->setTranslatedOptions($name, $translatedOptions, $scope);
}
}
+8 -5
View File
@@ -625,12 +625,19 @@ class Manager
$items = (array) $items;
}
$removeList = array();
$permissionDeniedList = array();
foreach ($items as $item) {
if (isset($dirPath)) {
$item = Utils\Util::concatPath($dirPath, $item);
}
if (!file_exists($item)) {
continue;
}
$removeList[] = $item;
if (!is_writable($item)) {
$permissionDeniedList[] = $item;
} else if (!is_writable(dirname($item))) {
@@ -644,11 +651,7 @@ class Manager
}
$result = true;
foreach ($items as $item) {
if (isset($dirPath)) {
$item = Utils\Util::concatPath($dirPath, $item);
}
foreach ($removeList as $item) {
if (is_dir($item)) {
$result &= $this->removeInDir($item, true);
} else {
@@ -0,0 +1,3 @@
{
}
@@ -0,0 +1,3 @@
{
}
@@ -94,7 +94,8 @@ return array ( 'defaultPermissions' =>
'restrictedMode',
'userLimit',
'portalUserLimit',
'stylesheet'
'stylesheet',
'userItems'
),
'adminItems' =>
array (
@@ -137,7 +138,18 @@ return array ( 'defaultPermissions' =>
'personalEmailMaxPortionSize',
'inboundEmailMaxPortionSize',
'authTokenLifetime',
'authTokenMaxIdleTime'
'authTokenMaxIdleTime',
'ldapUserDefaultTeamId',
'ldapUserDefaultTeamName',
'ldapUserTeamsIds',
'ldapUserTeamsNames'
),
'userItems' =>
array (
'outboundEmailFromAddress',
'outboundEmailFromName',
'integrations',
'googleMapsApiKey'
),
'isInstalled' => false,
'ldapUserNameAttribute' => 'sAMAccountName',
@@ -39,6 +39,7 @@
"presetFilters": {
"actual": "Aktuell",
"completed": "Abgeschlossen",
"deferred": "Zurückgestellt",
"todays": "Heutige",
"overdue": "Überfällig"
}
@@ -56,6 +56,7 @@
"Culture": "Culture",
"Education": "Education",
"Electronics": "Electronics",
"Electric Power": "Electric Power",
"Energy": "Energy",
"Entertainment & Leisure": "Entertainment & Leisure",
"Finance": "Finance",
@@ -67,11 +68,14 @@
"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",
"Service": "Service",
"Support": "Support",
"Sports": "Sports",
@@ -79,6 +83,7 @@
"Technology": "Technology",
"Telecommunications": "Telecommunications",
"Television": "Television",
"Testing, Inspection & Certification": "Testing, Inspection & Certification",
"Transportation": "Transportation",
"Venture Capital": "Venture Capital",
"Wholesale": "Wholesale",
@@ -45,5 +45,8 @@
"planned": "Planned",
"held": "Held",
"todays": "Today's"
},
"messages": {
"nothingHasBeenSent": "Nothing were sent"
}
}
@@ -40,6 +40,7 @@
"presetFilters": {
"actual": "Actual",
"completed": "Completed",
"deferred": "Deferred",
"todays": "Today's",
"overdue": "Overdue"
}
@@ -39,6 +39,7 @@
"presetFilters": {
"actual": "Actuales",
"completed": "Completado",
"deferred": "Diferido",
"todays": "De Hoy",
"overdue": "Atrazadas"
}
@@ -37,6 +37,7 @@
"actual": "En cours",
"completed": "Terminés",
"todays": "Aujourd'hui",
"deferred": "Reporté",
"overdue": "En retard"
}
}
@@ -40,6 +40,7 @@
"actual": "Sebenarnya",
"completed": "Lengkap",
"todays": "hari ini",
"deferred": "Tangguhan",
"overdue": "Terlambat"
}
}
@@ -40,6 +40,7 @@
"actual": "Attuale",
"completed": "Completato",
"todays": "Di Oggi",
"deferred": "Prorogare",
"overdue": "In RItardo"
}
}
@@ -22,8 +22,7 @@
"Not Started": "Niet Gestart",
"Started": "Gestart",
"Completed": "Voltooid",
"Canceled": "Geannuleerd",
"Deferred": "Deferred"
"Canceled": "Geannuleerd"
},
"priority" : {
"Low": "Laag",
@@ -39,6 +39,7 @@
"presetFilters": {
"actual": "Bieżący",
"completed": "Ukończone",
"deferred": "Opóźniony",
"todays": "Dzisiaj",
"overdue": "Przegrzane"
}
@@ -34,6 +34,7 @@
"actual": "Atual",
"completed": "Completa",
"todays": "Hoje",
"deferred": "Adiada",
"overdue": "Atrasada"
}
}
@@ -40,6 +40,7 @@
"actual": "Актуальные",
"completed": "Завершенные",
"todays": "На сегодня",
"deferred": "Отложенные",
"overdue": "Просроченные"
}
}
@@ -37,9 +37,10 @@
"Complete": "Завершити"
},
"presetFilters": {
"actual": "Актуально",
"completed": "Завершено",
"todays": "Сьогоднішнє",
"overdue": "Прострочено"
"actual": "Актуальні",
"completed": "Завершені",
"todays": "Сьогоднішні",
"deferred": "Відстрочені",
"overdue": "Прострочені"
}
}
@@ -0,0 +1,11 @@
{
"activities": {
"disabled": true
},
"history": {
"disabled": true
},
"tasks": {
"disabled": true
}
}
@@ -0,0 +1,11 @@
{
"activities": {
"disabled": true
},
"history": {
"disabled": true
},
"tasks": {
"disabled": true
}
}
@@ -0,0 +1,11 @@
{
"activities": {
"disabled": true
},
"history": {
"disabled": true
},
"tasks": {
"disabled": true
}
}
@@ -13,7 +13,7 @@
"rows": [
[{"name":"status"},{"name":"source"}],
[{"name":"opportunityAmount"},{"name":"campaign"}],
[false, {"name":"industry"}],
[{"name":"industry"}, false],
[{"name":"description", "fullWidth": true}]
]
}
@@ -0,0 +1,11 @@
{
"activities": {
"disabled": true
},
"history": {
"disabled": true
},
"tasks": {
"disabled": true
}
}
@@ -0,0 +1,11 @@
{
"activities": {
"disabled": true
},
"history": {
"disabled": true
},
"tasks": {
"disabled": true
}
}
@@ -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": {
@@ -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": [
@@ -4,6 +4,34 @@
"views":{
"detail":"crm:views/contact/detail"
},
"recordViews":{
"detail": "crm:views/contact/record/detail",
"detailQuick": "crm:views/contact/record/detail-small"
},
"defaultSidePanelFieldList": {
"detail": [
{
"name": "assignedUser"
},
{
"name": "teams"
},
{
"name": "portalUser"
}
],
"detailSmall": [
{
"name": "assignedUser"
},
{
"name": "teams"
},
{
"name": "portalUser"
}
]
},
"sidePanels":{
"detail":[
{
@@ -24,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": {
@@ -31,7 +31,8 @@
"label":"Converted To",
"view": "crm:views/lead/record/panels/converted-to",
"notRefreshable": true,
"hidden": true
"hidden": true,
"style": "success"
},
{
"name":"activities",
@@ -58,7 +59,8 @@
"label":"Converted To",
"view": "crm:views/lead/record/panels/converted-to",
"notRefreshable": true,
"hidden": true
"hidden": true,
"style": "success"
}
],
"detailSmall": [
@@ -67,7 +69,26 @@
"label":"Converted To",
"view": "crm:views/lead/record/panels/converted-to",
"notRefreshable": true,
"hidden": 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": [
@@ -76,7 +97,8 @@
"label":"Converted To",
"view": "crm:views/lead/record/panels/converted-to",
"notRefreshable": true,
"hidden": true
"hidden": true,
"style": "success"
}
]
},
@@ -1,54 +1,75 @@
{
"controller": "controllers/record",
"acl": "crm:acl/mass-email",
"recordViews": {
"detail": "crm:views/mass-email/record/detail",
"edit": "crm:views/mass-email/record/edit",
"editQuick": "crm:views/mass-email/record/edit-small"
},
"views": {
"detail": "crm:views/mass-email/detail"
},
"defaultSidePanel": {
"edit": false,
"editSmall": false
},
"formDependency": {
"status": {
"map": {
"Complete": [
{
"action": "setReadOnly",
"fields": [
"status"
"controller": "controllers/record",
"acl": "crm:acl/mass-email",
"recordViews": {
"detail": "crm:views/mass-email/record/detail",
"edit": "crm:views/mass-email/record/edit",
"editQuick": "crm:views/mass-email/record/edit-small"
},
"views": {
"detail": "crm:views/mass-email/detail"
},
"defaultSidePanel": {
"edit": false,
"editSmall": false
},
"dynamicLogic": {
"fields": {
"status": {
"readOnly": {
"conditionGroup": [
{
"type": "and",
"value": [
{
"type": "or",
"value": [
{
"type": "equals",
"attribute": "status",
"value": "Complete"
},
{
"type": "equals",
"attribute": "status",
"value": "In Process"
},
{
"type": "equals",
"attribute": "status",
"value": "Failed"
}
]
},
{
"type": "isNotEmpty",
"attribute": "id"
}
]
}
]
}
}
},
"options": {
"status": [
{
"optionList": [
"Draft",
"Pending"
],
"conditionGroup": [
{
"type": "in",
"attribute": "status",
"value": [
"Draft",
"Pending"
]
}
]
}
]
}
],
"In Process": [
{
"action": "setReadOnly",
"fields": [
"status"
]
}
],
"Failed": [
{
"action": "setReadOnly",
"fields": [
"status"
]
}
]
},
"default": [
{
"action": "setNotReadOnly",
"fields": [
"status"
]
}
]
}
}
}
@@ -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": [
@@ -55,6 +55,9 @@
{
"name":"overdue",
"style": "danger"
},
{
"name":"deferred"
}
],
"boolFilterList": ["onlyMy"]
@@ -43,7 +43,7 @@
"bool": {
"onlyMy": true
},
"primary": "actualNotDeferred"
"primary": "actualStartingNotInPast"
}
},
"layout": [
@@ -42,6 +42,7 @@
"Defense",
"Education",
"Electronics",
"Electric Power",
"Energy",
"Entertainment & Leisure",
"Finance",
@@ -54,18 +55,22 @@
"Manufacturing",
"Mass Media",
"Marketing",
"Mining",
"Music",
"Publishing",
"Petroleum",
"Real Estate",
"Retail",
"Service",
"Sports",
"Software",
"Support",
"Shipping",
"Technology",
"Telecommunications",
"Television",
"Transportation",
"Testing, Inspection & Certification",
"Venture Capital",
"Wholesale",
"Water"
@@ -74,7 +74,7 @@
},
"object": {
"type": "belongsToParent",
"entityList": ["Email"]
"entityList": ["Email", "CampaignTrackingUrl"]
}
},
"collection": {
@@ -30,6 +30,7 @@
"type": {
"type": "enum",
"options": ["", "Question", "Incident", "Problem"],
"default": "",
"audited": true
},
"description": {
@@ -182,6 +182,7 @@
"type": "link",
"layoutMassUpdateDisabled": true,
"layoutListDisabled": true,
"layoutDetailDisabled": true,
"readOnly": true,
"notStorable": true,
"view": "views/fields/link-one"
@@ -7,7 +7,7 @@
},
"status": {
"type": "enum",
"options": ["Draft", "Pending"],
"options": ["Draft", "Pending", "Complete", "In Process", "Failed"],
"default": "Pending"
},
"storeSentEmails": {
@@ -110,7 +110,8 @@
},
"assignedUser": {
"type": "belongsTo",
"entity": "User"
"entity": "User",
"foreign": "tasks"
},
"teams": {
"type": "hasMany",
@@ -44,11 +44,18 @@ class Task extends \Espo\Core\SelectManagers\Base
protected function filterActual(&$result)
{
$result['whereClause'][] = array(
'status!=' => ['Completed', 'Canceled']
'status!=' => ['Completed', 'Canceled', 'Deferred']
);
}
protected function filterActualNotDeferred(&$result)
protected function filterDeferred(&$result)
{
$result['whereClause'][] = array(
'status=' => 'Deferred'
);
}
protected function filterActualStartingNotInPast(&$result)
{
$result['whereClause'][] = array(
array(
@@ -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;
}
@@ -64,7 +64,9 @@
"massUpdate": "Mass Update",
"relationships": "Relationship Panels",
"sidePanelsDetail": "Side Panels (Detail)",
"sidePanelsEdit": "Side Panels (Edit)"
"sidePanelsEdit": "Side Panels (Edit)",
"sidePanelsDetailSmall": "Side Panels (Detail Small)",
"sidePanelsEditSmall": "Side Panels (Edit Small)"
},
"fieldTypes": {
"address": "Address",
@@ -191,7 +191,7 @@
"posting": "Posting...",
"confirmLeaveOutMessage": "Are you sure you want to leave the form?",
"notModified": "You have not modified the record",
"duplicate": "The record you are creating seems to be a duplicate",
"duplicate": "The record you are creating might already exist",
"dropToAttach": "Drop to attach",
"fieldIsRequired": "{field} is required",
"fieldShouldBeEmail": "{field} should be valid email",
@@ -3,12 +3,23 @@
"width": "Width (%)",
"link": "Link",
"notSortable": "Not Sortable",
"align": "Align"
"align": "Align",
"panelName": "Panel Name",
"style": "Style",
"sticked": "Sticked"
},
"options": {
"align": {
"left": "Left",
"right": "Right"
},
"style": {
"default": "Default",
"success": "Success",
"danger": "Danger",
"info": "Info",
"warning": "Warning",
"primary": "Primary"
}
}
}
@@ -84,7 +84,8 @@
"mentionEmailNotifications": "Send email notifications about mentions in posts",
"massEmailDisableMandatoryOptOutLink": "Disable mandatory opt-out link",
"activitiesEntityList": "Activities Entity List",
"historyEntityList": "History Entity List"
"historyEntityList": "History Entity List",
"b2cMode": "B2C Mode"
},
"options": {
"weekStart": {
@@ -121,7 +122,8 @@
"ldapAccountDomainName": "The domain which is used for authorization to LDAP server.",
"ldapAccountDomainNameShort": "The short domain which is used for authorization to LDAP server.",
"ldapUserTeams": "Teams for created user. For more, see user profile.",
"ldapUserDefaultTeam": "Default team for created user. For more, see user profile."
"ldapUserDefaultTeam": "Default team for created user. For more, see user profile.",
"b2cMode": "By default EspoCRM is adapted for B2B. You can switch it to B2C."
},
"labels": {
"System": "System",
@@ -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",
@@ -4,7 +4,7 @@
"rows": [
[{"name": "useCache"}, {"name": "siteUrl"}],
[{"name": "exportDisabled"}, {"name": "globalSearchEntityList"}],
[{"name": "followCreatedEntities"}, false]
[{"name": "followCreatedEntities"}, {"name": "b2cMode"}]
]
},
{
@@ -0,0 +1,11 @@
{
"activities": {
"disabled": true
},
"history": {
"disabled": true
},
"tasks": {
"disabled": true
}
}
@@ -129,6 +129,10 @@
"linkMultiple": {
"view": "views/admin/dynamic-logic/conditions/field-types/link-multiple",
"typeList": ["isEmpty", "isNotEmpty", "contains", "notContains"]
},
"id": {
"view": "views/admin/dynamic-logic/conditions/field-types/base",
"typeList": ["isEmpty", "isNotEmpty"]
}
},
"conditionTypes": {
@@ -1,7 +1,8 @@
{
"controller": "controllers/record",
"recordViews": {
"detail": "Template.Record.Detail"
"detail": "views/template/record/detail",
"edit": "views/template/record/edit"
},
"formDependency": {
"printFooter": {
@@ -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"
@@ -9,7 +9,12 @@
"status": {
"type": "enum",
"options": ["In Process", "Complete", "Failed"],
"readOnly": true
"readOnly": true,
"view": "views/fields/enum-styled",
"style": {
"Complete": "success",
"Failed": "danger"
}
},
"file": {
"type": "file",
@@ -61,7 +61,8 @@
},
"collection": {
"sortBy": "createdAt",
"asc": false
"asc": false,
"textFilterFields": ["name", "method", "serviceName"]
},
"indexes": {
"executeTime": {
@@ -1,7 +1,8 @@
{
"fields": {
"post": {
"type": "text"
"type": "text",
"rows": 8
},
"data": {
"type": "jsonObject",
@@ -58,9 +59,6 @@
"isInternal": {
"type": "bool"
},
"isToSelf": {
"type": "bool"
},
"createdAt": {
"type": "datetime",
"readOnly": true
@@ -6,16 +6,16 @@
},
"recordsPerPage": {
"type": "int",
"minValue": 1,
"maxValue": 1000,
"min": 1,
"max": 200,
"default": 20,
"required": true,
"tooltip": true
},
"recordsPerPageSmall": {
"type": "int",
"minValue": 1,
"maxValue": 100,
"min": 1,
"max": 100,
"default": 10,
"required": true,
"tooltip": true
@@ -306,7 +306,8 @@
},
"b2cMode": {
"type": "bool",
"default": false
"default": false,
"tooltip": true
},
"avatarsDisabled": {
"type": "bool",
@@ -402,7 +403,7 @@
},
"historyEntityList": {
"type": "multiEnum",
"view": "views/settings/fields/activities-entity-list"
"view": "views/settings/fields/history-entity-list"
},
"googleMapsApiKey": {
"type": "varchar"
@@ -253,6 +253,11 @@
"entity": "Account",
"foreign": "portalUsers",
"relationName": "AccountPortalUser"
},
"tasks": {
"type": "hasMany",
"entity": "Task",
"foreign": "assignedUser"
}
},
"collection": {
@@ -1,3 +1,3 @@
<p>{{assignerUserName}} has assigned {{entityTypeLowerFirst}} to you.</p>
<p><strong>{{name}}</strong></p>
<p><a href="{{recordUrl}}">View record</a></p>
<p><a href="{{recordUrl}}">View</a></p>
@@ -0,0 +1,4 @@
<p>Email recieved from {{fromName}}, related to {{entityTypeLowerFirst}} '{{parentName}}'.</p>
<p>{{subject}}</p>
<p>{{{post}}}</p>
<p><a href="{{url}}">View</a></p>
@@ -0,0 +1 @@
Email recieved: [{{{entityType}}}] {{{name}}}
@@ -52,7 +52,7 @@ class EmailNotification extends \Espo\Core\Services\Base
]);
}
protected $noteNotificationTypeList = ['Post', 'Status'];
protected $noteNotificationTypeList = ['Post', 'Status', 'EmailReceived'];
protected function getMailSender()
{
@@ -279,7 +279,6 @@ class EmailNotification extends \Espo\Core\Services\Base
$note = $this->getEntityManager()->getEntity('Note', $notification->get('relatedId'));
if (!$note) return;
$post = $note->get('post');
$parentId = $note->get('parentId');
$parentType = $note->get('parentType');
@@ -299,7 +298,7 @@ class EmailNotification extends \Espo\Core\Services\Base
$data['userName'] = $note->get('createdByName');
$data['post'] = $note->get('post');
$data['post'] = nl2br($note->get('post'));
$subjectTpl = $this->getTemplateFileManager()->getTemplate('mention', 'subject');
$bodyTpl = $this->getTemplateFileManager()->getTemplate('mention', 'body');
@@ -361,7 +360,6 @@ class EmailNotification extends \Espo\Core\Services\Base
protected function processNotificationNotePost($note, $user)
{
$post = $note->get('post');
$parentId = $note->get('parentId');
$parentType = $note->get('parentType');
@@ -371,7 +369,7 @@ class EmailNotification extends \Espo\Core\Services\Base
$data = array();
$data['userName'] = $note->get('createdByName');
$data['post'] = $note->get('post');
$data['post'] = nl2br($note->get('post'));
if ($parentId && $parentType) {
$parent = $this->getEntityManager()->getEntity($parentType, $parentId);
@@ -430,7 +428,6 @@ class EmailNotification extends \Espo\Core\Services\Base
protected function processNotificationNoteStatus($note, $user)
{
$post = $note->get('post');
$parentId = $note->get('parentId');
$parentType = $note->get('parentType');
@@ -491,4 +488,82 @@ class EmailNotification extends \Espo\Core\Services\Base
$GLOBALS['log']->error('EmailNotification: [' . $e->getCode() . '] ' .$e->getMessage());
}
}
protected function processNotificationNoteEmailReceived($note, $user)
{
$parentId = $note->get('parentId');
$parentType = $note->get('parentType');
$emailAddress = $user->get('emailAddress');
if (!$emailAddress) return;
$noteData = $note->get('data');
if (!($noteData instanceof \StdClass)) return;
if (!isset($noteData->emailId)) return;
$email = $this->getEntityManager()->getEntity('Email', $noteData->emailId);
if (!$email) return;
if ($email->hasLinkMultipleId('users', $user->id)) return;
$data = array();
$data['fromName'] = '';
if (isset($noteData->personEntityName)) {
$data['fromName'] = $noteData->personEntityName;
} else if (isset($noteData->fromString)) {
$data['fromName'] = $noteData->fromString;
}
$data['subject'] = '';
if (isset($noteData->emailName)) {
$data['subject'] = $noteData->emailName;
}
$data['post'] = nl2br($note->get('post'));
if (!$parentId || !$parentType) return;
$parent = $this->getEntityManager()->getEntity($parentType, $parentId);
if (!$parent) return;
$data['url'] = $this->getConfig()->getSiteUrl() . '/#' . $parentType . '/view/' . $parentId;
$data['parentName'] = $parent->get('name');
$data['parentType'] = $parentType;
$data['parentId'] = $parentId;
$data['name'] = $data['parentName'];
$data['entityType'] = $this->getLanguage()->translate($data['parentType'], 'scopeNames');
$data['entityTypeLowerFirst'] = lcfirst($data['entityType']);
$subjectTpl = $this->getTemplateFileManager()->getTemplate('noteEmailRecieved', 'subject', $parentType);
$bodyTpl = $this->getTemplateFileManager()->getTemplate('noteEmailRecieved', 'body', $parentType);
$subjectTpl = str_replace(array("\n", "\r"), '', $subjectTpl);
$subject = $this->getHtmlizer()->render($note, $subjectTpl, 'note-email-recieved-email-subject-' . $parentType, $data, true);
$body = $this->getHtmlizer()->render($note, $bodyTpl, 'note-email-recieved-email-body-' . $parentType, $data, true);
$email = $this->getEntityManager()->getEntity('Email');
$email->set(array(
'subject' => $subject,
'body' => $body,
'isHtml' => true,
'to' => $emailAddress,
'isSystem' => true
));
$email->set(array(
'parentId' => $parentId,
'parentType' => $parentType
));
try {
$this->getMailSender()->send($email);
} catch (\Exception $e) {
$GLOBALS['log']->error('EmailNotification: [' . $e->getCode() . '] ' .$e->getMessage());
}
}
}
+6 -1
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);
}
+16 -1
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'])) {
@@ -1,7 +1,7 @@
{{#if isOverdue}}
<span class="text-danger">
{{/if}}
{{value}}
{{dateValue}}
{{#if isOverdue}}
</span>
{{/if}}
@@ -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);
}
+20 -14
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),
});
}
@@ -0,0 +1,45 @@
/************************************************************************
* 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('crm:views/contact/record/detail-small', ['views/record/detail-small', 'crm:views/contact/record/detail'], function (Dep, Detail) {
return Dep.extend({
setup: function () {
Dep.prototype.setup.call(this);
this.controlPortalUserVisibility();
this.listenTo(this.model, 'change:portalUserId', this.controlPortalUserVisibility, this);
},
controlPortalUserVisibility: function () {
Detail.prototype.controlPortalUserVisibility.call(this);
}
});
});
@@ -0,0 +1,49 @@
/************************************************************************
* 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('crm:views/contact/record/detail', 'views/record/detail', function (Dep) {
return Dep.extend({
setup: function () {
Dep.prototype.setup.call(this);
this.controlPortalUserVisibility();
this.listenTo(this.model, 'change:portalUserId', this.controlPortalUserVisibility, this);
},
controlPortalUserVisibility: function () {
if (this.model.get('portalUserId')) {
this.showField('portalUser');
} else {
this.hideField('portalUser');
}
}
});
});
+52 -12
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),
});
}
+1 -1
View File
@@ -1,5 +1,5 @@
<% _.each(layout, function (panel, columnNumber) { %>
<div class="panel panel-default<% if (panel.name) { %>{{#if hiddenPanels.<%= panel.name %>}} hidden{{/if}}<% } %>"<% if (panel.name) print(' data-name="'+panel.name+'"') %>>
<div class="panel panel-<%= panel.style %><% if (panel.name) { %>{{#if hiddenPanels.<%= panel.name %>}} hidden{{/if}}<% } %>"<% if (panel.name) print(' data-name="'+panel.name+'"') %>>
{{#if "<%= panel.label %>"}}
<div class="panel-heading"><h4 class="panel-title"><%= "{{translate \"" + panel.label + "\" scope=\""+model.name+"\"}}" %></h4></div>
{{/if}}
+46 -46
View File
@@ -11,11 +11,11 @@
padding: 0;
margin: 0;
}
#layout ul > li {
background-color: #FFF;
}
#layout ul.panels > li {
padding: 5px;
margin: 5px;
@@ -26,13 +26,13 @@
{{/ifEqual}}
min-height: 80px;
border: 1px solid #CCC;
list-style: none;
list-style: none;
}
#layout ul.rows {
min-height: 80px;
}
#layout ul.rows > li {
list-style: none;
border: 1px solid #CCC;
@@ -40,111 +40,111 @@
padding: 5px;
height: 72px;
}
#layout ul.cells {
min-height: 30px;
margin-top: 12px;
margin-top: 12px;
}
#layout ul.panels ul.cells > li {
{{#ifEqual columnCount 1}}
width: 92%;
{{else}}
width: 46%;
{{/ifEqual}}
{{/ifEqual}}
float: left;
}
#layout ul.panels ul.cells > li[data-full-width="true"] {
width: 94%;
}
#layout ul.cells > li {
list-style: none;
border: 1px solid #CCC;
margin: 5px;
padding: 5px;
height: 32px;
height: 32px;
}
#layout ul.rows > li > div {
width: auto;
}
}
#layout ul.cells > li a {
float: right;
margin-left: 5px;
}
#layout ul.disabled {
min-height: 200px;
width: 100%;
}
#layout ul.disabled > li a {
display: none;
}
#layout header {
font-weight: bold;
}
#layout ul.panels > li label {
display: inline;
}
#layout ul.panels > li header a {
font-weight: normal;
font-weight: normal;
}
#layout ul.panels > li > div {
width: auto;
text-align: left;
margin-left: 5px;
margin-left: 5px;
}
ul.cells li.cell a.remove-field {
display: none;
}
ul.cells li.cell:hover a.remove-field {
display: block;
}
ul.panels > li a.remove-panel {
display: none;
}
ul.panels > li:hover a.remove-panel {
display: block;
}
ul.rows > li a.remove-row {
display: none;
}
ul.rows > li:hover a.remove-row {
display: block;
}
ul.panels > li a.edit-panel-label {
display: none;
}
ul.panels > li:hover a.edit-panel-label {
display: inline-block;
}
}
</style>
<div id="layout" class="row">
<div class="col-md-8">
<div class="well">
<header>Layout</header>
<a href="javascript:;" data-action="addPanel">{{translate 'Add Panel' scope='Admin'}}</a>
<header>Layout</header>
<a href="javascript:;" data-action="addPanel">{{translate 'Add Panel' scope='Admin'}}</a>
<ul class="panels"></ul>
</div>
</div>
</div>
<div class="col-md-4">
<div class="well">
@@ -152,21 +152,21 @@
<ul class="disabled cells clearfix">
{{#each disabledFields}}
<li class="cell" data-name="{{./this}}">{{translate this scope=../scope category='fields'}}
&nbsp;<a href="javascript:" data-action="removeField" class="remove-field"><i class="glyphicon glyphicon-remove"></i></a>
&nbsp;<a href="javascript:" data-action="removeField" class="remove-field"><i class="glyphicon glyphicon-remove"></i></a>
</li>
{{/each}}
</ul>
</div>
</div>
</div>
</div>
<div id="layout-panel-tpl" style="display: none;">
<li>
<header>
<header data-style="<%= style %>">
<label><%= label %></label>&nbsp;
<a href="javascript:" data-action="edit-panel-label" class="edit-panel-label"><i class="glyphicon glyphicon-pencil"></i></a>
<a href="javascript:" style="float: right;" data-action="removePanel" class="remove-panel"><i class="glyphicon glyphicon-remove"></i></a>
</header>
</header>
<ul class="rows">
<%
for (var i in rows) {
@@ -184,23 +184,23 @@
if (cell !== false) {
%>
<li class="cell" data-name="<%= cell.name %>" data-full-width="<%= cell.fullWidth || '' %>"><%= cell.label %>
<a href="javascript:" data-action="removeField" class="remove-field"><i class="glyphicon glyphicon-remove"></i></a>
<a href="javascript:" data-action="removeField" class="remove-field"><i class="glyphicon glyphicon-remove"></i></a>
</li>
<%
<%
} else {
%>
<li class="empty cell">
<a href="javascript:" data-action="minusCell" class="remove-field"><i class="glyphicon glyphicon-minus"></i></a>
</li>
<%
<%
}
}
%>
%>
</ul>
</li>
</li>
<%
}
%>
%>
</ul>
<div>
<a href="javascript:" data-action="addRow"><i class="glyphicon glyphicon-plus"></i></a>
+11 -13
View File
@@ -12,7 +12,7 @@
list-style-type: none;
margin: 0;
}
#layout ul li {
list-style: none;
border: 1px solid #CCC;
@@ -20,45 +20,43 @@
padding: 5px;
height: 32px;
}
#layout header {
font-weight: bold;
}
#layout ul > li .left {
float: left;
}
#layout ul > li {
background-color: #FFF;
}
#layout ul.enabled > li .right {
float: right;
}
#layout ul.disabled > li .right {
display: none;
}
#layout ul > li .width {
font-size: small;
}
#layout ul.disabled > li .width {
display: none;
}
#layout label {
font-weight: normal;
}
.enabled li a.edit-field {
display: none;
}
.enabled li:hover a.edit-field {
display: block;
}
@@ -70,7 +68,7 @@
<header>{{translate 'Enabled' scope='Admin'}}</header>
<ul class="enabled connected">
{{#each layout}}
<li draggable="true" {{#each ../dataAttributes}}data-{{toDom this}}="{{prop ../this this}}" {{/each}}>
<li draggable="true" {{#each ../dataAttributeList}}data-{{toDom this}}="{{prop ../this this}}" {{/each}}>
<div class="left">
<label>{{label}}</label>
</div>
@@ -0,0 +1 @@
{{dateValue}}
+3 -3
View File
@@ -1,6 +1,6 @@
<div class="input-group">
<input class="main-element form-control" type="text" name="{{name}}" value="{{value}}" autocomplete="off">
<span class="input-group-btn">
<button type="button" class="btn btn-default date-picker-btn" tabindex="-1"><i class="glyphicon glyphicon-calendar"></i></button>
<input class="main-element form-control" type="text" name="{{name}}" value="{{dateValue}}" autocomplete="off">
<span class="input-group-btn">
<button type="button" class="btn btn-default date-picker-btn" tabindex="-1"><i class="glyphicon glyphicon-calendar"></i></button>
</span>
</div>
@@ -0,0 +1 @@
{{dateValue}}
@@ -0,0 +1 @@
<span title="{{fullDateValue}}">{{dateValue}}</span>
@@ -0,0 +1 @@
<span title="{{fullDateValue}}">{{dateValue}}</span>
@@ -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}}
@@ -0,0 +1,3 @@
{{#if isNotEmpty}}
<span class="text-{{style}}">{{translateOption value scope=scope field=name}}</span>
{{/if}}
+5 -1
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}}
@@ -0,0 +1,3 @@
{{#if isNotEmpty}}
{{translateOption value scope=scope field=name translatedOptions=translatedOptions}}
{{/if}}
+1 -3
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}}
@@ -0,0 +1,4 @@
{{#if idValue}}
{{{avatar}}}
<a href="#{{foreignScope}}/view/{{idValue}}">{{nameValue}}</a>
{{/if}}
@@ -0,0 +1 @@
{{#if isNotEmpty}}{{value}}{{else}}{{translate 'None'}}{{/if}}
+3 -3
View File
@@ -1,14 +1,14 @@
{{#each panelList}}
<div class="panel panel-default panel-{{name}}{{#if hidden}} hidden{{/if}}{{#if sticked}} sticked{{/if}}" data-name="{{name}}" data-name="{{name}}">
<div class="panel panel-{{#if style}}{{style}}{{else}}default{{/if}} panel-{{name}}{{#if hidden}} hidden{{/if}}{{#if sticked}} sticked{{/if}}" data-name="{{name}}" data-name="{{name}}">
<div class="panel-heading">
<div class="pull-right btn-group">
{{#if buttonList}}
{{#each buttonList}}
<button type="button" class="btn btn-default btn-sm action{{#if hidden}} hidden{{/if}}" data-action="{{action}}" data-panel="{{../../name}}" {{#each data}} data-{{hyphen @key}}="{{./this}}"{{/each}} title="{{translate title scope=../../scope}}">{{#if html}}{{{html}}}{{else}}{{translate label scope=../../../../scope}}{{/if}}</button>
<button type="button" class="btn btn-{{#if ../../style}}{{../../style}}{{else}}default{{/if}} btn-sm action{{#if hidden}} hidden{{/if}}" data-action="{{action}}" data-panel="{{../../name}}" {{#each data}} data-{{hyphen @key}}="{{./this}}"{{/each}} title="{{translate title scope=../../scope}}">{{#if html}}{{{html}}}{{else}}{{translate label scope=../../../../scope}}{{/if}}</button>
{{/each}}
{{/if}}
{{#if actionList}}
<button type="button" class="btn btn-default btn-sm dropdown-toggle" data-toggle="dropdown">
<button type="button" class="btn btn-{{#if ../style}}{{../style}}{{else}}default{{/if}} btn-sm dropdown-toggle" data-toggle="dropdown">
<span class="caret"></span>
</button>
<ul class="dropdown-menu">
+3 -3
View File
@@ -1,15 +1,15 @@
{{#each panelList}}
<div class="panel panel-default panel-{{name}}{{#if hidden}} hidden{{/if}}{{#if sticked}} sticked{{/if}}" data-name="{{name}}" data-name="{{name}}">
<div class="panel panel-{{#if style}}{{style}}{{else}}default{{/if}} panel-{{name}}{{#if hidden}} hidden{{/if}}{{#if sticked}} sticked{{/if}}" data-name="{{name}}" data-name="{{name}}">
{{#if label}}
<div class="panel-heading">
<div class="pull-right btn-group">
{{#if buttonList}}
{{#each buttonList}}
<button type="button" class="btn btn-default btn-sm action{{#if hidden}} hidden{{/if}}" data-action="{{action}}" data-panel="{{../../name}}" {{#each data}} data-{{hyphen @key}}="{{./this}}"{{/each}} title="{{translate title scope=../scope}}">{{#if html}}{{{html}}}{{else}}{{translate label scope=../../../../scope}}{{/if}}</button>
<button type="button" class="btn btn-{{#if ../../style}}{{../../style}}{{else}}default{{/if}} btn-sm action{{#if hidden}} hidden{{/if}}" data-action="{{action}}" data-panel="{{../../name}}" {{#each data}} data-{{hyphen @key}}="{{./this}}"{{/each}} title="{{translate title scope=../scope}}">{{#if html}}{{{html}}}{{else}}{{translate label scope=../../../../scope}}{{/if}}</button>
{{/each}}
{{/if}}
{{#if actionList}}
<button type="button" class="btn btn-default btn-sm dropdown-toggle" data-toggle="dropdown">
<button type="button" class="btn btn-{{#if style}}{{style}}{{else}}default{{/if}} btn-sm dropdown-toggle" data-toggle="dropdown">
<span class="caret"></span>
</button>
<ul class="dropdown-menu">
+5 -1
View File
@@ -61,7 +61,7 @@ Espo.define(
this.setupAjax();
this.settings = new Settings(null, {cache: this.cache});
this.settings = new Settings(null);
this.language = new Language(this.cache);
this.metadata = new Metadata(this.cache);
this.fieldManager = new FieldManager();
@@ -470,8 +470,12 @@ Espo.define(
var preferencesData = options.preferences || null;
var aclData = options.acl || null;
var settingData = options.settings || {};
this.user.set(userData);
this.preferences.set(preferencesData);
this.settings.set(settingData);
this.acl.set(aclData);
if (!this.auth) {
+12 -11
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);
},
+5 -3
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');
}
});
+25
View File
@@ -31,6 +31,31 @@ Espo.define('models/settings', 'model-offline', function (Dep) {
name: 'Settings',
getByPath: function (arr) {
if (!arr.length) return null;
var p;
for (var i = 0; i < arr.length; i++) {
var item = arr[i];
if (i == 0) {
p = this.get(item);
} else {
if (item in p) {
p = p[item];
} else {
return null;
}
}
if (i === arr.length - 1) {
return p;
}
if (p === null || typeof p !== 'object') {
return null;
}
}
}
});
});

Some files were not shown because too many files have changed in this diff Show More