Compare commits
23 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a89872b785 | |||
| b6bc0471b8 | |||
| afd0f5a5ef | |||
| 533fff4d30 | |||
| 7b0b3d05ad | |||
| e09c101f82 | |||
| 4e6b300477 | |||
| 7453eb583a | |||
| b5d2eb93aa | |||
| b5b8ab4a8e | |||
| b6010a7ad7 | |||
| ebdb649bd7 | |||
| 220d4d1b77 | |||
| 2f8e1aeaf2 | |||
| 20db3b0bd2 | |||
| 6d796c0b20 | |||
| 100fec9409 | |||
| 0b5beeba85 | |||
| 909d31b9fe | |||
| d9522cf555 | |||
| e42a877bf6 | |||
| 8beeb64cbf | |||
| da3a18e766 |
@@ -61,7 +61,7 @@ class Extension extends \Espo\Core\Controllers\Record
|
||||
|
||||
$manager = new \Espo\Core\ExtensionManager($this->getContainer());
|
||||
|
||||
$manager->install($data['id']);
|
||||
$manager->install($data);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -74,7 +74,7 @@ class Extension extends \Espo\Core\Controllers\Record
|
||||
|
||||
$manager = new \Espo\Core\ExtensionManager($this->getContainer());
|
||||
|
||||
$manager->uninstall($data['id']);
|
||||
$manager->uninstall($data);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -103,7 +103,7 @@ class Extension extends \Espo\Core\Controllers\Record
|
||||
{
|
||||
$manager = new \Espo\Core\ExtensionManager($this->getContainer());
|
||||
|
||||
$manager->delete($params['id']);
|
||||
$manager->delete($params);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -28,7 +28,6 @@ class ExtensionManager extends Upgrades\Base
|
||||
|
||||
protected $params = array(
|
||||
'packagePath' => 'data/upload/extensions',
|
||||
|
||||
'backupPath' => 'data/.backup/extensions',
|
||||
|
||||
'scriptNames' => array(
|
||||
|
||||
@@ -24,6 +24,8 @@ namespace Espo\Core\Mail;
|
||||
|
||||
use \Zend\Mime\Mime as Mime;
|
||||
|
||||
use \Espo\ORM\Entity;
|
||||
|
||||
class Importer
|
||||
{
|
||||
private $entityManager;
|
||||
@@ -74,9 +76,13 @@ class Importer
|
||||
if (isset($message->from)) {
|
||||
$email->set('fromName', $message->from);
|
||||
}
|
||||
|
||||
$toArr = $this->getAddressListFromMessage($message, 'to');
|
||||
$ccArr = $this->getAddressListFromMessage($message, 'cc');
|
||||
|
||||
$email->set('from', $fromArr[0]);
|
||||
$email->set('to', implode(';', $this->getAddressListFromMessage($message, 'to')));
|
||||
$email->set('cc', implode(';', $this->getAddressListFromMessage($message, 'cc')));
|
||||
$email->set('to', implode(';', $toArr));
|
||||
$email->set('cc', implode(';', $ccArr));
|
||||
|
||||
if (isset($message->messageId) && !empty($message->messageId)) {
|
||||
$email->set('messageId', $message->messageId);
|
||||
@@ -122,6 +128,8 @@ class Importer
|
||||
$email->set('body', $body);
|
||||
}
|
||||
|
||||
$parentFound = false;
|
||||
|
||||
if (isset($message->references) && !empty($message->references)) {
|
||||
$reference = str_replace(array('/', '@'), " ", trim($message->references, '<>'));
|
||||
$parentType = $parentId = null;
|
||||
@@ -131,26 +139,19 @@ class Importer
|
||||
if (!empty($parentType) && !empty($parentId)) {
|
||||
$email->set('parentType', $parentType);
|
||||
$email->set('parentId', $parentId);
|
||||
$parentFound = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!$email->has('parentId')) {
|
||||
if (!$parentFound) {
|
||||
$from = $email->get('from');
|
||||
if ($from) {
|
||||
$contact = $this->getEntityManager()->getRepository('Contact')->where(array(
|
||||
'emailAddress' => $from
|
||||
))->findOne();
|
||||
if ($contact) {
|
||||
if (!$this->getConfig()->get('b2cMode')) {
|
||||
if ($contact->get('accountId')) {
|
||||
$email->set('parentType', 'Account');
|
||||
$email->set('parentId', $contact->get('accountId'));
|
||||
}
|
||||
} else {
|
||||
$email->set('parentType', 'Contact');
|
||||
$email->set('parentId', $contact->id);
|
||||
}
|
||||
$parentFound = $this->findParent($email, $from);
|
||||
}
|
||||
if (!$parentFound) {
|
||||
if (!empty($toArr)) {
|
||||
$parentFound = $this->findParent($email, $toArr[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -162,11 +163,40 @@ class Importer
|
||||
} catch (\Exception $e) {}
|
||||
}
|
||||
|
||||
protected function checkIsDuplicate($email)
|
||||
protected function findParent(Entity $email, $emailAddress)
|
||||
{
|
||||
if ($email->get('messageIdInternal')) {
|
||||
$contact = $this->getEntityManager()->getRepository('Contact')->where(array(
|
||||
'emailAddress' => $emailAddress
|
||||
))->findOne();
|
||||
if ($contact) {
|
||||
if (!$this->getConfig()->get('b2cMode')) {
|
||||
if ($contact->get('accountId')) {
|
||||
$email->set('parentType', 'Account');
|
||||
$email->set('parentId', $contact->get('accountId'));
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
$email->set('parentType', 'Contact');
|
||||
$email->set('parentId', $contact->id);
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
$account = $this->getEntityManager()->getRepository('Account')->where(array(
|
||||
'emailAddress' => $emailAddress
|
||||
))->findOne();
|
||||
if ($account) {
|
||||
$email->set('parentType', 'Account');
|
||||
$email->set('parentId', $account->id);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function checkIsDuplicate(Entity $email)
|
||||
{
|
||||
if ($email->get('messageId')) {
|
||||
$duplicate = $this->getEntityManager()->getRepository('Email')->where(array(
|
||||
'messageIdInternal' => $email->get('messageIdInternal')
|
||||
'messageId' => $email->get('messageId')
|
||||
))->findOne();
|
||||
if ($duplicate) {
|
||||
return true;
|
||||
|
||||
@@ -24,6 +24,15 @@ namespace Espo\Core\Mail\Storage;
|
||||
|
||||
class Message extends \Zend\Mail\Storage\Message
|
||||
{
|
||||
public function __isset($name)
|
||||
{
|
||||
$headers = $this->getHeaders();
|
||||
if (empty($headers) || !is_object($headers)) {
|
||||
return false;
|
||||
}
|
||||
return $this->getHeaders()->has($name);
|
||||
}
|
||||
|
||||
public function isMultipart()
|
||||
{
|
||||
if (!isset($this->contentType)) {
|
||||
|
||||
@@ -30,6 +30,7 @@ class UpgradeManager extends Upgrades\Base
|
||||
|
||||
protected $params = array(
|
||||
'packagePath' => 'data/upload/upgrades',
|
||||
'backupPath' => 'data/.backup/upgrades',
|
||||
|
||||
'scriptNames' => array(
|
||||
'before' => 'BeforeUpgrade',
|
||||
|
||||
@@ -304,7 +304,7 @@ abstract class Base
|
||||
/**
|
||||
* Get a list of files defined in manifest.json
|
||||
*
|
||||
* @return [type] [description]
|
||||
* @return array
|
||||
*/
|
||||
protected function getDeleteFileList()
|
||||
{
|
||||
@@ -352,6 +352,18 @@ abstract class Base
|
||||
return $this->data['fileList'];
|
||||
}
|
||||
|
||||
protected function getRestoreFileList()
|
||||
{
|
||||
if (!isset($this->data['restoreFileList'])) {
|
||||
$backupPath = $this->getPath('backupPath');
|
||||
$backupFilePath = Util::concatPath($backupPath, self::FILES);
|
||||
|
||||
$this->data['restoreFileList'] = $this->getFileManager()->getFileList($backupFilePath, true, '', true, true);
|
||||
}
|
||||
|
||||
return $this->data['restoreFileList'];
|
||||
}
|
||||
|
||||
protected function copy($sourcePath, $destPath, $recursively = false, array $fileList = null, $copyOnlyFiles = false)
|
||||
{
|
||||
try {
|
||||
@@ -478,18 +490,31 @@ abstract class Base
|
||||
/**
|
||||
* Execute an action. For ex., execute uninstall action in install
|
||||
*
|
||||
* @param [type] $actionName [description]
|
||||
* @param [type] $data [description]
|
||||
* @return [type] [description]
|
||||
* @param string $actionName
|
||||
* @param string $data
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function executeAction($actionName, $data)
|
||||
{
|
||||
$currentAction = $this->getActionManager()->getAction();
|
||||
$actionManager = $this->getActionManager();
|
||||
|
||||
$this->getActionManager()->setAction($actionName);
|
||||
$this->getActionManager()->run($data);
|
||||
$currentAction = $actionManager->getAction();
|
||||
|
||||
$actionManager->setAction($actionName);
|
||||
$actionManager->run($data);
|
||||
|
||||
$actionManager->setAction($currentAction);
|
||||
}
|
||||
|
||||
protected function initialize()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
protected function finalize()
|
||||
{
|
||||
|
||||
$this->getActionManager()->setAction($currentAction);
|
||||
}
|
||||
|
||||
protected function beforeRunAction()
|
||||
@@ -506,4 +531,23 @@ abstract class Base
|
||||
{
|
||||
return $this->getContainer()->get('dataManager')->clearCache();
|
||||
}
|
||||
|
||||
protected function checkIsWritable()
|
||||
{
|
||||
$fullFileList = array_merge($this->getDeleteFileList(), $this->getCopyFileList());
|
||||
|
||||
$result = $this->getFileManager()->isWritableList($fullFileList);
|
||||
if (!$result) {
|
||||
$permissionDeniedList = $this->getFileManager()->getLastPermissionDeniedList();
|
||||
throw new Error("Permission denied in <br>". implode(", <br>", $permissionDeniedList));
|
||||
}
|
||||
}
|
||||
|
||||
protected function backupExistingFiles()
|
||||
{
|
||||
$fullFileList = array_merge($this->getDeleteFileList(), $this->getCopyFileList());
|
||||
|
||||
$backupPath = $this->getPath('backupPath');
|
||||
return $this->copy('', array($backupPath, self::FILES), false, $fullFileList);
|
||||
}
|
||||
}
|
||||
@@ -24,14 +24,18 @@ namespace Espo\Core\Upgrades\Actions\Base;
|
||||
|
||||
class Delete extends \Espo\Core\Upgrades\Actions\Base
|
||||
{
|
||||
public function run($processId)
|
||||
public function run($data)
|
||||
{
|
||||
$processId = $data['id'];
|
||||
|
||||
$GLOBALS['log']->debug('Delete package process ['.$processId.']: start run.');
|
||||
|
||||
if (empty($processId)) {
|
||||
throw new Error('Delete package package ID was not specified.');
|
||||
}
|
||||
|
||||
$this->initialize();
|
||||
|
||||
$this->setProcessId($processId);
|
||||
|
||||
$this->beforeRunAction();
|
||||
@@ -41,6 +45,8 @@ class Delete extends \Espo\Core\Upgrades\Actions\Base
|
||||
|
||||
$this->afterRunAction();
|
||||
|
||||
$this->finalize();
|
||||
|
||||
$GLOBALS['log']->debug('Delete package process ['.$processId.']: end run.');
|
||||
}
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
namespace Espo\Core\Upgrades\Actions\Base;
|
||||
|
||||
use Espo\Core\Exceptions\Error;
|
||||
use Espo\Core\Utils\Util;
|
||||
|
||||
class Install extends \Espo\Core\Upgrades\Actions\Base
|
||||
{
|
||||
@@ -39,8 +40,10 @@ class Install extends \Espo\Core\Upgrades\Actions\Base
|
||||
* @param string $processId Upgrade/Extension ID, gotten in upload stage
|
||||
* @return bool
|
||||
*/
|
||||
public function run($processId)
|
||||
public function run($data)
|
||||
{
|
||||
$processId = $data['id'];
|
||||
|
||||
$GLOBALS['log']->debug('Installation process ['.$processId.']: start run.');
|
||||
|
||||
if (empty($processId)) {
|
||||
@@ -49,6 +52,8 @@ class Install extends \Espo\Core\Upgrades\Actions\Base
|
||||
|
||||
$this->setProcessId($processId);
|
||||
|
||||
$this->initialize();
|
||||
|
||||
$this->isCopied = false;
|
||||
|
||||
/** check if an archive is unzipped, if no then unzip */
|
||||
@@ -58,22 +63,25 @@ class Install extends \Espo\Core\Upgrades\Actions\Base
|
||||
$this->isAcceptable();
|
||||
}
|
||||
|
||||
//check permissions copied and deleted files
|
||||
$this->checkIsWritable();
|
||||
|
||||
$this->backupExistingFiles();
|
||||
|
||||
$this->beforeRunAction();
|
||||
|
||||
/* run before install script */
|
||||
$this->runScript('before');
|
||||
|
||||
/* remove files defined in a manifest */
|
||||
if (!$this->deleteFiles()) {
|
||||
$this->throwErrorAndRemovePackage('Permission denied to delete files.');
|
||||
}
|
||||
|
||||
/* copy files from directory "Files" to EspoCRM files */
|
||||
if (!$this->copyFiles()) {
|
||||
$this->throwErrorAndRemovePackage('Cannot copy files.');
|
||||
}
|
||||
$this->isCopied = true;
|
||||
|
||||
/* remove files defined in a manifest */
|
||||
$this->deleteFiles(true);
|
||||
|
||||
if (!$this->systemRebuild()) {
|
||||
$this->throwErrorAndRemovePackage('Error occurred while EspoCRM rebuild.');
|
||||
}
|
||||
@@ -88,20 +96,32 @@ class Install extends \Espo\Core\Upgrades\Actions\Base
|
||||
/* delete unziped files */
|
||||
$this->deletePackageFiles();
|
||||
|
||||
$this->finalize();
|
||||
|
||||
$GLOBALS['log']->debug('Installation process ['.$processId.']: end run.');
|
||||
}
|
||||
|
||||
protected function restoreFiles()
|
||||
{
|
||||
$backupPath = $this->getPath('backupPath');
|
||||
|
||||
$res = true;
|
||||
if ($this->isCopied) {
|
||||
$res &= $this->copy(array($backupPath, self::FILES), '', true);
|
||||
$GLOBALS['log']->info('Restore: copy back');
|
||||
if (!$this->isCopied) {
|
||||
return;
|
||||
}
|
||||
|
||||
$res &= $this->getFileManager()->removeInDir($backupPath, true);
|
||||
$GLOBALS['log']->info('Installer: Restore previous files.');
|
||||
|
||||
$backupPath = $this->getPath('backupPath');
|
||||
$backupFilePath = Util::concatPath($backupPath, self::FILES);
|
||||
|
||||
$backupFileList = $this->getRestoreFileList();
|
||||
$copyFileList = $this->getCopyFileList();
|
||||
$deleteFileList = array_diff($copyFileList, $backupFileList);
|
||||
|
||||
$res = $this->copy($backupFilePath, '', true);
|
||||
$res &= $this->getFileManager()->remove($deleteFileList, null, true);
|
||||
|
||||
if ($res) {
|
||||
$this->getFileManager()->removeInDir($backupPath, true);
|
||||
}
|
||||
|
||||
return $res;
|
||||
}
|
||||
|
||||
@@ -22,13 +22,16 @@
|
||||
|
||||
namespace Espo\Core\Upgrades\Actions\Base;
|
||||
|
||||
use Espo\Core\Exceptions\Error,
|
||||
Espo\Core\Utils\Util;
|
||||
use Espo\Core\Exceptions\Error;
|
||||
use Espo\Core\Utils\Util;
|
||||
use Espo\Core\Utils\Json;
|
||||
|
||||
class Uninstall extends \Espo\Core\Upgrades\Actions\Base
|
||||
{
|
||||
public function run($processId)
|
||||
public function run($data)
|
||||
{
|
||||
$processId = $data['id'];
|
||||
|
||||
$GLOBALS['log']->debug('Uninstallation process ['.$processId.']: start run.');
|
||||
|
||||
if (empty($processId)) {
|
||||
@@ -37,31 +40,39 @@ class Uninstall extends \Espo\Core\Upgrades\Actions\Base
|
||||
|
||||
$this->setProcessId($processId);
|
||||
|
||||
$this->initialize();
|
||||
|
||||
$this->checkIsWritable();
|
||||
|
||||
$this->beforeRunAction();
|
||||
|
||||
/* run before install script */
|
||||
$this->runScript('beforeUninstall');
|
||||
if (!isset($data['isNotRunScriptBefore']) || !$data['isNotRunScriptBefore']) {
|
||||
$this->runScript('beforeUninstall');
|
||||
}
|
||||
|
||||
$backupPath = $this->getPath('backupPath');
|
||||
if (file_exists($backupPath)) {
|
||||
|
||||
/* remove extension files, saved in fileList */
|
||||
if (!$this->deleteFiles(true)) {
|
||||
throw new Error('Permission denied to delete files.');
|
||||
}
|
||||
|
||||
/* copy core files */
|
||||
if (!$this->copyFiles()) {
|
||||
throw new Error('Cannot copy files.');
|
||||
throw new $this->throwErrorAndRemovePackage('Cannot copy files.');
|
||||
}
|
||||
|
||||
/* remove extension files, saved in fileList */
|
||||
if (!$this->deleteFiles(true)) {
|
||||
throw new $this->throwErrorAndRemovePackage('Permission denied to delete files.');
|
||||
}
|
||||
}
|
||||
|
||||
if (!$this->systemRebuild()) {
|
||||
throw new Error('Error occurred while EspoCRM rebuild.');
|
||||
throw new $this->throwErrorAndRemovePackage('Error occurred while EspoCRM rebuild.');
|
||||
}
|
||||
|
||||
/* run before install script */
|
||||
$this->runScript('afterUninstall');
|
||||
/* run after uninstall script */
|
||||
if (!isset($data['isNotRunScriptAfter']) || !$data['isNotRunScriptAfter']) {
|
||||
$this->runScript('afterUninstall');
|
||||
}
|
||||
|
||||
$this->afterRunAction();
|
||||
|
||||
@@ -70,13 +81,9 @@ class Uninstall extends \Espo\Core\Upgrades\Actions\Base
|
||||
/* delete backup files */
|
||||
$this->deletePackageFiles();
|
||||
|
||||
$GLOBALS['log']->debug('Uninstallation process ['.$processId.']: end run.');
|
||||
}
|
||||
$this->finalize();
|
||||
|
||||
protected function getDeleteFileList()
|
||||
{
|
||||
$extensionEntity = $this->getExtensionEntity();
|
||||
return $extensionEntity->get('fileList');
|
||||
$GLOBALS['log']->debug('Uninstallation process ['.$processId.']: end run.');
|
||||
}
|
||||
|
||||
protected function restoreFiles()
|
||||
@@ -89,6 +96,13 @@ class Uninstall extends \Espo\Core\Upgrades\Actions\Base
|
||||
}
|
||||
|
||||
$res = $this->copy($filesPath, '', true);
|
||||
|
||||
$manifestJson = $this->getFileManager()->getContents(array($packagePath, $this->manifestName));
|
||||
$manifest = Json::decode($manifestJson, true);
|
||||
if (!empty($manifest['delete'])) {
|
||||
$res &= $this->getFileManager()->remove($manifest['delete'], null, true);
|
||||
}
|
||||
|
||||
$res &= $this->getFileManager()->removeInDir($packagePath, true);
|
||||
|
||||
return $res;
|
||||
@@ -131,4 +145,41 @@ class Uninstall extends \Espo\Core\Upgrades\Actions\Base
|
||||
throw new Error($errorMessage);
|
||||
}
|
||||
|
||||
protected function getCopyFileList()
|
||||
{
|
||||
if (!isset($this->data['fileList'])) {
|
||||
$backupPath = $this->getPath('backupPath');
|
||||
$filesPath = Util::concatPath($backupPath, self::FILES);
|
||||
|
||||
$this->data['fileList'] = $this->getFileManager()->getFileList($filesPath, true, '', true, true);
|
||||
}
|
||||
|
||||
return $this->data['fileList'];
|
||||
}
|
||||
|
||||
protected function getRestoreFileList()
|
||||
{
|
||||
if (!isset($this->data['restoreFileList'])) {
|
||||
$packagePath = $this->getPackagePath();
|
||||
$filesPath = Util::concatPath($packagePath, self::FILES);
|
||||
|
||||
if (!file_exists($filesPath)) {
|
||||
$this->unzipArchive($packagePath);
|
||||
}
|
||||
|
||||
$this->data['restoreFileList'] = $this->getFileManager()->getFileList($filesPath, true, '', true, true);
|
||||
}
|
||||
|
||||
return $this->data['restoreFileList'];
|
||||
}
|
||||
|
||||
protected function getDeleteFileList()
|
||||
{
|
||||
$packageFileList = $this->getRestoreFileList();
|
||||
$backupFileList = $this->getCopyFileList();
|
||||
|
||||
$deleteFileList = array_diff($packageFileList, $backupFileList);
|
||||
|
||||
return $deleteFileList;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,6 +38,10 @@ class Upload extends \Espo\Core\Upgrades\Actions\Base
|
||||
|
||||
$GLOBALS['log']->debug('Installation process ['.$processId.']: start upload the package.');
|
||||
|
||||
$this->initialize();
|
||||
|
||||
$this->beforeRunAction();
|
||||
|
||||
$packagePath = $this->getPackagePath();
|
||||
$packageArchivePath = $this->getPackagePath(true);
|
||||
|
||||
@@ -55,6 +59,10 @@ class Upload extends \Espo\Core\Upgrades\Actions\Base
|
||||
|
||||
$this->isAcceptable();
|
||||
|
||||
$this->afterRunAction();
|
||||
|
||||
$this->finalize();
|
||||
|
||||
$GLOBALS['log']->debug('Installation process ['.$processId.']: end upload the package.');
|
||||
|
||||
return $processId;
|
||||
|
||||
@@ -35,29 +35,15 @@ class Delete extends \Espo\Core\Upgrades\Actions\Base\Delete
|
||||
*/
|
||||
protected function getExtensionEntity()
|
||||
{
|
||||
return $this->extensionEntity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Extension Entity
|
||||
*
|
||||
* @param \Espo\Entities\Extension $extensionEntity
|
||||
*/
|
||||
protected function setExtensionEntity(\Espo\Entities\Extension $extensionEntity)
|
||||
{
|
||||
$this->extensionEntity = $extensionEntity;
|
||||
}
|
||||
|
||||
protected function beforeRunAction()
|
||||
{
|
||||
$processId = $this->getProcessId();
|
||||
|
||||
/** get extension entity */
|
||||
$extensionEntity = $this->getEntityManager()->getEntity('Extension', $processId);
|
||||
if (!isset($extensionEntity)) {
|
||||
throw new Error('Extension Entity not found.');
|
||||
if (!isset($this->extensionEntity)) {
|
||||
$processId = $this->getProcessId();
|
||||
$this->extensionEntity = $this->getEntityManager()->getEntity('Extension', $processId);
|
||||
if (!isset($this->extensionEntity)) {
|
||||
throw new Error('Extension Entity not found.');
|
||||
}
|
||||
}
|
||||
$this->setExtensionEntity($extensionEntity);
|
||||
|
||||
return $this->extensionEntity;
|
||||
}
|
||||
|
||||
protected function afterRunAction()
|
||||
|
||||
@@ -37,8 +37,6 @@ class Install extends \Espo\Core\Upgrades\Actions\Base\Install
|
||||
$this->uninstallExtension();
|
||||
$this->deleteExtension();
|
||||
}
|
||||
|
||||
$this->copyExistingFiles();
|
||||
}
|
||||
|
||||
protected function afterRunAction()
|
||||
@@ -51,12 +49,11 @@ class Install extends \Espo\Core\Upgrades\Actions\Base\Install
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function copyExistingFiles()
|
||||
protected function backupExistingFiles()
|
||||
{
|
||||
$fileList = $this->getCopyFileList();
|
||||
$backupPath = $this->getPath('backupPath');
|
||||
parent::backupExistingFiles();
|
||||
|
||||
$res = $this->copy('', array($backupPath, self::FILES), false, $fileList);
|
||||
$backupPath = $this->getPath('backupPath');
|
||||
|
||||
/** copy scripts files */
|
||||
$packagePath = $this->getPackagePath();
|
||||
@@ -65,19 +62,6 @@ class Install extends \Espo\Core\Upgrades\Actions\Base\Install
|
||||
return $res;
|
||||
}
|
||||
|
||||
protected function restoreFiles()
|
||||
{
|
||||
$res = true;
|
||||
if ($this->isCopied) {
|
||||
$extensionFileList = $this->getCopyFileList();
|
||||
$res &= $this->getFileManager()->remove($extensionFileList);
|
||||
}
|
||||
|
||||
$res &= parent::restoreFiles();
|
||||
|
||||
return $res;
|
||||
}
|
||||
|
||||
protected function isNew()
|
||||
{
|
||||
$extensionEntity = $this->getExtensionEntity();
|
||||
@@ -208,7 +192,11 @@ class Install extends \Espo\Core\Upgrades\Actions\Base\Install
|
||||
{
|
||||
$extensionEntity = $this->getExtensionEntity();
|
||||
|
||||
$this->executeAction(ExtensionManager::UNINSTALL, $extensionEntity->get('id'));
|
||||
$this->executeAction(ExtensionManager::UNINSTALL, array(
|
||||
'id' => $extensionEntity->get('id'),
|
||||
'isNotRunScriptAfter' => true,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -220,7 +208,7 @@ class Install extends \Espo\Core\Upgrades\Actions\Base\Install
|
||||
{
|
||||
$extensionEntity = $this->getExtensionEntity();
|
||||
|
||||
$this->executeAction(ExtensionManager::DELETE, $extensionEntity->get('id'));
|
||||
$this->executeAction(ExtensionManager::DELETE, array('id' => $extensionEntity->get('id')));
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -35,29 +35,15 @@ class Uninstall extends \Espo\Core\Upgrades\Actions\Base\Uninstall
|
||||
*/
|
||||
protected function getExtensionEntity()
|
||||
{
|
||||
return $this->extensionEntity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Extension Entity
|
||||
*
|
||||
* @param \Espo\Entities\Extension $extensionEntity [description]
|
||||
*/
|
||||
protected function setExtensionEntity(\Espo\Entities\Extension $extensionEntity)
|
||||
{
|
||||
$this->extensionEntity = $extensionEntity;
|
||||
}
|
||||
|
||||
protected function beforeRunAction()
|
||||
{
|
||||
$processId = $this->getProcessId();
|
||||
|
||||
/** get extension entity */
|
||||
$extensionEntity = $this->getEntityManager()->getEntity('Extension', $processId);
|
||||
if (!isset($extensionEntity)) {
|
||||
throw new Error('Extension Entity not found.');
|
||||
if (!isset($this->extensionEntity)) {
|
||||
$processId = $this->getProcessId();
|
||||
$this->extensionEntity = $this->getEntityManager()->getEntity('Extension', $processId);
|
||||
if (!isset($this->extensionEntity)) {
|
||||
throw new Error('Extension Entity not found.');
|
||||
}
|
||||
}
|
||||
$this->setExtensionEntity($extensionEntity);
|
||||
|
||||
return $this->extensionEntity;
|
||||
}
|
||||
|
||||
protected function afterRunAction()
|
||||
@@ -68,4 +54,14 @@ class Uninstall extends \Espo\Core\Upgrades\Actions\Base\Uninstall
|
||||
$extensionEntity->set('isInstalled', false);
|
||||
$this->getEntityManager()->saveEntity($extensionEntity);
|
||||
}
|
||||
|
||||
protected function getRestoreFileList()
|
||||
{
|
||||
if (!isset($this->data['restoreFileList'])) {
|
||||
$extensionEntity = $this->getExtensionEntity();
|
||||
$this->data['restoreFileList'] = $extensionEntity->get('fileList');
|
||||
}
|
||||
|
||||
return $this->data['restoreFileList'];
|
||||
}
|
||||
}
|
||||
@@ -24,17 +24,12 @@ namespace Espo\Core\Upgrades\Actions\Upgrade;
|
||||
|
||||
class Install extends \Espo\Core\Upgrades\Actions\Base\Install
|
||||
{
|
||||
protected function systemRebuild()
|
||||
protected function finalize()
|
||||
{
|
||||
$manifest = $this->getManifest();
|
||||
|
||||
$res = $this->getConfig()->set('version', $manifest['version']);
|
||||
if (method_exists($this->getConfig(), 'save')) {
|
||||
$res = $this->getConfig()->save();
|
||||
}
|
||||
$res &= parent::systemRebuild();
|
||||
|
||||
return $res;
|
||||
$this->getConfig()->set('version', $manifest['version']);
|
||||
$this->getConfig()->save();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -29,6 +29,8 @@ class Manager
|
||||
{
|
||||
private $permission;
|
||||
|
||||
private $permissionDeniedList = array();
|
||||
|
||||
public function __construct(\Espo\Core\Utils\Config $config = null)
|
||||
{
|
||||
$params = null;
|
||||
@@ -774,5 +776,73 @@ return '.var_export($content, true).';
|
||||
?>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if $paths are writable. Permission denied list are defined in getLastPermissionDeniedList()
|
||||
*
|
||||
* @param array $paths
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function isWritableList(array $paths)
|
||||
{
|
||||
$permissionDeniedList = array();
|
||||
|
||||
$result = true;
|
||||
foreach ($paths as $path) {
|
||||
$rowResult = $this->isWritable($path);
|
||||
if (!$rowResult) {
|
||||
$permissionDeniedList[] = $path;
|
||||
}
|
||||
$result &= $rowResult;
|
||||
}
|
||||
|
||||
if (!empty($permissionDeniedList)) {
|
||||
$this->permissionDeniedList = $this->getPermissionUtils()->arrangePermissionList($permissionDeniedList);
|
||||
}
|
||||
|
||||
return (bool) $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get last permission denied list
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getLastPermissionDeniedList()
|
||||
{
|
||||
return $this->permissionDeniedList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if $path is writable
|
||||
*
|
||||
* @param string | array $path
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function isWritable($path)
|
||||
{
|
||||
$existFile = $this->getExistsPath($path);
|
||||
|
||||
return is_writable($existFile);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get exists path. Ex. if check /var/www/espocrm/custom/someFile.php and this file doesn't extist, result will be /var/www/espocrm/custom
|
||||
*
|
||||
* @param string | array $path
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getExistsPath($path)
|
||||
{
|
||||
$fullPath = $this->concatPaths($path);
|
||||
|
||||
if (!file_exists($fullPath)) {
|
||||
$fullPath = $this->getExistsPath(pathinfo($fullPath, PATHINFO_DIRNAME));
|
||||
}
|
||||
|
||||
return $fullPath;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -94,6 +94,7 @@ return array (
|
||||
'assignmentEmailNotifications' => false,
|
||||
'assignmentEmailNotificationsEntityList' => array('Lead', 'Opportunity', 'Task', 'Case'),
|
||||
'emailMessageMaxSize' => 10,
|
||||
'notificationsCheckInterval' => 10,
|
||||
'isInstalled' => false,
|
||||
);
|
||||
|
||||
|
||||
@@ -33,7 +33,9 @@ class CheckEmailAccounts extends \Espo\Core\Jobs\Base
|
||||
foreach ($collection as $entity) {
|
||||
try {
|
||||
$service->fetchFromMailServer($entity);
|
||||
} catch (\Exception $e) {}
|
||||
} catch (\Exception $e) {
|
||||
$GLOBALS['log']->error('Job CheckEmailAccounts: [' . $e->getCode() . '] ' .$e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
@@ -33,9 +33,11 @@ class CheckInboundEmails extends \Espo\Core\Jobs\Base
|
||||
foreach ($collection as $entity) {
|
||||
try {
|
||||
$service->fetchFromMailServer($entity);
|
||||
} catch (\Exception $e) {}
|
||||
} catch (\Exception $e) {
|
||||
$GLOBALS['log']->error('Job CheckInboundEmails: [' . $e->getCode() . '] ' .$e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -204,9 +204,13 @@ class InboundEmail extends \Espo\Services\Record
|
||||
|
||||
$message = $storage->getMessage($id);
|
||||
|
||||
$email = $importer->importMessage($message, $userId, array($teamId));
|
||||
try {
|
||||
$email = $importer->importMessage($message, $userId, array($teamId));
|
||||
} catch (\Exception $e) {
|
||||
$GLOBALS['log']->error('InboundEmail (Importing Message): [' . $e->getCode() . '] ' .$e->getMessage());
|
||||
}
|
||||
|
||||
if ($email) {
|
||||
if (!empty($email)) {
|
||||
$this->noteAboutEmail($email);
|
||||
|
||||
if ($inboundEmail->get('createCase')) {
|
||||
|
||||
@@ -2,13 +2,13 @@
|
||||
{
|
||||
"label":"",
|
||||
"rows":[
|
||||
[{"name":"dateSent"}],
|
||||
[{"name":"parent"}],
|
||||
[{"name":"from"}],
|
||||
[{"name":"dateSent"}, {"name":"assignedUser"}],
|
||||
[{"name":"parent"}, {"name":"teams"}],
|
||||
[{"name":"from"}, {"name":"cc"}],
|
||||
[{"name":"to"}],
|
||||
[{"name":"name"}],
|
||||
[{"name":"body"}],
|
||||
[{"name":"attachments"}]
|
||||
[{"name":"name", "fullWidth": true}],
|
||||
[{"name":"body", "fullWidth": true}],
|
||||
[{"name":"attachments", "fullWidth": true}]
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
@@ -2,13 +2,13 @@
|
||||
{
|
||||
"label":"",
|
||||
"rows":[
|
||||
[{"name":"dateSent", "readOnly": true}],
|
||||
[{"name":"parent"}],
|
||||
[{"name":"from", "readOnly": true}],
|
||||
[{"name":"dateSent", "readOnly": true}, {"name":"assignedUser"}],
|
||||
[{"name":"parent"}, {"name":"teams"}],
|
||||
[{"name":"from", "readOnly": true}, {"name":"cc", "readOnly": true}],
|
||||
[{"name":"to", "readOnly": true}],
|
||||
[{"name":"name", "readOnly": true}],
|
||||
[{"name":"body", "readOnly": true}],
|
||||
[{"name":"attachments", "readOnly": true}]
|
||||
[{"name":"name", "readOnly": true, "fullWidth": true}],
|
||||
[{"name":"body", "readOnly": true, "fullWidth": true}],
|
||||
[{"name":"attachments", "readOnly": true, "fullWidth": true}]
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
@@ -198,9 +198,13 @@ class EmailAccount extends Record
|
||||
|
||||
$message = $storage->getMessage($id);
|
||||
|
||||
$email = $importer->importMessage($message, $userId, array($teamId));
|
||||
try {
|
||||
$email = $importer->importMessage($message, $userId, array($teamId));
|
||||
} catch (\Exception $e) {
|
||||
$GLOBALS['log']->error('EmailAccount (Importing Message): [' . $e->getCode() . '] ' .$e->getMessage());
|
||||
}
|
||||
|
||||
if ($email) {
|
||||
if (!empty($email)) {
|
||||
$this->noteAboutEmail($email);
|
||||
}
|
||||
|
||||
|
||||
Generated
+12
-5
@@ -1592,11 +1592,18 @@
|
||||
"time": "2014-01-07 13:28:54"
|
||||
}
|
||||
],
|
||||
"aliases": [],
|
||||
"aliases": [
|
||||
|
||||
],
|
||||
"minimum-stability": "stable",
|
||||
"stability-flags": [],
|
||||
"stability-flags": [
|
||||
|
||||
],
|
||||
"prefer-stable": false,
|
||||
"prefer-lowest": false,
|
||||
"platform": [],
|
||||
"platform-dev": []
|
||||
"platform": [
|
||||
|
||||
],
|
||||
"platform-dev": [
|
||||
|
||||
]
|
||||
}
|
||||
|
||||
@@ -9,8 +9,12 @@
|
||||
{{/unless}}
|
||||
|
||||
<div class="stream-head-container">
|
||||
{{{avatar}}}
|
||||
<span class="text-muted message">{{{message}}}</span>
|
||||
<div class="pull-left">
|
||||
{{{avatar}}}
|
||||
</div>
|
||||
<div class="stream-head-text-container">
|
||||
<span class="text-muted message">{{{message}}}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="stream-date-container">
|
||||
<span class="text-muted small">{{{createdAt}}}</span>
|
||||
|
||||
@@ -9,8 +9,12 @@
|
||||
{{/unless}}
|
||||
|
||||
<div class="stream-head-container">
|
||||
{{{avatar}}}
|
||||
<span class="text-muted message">{{{message}}}</span>
|
||||
<div class="pull-left">
|
||||
{{{avatar}}}
|
||||
</div>
|
||||
<div class="stream-head-text-container">
|
||||
<span class="text-muted message">{{{message}}}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="stream-date-container">
|
||||
|
||||
@@ -9,11 +9,15 @@
|
||||
{{/unless}}
|
||||
|
||||
<div class="stream-head-container">
|
||||
{{{avatar}}}
|
||||
{{#if statusText}}
|
||||
<span class="label label-{{statusStyle}}">{{statusText}}</span>
|
||||
{{/if}}
|
||||
<span class="text-muted message">{{{message}}}</span>
|
||||
<div class="pull-left">
|
||||
{{{avatar}}}
|
||||
</div>
|
||||
<div class="stream-head-text-container">
|
||||
{{#if statusText}}
|
||||
<span class="label label-{{statusStyle}}">{{statusText}}</span>
|
||||
{{/if}}
|
||||
<span class="text-muted message">{{{message}}}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="stream-date-container">
|
||||
|
||||
@@ -9,10 +9,14 @@
|
||||
{{/unless}}
|
||||
|
||||
<div class="stream-head-container">
|
||||
{{{avatar}}}
|
||||
<span class="text-muted"><span class="glyphicon glyphicon-envelope "></span>
|
||||
{{{message}}}
|
||||
</span>
|
||||
<div class="pull-left">
|
||||
{{{avatar}}}
|
||||
</div>
|
||||
<div class="stream-head-text-container">
|
||||
<span class="text-muted"><span class="glyphicon glyphicon-envelope "></span>
|
||||
{{{message}}}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
@@ -9,8 +9,12 @@
|
||||
{{/unless}}
|
||||
|
||||
<div class="stream-head-container">
|
||||
{{{avatar}}}
|
||||
<span class="text-muted message">{{{message}}}</span>
|
||||
<div class="pull-left">
|
||||
{{{avatar}}}
|
||||
</div>
|
||||
<div class="stream-head-text-container">
|
||||
<span class="text-muted message">{{{message}}}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{#if post}}
|
||||
|
||||
@@ -9,9 +9,13 @@
|
||||
{{/unless}}
|
||||
|
||||
<div class="stream-head-container">
|
||||
{{{avatar}}}
|
||||
<span class="label label-{{style}}">{{statusText}}</span>
|
||||
<span class="text-muted message">{{{message}}}</span>
|
||||
<div class="pull-left">
|
||||
{{{avatar}}}
|
||||
</div>
|
||||
<div class="stream-head-text-container">
|
||||
<span class="label label-{{style}}">{{statusText}}</span>
|
||||
<span class="text-muted message">{{{message}}}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="stream-date-container">
|
||||
|
||||
@@ -9,8 +9,12 @@
|
||||
{{/unless}}
|
||||
|
||||
<div class="stream-head-container">
|
||||
{{{avatar}}}
|
||||
<span class="text-muted message">{{{message}}}</span> <a href="javascript:" data-action="expandDetails"><span class="glyphicon glyphicon-chevron-down"></span></a>
|
||||
<div class="pull-left">
|
||||
{{{avatar}}}
|
||||
</div>
|
||||
<div class="stream-head-text-container">
|
||||
<span class="text-muted message">{{{message}}}</span> <a href="javascript:" data-action="expandDetails"><span class="glyphicon glyphicon-chevron-down"></span></a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="hidden details stream-details-container">
|
||||
|
||||
@@ -17,17 +17,16 @@
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
|
||||
************************************************************************/
|
||||
************************************************************************/
|
||||
|
||||
Espo.define('Views.Email.Record.Compose', 'Views.Record.Edit', function (Dep) {
|
||||
|
||||
|
||||
return Dep.extend({
|
||||
|
||||
|
||||
isWide: true,
|
||||
|
||||
|
||||
sideView: false,
|
||||
|
||||
|
||||
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
|
||||
@@ -17,34 +17,34 @@
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
|
||||
************************************************************************/
|
||||
************************************************************************/
|
||||
|
||||
Espo.define('Views.Email.Record.Detail', 'Views.Record.Detail', function (Dep) {
|
||||
|
||||
return Dep.extend({
|
||||
|
||||
|
||||
layoutNameConfigure: function () {
|
||||
if (!this.model.isNew()) {
|
||||
var isRestricted = false;
|
||||
|
||||
|
||||
if (this.model.get('status') == 'Sent') {
|
||||
this.layoutName += 'Restricted';
|
||||
isRestricted = true;
|
||||
}
|
||||
|
||||
if (this.model.get('status') == 'Archived' && this.model.get('createdById') == 'system') {
|
||||
|
||||
if (this.model.get('status') == 'Archived' && this.model.get('createdById') == 'system') {
|
||||
this.layoutName += 'Restricted';
|
||||
isRestricted = true;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
init: function () {
|
||||
Dep.prototype.init.call(this);
|
||||
|
||||
|
||||
this.layoutNameConfigure();
|
||||
},
|
||||
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -17,19 +17,25 @@
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
|
||||
************************************************************************/
|
||||
************************************************************************/
|
||||
|
||||
Espo.define('Views.Email.Record.EditQuick', 'Views.Email.Record.Edit', function (Dep, Detail) {
|
||||
|
||||
return Dep.extend({
|
||||
|
||||
|
||||
|
||||
isWide: true,
|
||||
|
||||
sideView: false,
|
||||
|
||||
init: function () {
|
||||
Dep.prototype.init.call(this);
|
||||
|
||||
|
||||
Dep.prototype.init.call(this);
|
||||
this.columnCount = 2;
|
||||
},
|
||||
|
||||
|
||||
setup: function () {
|
||||
Dep.prototype.setup.call(this);
|
||||
},
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -17,20 +17,19 @@
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
|
||||
************************************************************************/
|
||||
************************************************************************/
|
||||
|
||||
Espo.define('Views.Email.Record.Edit', ['Views.Record.Edit', 'Views.Email.Record.Detail'], function (Dep, Detail) {
|
||||
|
||||
return Dep.extend({
|
||||
|
||||
|
||||
init: function () {
|
||||
Dep.prototype.init.call(this);
|
||||
|
||||
|
||||
Detail.prototype.layoutNameConfigure.call(this);
|
||||
|
||||
Detail.prototype.layoutNameConfigure.call(this);
|
||||
|
||||
},
|
||||
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -17,88 +17,93 @@
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
|
||||
************************************************************************/
|
||||
************************************************************************/
|
||||
|
||||
Espo.define('Views.Fields.Wysiwyg', ['Views.Fields.Text', 'lib!Summernote'], function (Dep, Summernote) {
|
||||
|
||||
return Dep.extend({
|
||||
|
||||
type: 'wysiwyg',
|
||||
|
||||
return Dep.extend({
|
||||
|
||||
type: 'wysiwyg',
|
||||
|
||||
detailTemplate: 'fields.wysiwyg.detail',
|
||||
|
||||
|
||||
editTemplate: 'fields.wysiwyg.edit',
|
||||
|
||||
setup: function () {
|
||||
Dep.prototype.setup.call(this);
|
||||
|
||||
this.listenTo(this.model, 'change:isHtml', function (model) {
|
||||
|
||||
setup: function () {
|
||||
Dep.prototype.setup.call(this);
|
||||
|
||||
this.listenTo(this.model, 'change:isHtml', function (model) {
|
||||
if (!model.has('isHtml') || model.get('isHtml')) {
|
||||
this.enableWysiwygMode();
|
||||
} else {
|
||||
this.disableWysiwygMode();
|
||||
}
|
||||
}.bind(this));
|
||||
|
||||
|
||||
this.once('remove', function () {
|
||||
$('body > .tooltip').remove();
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
afterRender: function () {
|
||||
Dep.prototype.afterRender.call(this);
|
||||
|
||||
Dep.prototype.afterRender.call(this);
|
||||
|
||||
var language = this.getConfig().get('language');
|
||||
|
||||
|
||||
if (!(language in $.summernote.lang)) {
|
||||
$.summernote.lang[language] = this.getLanguage().translate('summernote', 'sets');
|
||||
}
|
||||
|
||||
|
||||
if (this.mode == 'edit') {
|
||||
if (!this.model.has('isHtml') || this.model.get('isHtml')) {
|
||||
this.enableWysiwygMode();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (this.mode == 'detail') {
|
||||
if (!this.model.has('isHtml') || this.model.get('isHtml')) {
|
||||
this.$el.find('iframe').removeClass('hidden');
|
||||
var iframe = this.$el.find('iframe').get(0);
|
||||
var document = iframe.contentWindow.document;
|
||||
|
||||
var link = '<link href="client/css/iframe.css" rel="stylesheet" type="text/css"></link>'
|
||||
|
||||
document.open('text/html', 'replace');
|
||||
var iframe = this.iframe = this.$el.find('iframe').get(0);
|
||||
|
||||
iframe.onload = function () {
|
||||
var height = $(iframe).contents().find('html body').height();
|
||||
iframe.style.height = height + 'px';
|
||||
};
|
||||
|
||||
var doc = iframe.contentWindow.document;
|
||||
|
||||
var link = '<link href="client/css/iframe.css" rel="stylesheet" type="text/css"></link>';
|
||||
|
||||
doc.open('text/html', 'replace');
|
||||
var body = this.model.get('body');
|
||||
body += link;
|
||||
|
||||
document.write(body);
|
||||
|
||||
document.close();
|
||||
iframe.style.height = document.body.scrollHeight + 'px';
|
||||
|
||||
doc.write(body);
|
||||
doc.close();
|
||||
|
||||
} else {
|
||||
this.$el.find('.plain').removeClass('hidden');
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
enableWysiwygMode: function () {
|
||||
this.$summernote = this.$element.summernote({
|
||||
height: 250,
|
||||
lang: this.getConfig().get('language'),
|
||||
onImageUpload: function (files, editor, welEditable) {
|
||||
var file = files[0];
|
||||
this.notify('Uploading...');
|
||||
this.getModelFactory().create('Attachment', function (attachment) {
|
||||
this.notify('Uploading...');
|
||||
this.getModelFactory().create('Attachment', function (attachment) {
|
||||
var fileReader = new FileReader();
|
||||
fileReader.onload = function (e) {
|
||||
fileReader.onload = function (e) {
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
url: 'Attachment/action/upload',
|
||||
data: e.target.result,
|
||||
contentType: 'multipart/encrypted',
|
||||
}).done(function (data) {
|
||||
attachment.id = data.attachmentId;
|
||||
}).done(function (data) {
|
||||
attachment.id = data.attachmentId;
|
||||
attachment.set('name', file.name);
|
||||
attachment.set('type', file.type);
|
||||
attachment.set('role', 'Inline Attachment');
|
||||
@@ -108,12 +113,12 @@ Espo.define('Views.Fields.Wysiwyg', ['Views.Fields.Text', 'lib!Summernote'], fun
|
||||
var url = '?entryPoint=attachment&id=' + attachment.id;
|
||||
editor.insertImage(welEditable, url);
|
||||
this.notify(false);
|
||||
}, this);
|
||||
}, this);
|
||||
attachment.save();
|
||||
}.bind(this));
|
||||
}.bind(this);
|
||||
fileReader.readAsDataURL(file);
|
||||
|
||||
|
||||
}, this);
|
||||
}.bind(this),
|
||||
toolbar: [
|
||||
@@ -121,23 +126,23 @@ Espo.define('Views.Fields.Wysiwyg', ['Views.Fields.Text', 'lib!Summernote'], fun
|
||||
['style', ['bold', 'italic', 'underline', 'clear']],
|
||||
['color', ['color']],
|
||||
['para', ['ul', 'ol', 'paragraph']],
|
||||
['height', ['height']],
|
||||
['height', ['height']],
|
||||
['table', ['table', 'link', 'picture']],
|
||||
['misc',['codeview']]
|
||||
]
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
disableWysiwygMode: function () {
|
||||
this.$element.destroy();
|
||||
},
|
||||
|
||||
|
||||
fetch: function () {
|
||||
var data = {};
|
||||
if (!this.model.has('isHtml') || this.model.get('isHtml')) {
|
||||
data[this.name] = this.$element.code();
|
||||
} else {
|
||||
data[this.name] = this.$element.val();
|
||||
data[this.name] = this.$element.val();
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
@@ -25,15 +25,20 @@ Espo.define('Views.Notifications.Badge', 'View', function (Dep) {
|
||||
|
||||
template: 'notifications.badge',
|
||||
|
||||
updateFrequency: 10,
|
||||
notificationsCheckInterval: 10,
|
||||
|
||||
timeout: null,
|
||||
|
||||
popupNotificationsData: null,
|
||||
|
||||
soundPath: 'client/sounds/pop_cork',
|
||||
|
||||
events: {
|
||||
'click a[data-action="showNotifications"]': function (e) {
|
||||
this.showNotifications();
|
||||
setTimeout(function () {
|
||||
this.checkUpdates();
|
||||
}.bind(this), 100);
|
||||
},
|
||||
},
|
||||
|
||||
@@ -47,6 +52,8 @@ Espo.define('Views.Notifications.Badge', 'View', function (Dep) {
|
||||
}
|
||||
}, this);
|
||||
|
||||
this.notificationsCheckInterval = this.getConfig().get('notificationsCheckInterval') || this.notificationsCheckInterval;
|
||||
|
||||
this.popupCheckIteration = 0;
|
||||
this.lastId = 0;
|
||||
this.shownNotificationIds = [];
|
||||
@@ -74,7 +81,7 @@ Espo.define('Views.Notifications.Badge', 'View', function (Dep) {
|
||||
this.$badge = this.$el.find('.notifications-button');
|
||||
this.$icon = this.$el.find('.notifications-button .icon');
|
||||
|
||||
this.checkUpdates();
|
||||
this.runCheckUpdates(true);
|
||||
|
||||
this.$popupContainer = $('#popup-notifications-container');
|
||||
if (!$(this.$popupContainer).size()) {
|
||||
@@ -87,6 +94,17 @@ Espo.define('Views.Notifications.Badge', 'View', function (Dep) {
|
||||
}
|
||||
},
|
||||
|
||||
playSound: function () {
|
||||
var html = '' +
|
||||
'<audio autoplay="autoplay">'+
|
||||
'<source src="' + this.soundPath + '.mp3" type="audio/mpeg" />'+
|
||||
'<source src="' + this.soundPath + '.ogg" type="audio/ogg" />'+
|
||||
'<embed hidden="true" autostart="true" loop="false" src="' + this.soundPath +'.mp3" />'+
|
||||
'</audio>';
|
||||
$(html).get(0).volume = 0.3;
|
||||
$(html).get(0).play();
|
||||
},
|
||||
|
||||
showNotRead: function (count) {
|
||||
this.$icon.addClass('warning');
|
||||
this.$badge.attr('title', this.translate('New notifications') + ': ' + count);
|
||||
@@ -97,18 +115,26 @@ Espo.define('Views.Notifications.Badge', 'View', function (Dep) {
|
||||
this.$badge.attr('title', '');
|
||||
},
|
||||
|
||||
checkUpdates: function () {
|
||||
checkUpdates: function (isFirstCheck) {
|
||||
$.ajax('Notification/action/notReadCount').done(function (count) {
|
||||
if (!isFirstCheck && count > this.unreadCount) {
|
||||
this.playSound();
|
||||
}
|
||||
this.unreadCount = count;
|
||||
if (count) {
|
||||
this.showNotRead(count);
|
||||
} else {
|
||||
this.hideNotRead();
|
||||
}
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
runCheckUpdates: function (isFirstCheck) {
|
||||
this.checkUpdates(isFirstCheck);
|
||||
|
||||
this.timeout = setTimeout(function () {
|
||||
this.checkUpdates();
|
||||
}.bind(this), this.updateFrequency * 1000);
|
||||
this.runCheckUpdates();
|
||||
}.bind(this), this.notificationsCheckInterval * 1000);
|
||||
},
|
||||
|
||||
checkPopupNotifications: function (name) {
|
||||
@@ -185,7 +211,7 @@ Espo.define('Views.Notifications.Badge', 'View', function (Dep) {
|
||||
|
||||
var $container = $('<div>').attr('id', 'notifications-panel').css({
|
||||
'position': 'absolute',
|
||||
'width': '500px',
|
||||
'width': '600px',
|
||||
'z-index': 1001,
|
||||
'right': 0,
|
||||
'left': 'auto'
|
||||
|
||||
@@ -47,7 +47,6 @@ Espo.define('Views.PopupNotification', 'View', function (Dep) {
|
||||
.addClass(className)
|
||||
.addClass('popup-notification-' + this.style)
|
||||
.appendTo('#popup-notifications-container');
|
||||
|
||||
this.setElement(containerSelector);
|
||||
}, this);
|
||||
|
||||
@@ -60,7 +59,7 @@ Espo.define('Views.PopupNotification', 'View', function (Dep) {
|
||||
this.once('after:render', function () {
|
||||
this.onShow();
|
||||
}.bind(this));
|
||||
|
||||
|
||||
this.once('remove', function () {
|
||||
$(containerSelector).remove();
|
||||
});
|
||||
|
||||
@@ -168,16 +168,12 @@ Espo.define('Views.Site.Navbar', 'View', function (Dep) {
|
||||
|
||||
var $navbar = $('#navbar .navbar');
|
||||
|
||||
var processMaxCount = 20;
|
||||
var processCount = 0;
|
||||
var processUpdateWidth = function () {
|
||||
processCount++;
|
||||
if (processCount > processMaxCount) return;
|
||||
if ($navbar.height() > 45) {
|
||||
updateWidth();
|
||||
setTimeout(function () {
|
||||
processUpdateWidth();
|
||||
}, 400);
|
||||
}, 200);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -675,6 +675,10 @@ body > footer > p a:visited {
|
||||
padding-bottom: 3px;
|
||||
}
|
||||
|
||||
.stream-head-text-container {
|
||||
padding-left: 26px;
|
||||
}
|
||||
|
||||
.stream-head-container > img.avatar {
|
||||
display: inline-block;
|
||||
margin-top: -3px;
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "espocrm",
|
||||
"version": "3.0.0",
|
||||
"version": "3.0.1",
|
||||
"description": "",
|
||||
"main": "index.php",
|
||||
"repository": {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<?php
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
@@ -389,7 +389,23 @@ class ManagerTest extends \PHPUnit_Framework_TestCase
|
||||
}
|
||||
}
|
||||
|
||||
public function existsPathSet()
|
||||
{
|
||||
return array(
|
||||
array( 'application/Espo/Core/Application.php', 'application/Espo/Core/Application.php', ),
|
||||
array( 'application/Espo/Core/NotRealApplication.php', 'application/Espo/Core'),
|
||||
array( array('application', 'Espo/Core', 'NotRealApplication.php'), 'application/Espo/Core'),
|
||||
array( 'application/NoEspo/Core/Application.php', 'application'),
|
||||
array( 'notRealPath/Espo/Core/Application.php', '.'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider existsPathSet
|
||||
*/
|
||||
public function testGetExistsPath($input, $result)
|
||||
{
|
||||
$this->assertEquals($result, $this->reflection->invokeMethod('getExistsPath', array($input)) );
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
Reference in New Issue
Block a user