Compare commits
33 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e0c3530ae5 | |||
| 0b6c5c2862 | |||
| ce9ff10cb0 | |||
| de688bfa12 | |||
| 95fd66a7a6 | |||
| 171df33736 | |||
| ef05e4e9f4 | |||
| b67580ee1d | |||
| ac56c3f79e | |||
| e218056683 | |||
| e74b90d048 | |||
| b2a653a3dc | |||
| 48ee7d4cc7 | |||
| 1d38f80748 | |||
| c712366737 | |||
| 71f40fd440 | |||
| 9049ebfa8c | |||
| 632ee66155 | |||
| 4446bc3167 | |||
| 785934c7cc | |||
| ac36096d55 | |||
| 8ec8cb3c56 | |||
| 1f7cb2402d | |||
| 3825893ec1 | |||
| a4ed78b953 | |||
| eed9059913 | |||
| 20a1053413 | |||
| e814ea9ec6 | |||
| de16e9a9ce | |||
| 62fba991e2 | |||
| 832c63e014 | |||
| 0194d364b8 | |||
| 1c7785f6ba |
@@ -18,13 +18,13 @@
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
|
||||
************************************************************************/
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Controllers;
|
||||
|
||||
use \Espo\Core\Exceptions\BadRequest;
|
||||
|
||||
class App extends \Espo\Core\Controllers\Record
|
||||
class App extends \Espo\Core\Controllers\Base
|
||||
{
|
||||
public function actionUser()
|
||||
{
|
||||
|
||||
@@ -18,16 +18,16 @@
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
|
||||
************************************************************************/
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Controllers;
|
||||
|
||||
use \Espo\Core\Exceptions\Forbidden;
|
||||
|
||||
class EmailAccount extends \Espo\Core\Controllers\Record
|
||||
{
|
||||
{
|
||||
public function actionGetFolders($params, $data, $request)
|
||||
{
|
||||
{
|
||||
return $this->getRecordService()->getFolders(array(
|
||||
'host' => $request->get('host'),
|
||||
'port' => $request->get('port'),
|
||||
|
||||
@@ -28,10 +28,10 @@ use \Espo\Core\Exceptions\Forbidden;
|
||||
class ExternalAccount extends \Espo\Core\Controllers\Record
|
||||
{
|
||||
public static $defaultAction = 'list';
|
||||
|
||||
|
||||
public function actionList($params, $data, $request)
|
||||
{
|
||||
$integrations = $this->getEntityManager()->getRepository('Integration')->find();
|
||||
$integrations = $this->getEntityManager()->getRepository('Integration')->find();
|
||||
$arr = array();
|
||||
foreach ($integrations as $entity) {
|
||||
if ($entity->get('enabled') && $this->getMetadata()->get('integrations.' . $entity->id .'.allowUserAccounts')) {
|
||||
@@ -44,22 +44,22 @@ class ExternalAccount extends \Espo\Core\Controllers\Record
|
||||
'list' => $arr
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
public function actionGetOAuth2Info($params, $data, $request)
|
||||
{
|
||||
$id = $request->get('id');
|
||||
list($integration, $userId) = explode('__', $id);
|
||||
|
||||
|
||||
|
||||
if ($this->getUser()->id != $userId) {
|
||||
throw new Forbidden();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$entity = $this->getEntityManager()->getEntity('Integration', $integration);
|
||||
if ($entity) {
|
||||
return array(
|
||||
'clientId' => $entity->get('clientId'),
|
||||
'redirectUri' => $this->getConfig()->get('siteUrl') . '/oauthcallback',
|
||||
'redirectUri' => $this->getConfig()->get('siteUrl') . '?entryPoint=oauthCallback',
|
||||
'isConnected' => $this->getRecordService()->ping($integration, $userId)
|
||||
);
|
||||
}
|
||||
@@ -67,57 +67,57 @@ class ExternalAccount extends \Espo\Core\Controllers\Record
|
||||
|
||||
public function actionRead($params, $data, $request)
|
||||
{
|
||||
list($integration, $userId) = explode('__', $params['id']);
|
||||
list($integration, $userId) = explode('__', $params['id']);
|
||||
|
||||
if ($this->getUser()->id != $userId) {
|
||||
throw new Forbidden();
|
||||
}
|
||||
|
||||
$entity = $this->getEntityManager()->getEntity('ExternalAccount', $params['id']);
|
||||
}
|
||||
|
||||
$entity = $this->getEntityManager()->getEntity('ExternalAccount', $params['id']);
|
||||
return $entity->toArray();
|
||||
}
|
||||
|
||||
|
||||
public function actionUpdate($params, $data)
|
||||
{
|
||||
return $this->actionPatch($params, $data);
|
||||
}
|
||||
|
||||
|
||||
public function actionPatch($params, $data)
|
||||
{
|
||||
list($integration, $userId) = explode('__', $params['id']);
|
||||
|
||||
|
||||
|
||||
if ($this->getUser()->id != $userId) {
|
||||
throw new Forbidden();
|
||||
}
|
||||
|
||||
|
||||
if (isset($data['enabled']) && !$data['enabled']) {
|
||||
$data['data'] = null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$entity = $this->getEntityManager()->getEntity('ExternalAccount', $params['id']);
|
||||
$entity->set($data);
|
||||
$this->getEntityManager()->saveEntity($entity);
|
||||
|
||||
return $entity->toArray();
|
||||
|
||||
return $entity->toArray();
|
||||
}
|
||||
|
||||
|
||||
public function actionAuthorizationCode($params, $data, $request)
|
||||
{
|
||||
if (!$request->isPost()) {
|
||||
throw new Error('Bad HTTP method type.');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$id = $data['id'];
|
||||
$code = $data['code'];
|
||||
|
||||
list($integration, $userId) = explode('__', $id);
|
||||
|
||||
list($integration, $userId) = explode('__', $id);
|
||||
|
||||
if ($this->getUser()->id != $userId) {
|
||||
throw new Forbidden();
|
||||
}
|
||||
|
||||
$service = $this->getRecordService();
|
||||
|
||||
$service = $this->getRecordService();
|
||||
return $service->authorizationCode($integration, $userId, $code);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ class Layout extends \Espo\Core\Controllers\Base
|
||||
{
|
||||
$data = $this->getContainer()->get('layout')->get($params['scope'], $params['name']);
|
||||
if (empty($data)) {
|
||||
throw new NotFound("Layout " . $params['scope'] . ":" . $params['name'] . ' is not found');
|
||||
throw new NotFound("Layout " . $params['scope'] . ":" . $params['name'] . ' is not found.');
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
@@ -43,16 +43,18 @@ class Layout extends \Espo\Core\Controllers\Base
|
||||
if (!$this->getUser()->isAdmin()) {
|
||||
throw new Forbidden();
|
||||
}
|
||||
|
||||
$result = $this->getContainer()->get('layout')->set($data, $params['scope'], $params['name']);
|
||||
|
||||
$layoutManager = $this->getContainer()->get('layout');
|
||||
$layoutManager->set($data, $params['scope'], $params['name']);
|
||||
$result = $layoutManager->save();
|
||||
|
||||
if ($result === false) {
|
||||
throw new Error("Error while saving layout");
|
||||
throw new Error("Error while saving layout.");
|
||||
}
|
||||
|
||||
$this->getContainer()->get('dataManager')->updateCacheTimestamp();
|
||||
|
||||
return $this->getContainer()->get('layout')->get($params['scope'], $params['name']);
|
||||
return $layoutManager->get($params['scope'], $params['name']);
|
||||
}
|
||||
|
||||
public function actionPatch($params, $data)
|
||||
|
||||
@@ -221,7 +221,7 @@ class Container
|
||||
|
||||
private function loadScheduledJob()
|
||||
{
|
||||
return new \Espo\Core\Cron\ScheduledJob(
|
||||
return new \Espo\Core\Utils\ScheduledJob(
|
||||
$this
|
||||
);
|
||||
}
|
||||
|
||||
@@ -22,17 +22,18 @@
|
||||
|
||||
namespace Espo\Core;
|
||||
|
||||
use \PDO;
|
||||
use Espo\Core\Utils\Json;
|
||||
use Espo\Core\Exceptions\NotFound;
|
||||
|
||||
class CronManager
|
||||
{
|
||||
private $container;
|
||||
private $config;
|
||||
private $fileManager;
|
||||
private $entityManager;
|
||||
|
||||
private $scheduledJobCron;
|
||||
private $serviceCron;
|
||||
|
||||
private $jobService;
|
||||
private $scheduledJobService;
|
||||
private $scheduledJobUtil;
|
||||
|
||||
const PENDING = 'Pending';
|
||||
const RUNNING = 'Running';
|
||||
@@ -41,19 +42,18 @@ class CronManager
|
||||
|
||||
protected $lastRunTime = 'data/cache/application/cronLastRunTime.php';
|
||||
|
||||
|
||||
public function __construct(\Espo\Core\Container $container)
|
||||
{
|
||||
$this->container = $container;
|
||||
|
||||
$this->config = $this->container->get('config');
|
||||
$this->fileManager = $this->container->get('fileManager');
|
||||
$this->entityManager = $this->container->get('entityManager');
|
||||
$this->serviceFactory = $this->container->get('serviceFactory');
|
||||
|
||||
$this->scheduledJobCron = $this->container->get('scheduledJob');
|
||||
$this->serviceCron = new \Espo\Core\Cron\Service( $this->container->get('serviceFactory'));
|
||||
|
||||
$this->jobService = $this->container->get('serviceFactory')->create('job');
|
||||
$this->scheduledJobService = $this->container->get('serviceFactory')->create('scheduledJob');
|
||||
$this->scheduledJobUtil = $this->container->get('scheduledJob');
|
||||
$this->cronJob = new \Espo\Core\Utils\Cron\Job($this->config, $this->entityManager);
|
||||
$this->cronScheduledJob = new \Espo\Core\Utils\Cron\ScheduledJob($this->config, $this->entityManager);
|
||||
}
|
||||
|
||||
protected function getContainer()
|
||||
@@ -71,30 +71,34 @@ class CronManager
|
||||
return $this->fileManager;
|
||||
}
|
||||
|
||||
protected function getJobService()
|
||||
protected function getEntityManager()
|
||||
{
|
||||
return $this->jobService;
|
||||
return $this->entityManager;
|
||||
}
|
||||
|
||||
protected function getScheduledJobService()
|
||||
protected function getServiceFactory()
|
||||
{
|
||||
return $this->scheduledJobService;
|
||||
return $this->serviceFactory;
|
||||
}
|
||||
|
||||
protected function getScheduledJobCron()
|
||||
protected function getScheduledJobUtil()
|
||||
{
|
||||
return $this->scheduledJobCron;
|
||||
return $this->scheduledJobUtil;
|
||||
}
|
||||
|
||||
protected function getServiceCron()
|
||||
protected function getCronJob()
|
||||
{
|
||||
return $this->serviceCron;
|
||||
return $this->cronJob;
|
||||
}
|
||||
|
||||
protected function getCronScheduledJob()
|
||||
{
|
||||
return $this->cronScheduledJob;
|
||||
}
|
||||
|
||||
protected function getLastRunTime()
|
||||
{
|
||||
$lastRunTime = $this->getFileManager()->getContents($this->lastRunTime);
|
||||
$lastRunTime = $this->getFileManager()->getPhpContents($this->lastRunTime);
|
||||
if (!is_int($lastRunTime)) {
|
||||
$lastRunTime = time() - (intval($this->getConfig()->get('cron.minExecutionTime')) + 60);
|
||||
}
|
||||
@@ -104,7 +108,7 @@ class CronManager
|
||||
|
||||
protected function setLastRunTime($time)
|
||||
{
|
||||
return $this->getFileManager()->putContentsPHP($this->lastRunTime, $time);
|
||||
return $this->getFileManager()->putPhpContents($this->lastRunTime, $time);
|
||||
}
|
||||
|
||||
protected function checkLastRunTime()
|
||||
@@ -120,65 +124,144 @@ class CronManager
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Run Cron
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function run()
|
||||
{
|
||||
if (!$this->checkLastRunTime()) {
|
||||
$GLOBALS['log']->info('Cron Manager: Stop cron running, too frequency execution');
|
||||
return; //stop cron running, too frequency execution
|
||||
$GLOBALS['log']->info('CronManager: Stop cron running, too frequent execution.');
|
||||
return;
|
||||
}
|
||||
|
||||
$this->setLastRunTime(time());
|
||||
|
||||
$entityManager = $this->getEntityManager();
|
||||
|
||||
$cronJob = $this->getCronJob();
|
||||
$cronScheduledJob = $this->getCronScheduledJob();
|
||||
|
||||
//Check scheduled jobs and create related jobs
|
||||
$this->createJobsFromScheduledJobs();
|
||||
|
||||
$pendingJobs = $this->getJobService()->getPendingJobs();
|
||||
$pendingJobs = $cronJob->getPendingJobs();
|
||||
|
||||
foreach ($pendingJobs as $job) {
|
||||
|
||||
$this->getJobService()->updateEntity($job['id'], array(
|
||||
'status' => self::RUNNING,
|
||||
));
|
||||
$jobEntity = $entityManager->getEntity('Job', $job['id']);
|
||||
|
||||
if (!isset($jobEntity)) {
|
||||
$GLOBALS['log']->error('CronManager: empty Job entity ['.$job['id'].'].');
|
||||
continue;
|
||||
}
|
||||
|
||||
$jobEntity->set('status', self::RUNNING);
|
||||
$entityManager->saveEntity($jobEntity);
|
||||
|
||||
$isSuccess = true;
|
||||
|
||||
try {
|
||||
if (!empty($job['scheduled_job_id'])) {
|
||||
$this->getScheduledJobCron()->run($job);
|
||||
$this->runScheduledJob($job);
|
||||
} else {
|
||||
$this->getServiceCron()->run($job);
|
||||
$this->runService($job);
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
$isSuccess = false;
|
||||
$GLOBALS['log']->error('Failed job running, job ['.$job['id'].']. Error Details: '.$e->getMessage());
|
||||
$GLOBALS['log']->error('CronManager: Failed job running, job ['.$job['id'].']. Error Details: '.$e->getMessage());
|
||||
}
|
||||
|
||||
$status = $isSuccess ? self::SUCCESS : self::FAILED;
|
||||
|
||||
$this->getJobService()->updateEntity($job['id'], array(
|
||||
'status' => $status,
|
||||
));
|
||||
$jobEntity->set('status', $status);
|
||||
$entityManager->saveEntity($jobEntity);
|
||||
|
||||
//set status in the schedulerJobLog
|
||||
if (!empty($job['scheduled_job_id'])) {
|
||||
$this->getScheduledJobService()->addLogRecord($job['scheduled_job_id'], $status);
|
||||
$cronScheduledJob->addLogRecord($job['scheduled_job_id'], $status);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Run Scheduled Job
|
||||
*
|
||||
* @param array $job
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function runScheduledJob(array $job)
|
||||
{
|
||||
$jobName = $job['method'];
|
||||
|
||||
$className = $this->getScheduledJobUtil()->get($jobName);
|
||||
if ($className === false) {
|
||||
throw new NotFound();
|
||||
}
|
||||
|
||||
$jobClass = new $className($this->container);
|
||||
$method = $this->getScheduledJobUtil()->getMethodName();
|
||||
if (!method_exists($jobClass, $method)) {
|
||||
throw new NotFound();
|
||||
}
|
||||
|
||||
$jobClass->$method();
|
||||
}
|
||||
|
||||
/**
|
||||
* Run Service
|
||||
*
|
||||
* @param array $job
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function runService(array $job)
|
||||
{
|
||||
$serviceName = $job['service_name'];
|
||||
|
||||
if (!$this->getServiceFactory()->checkExists($serviceName)) {
|
||||
throw new NotFound();
|
||||
}
|
||||
|
||||
$service = $this->getServiceFactory()->create($serviceName);
|
||||
$serviceMethod = $job['method'];
|
||||
|
||||
if (!method_exists($service, $serviceMethod)) {
|
||||
throw new NotFound();
|
||||
}
|
||||
|
||||
$data = $job['data'];
|
||||
if (Json::isJSON($data)) {
|
||||
$data = Json::decode($data, true);
|
||||
}
|
||||
|
||||
$service->$serviceMethod($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check scheduled jobs and create related jobs
|
||||
* @return array List of created Jobs
|
||||
*
|
||||
* @return array List of created Jobs
|
||||
*/
|
||||
protected function createJobsFromScheduledJobs()
|
||||
{
|
||||
$activeScheduledJobs = $this->getScheduledJobService()->getActiveJobs();
|
||||
$entityManager = $this->getEntityManager();
|
||||
|
||||
$activeScheduledJobs = $this->getCronScheduledJob()->getActiveJobs();
|
||||
|
||||
$cronJob = $this->getCronJob();
|
||||
$runningScheduledJobs = $cronJob->getActiveJobs('scheduled_job_id', self::RUNNING, PDO::FETCH_COLUMN);
|
||||
|
||||
$createdJobs = array();
|
||||
foreach ($activeScheduledJobs as $scheduledJob) {
|
||||
|
||||
if (in_array($scheduledJob['id'], $runningScheduledJobs)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$scheduling = $scheduledJob['scheduling'];
|
||||
|
||||
$cronExpression = \Cron\CronExpression::factory($scheduling);
|
||||
@@ -186,26 +269,30 @@ class CronManager
|
||||
try {
|
||||
$prevDate = $cronExpression->getPreviousRunDate()->format('Y-m-d H:i:s');
|
||||
} catch (\Exception $e) {
|
||||
$GLOBALS['log']->error('ScheduledJob ['.$scheduledJob['id'].']: CronExpression - Impossible CRON expression ['.$scheduling.']');
|
||||
$GLOBALS['log']->error('CronManager: ScheduledJob ['.$scheduledJob['id'].']: CronExpression - Impossible CRON expression ['.$scheduling.']');
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($cronExpression->isDue()) {
|
||||
$prevDate = date('Y-m-d H:i:00');
|
||||
$prevDate = date('Y-m-d H:i:s');
|
||||
}
|
||||
|
||||
$existsJob = $this->getJobService()->getJobByScheduledJob($scheduledJob['id'], $prevDate);
|
||||
$existsJob = $cronJob->getJobByScheduledJob($scheduledJob['id'], $prevDate);
|
||||
|
||||
if (!isset($existsJob) || empty($existsJob)) {
|
||||
//create a job
|
||||
$data = array(
|
||||
//create a new job
|
||||
$jobEntity = $entityManager->getEntity('Job');
|
||||
$jobEntity->set(array(
|
||||
'name' => $scheduledJob['name'],
|
||||
'status' => self::PENDING,
|
||||
'scheduledJobId' => $scheduledJob['id'],
|
||||
'executeTime' => $prevDate,
|
||||
'method' => $scheduledJob['job'],
|
||||
);
|
||||
$createdJobs[] = $this->getJobService()->createEntity($data);
|
||||
));
|
||||
$jobEntityId = $entityManager->saveEntity($jobEntity);
|
||||
if (!empty($jobEntityId)) {
|
||||
$createdJobs[] = $jobEntityId;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -29,33 +29,33 @@ use \Espo\Core\Exceptions\NotFound;
|
||||
class ClientManager
|
||||
{
|
||||
protected $entityManager;
|
||||
|
||||
|
||||
protected $metadata;
|
||||
|
||||
|
||||
protected $clientMap = array();
|
||||
|
||||
|
||||
public function __construct($entityManager, $metadata, $config)
|
||||
{
|
||||
$this->entityManager = $entityManager;
|
||||
$this->metadata = $metadata;
|
||||
$this->config = $config;
|
||||
}
|
||||
|
||||
|
||||
protected function getMetadata()
|
||||
{
|
||||
return $this->metadata;
|
||||
}
|
||||
|
||||
|
||||
protected function getEntityManager()
|
||||
{
|
||||
return $this->entityManager;
|
||||
}
|
||||
|
||||
|
||||
protected function getConfig()
|
||||
{
|
||||
return $this->config;
|
||||
}
|
||||
|
||||
|
||||
public function storeAccessToken($hash, $data)
|
||||
{
|
||||
if (!empty($this->clientMap[$hash]) && !empty($this->clientMap[$hash]['externalAccountEntity'])) {
|
||||
@@ -64,37 +64,37 @@ class ClientManager
|
||||
$externalAccountEntity->set('tokenType', $data['tokenType']);
|
||||
$this->getEntityManager()->saveEntity($externalAccountEntity);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public function create($integration, $userId)
|
||||
{
|
||||
$authMethod = $this->getMetadata()->get("integrations.{$integration}.authMethod");
|
||||
$methodName = 'create' . ucfirst($authMethod);
|
||||
return $this->$methodName($integration, $userId);
|
||||
$authMethod = $this->getMetadata()->get("integrations.{$integration}.authMethod");
|
||||
$methodName = 'create' . ucfirst($authMethod);
|
||||
return $this->$methodName($integration, $userId);
|
||||
}
|
||||
|
||||
|
||||
protected function createOAuth2($integration, $userId)
|
||||
{
|
||||
{
|
||||
$integrationEntity = $this->getEntityManager()->getEntity('Integration', $integration);
|
||||
$externalAccountEntity = $this->getEntityManager()->getEntity('ExternalAccount', $integration . '__' . $userId);
|
||||
|
||||
|
||||
$className = $this->getMetadata()->get("integrations.{$integration}.clientClassName");
|
||||
|
||||
$redirectUri = $this->getConfig()->get('siteUrl') . '/oauthcallback'; // TODO move to client class
|
||||
|
||||
|
||||
$redirectUri = $this->getConfig()->get('siteUrl') . '?entryPoint=oauthCallback'; // TODO move to client class
|
||||
|
||||
if (!$externalAccountEntity) {
|
||||
throw new Error("External Account {$integration} not found for {$userId}");
|
||||
}
|
||||
|
||||
|
||||
if (!$integrationEntity->get('enabled')) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
if (!$externalAccountEntity->get('enabled')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$oauth2Client = new \Espo\Core\ExternalAccount\OAuth2\Client();
|
||||
|
||||
}
|
||||
|
||||
$oauth2Client = new \Espo\Core\ExternalAccount\OAuth2\Client();
|
||||
|
||||
$client = new $className($oauth2Client, array(
|
||||
'endpoint' => $this->getMetadata()->get("integrations.{$integration}.params.endpoint"),
|
||||
'tokenEndpoint' => $this->getMetadata()->get("integrations.{$integration}.params.tokenEndpoint"),
|
||||
@@ -105,12 +105,12 @@ class ClientManager
|
||||
'refreshToken' => $externalAccountEntity->get('refreshToken'),
|
||||
'tokenType' => $externalAccountEntity->get('tokenType'),
|
||||
), $this);
|
||||
|
||||
|
||||
$this->addToClientMap($client, $integrationEntity, $externalAccountEntity, $userId);
|
||||
|
||||
return $client;
|
||||
|
||||
return $client;
|
||||
}
|
||||
|
||||
|
||||
protected function addToClientMap($client, $integrationEntity, $externalAccountEntity, $userId)
|
||||
{
|
||||
$this->clientMap[spl_object_hash($client)] = array(
|
||||
|
||||
@@ -29,9 +29,9 @@ use \Espo\Core\ExternalAccount\OAuth2\Client;
|
||||
abstract class OAuth2Abstract implements IClient
|
||||
{
|
||||
protected $client = null;
|
||||
|
||||
|
||||
protected $manager = null;
|
||||
|
||||
|
||||
protected $paramList = array(
|
||||
'endpoint',
|
||||
'tokenEndpoint',
|
||||
@@ -42,33 +42,33 @@ abstract class OAuth2Abstract implements IClient
|
||||
'refreshToken',
|
||||
'redirectUri',
|
||||
);
|
||||
|
||||
|
||||
protected $clientId = null;
|
||||
|
||||
|
||||
protected $clientSecret = null;
|
||||
|
||||
|
||||
protected $accessToken = null;
|
||||
|
||||
|
||||
protected $refreshToken = null;
|
||||
|
||||
|
||||
protected $redirectUri = null;
|
||||
|
||||
|
||||
public function __construct($client, array $params = array(), $manager = null)
|
||||
{
|
||||
$this->client = $client;
|
||||
|
||||
$this->setParams($params);
|
||||
|
||||
|
||||
$this->manager = $manager;
|
||||
}
|
||||
|
||||
|
||||
public function getParam($name)
|
||||
{
|
||||
if (in_array($name, $this->paramList)) {
|
||||
return $this->$name;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function setParam($name, $value)
|
||||
{
|
||||
if (in_array($name, $this->paramList)) {
|
||||
@@ -79,7 +79,7 @@ abstract class OAuth2Abstract implements IClient
|
||||
$this->$name = $value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function setParams(array $params)
|
||||
{
|
||||
foreach ($this->paramList as $name) {
|
||||
@@ -88,42 +88,41 @@ abstract class OAuth2Abstract implements IClient
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected function afterTokenRefreshed($data)
|
||||
{
|
||||
if ($this->manager) {
|
||||
$this->manager->storeAccessToken(spl_object_hash($this), $data);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function getAccessTokenFromAuthorizationCode($code)
|
||||
{
|
||||
$r = $this->client->getAccessToken($this->getParam('tokenEndpoint'), Client::GRANT_TYPE_AUTHORIZATION_CODE, array(
|
||||
'code' => $code,
|
||||
'redirect_uri' => $this->getParam('redirectUri')
|
||||
));
|
||||
|
||||
|
||||
|
||||
if ($r['code'] == 200) {
|
||||
$data = array();
|
||||
if (!empty($r['result'])) {
|
||||
$data['accessToken'] = $r['result']['access_token'];
|
||||
$data['tokenType'] = $r['result']['token_type'];
|
||||
$data['refreshToken'] = $r['result']['refresh_token'];
|
||||
$data['refreshToken'] = $r['result']['refresh_token'];
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
abstract protected function getPingUrl();
|
||||
|
||||
|
||||
public function ping()
|
||||
{
|
||||
if (empty($this->accessToken) || empty($this->clientId) || empty($this->clientSecret)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
$url = $this->getPingUrl();
|
||||
|
||||
try {
|
||||
@@ -133,35 +132,48 @@ abstract class OAuth2Abstract implements IClient
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public function request($url, $params = array(), $httpMethod = Client::HTTP_METHOD_GET, $allowRenew = true)
|
||||
|
||||
public function request($url, $params = null, $httpMethod = Client::HTTP_METHOD_GET, $contentType = null, $allowRenew = true)
|
||||
{
|
||||
$r = $this->client->request($url, $params, $httpMethod);
|
||||
|
||||
$httpHeaders = array();
|
||||
if (!empty($contentType)) {
|
||||
$httpHeaders['Content-Type'] = $contentType;
|
||||
switch ($contentType) {
|
||||
case Client::CONTENT_TYPE_MULTIPART_FORM_DATA:
|
||||
$httpHeaders['Content-Length'] = strlen($params);
|
||||
break;
|
||||
case Client::CONTENT_TYPE_APPLICATION_JSON:
|
||||
$httpHeaders['Content-Length'] = strlen($params);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$r = $this->client->request($url, $params, $httpMethod, $httpHeaders);
|
||||
|
||||
$code = null;
|
||||
if (!empty($r['code'])) {
|
||||
$code = $r['code'];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if ($code == 200) {
|
||||
return $r['result'];
|
||||
} else {
|
||||
$handledData = $this->handleErrorResponse($r);
|
||||
|
||||
|
||||
if ($allowRenew && is_array($handledData)) {
|
||||
if ($handledData['action'] == 'refreshToken') {
|
||||
if ($this->refreshToken()) {
|
||||
return $this->request($url, $params, $httpMethod, false);
|
||||
if ($this->refreshToken()) {
|
||||
return $this->request($url, $params, $httpMethod, $contentType, false);
|
||||
}
|
||||
} else if ($handledData['action'] == 'renew') {
|
||||
return $this->request($url, $params, $httpMethod, false);
|
||||
return $this->request($url, $params, $httpMethod, $contentType, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
throw new Error("Error after requesting {$httpMethod} {$url}.", $code);
|
||||
}
|
||||
|
||||
|
||||
protected function refreshToken()
|
||||
{
|
||||
if (!empty($this->refreshToken)) {
|
||||
@@ -183,11 +195,11 @@ abstract class OAuth2Abstract implements IClient
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected function handleErrorResponse($r)
|
||||
{
|
||||
if ($r['code'] == 401 && !empty($r['result'])) {
|
||||
$result = $r['result'];
|
||||
$result = $r['result'];
|
||||
if (strpos($r['header'], 'error=invalid_token') !== false) {
|
||||
return array(
|
||||
'action' => 'refreshToken'
|
||||
@@ -198,7 +210,7 @@ abstract class OAuth2Abstract implements IClient
|
||||
);
|
||||
}
|
||||
} else if ($r['code'] == 400 && !empty($r['result'])) {
|
||||
if ($r['result']['error'] == 'invalid_token') {
|
||||
if ($r['result']['error'] == 'invalid_token') {
|
||||
return array(
|
||||
'action' => 'refreshToken'
|
||||
);
|
||||
|
||||
@@ -32,8 +32,9 @@ class Client
|
||||
const TOKEN_TYPE_BEARER = 'Bearer';
|
||||
const TOKEN_TYPE_OAUTH = 'OAuth';
|
||||
|
||||
const CONTENT_TYPE_APPLICATION = 0;
|
||||
const CONTENT_TYPE_MULTIPART = 1;
|
||||
const CONTENT_TYPE_APPLICATION_X_WWW_FORM_URLENENCODED = 'application/x-www-form-urlencoded';
|
||||
const CONTENT_TYPE_MULTIPART_FORM_DATA = 'multipart/form-data';
|
||||
const CONTENT_TYPE_APPLICATION_JSON = 'application/json';
|
||||
|
||||
const HTTP_METHOD_GET = 'GET';
|
||||
const HTTP_METHOD_POST = 'POST';
|
||||
@@ -118,7 +119,7 @@ class Client
|
||||
$this->accessTokenSecret = $accessTokenSecret;
|
||||
}
|
||||
|
||||
public function request($url, $params = array(), $httpMethod = self::HTTP_METHOD_GET, array $httpHeaders = array(), $contentType = self::CONTENT_TYPE_MULTIPART)
|
||||
public function request($url, $params = null, $httpMethod = self::HTTP_METHOD_GET, array $httpHeaders = array())
|
||||
{
|
||||
if ($this->accessToken) {
|
||||
switch ($this->tokenType) {
|
||||
@@ -137,10 +138,10 @@ class Client
|
||||
}
|
||||
}
|
||||
|
||||
return $this->execute($url, $params, $httpMethod, $httpHeaders, $contentType);
|
||||
return $this->execute($url, $params, $httpMethod, $httpHeaders);
|
||||
}
|
||||
|
||||
private function execute($url, $params = array(), $httpMethod, array $httpHeaders = array(), $contentType = self::CONTENT_TYPE_MULTIPART)
|
||||
private function execute($url, $params = null, $httpMethod, array $httpHeaders = array())
|
||||
{
|
||||
$curlOptions = array(
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
@@ -153,8 +154,10 @@ class Client
|
||||
$curlOptions[CURLOPT_POST] = true;
|
||||
case self::HTTP_METHOD_PUT:
|
||||
case self::HTTP_METHOD_PATCH:
|
||||
if (self::CONTENT_TYPE_APPLICATION === $contentType) {
|
||||
if (is_array($params)) {
|
||||
$postFields = http_build_query($params, null, '&');
|
||||
} else {
|
||||
$postFields = $params;
|
||||
}
|
||||
$curlOptions[CURLOPT_POSTFIELDS] = $postFields;
|
||||
break;
|
||||
@@ -165,7 +168,9 @@ class Client
|
||||
if (strpos($url, '?') === false) {
|
||||
$url .= '?';
|
||||
}
|
||||
$url .= http_build_query($params, null, '&');
|
||||
if (is_array($params)) {
|
||||
$url .= http_build_query($params, null, '&');
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@@ -242,7 +247,7 @@ class Client
|
||||
throw new \Exception();
|
||||
}
|
||||
|
||||
return $this->execute($url, $params, self::HTTP_METHOD_POST, $httpHeaders, self::CONTENT_TYPE_APPLICATION);
|
||||
return $this->execute($url, $params, self::HTTP_METHOD_POST, $httpHeaders);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -73,7 +73,7 @@ class HookManager
|
||||
protected function loadHooks()
|
||||
{
|
||||
if ($this->getConfig()->get('useCache') && file_exists($this->cacheFile)) {
|
||||
$this->data = $this->getFileManager()->getContents($this->cacheFile);
|
||||
$this->data = $this->getFileManager()->getPhpContents($this->cacheFile);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -89,7 +89,7 @@ class HookManager
|
||||
$this->data = array_merge_recursive($this->data, $this->getHookData($this->paths['customPath']));
|
||||
|
||||
if ($this->getConfig()->get('useCache')) {
|
||||
$this->getFileManager()->putContentsPHP($this->cacheFile, $this->data);
|
||||
$this->getFileManager()->putPhpContents($this->cacheFile, $this->data);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,6 @@ namespace Espo\Core\Jobs;
|
||||
|
||||
use \Espo\Core\Container;
|
||||
|
||||
|
||||
abstract class Base
|
||||
{
|
||||
private $container;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<?php
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
@@ -27,18 +27,18 @@ use \Zend\Mime\Mime as Mime;
|
||||
class Importer
|
||||
{
|
||||
private $entityManager;
|
||||
|
||||
|
||||
private $fileManager;
|
||||
|
||||
private $config;
|
||||
|
||||
|
||||
public function __construct($entityManager, $fileManager, $config)
|
||||
{
|
||||
$this->entityManager = $entityManager;
|
||||
$this->fileManager = $fileManager;
|
||||
$this->config = $config;
|
||||
}
|
||||
|
||||
|
||||
protected function getEntityManager()
|
||||
{
|
||||
return $this->entityManager;
|
||||
@@ -47,29 +47,29 @@ class Importer
|
||||
{
|
||||
return $this->config;
|
||||
}
|
||||
|
||||
|
||||
protected function getFileManager()
|
||||
{
|
||||
return $this->fileManager;
|
||||
}
|
||||
|
||||
|
||||
public function importMessage($message, $userId, $teamsIds = array())
|
||||
{
|
||||
try {
|
||||
$email = $this->getEntityManager()->getEntity('Email');
|
||||
|
||||
|
||||
$subject = $message->subject;
|
||||
if ($subject !== '0' && empty($subject)) {
|
||||
$subject = '--empty--';
|
||||
}
|
||||
|
||||
|
||||
$email->set('isHtml', false);
|
||||
$email->set('name', $subject);
|
||||
$email->set('status', 'Archived');
|
||||
$email->set('attachmentsIds', array());
|
||||
$email->set('assignedUserId', $userId);
|
||||
$email->set('teamsIds', $teamsIds);
|
||||
|
||||
|
||||
$fromArr = $this->getAddressListFromMessage($message, 'from');
|
||||
if (isset($message->from)) {
|
||||
$email->set('fromName', $message->from);
|
||||
@@ -77,14 +77,14 @@ class Importer
|
||||
$email->set('from', $fromArr[0]);
|
||||
$email->set('to', implode(';', $this->getAddressListFromMessage($message, 'to')));
|
||||
$email->set('cc', implode(';', $this->getAddressListFromMessage($message, 'cc')));
|
||||
|
||||
|
||||
if (isset($message->messageId) && !empty($message->messageId)) {
|
||||
$email->set('messageId', $message->messageId);
|
||||
if (isset($message->deliveredTo)) {
|
||||
$email->set('messageIdInternal', $message->messageId . '-' . $message->deliveredTo);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if ($this->checkIsDuplicate($email)) {
|
||||
return false;
|
||||
}
|
||||
@@ -103,17 +103,18 @@ class Importer
|
||||
$email->set('deliveryDate', $deliveryDate);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$inlineIds = array();
|
||||
|
||||
|
||||
if ($message->isMultipart()) {
|
||||
foreach (new \RecursiveIteratorIterator($message) as $part) {
|
||||
echo "-";
|
||||
$this->importPartDataToEmail($email, $part, $inlineIds);
|
||||
}
|
||||
} else {
|
||||
$this->importPartDataToEmail($email, $message, $inlineIds);
|
||||
}
|
||||
|
||||
|
||||
$body = $email->get('body');
|
||||
if (!empty($body)) {
|
||||
foreach ($inlineIds as $cid => $attachmentId) {
|
||||
@@ -156,12 +157,12 @@ class Importer
|
||||
}
|
||||
|
||||
$this->getEntityManager()->saveEntity($email);
|
||||
|
||||
|
||||
return $email;
|
||||
|
||||
|
||||
} catch (\Exception $e) {}
|
||||
}
|
||||
|
||||
|
||||
protected function checkIsDuplicate($email)
|
||||
{
|
||||
if ($email->get('messageIdInternal')) {
|
||||
@@ -173,12 +174,12 @@ class Importer
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected function getAddressListFromMessage($message, $type)
|
||||
{
|
||||
$addressList = array();
|
||||
if (isset($message->$type)) {
|
||||
|
||||
|
||||
$list = $message->getHeader($type)->getAddressList();
|
||||
foreach ($list as $address) {
|
||||
$addressList[] = $address->getEmail();
|
||||
@@ -186,13 +187,17 @@ class Importer
|
||||
}
|
||||
return $addressList;
|
||||
}
|
||||
|
||||
|
||||
protected function importPartDataToEmail(\Espo\Entities\Email $email, $part, &$inlineIds = array())
|
||||
{
|
||||
try {
|
||||
if (!$part->getHeaders() || !isset($part->contentType)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$type = strtok($part->contentType, ';');
|
||||
$encoding = null;
|
||||
|
||||
|
||||
switch ($type) {
|
||||
case 'text/plain':
|
||||
$content = $this->getContentFromPart($part);
|
||||
@@ -209,10 +214,10 @@ class Importer
|
||||
default:
|
||||
$content = $part->getContent();
|
||||
$disposition = null;
|
||||
|
||||
|
||||
$fileName = null;
|
||||
$contentId = null;
|
||||
|
||||
|
||||
if (isset($part->ContentDisposition)) {
|
||||
if (strpos($part->ContentDisposition, 'attachment') === 0) {
|
||||
if (preg_match('/filename="?([^"]+)"?/i', $part->ContentDisposition, $m)) {
|
||||
@@ -225,32 +230,32 @@ class Importer
|
||||
$disposition = 'inline';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (isset($part->contentTransferEncoding)) {
|
||||
$encoding = strtolower($part->getHeader('Content-Transfer-Encoding')->getTransferEncoding());
|
||||
}
|
||||
|
||||
|
||||
$attachment = $this->getEntityManager()->getEntity('Attachment');
|
||||
$attachment->set('name', $fileName);
|
||||
$attachment->set('type', $type);
|
||||
|
||||
|
||||
if ($disposition == 'inline') {
|
||||
$attachment->set('role', 'Inline Attachment');
|
||||
} else {
|
||||
$attachment->set('role', 'Attachment');
|
||||
}
|
||||
|
||||
|
||||
if ($encoding == 'base64') {
|
||||
$content = base64_decode($content);
|
||||
}
|
||||
|
||||
|
||||
$attachment->set('size', strlen($content));
|
||||
|
||||
|
||||
$this->getEntityManager()->saveEntity($attachment);
|
||||
|
||||
|
||||
$path = 'data/upload/' . $attachment->id;
|
||||
$this->getFileManager()->putContents($path, $content);
|
||||
|
||||
|
||||
if ($disposition == 'attachment') {
|
||||
$attachmentsIds = $email->get('attachmentsIds');
|
||||
$attachmentsIds[] = $attachment->id;
|
||||
@@ -261,7 +266,7 @@ class Importer
|
||||
}
|
||||
} catch (\Exception $e) {}
|
||||
}
|
||||
|
||||
|
||||
protected function getContentFromPart($part)
|
||||
{
|
||||
if ($part instanceof \Zend\Mime\Part) {
|
||||
@@ -271,20 +276,20 @@ class Importer
|
||||
}
|
||||
} else {
|
||||
$content = $part->getContent();
|
||||
|
||||
|
||||
$encoding = null;
|
||||
|
||||
|
||||
if (isset($part->contentTransferEncoding)) {
|
||||
$cteHeader = $part->getHeader('Content-Transfer-Encoding');
|
||||
$encoding = strtolower($cteHeader->getTransferEncoding());
|
||||
}
|
||||
|
||||
|
||||
if ($encoding == 'base64') {
|
||||
$content = base64_decode($content);
|
||||
}
|
||||
|
||||
|
||||
$charset = 'UTF-8';
|
||||
|
||||
|
||||
if (isset($part->contentType)) {
|
||||
$ctHeader = $part->getHeader('Content-Type');
|
||||
$charsetParamValue = $ctHeader->getParameter('charset');
|
||||
@@ -292,11 +297,11 @@ class Importer
|
||||
$charset = strtoupper($charsetParamValue);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if ($charset !== 'UTF-8') {
|
||||
$content = mb_convert_encoding($content, 'UTF-8', $charset);
|
||||
}
|
||||
|
||||
|
||||
if (isset($part->contentTransferEncoding)) {
|
||||
$cteHeader = $part->getHeader('Content-Transfer-Encoding');
|
||||
if ($cteHeader->getTransferEncoding() == 'quoted-printable') {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<?php
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
@@ -23,17 +23,19 @@
|
||||
namespace Espo\Core\Mail\Storage;
|
||||
|
||||
class Imap extends \Zend\Mail\Storage\Imap
|
||||
{
|
||||
{
|
||||
protected $messageClass = '\\Espo\\Core\\Mail\\Storage\\Message';
|
||||
|
||||
public function getIdsFromUID($uid)
|
||||
{
|
||||
$uid = intval($uid) + 1;
|
||||
return $this->protocol->search(array('UID ' . $uid . ':*'));
|
||||
}
|
||||
|
||||
|
||||
public function getIdsFromDate($date)
|
||||
{
|
||||
{
|
||||
return $this->protocol->search(array('SINCE "' . $date . '"'));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
+10
-37
@@ -20,48 +20,21 @@
|
||||
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Core\Cron;
|
||||
namespace Espo\Core\Mail\Storage;
|
||||
|
||||
use Espo\Core\Utils\Json,
|
||||
Espo\Core\Exceptions\NotFound;
|
||||
|
||||
class Service
|
||||
class Message extends \Zend\Mail\Storage\Message
|
||||
{
|
||||
private $serviceFactory;
|
||||
|
||||
public function __construct(\Espo\Core\ServiceFactory $serviceFactory)
|
||||
public function isMultipart()
|
||||
{
|
||||
$this->serviceFactory = $serviceFactory;
|
||||
}
|
||||
|
||||
protected function getServiceFactory()
|
||||
{
|
||||
return $this->serviceFactory;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public function run($job)
|
||||
{
|
||||
$serviceName = $job['service_name'];
|
||||
|
||||
if (!$this->getServiceFactory()->checkExists($serviceName)) {
|
||||
throw new NotFound();
|
||||
if (!isset($this->contentType)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$service = $this->getServiceFactory()->create($serviceName);
|
||||
$serviceMethod = $job['method'];
|
||||
|
||||
if (!method_exists($service, $serviceMethod)) {
|
||||
throw new NotFound();
|
||||
try {
|
||||
return stripos($this->contentType, 'multipart/') === 0;
|
||||
} catch (Exception\ExceptionInterface $e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$data = $job['data'];
|
||||
if (Json::isJSON($data)) {
|
||||
$data = Json::decode($data, true);
|
||||
}
|
||||
|
||||
$service->$serviceMethod($data);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -161,12 +161,18 @@ class RDB extends \Espo\ORM\Repositories\RDB implements Injectable
|
||||
protected function beforeSave(Entity $entity)
|
||||
{
|
||||
parent::beforeSave($entity);
|
||||
|
||||
$this->getEntityManager()->getHookManager()->process($this->entityName, 'beforeSave', $entity);
|
||||
}
|
||||
|
||||
protected function afterSave(Entity $entity)
|
||||
{
|
||||
parent::afterSave($entity);
|
||||
|
||||
$this->handleEmailAddressSave($entity);
|
||||
$this->handlePhoneNumberSave($entity);
|
||||
$this->handleSpecifiedRelations($entity);
|
||||
|
||||
$this->getEntityManager()->getHookManager()->process($this->entityName, 'afterSave', $entity);
|
||||
}
|
||||
|
||||
@@ -218,9 +224,7 @@ class RDB extends \Espo\ORM\Repositories\RDB implements Injectable
|
||||
|
||||
$entity->set($restoreData);
|
||||
|
||||
$this->handleEmailAddressSave($entity);
|
||||
$this->handlePhoneNumberSave($entity);
|
||||
$this->handleSpecifiedRelations($entity);
|
||||
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
@@ -132,14 +132,16 @@ class Base
|
||||
$d[$field . '*'] = $item['value'] . '%';
|
||||
}
|
||||
}
|
||||
$where['OR'] = $d;
|
||||
$where[] = array(
|
||||
'OR' => $d
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$linkedWith = array();
|
||||
$ignoreList = array('linkedWith', 'boolFilters');
|
||||
foreach ($params['where'] as $item) {
|
||||
foreach ($params['where'] as $item) {
|
||||
if (!in_array($item['type'], $ignoreList)) {
|
||||
$part = $this->getWherePart($item);
|
||||
if (!empty($part)) {
|
||||
@@ -160,7 +162,6 @@ class Base
|
||||
if (is_array($idsValue) && count($idsValue) == 1) {
|
||||
$idsValue = $idsValue[0];
|
||||
}
|
||||
|
||||
|
||||
$relDefs = $this->getSeed()->getRelations();
|
||||
|
||||
@@ -180,8 +181,6 @@ class Base
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
if (!empty($part)) {
|
||||
@@ -221,7 +220,9 @@ class Base
|
||||
}
|
||||
}
|
||||
|
||||
$result['whereClause']['OR'] = $d;
|
||||
$result['whereClause'][] = array(
|
||||
'OR' => $d
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -246,9 +247,11 @@ class Base
|
||||
$result['leftJoins'][] = 'teams';
|
||||
}
|
||||
|
||||
$result['whereClause']['OR'] = array(
|
||||
'Team.id' => $this->user->get('teamsIds'),
|
||||
'assignedUserId' => $this->user->id
|
||||
$result['whereClause'][] = array(
|
||||
'OR' => array(
|
||||
'Team.id' => $this->user->get('teamsIds'),
|
||||
'assignedUserId' => $this->user->id
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ class ServiceFactory
|
||||
$config = $this->getContainer()->get('config');
|
||||
|
||||
if (file_exists($this->cacheFile) && $config->get('useCache')) {
|
||||
$this->data = $this->getFileManager()->getContents($this->cacheFile);
|
||||
$this->data = $this->getFileManager()->getPhpContents($this->cacheFile);
|
||||
} else {
|
||||
$this->data = $this->getClassNameHash($this->paths['corePath']);
|
||||
|
||||
@@ -65,7 +65,7 @@ class ServiceFactory
|
||||
$this->data = array_merge($this->data, $this->getClassNameHash($this->paths['customPath']));
|
||||
|
||||
if ($config->get('useCache')) {
|
||||
$result = $this->getFileManager()->putContentsPHP($this->cacheFile, $this->data);
|
||||
$result = $this->getFileManager()->putPhpContents($this->cacheFile, $this->data);
|
||||
if ($result == false) {
|
||||
throw new \Espo\Core\Exceptions\Error();
|
||||
}
|
||||
|
||||
@@ -82,14 +82,14 @@ class Autoload
|
||||
protected function init()
|
||||
{
|
||||
if (file_exists($this->cacheFile) && $this->getConfig()->get('useCache')) {
|
||||
$this->data = $this->getFileManager()->getContents($this->cacheFile);
|
||||
$this->data = $this->getFileManager()->getPhpContents($this->cacheFile);
|
||||
return;
|
||||
}
|
||||
|
||||
$this->data = $this->unify();
|
||||
|
||||
if ($this->getConfig()->get('useCache')) {
|
||||
$result = $this->getFileManager()->putContentsPHP($this->cacheFile, $this->data);
|
||||
$result = $this->getFileManager()->putPhpContents($this->cacheFile, $this->data);
|
||||
if ($result == false) {
|
||||
throw new \Espo\Core\Exceptions\Error('Autoload: Cannot save unified autoload.');
|
||||
}
|
||||
|
||||
@@ -150,7 +150,8 @@ class Config
|
||||
|
||||
$removeData = empty($this->removeData) ? null : $this->removeData;
|
||||
|
||||
$result = $this->getFileManager()->mergeContentsPHP($this->configPath, $values, $removeData);
|
||||
$result = $this->getFileManager()->mergePhpContents($this->configPath, $values, $removeData);
|
||||
|
||||
if ($result) {
|
||||
$this->changedData = array();
|
||||
$this->removeData = array();
|
||||
@@ -162,7 +163,7 @@ class Config
|
||||
|
||||
public function getDefaults()
|
||||
{
|
||||
return $this->getFileManager()->getContents($this->defaultConfigPath);
|
||||
return $this->getFileManager()->getPhpContents($this->defaultConfigPath);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -178,9 +179,9 @@ class Config
|
||||
|
||||
$configPath = file_exists($this->configPath) ? $this->configPath : $this->defaultConfigPath;
|
||||
|
||||
$this->data = $this->getFileManager()->getContents($configPath);
|
||||
$this->data = $this->getFileManager()->getPhpContents($configPath);
|
||||
|
||||
$systemConfig = $this->getFileManager()->getContents($this->systemConfigPath);
|
||||
$systemConfig = $this->getFileManager()->getPhpContents($this->systemConfigPath);
|
||||
$this->data = Util::merge($systemConfig, $this->data);
|
||||
|
||||
return $this->data;
|
||||
|
||||
@@ -20,13 +20,40 @@
|
||||
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Services;
|
||||
namespace Espo\Core\Utils\Cron;
|
||||
|
||||
use \PDO;
|
||||
use \Espo\Core\CronManager;
|
||||
use \Espo\Core\Utils\Config;
|
||||
use \Espo\Core\ORM\EntityManager;
|
||||
|
||||
class Job extends Record
|
||||
class Job
|
||||
{
|
||||
private $config;
|
||||
|
||||
private $entityManager;
|
||||
|
||||
public function __construct(Config $config, EntityManager $entityManager)
|
||||
{
|
||||
$this->config = $config;
|
||||
$this->entityManager = $entityManager;
|
||||
}
|
||||
|
||||
protected function getConfig()
|
||||
{
|
||||
return $this->config;
|
||||
}
|
||||
|
||||
protected function getEntityManager()
|
||||
{
|
||||
return $this->entityManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Pending Jobs
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getPendingJobs()
|
||||
{
|
||||
/** Mark Failed old jobs and remove pending duplicates */
|
||||
@@ -52,9 +79,10 @@ class Job extends Record
|
||||
*
|
||||
* @param string $displayColumns
|
||||
* @param string $status
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getActiveJobs($displayColumns = '*', $status = CronManager::PENDING, $fetchMode = PDO::FETCH_ASSOC)
|
||||
public function getActiveJobs($displayColumns = '*', $status = CronManager::PENDING, $fetchMode = PDO::FETCH_ASSOC)
|
||||
{
|
||||
$jobConfigs = $this->getConfig()->get('cron');
|
||||
|
||||
@@ -66,7 +94,7 @@ class Job extends Record
|
||||
`status` = '" . $status . "'
|
||||
AND execute_time BETWEEN '".date('Y-m-d H:i:s', $periodTime)."' AND '".date('Y-m-d H:i:s', $currentTime)."'
|
||||
AND deleted = 0
|
||||
ORDER BY execute_time DESC ".$limit;
|
||||
ORDER BY execute_time ASC ".$limit;
|
||||
|
||||
$pdo = $this->getEntityManager()->getPDO();
|
||||
$sth = $pdo->prepare($query);
|
||||
@@ -77,6 +105,14 @@ class Job extends Record
|
||||
return $rows;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Jobs by ScheduledJobId and date
|
||||
*
|
||||
* @param string $scheduledJobId
|
||||
* @param string $date
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getJobByScheduledJob($scheduledJobId, $date)
|
||||
{
|
||||
$query = "SELECT * FROM job WHERE
|
||||
@@ -146,7 +182,4 @@ class Job extends Record
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+42
-20
@@ -18,41 +18,68 @@
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
|
||||
************************************************************************/
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Services;
|
||||
namespace Espo\Core\Utils\Cron;
|
||||
|
||||
use \PDO;
|
||||
use \Espo\Core\Utils\Config;
|
||||
use \Espo\Core\ORM\EntityManager;
|
||||
|
||||
class ScheduledJob extends \Espo\Services\Record
|
||||
{
|
||||
class ScheduledJob
|
||||
{
|
||||
private $config;
|
||||
|
||||
private $entityManager;
|
||||
|
||||
public function __construct(Config $config, EntityManager $entityManager)
|
||||
{
|
||||
$this->config = $config;
|
||||
$this->entityManager = $entityManager;
|
||||
}
|
||||
|
||||
protected function getConfig()
|
||||
{
|
||||
return $this->config;
|
||||
}
|
||||
|
||||
protected function getEntityManager()
|
||||
{
|
||||
return $this->entityManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get active Scheduler Jobs
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getActiveJobs()
|
||||
{
|
||||
$query = "SELECT * FROM scheduled_job WHERE
|
||||
`status` = 'Active'
|
||||
AND deleted = 0";
|
||||
AND deleted = 0";
|
||||
|
||||
$pdo = $this->getEntityManager()->getPDO();
|
||||
$sth = $pdo->prepare($query);
|
||||
$sth = $pdo->prepare($query);
|
||||
$sth->execute();
|
||||
|
||||
$rows = $sth->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
|
||||
$list = array();
|
||||
foreach ($rows as $row) {
|
||||
$list[] = $row;
|
||||
}
|
||||
|
||||
return $list;
|
||||
}
|
||||
return $list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add record to ScheduledJobLogRecord about executed job
|
||||
* @param string $scheduledJobId
|
||||
* @param string $status
|
||||
*
|
||||
* @return string Id of created ScheduledJobLogRecord
|
||||
* @param string $scheduledJobId
|
||||
* @param string $status
|
||||
*
|
||||
* @return string ID of created ScheduledJobLogRecord
|
||||
*/
|
||||
public function addLogRecord($scheduledJobId, $status)
|
||||
{
|
||||
@@ -72,12 +99,7 @@ class ScheduledJob extends \Espo\Services\Record
|
||||
'executionTime' => $lastRun,
|
||||
));
|
||||
$scheduledJobLogId = $entityManager->saveEntity($scheduledJobLog);
|
||||
//$entityManager->getRepository('ScheduledJobLogRecord')->relate($scheduledJobLog, 'scheduledJob', $scheduledJob);
|
||||
|
||||
return $scheduledJobLogId;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
return $scheduledJobLogId;
|
||||
}
|
||||
}
|
||||
@@ -333,7 +333,7 @@ class Converter
|
||||
$fileList = $this->getFileManager()->getFileList($this->customTablePath, false, '\.php$', true);
|
||||
|
||||
foreach($fileList as $fileName) {
|
||||
$fileData = $this->getFileManager()->getContents( array($this->customTablePath, $fileName) );
|
||||
$fileData = $this->getFileManager()->getPhpContents( array($this->customTablePath, $fileName) );
|
||||
if (is_array($fileData)) {
|
||||
$customTables = Util::merge($customTables, $fileData);
|
||||
}
|
||||
|
||||
@@ -39,7 +39,6 @@ class FieldManager
|
||||
|
||||
protected $customOptionName = 'isCustom';
|
||||
|
||||
|
||||
public function __construct(Metadata $metadata, Language $language)
|
||||
{
|
||||
$this->metadata = $metadata;
|
||||
@@ -122,7 +121,8 @@ class FieldManager
|
||||
'links.'.$name,
|
||||
);
|
||||
|
||||
$res = $this->getMetadata()->delete($unsets, $this->metadataType, $scope);
|
||||
$this->getMetadata()->delete($this->metadataType, $scope, $unsets);
|
||||
$res = $this->getMetadata()->save();
|
||||
$res &= $this->deleteLabel($name, $scope);
|
||||
|
||||
return (bool) $res;
|
||||
@@ -132,25 +132,25 @@ class FieldManager
|
||||
{
|
||||
$fieldDef = $this->normalizeDefs($name, $fieldDef, $scope);
|
||||
|
||||
$data = Json::encode($fieldDef);
|
||||
$res = $this->getMetadata()->set($data, $this->metadataType, $scope);
|
||||
$this->getMetadata()->set($this->metadataType, $scope, $fieldDef);
|
||||
$res = $this->getMetadata()->save();
|
||||
|
||||
return $res;
|
||||
}
|
||||
|
||||
protected function setTranslatedOptions($name, $value, $scope)
|
||||
{
|
||||
return $this->getLanguage()->set($name, $value, 'options', $scope);
|
||||
return $this->getLanguage()->set($scope, 'options', $name, $value);
|
||||
}
|
||||
|
||||
protected function setLabel($name, $value, $scope)
|
||||
{
|
||||
return $this->getLanguage()->set($name, $value, 'fields', $scope);
|
||||
return $this->getLanguage()->set($scope, 'fields', $name, $value);
|
||||
}
|
||||
|
||||
protected function deleteLabel($name, $scope)
|
||||
{
|
||||
$this->getLanguage()->delete($name, 'fields', $scope);
|
||||
$this->getLanguage()->delete($scope, 'fields', $name);
|
||||
return $this->getLanguage()->save();
|
||||
}
|
||||
|
||||
|
||||
@@ -87,7 +87,7 @@ class ClassParser
|
||||
}
|
||||
|
||||
if ($cacheFile && file_exists($cacheFile) && $this->getConfig()->get('useCache')) {
|
||||
$data = $this->getFileManager()->getContents($cacheFile);
|
||||
$data = $this->getFileManager()->getPhpContents($cacheFile);
|
||||
} else {
|
||||
$data = $this->getClassNameHash($paths['corePath']);
|
||||
|
||||
@@ -104,7 +104,7 @@ class ClassParser
|
||||
}
|
||||
|
||||
if ($cacheFile && $this->getConfig()->get('useCache')) {
|
||||
$result = $this->getFileManager()->putContentsPHP($cacheFile, $data);
|
||||
$result = $this->getFileManager()->putPhpContents($cacheFile, $data);
|
||||
if ($result == false) {
|
||||
throw new \Espo\Core\Exceptions\Error();
|
||||
}
|
||||
|
||||
@@ -156,17 +156,31 @@ class Manager
|
||||
$fullPath = $this->concatPaths($path);
|
||||
|
||||
if (file_exists($fullPath)) {
|
||||
|
||||
if (strtolower(substr($fullPath, -4))=='.php') {
|
||||
return include($fullPath);
|
||||
if (isset($maxlen)) {
|
||||
return file_get_contents($fullPath, $useIncludePath, $context, $offset, $maxlen);
|
||||
} else {
|
||||
if (isset($maxlen)) {
|
||||
return file_get_contents($fullPath, $useIncludePath, $context, $offset, $maxlen);
|
||||
} else {
|
||||
return file_get_contents($fullPath, $useIncludePath, $context, $offset);
|
||||
}
|
||||
return file_get_contents($fullPath, $useIncludePath, $context, $offset);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get PHP array from PHP file
|
||||
*
|
||||
* @param string | array $path
|
||||
* @return array | bool
|
||||
*/
|
||||
public function getPhpContents($path)
|
||||
{
|
||||
$fullPath = $this->concatPaths($path);
|
||||
|
||||
if (file_exists($fullPath) && strtolower(substr($fullPath, -4)) == '.php') {
|
||||
$phpContents = include($fullPath);
|
||||
if (is_array($phpContents)) {
|
||||
return $phpContents;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -206,7 +220,7 @@ class Manager
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function putContentsPHP($path, $data)
|
||||
public function putPhpContents($path, $data)
|
||||
{
|
||||
return $this->putContents($path, $this->getPHPFormat($data), LOCK_EX);
|
||||
}
|
||||
@@ -235,19 +249,23 @@ class Manager
|
||||
*
|
||||
* @param string | array $path
|
||||
* @param string $content JSON string
|
||||
* @param bool $isJSON
|
||||
* @param bool $isReturnJson
|
||||
* @param string | array $removeOptions - List of unset keys from content
|
||||
* @param bool $isReturn - Is result to be returned or stored
|
||||
* @param bool $isPhp - Is merge php files
|
||||
*
|
||||
* @return bool | array
|
||||
*/
|
||||
public function mergeContents($path, $content, $isJSON = false, $removeOptions = null, $isReturn = false)
|
||||
public function mergeContents($path, $content, $isReturnJson = false, $removeOptions = null, $isPhp = false)
|
||||
{
|
||||
$fileContent = $this->getContents($path);
|
||||
if ($isPhp) {
|
||||
$fileContent = $this->getPhpContents($path);
|
||||
} else {
|
||||
$fileContent = $this->getContents($path);
|
||||
}
|
||||
|
||||
$fullPath = $this->concatPaths($path);
|
||||
if (file_exists($fullPath) && ($fileContent === false || empty($fileContent))) {
|
||||
throw new Error('Failed to read file [' . $fullPath .'].');
|
||||
throw new Error('FileManager: Failed to read file [' . $fullPath .'].');
|
||||
}
|
||||
|
||||
$savedDataArray = Utils\Json::getArrayData($fileContent);
|
||||
@@ -259,12 +277,13 @@ class Manager
|
||||
}
|
||||
|
||||
$data = Utils\Util::merge($savedDataArray, $newDataArray);
|
||||
if ($isJSON) {
|
||||
|
||||
if ($isReturnJson) {
|
||||
$data = Utils\Json::encode($data, JSON_PRETTY_PRINT);
|
||||
}
|
||||
|
||||
if ($isReturn) {
|
||||
return $data;
|
||||
if ($isPhp) {
|
||||
return $this->putPhpContents($path, $data);
|
||||
}
|
||||
|
||||
return $this->putContents($path, $data);
|
||||
@@ -278,11 +297,9 @@ class Manager
|
||||
* @param string | array $removeOptions - List of unset keys from content
|
||||
* @return bool
|
||||
*/
|
||||
public function mergeContentsPHP($path, $content, $removeOptions = null)
|
||||
public function mergePhpContents($path, $content, $removeOptions = null)
|
||||
{
|
||||
$data = $this->mergeContents($path, $content, false, $removeOptions, true);
|
||||
|
||||
return $this->putContentsPHP($path, $data);
|
||||
return $this->mergeContents($path, $content, false, $removeOptions, true);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -220,7 +220,6 @@ class Language
|
||||
}
|
||||
|
||||
$this->clearChanges();
|
||||
$this->init(true);
|
||||
|
||||
return (bool) $result;
|
||||
}
|
||||
@@ -234,6 +233,7 @@ class Language
|
||||
{
|
||||
$this->changedData = array();
|
||||
$this->deletedData = array();
|
||||
$this->init(true);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -253,53 +253,76 @@ class Language
|
||||
|
||||
/**
|
||||
* Set/change a label
|
||||
* @param string | array $label
|
||||
* @param mixed $value
|
||||
* @param string $category
|
||||
*
|
||||
* @param string $scope
|
||||
* @param string $category
|
||||
* @param string | array $name
|
||||
* @param mixed $value
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function set($label, $value, $category = 'labels', $scope = 'Global')
|
||||
public function set($scope, $category, $name, $value)
|
||||
{
|
||||
if (is_array($label)) {
|
||||
foreach ($label as $rowLabel => $rowValue) {
|
||||
$this->set($rowLabel, $rowValue, $category, $scope);
|
||||
if (is_array($name)) {
|
||||
foreach ($name as $rowLabel => $rowValue) {
|
||||
$this->set($scope, $category, $rowLabel, $rowValue);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
$this->changedData[$scope][$category][$label] = $value;
|
||||
$this->changedData[$scope][$category][$name] = $value;
|
||||
|
||||
$currentLanguage = $this->getLanguage();
|
||||
$this->data[$currentLanguage][$scope][$category][$label] = $value;
|
||||
if (!isset($this->data[$currentLanguage])) {
|
||||
$this->init();
|
||||
}
|
||||
$this->data[$currentLanguage][$scope][$category][$name] = $value;
|
||||
|
||||
$this->undelete($scope, $category, $name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a label
|
||||
*
|
||||
* @param string $label
|
||||
* @param string $name
|
||||
* @param string $category
|
||||
* @param string $scope
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function delete($label, $category = 'labels', $scope = 'Global')
|
||||
public function delete($scope, $category, $name)
|
||||
{
|
||||
if (is_array($label)) {
|
||||
foreach ($label as $rowLabel) {
|
||||
$this->delete($rowLabel, $category, $scope);
|
||||
if (is_array($name)) {
|
||||
foreach ($name as $rowLabel) {
|
||||
$this->delete($scope, $category, $rowLabel);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
$this->deletedData[$scope][$category][] = $label;
|
||||
$this->deletedData[$scope][$category][] = $name;
|
||||
|
||||
$currentLanguage = $this->getLanguage();
|
||||
if (isset($this->data[$currentLanguage][$scope][$category][$label])) {
|
||||
unset($this->data[$currentLanguage][$scope][$category][$label]);
|
||||
if (!isset($this->data[$currentLanguage])) {
|
||||
$this->init();
|
||||
}
|
||||
|
||||
if (isset($this->changedData[$scope][$category][$label])) {
|
||||
unset($this->changedData[$scope][$category][$label]);
|
||||
if (isset($this->data[$currentLanguage][$scope][$category][$name])) {
|
||||
unset($this->data[$currentLanguage][$scope][$category][$name]);
|
||||
}
|
||||
|
||||
if (isset($this->changedData[$scope][$category][$name])) {
|
||||
unset($this->changedData[$scope][$category][$name]);
|
||||
}
|
||||
}
|
||||
|
||||
protected function undelete($scope, $category, $name)
|
||||
{
|
||||
if (isset($this->deletedData[$scope][$category])) {
|
||||
foreach ($this->deletedData[$scope][$category] as $key => $labelName) {
|
||||
if ($name === $labelName) {
|
||||
unset($this->deletedData[$scope][$category][$key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -319,7 +342,7 @@ class Language
|
||||
|
||||
if ($this->getConfig()->get('useCache')) {
|
||||
$i18nCacheFile = str_replace('{*}', $i18nName, $this->cacheFile);
|
||||
$result &= $this->getFileManager()->putContentsPHP($i18nCacheFile, $i18nData);
|
||||
$result &= $this->getFileManager()->putPhpContents($i18nCacheFile, $i18nData);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -330,7 +353,7 @@ class Language
|
||||
|
||||
$currentLanguage = $this->getLanguage();
|
||||
if (empty($this->data[$currentLanguage])) {
|
||||
$this->data[$currentLanguage] = $this->getFileManager()->getContents($this->getLangCacheFile());
|
||||
$this->data[$currentLanguage] = $this->getFileManager()->getPhpContents($this->getLangCacheFile());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,23 +18,26 @@
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
|
||||
************************************************************************/
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Core\Utils;
|
||||
|
||||
class Layout
|
||||
{
|
||||
private $fileManager;
|
||||
private $metadata;
|
||||
|
||||
|
||||
private $metadata;
|
||||
|
||||
private $changedData = array();
|
||||
|
||||
/**
|
||||
* @var string - uses for loading default values
|
||||
*/
|
||||
private $name = 'layout';
|
||||
private $name = 'layout';
|
||||
|
||||
protected $params = array(
|
||||
protected $params = array(
|
||||
'defaultsPath' => 'application/Espo/Core/defaults',
|
||||
);
|
||||
);
|
||||
|
||||
|
||||
/**
|
||||
@@ -42,10 +45,10 @@ class Layout
|
||||
*/
|
||||
private $paths = array(
|
||||
'corePath' => 'application/Espo/Resources/layouts',
|
||||
'modulePath' => 'application/Espo/Modules/{*}/Resources/layouts',
|
||||
'customPath' => 'custom/Espo/Custom/Resources/layouts',
|
||||
);
|
||||
|
||||
'modulePath' => 'application/Espo/Modules/{*}/Resources/layouts',
|
||||
'customPath' => 'custom/Espo/Custom/Resources/layouts',
|
||||
);
|
||||
|
||||
|
||||
public function __construct(\Espo\Core\Utils\File\Manager $fileManager, \Espo\Core\Utils\Metadata $metadata)
|
||||
{
|
||||
@@ -63,7 +66,6 @@ class Layout
|
||||
return $this->metadata;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get Layout context
|
||||
*
|
||||
@@ -73,13 +75,17 @@ class Layout
|
||||
* @return json
|
||||
*/
|
||||
public function get($controller, $name)
|
||||
{
|
||||
$fileFullPath = Util::concatPath($this->getLayoutPath($controller, true), $name.'.json');
|
||||
if (!file_exists($fileFullPath)) {
|
||||
$fileFullPath = Util::concatPath($this->getLayoutPath($controller), $name.'.json');
|
||||
}
|
||||
{
|
||||
if (isset($this->changedData[$controller][$name])) {
|
||||
return Json::encode($this->changedData[$controller][$name]);
|
||||
}
|
||||
|
||||
if (!file_exists($fileFullPath)) {
|
||||
$fileFullPath = Util::concatPath($this->getLayoutPath($controller, true), $name.'.json');
|
||||
if (!file_exists($fileFullPath)) {
|
||||
$fileFullPath = Util::concatPath($this->getLayoutPath($controller), $name.'.json');
|
||||
}
|
||||
|
||||
if (!file_exists($fileFullPath)) {
|
||||
//load defaults
|
||||
$defaultPath = $this->params['defaultsPath'];
|
||||
$fileFullPath = Util::concatPath( Util::concatPath($defaultPath, $this->name), $name.'.json' );
|
||||
@@ -91,34 +97,68 @@ class Layout
|
||||
}
|
||||
|
||||
return $this->getFileManager()->getContents($fileFullPath);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Layout data
|
||||
* Ex. $controller= Account, $name= detail then will be created a file layoutFolder/Account/detail.json
|
||||
* Ex. $controller = Account, $name = detail then will be created a file layoutFolder/Account/detail.json
|
||||
*
|
||||
* @param JSON string $data
|
||||
* @param array $data
|
||||
* @param string $controller - ex. Account
|
||||
* @param string $name - detail
|
||||
*
|
||||
* @return bool
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function set($data, $controller, $name)
|
||||
{
|
||||
if (empty($controller) || empty($name)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$layoutPath = $this->getLayoutPath($controller, true);
|
||||
|
||||
if (!Json::isJSON($data)) {
|
||||
$data = Json::encode($data);
|
||||
}
|
||||
|
||||
return $this->getFileManager()->putContents(array($layoutPath, $name.'.json'), $data);
|
||||
$this->changedData[$controller][$name] = $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save changes
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function save()
|
||||
{
|
||||
$result = true;
|
||||
|
||||
if (!empty($this->changedData)) {
|
||||
foreach ($this->changedData as $controllerName => $rowData) {
|
||||
foreach ($rowData as $layoutName => $layoutData) {
|
||||
|
||||
if (empty($controllerName) || empty($layoutName)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$layoutPath = $this->getLayoutPath($controllerName, true);
|
||||
$data = Json::encode($layoutData);
|
||||
|
||||
$result &= $this->getFileManager()->putContents(array($layoutPath, $layoutName.'.json'), $data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($result == true) {
|
||||
$this->clearChanges();
|
||||
}
|
||||
|
||||
return (bool) $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear unsaved changes
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function clearChanges()
|
||||
{
|
||||
$this->changedData = array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge layout data
|
||||
@@ -133,15 +173,15 @@ class Layout
|
||||
public function merge($data, $controller, $name)
|
||||
{
|
||||
$prevData = $this->get($controller, $name);
|
||||
|
||||
$prevDataArray= Json::getArrayData($prevData);
|
||||
$dataArray= Json::getArrayData($data);
|
||||
|
||||
$data= Util::merge($prevDataArray, $dataArray);
|
||||
$data= Json::encode($data);
|
||||
$prevDataArray = Json::getArrayData($prevData);
|
||||
$dataArray = Json::getArrayData($data);
|
||||
|
||||
$data = Util::merge($prevDataArray, $dataArray);
|
||||
$data = Json::encode($data);
|
||||
|
||||
return $this->set($data, $controller, $name);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Layout path, ex. application/Modules/Crm/Layouts/Account
|
||||
@@ -152,23 +192,23 @@ class Layout
|
||||
* @return string
|
||||
*/
|
||||
protected function getLayoutPath($entityName, $isCustom = false)
|
||||
{
|
||||
$path = $this->paths['customPath'];
|
||||
{
|
||||
$path = $this->paths['customPath'];
|
||||
|
||||
if (!$isCustom) {
|
||||
$moduleName = $this->getMetadata()->getScopeModuleName($entityName);
|
||||
|
||||
|
||||
$path = $this->paths['corePath'];
|
||||
if ($moduleName !== false) {
|
||||
$path = str_replace('{*}', $moduleName, $this->paths['modulePath']);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
$path = Util::concatPath($path, $entityName);
|
||||
|
||||
return $path;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -61,6 +61,10 @@ class Metadata
|
||||
*/
|
||||
protected $defaultModuleOrder = 10;
|
||||
|
||||
private $deletedData = array();
|
||||
|
||||
private $changedData = array();
|
||||
|
||||
public function __construct(\Espo\Core\Utils\Config $config, \Espo\Core\Utils\File\Manager $fileManager)
|
||||
{
|
||||
$this->config = $config;
|
||||
@@ -124,13 +128,13 @@ class Metadata
|
||||
}
|
||||
|
||||
if (file_exists($this->cacheFile) && !$reload) {
|
||||
$this->meta = $this->getFileManager()->getContents($this->cacheFile);
|
||||
$this->meta = $this->getFileManager()->getPhpContents($this->cacheFile);
|
||||
} else {
|
||||
$this->meta = $this->getUnifier()->unify($this->name, $this->paths, true);
|
||||
$this->meta = $this->setLanguageFromConfig($this->meta);
|
||||
|
||||
if ($this->getConfig()->get('useCache')) {
|
||||
$isSaved = $this->getFileManager()->putContentsPHP($this->cacheFile, $this->meta);
|
||||
$isSaved = $this->getFileManager()->putPhpContents($this->cacheFile, $this->meta);
|
||||
if ($isSaved === false) {
|
||||
$GLOBALS['log']->emergency('Metadata:init() - metadata has not been saved to a cache file');
|
||||
}
|
||||
@@ -213,52 +217,139 @@ class Metadata
|
||||
|
||||
/**
|
||||
* Set Metadata data
|
||||
* Ex. $type= menu, $scope= Account then will be created a file metadataFolder/menu/Account.json
|
||||
* Ex. $key1 = menu, $key2 = Account then will be created a file metadataFolder/menu/Account.json
|
||||
*
|
||||
* @param string $key1
|
||||
* @param string $key2
|
||||
* @param JSON string $data
|
||||
* @param string $type - ex. menu
|
||||
* @param string $scope - Account
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function set($data, $type, $scope)
|
||||
public function set($key1, $key2, $data)
|
||||
{
|
||||
$path = $this->paths['customPath'];
|
||||
$newData = array(
|
||||
$key1 => array(
|
||||
$key2 => $data,
|
||||
),
|
||||
);
|
||||
|
||||
$result = $this->getFileManager()->mergeContents(array($path, $type, $scope.'.json'), $data, true);
|
||||
if ($result === false) {
|
||||
throw new Error("Error saving metadata. See log file for details.");
|
||||
}
|
||||
$this->changedData = Util::merge($this->changedData, $newData);
|
||||
$this->meta = Util::merge($this->getData(), $newData);
|
||||
|
||||
$this->init(true);
|
||||
|
||||
return $result;
|
||||
$this->undelete($key1, $key2, $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unset some fields and other stuff in metadat
|
||||
*
|
||||
* @param string $key1
|
||||
* @param string $key2
|
||||
* @param array | string $unsets Ex. 'fields.name'
|
||||
* @param string $type Ex. 'entityDefs'
|
||||
* @param string $scope
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function delete($unsets, $type, $scope)
|
||||
public function delete($key1, $key2, $unsets)
|
||||
{
|
||||
if (!is_array($unsets)) {
|
||||
$unsets = (array) $unsets;
|
||||
}
|
||||
|
||||
$normalizedData = array(
|
||||
'__APPEND__',
|
||||
);
|
||||
$metaUnsetData = array();
|
||||
foreach ($unsets as $unsetItem) {
|
||||
$normalizedData[] = $unsetItem;
|
||||
$metaUnsetData[] = implode('.', array($key1, $key2, $unsetItem));
|
||||
}
|
||||
|
||||
$unsetData = array(
|
||||
$key1 => array(
|
||||
$key2 => $normalizedData,
|
||||
),
|
||||
);
|
||||
|
||||
$this->deletedData = Util::merge($this->deletedData, $unsetData);
|
||||
$this->deletedData = Util::unsetInArrayByValue('__APPEND__', $this->deletedData);
|
||||
|
||||
$this->meta = Util::unsetInArray($this->getData(), $metaUnsetData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Undelete the deleted items
|
||||
*
|
||||
* @param string $key1
|
||||
* @param string $key2
|
||||
* @param array $data
|
||||
* @return void
|
||||
*/
|
||||
protected function undelete($key1, $key2, $data)
|
||||
{
|
||||
if (isset($this->deletedData[$key1][$key2])) {
|
||||
foreach ($this->deletedData[$key1][$key2] as $unsetIndex => $unsetItem) {
|
||||
$value = Util::getValueByKey($data, $unsetItem);
|
||||
if (isset($value)) {
|
||||
unset($this->deletedData[$key1][$key2][$unsetIndex]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear unsaved changes
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function clearChanges()
|
||||
{
|
||||
$this->changedData = array();
|
||||
$this->deletedData = array();
|
||||
$this->init(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Save changes
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function save()
|
||||
{
|
||||
$path = $this->paths['customPath'];
|
||||
|
||||
$result = $this->getFileManager()->unsetContents(array($path, $type, $scope.'.json'), $unsets, true);
|
||||
|
||||
if ($result == false) {
|
||||
$GLOBALS['log']->warning('Delete metadata items available only for custom code.');
|
||||
$result = true;
|
||||
if (!empty($this->changedData)) {
|
||||
foreach ($this->changedData as $key1 => $keyData) {
|
||||
foreach ($keyData as $key2 => $data) {
|
||||
if (!empty($data)) {
|
||||
$result &= $this->getFileManager()->mergeContents(array($path, $key1, $key2.'.json'), $data, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->init(true);
|
||||
if (!empty($this->deletedData)) {
|
||||
foreach ($this->deletedData as $key1 => $keyData) {
|
||||
foreach ($keyData as $key2 => $unsetData) {
|
||||
if (!empty($unsetData)) {
|
||||
$rowResult = $this->getFileManager()->unsetContents(array($path, $key1, $key2.'.json'), $unsetData, true);
|
||||
if ($rowResult == false) {
|
||||
$GLOBALS['log']->warning('Metadata items ['.$key1.'.'.$key2.'] can be deleted for custom code only.');
|
||||
}
|
||||
$result &= $rowResult;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
if ($result == false) {
|
||||
throw new Error("Error saving metadata. See log file for details.");
|
||||
}
|
||||
|
||||
$this->clearChanges();
|
||||
|
||||
return (bool) $result;
|
||||
}
|
||||
|
||||
|
||||
public function getOrmMetadata($reload = false)
|
||||
{
|
||||
if (!empty($this->ormMeta) && is_array($this->ormMeta) && !$reload) {
|
||||
@@ -270,7 +361,7 @@ class Metadata
|
||||
}
|
||||
|
||||
if (empty($this->ormMeta)) {
|
||||
$this->ormMeta = $this->getFileManager()->getContents($this->ormCacheFile);
|
||||
$this->ormMeta = $this->getFileManager()->getPhpContents($this->ormCacheFile);
|
||||
}
|
||||
|
||||
return $this->ormMeta;
|
||||
@@ -279,7 +370,7 @@ class Metadata
|
||||
public function setOrmMetadata(array $ormMeta)
|
||||
{
|
||||
if ($this->getConfig()->get('useCache')) {
|
||||
$result = $this->getFileManager()->putContentsPHP($this->ormCacheFile, $ormMeta);
|
||||
$result = $this->getFileManager()->putPhpContents($this->ormCacheFile, $ormMeta);
|
||||
if ($result == false) {
|
||||
throw new \Espo\Core\Exceptions\Error('Metadata::setOrmMetadata() - Cannot save ormMetadata to a file');
|
||||
}
|
||||
|
||||
@@ -84,12 +84,12 @@ class Module
|
||||
protected function init()
|
||||
{
|
||||
if (file_exists($this->cacheFile) && $this->getConfig()->get('useCache')) {
|
||||
$this->data = $this->getFileManager()->getContents($this->cacheFile);
|
||||
$this->data = $this->getFileManager()->getPhpContents($this->cacheFile);
|
||||
} else {
|
||||
$this->data = $this->getUnifier()->unify($this->paths, true);
|
||||
|
||||
if ($this->getConfig()->get('useCache')) {
|
||||
$result = $this->getFileManager()->putContentsPHP($this->cacheFile, $this->data);
|
||||
$result = $this->getFileManager()->putPhpContents($this->cacheFile, $this->data);
|
||||
if ($result == false) {
|
||||
throw new \Espo\Core\Exceptions\Error('Module - Cannot save unified modules.');
|
||||
}
|
||||
|
||||
@@ -95,12 +95,12 @@ class Route
|
||||
protected function init()
|
||||
{
|
||||
if (file_exists($this->cacheFile) && $this->getConfig()->get('useCache')) {
|
||||
$this->data = $this->getFileManager()->getContents($this->cacheFile);
|
||||
$this->data = $this->getFileManager()->getPhpContents($this->cacheFile);
|
||||
} else {
|
||||
$this->data = $this->unify();
|
||||
|
||||
if ($this->getConfig()->get('useCache')) {
|
||||
$result = $this->getFileManager()->putContentsPHP($this->cacheFile, $this->data);
|
||||
$result = $this->getFileManager()->putPhpContents($this->cacheFile, $this->data);
|
||||
if ($result == false) {
|
||||
throw new \Espo\Core\Exceptions\Error('Route - Cannot save unified routes');
|
||||
}
|
||||
@@ -127,7 +127,7 @@ class Route
|
||||
protected function getAddData($currData, $routeFile)
|
||||
{
|
||||
if (file_exists($routeFile)) {
|
||||
$content= $this->getFileManager()->getContents($routeFile);
|
||||
$content = $this->getFileManager()->getContents($routeFile);
|
||||
$arrayContent = Json::getArrayData($content);
|
||||
if (empty($arrayContent)) {
|
||||
$GLOBALS['log']->error('Route::unify() - Empty file or syntax error - ['.$routeFile.']');
|
||||
|
||||
+5
-17
@@ -20,14 +20,14 @@
|
||||
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Core\Cron;
|
||||
namespace Espo\Core\Utils;
|
||||
|
||||
use Espo\Core\Exceptions\NotFound,
|
||||
Espo\Core\Utils\Util;
|
||||
use Espo\Core\Exceptions\NotFound;
|
||||
|
||||
class ScheduledJob
|
||||
{
|
||||
private $container;
|
||||
|
||||
private $systemUtil;
|
||||
|
||||
protected $data = null;
|
||||
@@ -54,7 +54,6 @@ class ScheduledJob
|
||||
'default' => '* * * * * {PHP-BIN-DIR} -f {CRON-FILE}',
|
||||
);
|
||||
|
||||
|
||||
public function __construct(\Espo\Core\Container $container)
|
||||
{
|
||||
$this->container = $container;
|
||||
@@ -76,19 +75,9 @@ class ScheduledJob
|
||||
return $this->systemUtil;
|
||||
}
|
||||
|
||||
public function run(array $job)
|
||||
public function getMethodName()
|
||||
{
|
||||
$jobName = $job['method'];
|
||||
|
||||
$className = $this->getClassName($jobName);
|
||||
if ($className === false) {
|
||||
throw new NotFound();
|
||||
}
|
||||
|
||||
$jobClass = new $className($this->container);
|
||||
$method = $this->allowedMethod;
|
||||
|
||||
$jobClass->$method();
|
||||
return $this->allowedMethod;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -161,7 +150,6 @@ class ScheduledJob
|
||||
$this->data = $classParser->getData($this->paths, $this->cacheFile);
|
||||
}
|
||||
|
||||
|
||||
public function getSetupMessage()
|
||||
{
|
||||
$language = $this->getContainer()->get('language');
|
||||
@@ -83,6 +83,7 @@ return array (
|
||||
'permissionMap',
|
||||
'permissionRules',
|
||||
'passwordSalt',
|
||||
'cryptKey'
|
||||
),
|
||||
'adminItems' =>
|
||||
array (
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* 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/.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\EntryPoints;
|
||||
|
||||
use \Espo\Core\Exceptions\NotFound;
|
||||
use \Espo\Core\Exceptions\Forbidden;
|
||||
use \Espo\Core\Exceptions\BadRequest;
|
||||
|
||||
class OauthCallback extends \Espo\Core\EntryPoints\Base
|
||||
{
|
||||
public static $authRequired = false;
|
||||
|
||||
public function run()
|
||||
{
|
||||
echo "EspoCRM rocks !!!";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* 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/.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Jobs;
|
||||
|
||||
use \Espo\Core\Exceptions;
|
||||
|
||||
class Cleanup extends \Espo\Core\Jobs\Base
|
||||
{
|
||||
protected $period = '-1 month';
|
||||
|
||||
public function run()
|
||||
{
|
||||
$this->cleanupJobs();
|
||||
$this->cleanupScheduledJobLog();
|
||||
}
|
||||
|
||||
protected function cleanupJobs()
|
||||
{
|
||||
$query = "DELETE FROM `job` WHERE DATE(modified_at) < '".$this->getDate()."' ";
|
||||
|
||||
$pdo = $this->getEntityManager()->getPDO();
|
||||
$sth = $pdo->prepare($query);
|
||||
$sth->execute();
|
||||
}
|
||||
|
||||
protected function cleanupScheduledJobLog()
|
||||
{
|
||||
$lastTenRecords = "SELECT c.id FROM (
|
||||
SELECT i1.id
|
||||
FROM scheduled_job_log_record i1
|
||||
CROSS JOIN scheduled_job_log_record i2 ON ( i1.scheduled_job_id = i2.scheduled_job_id
|
||||
AND i1.id < i2.id )
|
||||
GROUP BY i1.id
|
||||
HAVING COUNT( * ) <10
|
||||
ORDER BY i1.created_at DESC
|
||||
) AS c";
|
||||
|
||||
$query = "DELETE FROM `scheduled_job_log_record` WHERE DATE(created_at) < '".$this->getDate()."' AND id NOT IN (".$lastTenRecords.") ";
|
||||
|
||||
$pdo = $this->getEntityManager()->getPDO();
|
||||
$sth = $pdo->prepare($query);
|
||||
$sth->execute();
|
||||
}
|
||||
|
||||
protected function getDate($format = 'Y-m-d')
|
||||
{
|
||||
$datetime = new \DateTime();
|
||||
$datetime->modify($this->period);
|
||||
return $datetime->format($format);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* 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/.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Modules\Crm\Jobs;
|
||||
|
||||
use \Espo\Core\Exceptions;
|
||||
|
||||
class CheckEmailAccounts extends \Espo\Core\Jobs\Base
|
||||
{
|
||||
public function run()
|
||||
{
|
||||
$service = $this->getServiceFactory()->create('EmailAccount');
|
||||
$collection = $this->getEntityManager()->getRepository('EmailAccount')->where(array('status' => 'Active'))->find();
|
||||
foreach ($collection as $entity) {
|
||||
try {
|
||||
$service->fetchFromMailServer($entity);
|
||||
} catch (\Exception $e) {}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
|
||||
************************************************************************/
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Modules\Crm\Jobs;
|
||||
|
||||
@@ -27,8 +27,8 @@ use \Espo\Core\Exceptions;
|
||||
class CheckInboundEmails extends \Espo\Core\Jobs\Base
|
||||
{
|
||||
public function run()
|
||||
{
|
||||
$service = $this->getServiceFactory()->create('InboundEmail');
|
||||
{
|
||||
$service = $this->getServiceFactory()->create('InboundEmail');
|
||||
$collection = $this->getEntityManager()->getRepository('InboundEmail')->where(array('status' => 'Active'))->find();
|
||||
foreach ($collection as $entity) {
|
||||
try {
|
||||
@@ -36,15 +36,7 @@ class CheckInboundEmails extends \Espo\Core\Jobs\Base
|
||||
} catch (\Exception $e) {}
|
||||
}
|
||||
|
||||
$service = $this->getServiceFactory()->create('EmailAccount');
|
||||
$collection = $this->getEntityManager()->getRepository('EmailAccount')->where(array('status' => 'Active'))->find();
|
||||
foreach ($collection as $entity) {
|
||||
try {
|
||||
$service->fetchFromMailServer($entity);
|
||||
} catch (\Exception $e) {}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
"options": {
|
||||
"job": {
|
||||
"CheckInboundEmails": "Check Inbound Emails",
|
||||
"CheckEmailAccounts": "Check Personal Email Accounts",
|
||||
"SendEmailReminders": "Send Email Reminders"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[
|
||||
{"name":"name", "width": 32, "link": true},
|
||||
{"name":"account"},
|
||||
{"name":"stage"},
|
||||
{"name":"amount"},
|
||||
{"name":"createdAt"}
|
||||
]
|
||||
@@ -5,7 +5,7 @@
|
||||
},
|
||||
"salutationName": {
|
||||
"type": "enum",
|
||||
"options": ["", "Mr.", "Mrs.", "Dr.", "Drs."]
|
||||
"options": ["", "Mr.", "Mrs.", "Dr."]
|
||||
},
|
||||
"firstName": {
|
||||
"type": "varchar",
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
},
|
||||
"salutationName": {
|
||||
"type": "enum",
|
||||
"options": ["", "Mr.", "Mrs.", "Dr.", "Drs."]
|
||||
"options": ["", "Mr.", "Mrs.", "Dr."]
|
||||
},
|
||||
"firstName": {
|
||||
"type": "varchar",
|
||||
|
||||
@@ -28,8 +28,10 @@ abstract class Entity implements IEntity
|
||||
|
||||
private $isNew = false;
|
||||
|
||||
private $isSaved = false;
|
||||
|
||||
/**
|
||||
* Entity name.
|
||||
* Entity name (entity type).
|
||||
* @var string
|
||||
*/
|
||||
protected $entityName;
|
||||
@@ -226,6 +228,16 @@ abstract class Entity implements IEntity
|
||||
$this->isNew = $isNew;
|
||||
}
|
||||
|
||||
public function isSaved()
|
||||
{
|
||||
return $this->isSaved;
|
||||
}
|
||||
|
||||
public function setIsSaved($isSaved)
|
||||
{
|
||||
$this->isSaved = $isSaved;
|
||||
}
|
||||
|
||||
public function getEntityName()
|
||||
{
|
||||
return $this->entityName;
|
||||
|
||||
@@ -132,12 +132,13 @@ class RDB extends \Espo\ORM\Repository
|
||||
public function save(Entity $entity)
|
||||
{
|
||||
$this->beforeSave($entity);
|
||||
if ($entity->isNew()) {
|
||||
if ($entity->isNew() && !$entity->isSaved()) {
|
||||
$result = $this->getMapper()->insert($entity);
|
||||
} else {
|
||||
$result = $this->getMapper()->update($entity);
|
||||
}
|
||||
if ($result) {
|
||||
$entity->setIsSaved(true);
|
||||
$this->afterSave($entity);
|
||||
if ($entity->isNew()) {
|
||||
$entity->setIsNew(false);
|
||||
|
||||
@@ -132,7 +132,8 @@
|
||||
"authTokens": "Active auth sessions. IP address and last access date.",
|
||||
"authentication": "Authentication settings.",
|
||||
"currency": "Currency settings and rates.",
|
||||
"extensions": "Install or uninstall extensions."
|
||||
"extensions": "Install or uninstall extensions.",
|
||||
"integrations": "Integration with third-party services."
|
||||
},
|
||||
"options": {
|
||||
"previewSize": {
|
||||
|
||||
@@ -235,8 +235,7 @@
|
||||
"salutationName": {
|
||||
"Mr.": "Mr.",
|
||||
"Mrs.": "Mrs.",
|
||||
"Dr.": "Dr.",
|
||||
"Drs.": "Drs."
|
||||
"Dr.": "Dr."
|
||||
},
|
||||
"language": {
|
||||
"af_ZA":"Afrikaans",
|
||||
|
||||
@@ -6,7 +6,8 @@
|
||||
"redirectUri": "Redirect URI"
|
||||
},
|
||||
"messages": {
|
||||
"selectIntegration": "Select an integration in menu."
|
||||
"selectIntegration": "Select an integration from menu.",
|
||||
"noIntegrations": "No Integrations is available."
|
||||
},
|
||||
"help": {
|
||||
"Google": "<p><b>Obtain OAuth 2.0 credentials from the Google Developers Console.</b></p><p>Visit <a href=\"https://console.developers.google.com/project\">Google Developers Console</a> to obtain OAuth 2.0 credentials such as a Client ID and Client Secret that are known to both Google and EspoCRM application.</p>"
|
||||
|
||||
@@ -28,7 +28,9 @@
|
||||
"Teams and Access Control": "Teams and Access Control",
|
||||
"Forgot Password?": "Forgot Password?",
|
||||
"Password Change Request": "Password Change Request",
|
||||
"Email Address": "Email Address"
|
||||
"Email Address": "Email Address",
|
||||
"External Accounts": "External Accounts",
|
||||
"Email Accounts": "Email Accounts"
|
||||
},
|
||||
"tooltips": {
|
||||
"defaultTeam": "All records created by this user will be related to this team by default.",
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
"users": "Usuários"
|
||||
},
|
||||
"tooltips": {
|
||||
"roles": "Todos os usuários deste time terão acesso as configurações das regras selecionadas."
|
||||
"roles": "Todos os usuários deste time terão acesso as configurações das regras selecionadas.",
|
||||
"positionList": "Posições disponíveis neste time. E.g. Vendedor, Gerente."
|
||||
},
|
||||
"labels": {
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
"messages": {
|
||||
"passwordWillBeSent": "A senha será enviada para o email do usuário.",
|
||||
"accountInfoEmailSubject": "Informações da Conta",
|
||||
"accountInfoEmailBody": "Informações da sua conta:\n\nUsuário: {userName}\nSenha: {password}\n\n{siteUrl}",,
|
||||
"accountInfoEmailBody": "Informações da sua conta:\n\nUsuário: {userName}\nSenha: {password}\n\n{siteUrl}",
|
||||
"passwordChangeLinkEmailSubject": "Solicitação para troca da senha",
|
||||
"passwordChangeLinkEmailBody": "Você pode atualizar sua senha usando este link {link}. Esta URL é única e expirará em breve.",
|
||||
"passwordChanged": "A senha foi atualizada",
|
||||
|
||||
@@ -1 +1,6 @@
|
||||
[{"name":"name","width":30,"link":true},{"name":"host"},{"name":"status"}]
|
||||
[
|
||||
{"name":"name","width":30,"link":true},
|
||||
{"name":"host"},
|
||||
{"name":"status"},
|
||||
{"name": "assignedUser"}
|
||||
]
|
||||
|
||||
@@ -22,6 +22,11 @@
|
||||
"label":"Currency",
|
||||
"description":"currency"
|
||||
},
|
||||
{
|
||||
"url":"#Admin/integrations",
|
||||
"label":"Integrations",
|
||||
"description":"integrations"
|
||||
},
|
||||
{
|
||||
"url":"#Admin/upgrade",
|
||||
"label":"Upgrade",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"controller": "Controllers.Record",
|
||||
"controller": "Controllers.EmailAccount",
|
||||
"recordViews":{
|
||||
"list":"EmailAccount.Record.List",
|
||||
"detail": "EmailAccount.Record.Detail",
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
},
|
||||
"salutationName": {
|
||||
"type": "enum",
|
||||
"options": ["", "Mr.", "Mrs.", "Dr.", "Drs."]
|
||||
"options": ["", "Mr.", "Mrs.", "Dr."]
|
||||
},
|
||||
"firstName": {
|
||||
"type": "varchar",
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
{
|
||||
"fields": {
|
||||
"clientId": {
|
||||
"type": "varchar",
|
||||
"maxLength": 255,
|
||||
"required": true
|
||||
},
|
||||
"clientSecret": {
|
||||
"type": "varchar",
|
||||
"maxLength": 255,
|
||||
"required": true
|
||||
}
|
||||
},
|
||||
"params": {
|
||||
"endpoint": "https://accounts.google.com/o/oauth2/auth",
|
||||
"tokenEndpoint": "https://accounts.google.com/o/oauth2/token",
|
||||
"scope": "https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/calendar"
|
||||
},
|
||||
"allowUserAccounts": true,
|
||||
"authMethod": "OAuth2",
|
||||
"clientClassName": "\\Espo\\Core\\ExternalAccount\\Clients\\Google"
|
||||
}
|
||||
@@ -18,19 +18,20 @@
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
|
||||
************************************************************************/
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\SelectManagers;
|
||||
|
||||
class EmailAccount extends \Espo\Core\SelectManagers\Base
|
||||
{
|
||||
{
|
||||
protected function access(&$result)
|
||||
{
|
||||
if (!array_key_exists('whereClause', $result)) {
|
||||
$result['whereClause'] = array();
|
||||
}
|
||||
$result['whereClause']['assignedUserId'] = $this->user->id;
|
||||
if (!$this->user->isAdmin()) {
|
||||
$result['whereClause']['assignedUserId'] = $this->user->id;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -94,7 +94,7 @@ class Email extends Record
|
||||
public function getEntity($id = null)
|
||||
{
|
||||
$entity = parent::getEntity($id);
|
||||
if (!empty($id)) {
|
||||
if (!empty($entity) && !empty($id)) {
|
||||
|
||||
if ($entity->get('fromEmailAddressName')) {
|
||||
$entity->set('from', $entity->get('fromEmailAddressName'));
|
||||
|
||||
@@ -28,102 +28,104 @@ use \Espo\Core\Exceptions\Error;
|
||||
use \Espo\Core\Exceptions\Forbidden;
|
||||
|
||||
class EmailAccount extends Record
|
||||
{
|
||||
{
|
||||
protected $internalFields = array('password');
|
||||
|
||||
protected $readOnlyFields = array('assignedUserId', 'fetchData');
|
||||
|
||||
|
||||
protected $readOnlyFields = array('fetchData');
|
||||
|
||||
const PORTION_LIMIT = 10;
|
||||
|
||||
|
||||
protected function init()
|
||||
{
|
||||
$this->dependencies[] = 'fileManager';
|
||||
$this->dependencies[] = 'crypt';
|
||||
}
|
||||
|
||||
|
||||
protected function getFileManager()
|
||||
{
|
||||
return $this->injections['fileManager'];
|
||||
}
|
||||
|
||||
|
||||
protected function getCrypt()
|
||||
{
|
||||
return $this->injections['crypt'];
|
||||
}
|
||||
|
||||
|
||||
protected function handleInput(&$data)
|
||||
{
|
||||
parent::handleInput($data);
|
||||
{
|
||||
parent::handleInput($data);
|
||||
if (array_key_exists('password', $data)) {
|
||||
$data['password'] = $this->getCrypt()->encrypt($data['password']);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function getFolders($params)
|
||||
{
|
||||
{
|
||||
$password = $params['password'];
|
||||
|
||||
|
||||
if (!empty($params['id'])) {
|
||||
$entity = $this->getEntityManager()->getEntity('EmailAccount', $params['id']);
|
||||
if ($entity) {
|
||||
$password = $this->getCrypt()->decrypt($entity->get('password'));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$imapParams = array(
|
||||
'host' => $params['host'],
|
||||
'port' => $params['port'],
|
||||
'user' => $params['username'],
|
||||
'password' => $password,
|
||||
);
|
||||
|
||||
|
||||
if (!empty($params['ssl'])) {
|
||||
$imapParams['ssl'] = 'SSL';
|
||||
}
|
||||
|
||||
$foldersArr = array();
|
||||
|
||||
$storage = new \Zend\Mail\Storage\Imap($imapParams);
|
||||
|
||||
}
|
||||
|
||||
$foldersArr = array();
|
||||
|
||||
$storage = new \Zend\Mail\Storage\Imap($imapParams);
|
||||
|
||||
$folders = new \RecursiveIteratorIterator($storage->getFolders(), \RecursiveIteratorIterator::SELF_FIRST);
|
||||
foreach ($folders as $name => $folder) {
|
||||
foreach ($folders as $name => $folder) {
|
||||
$foldersArr[] = mb_convert_encoding($folder->getGlobalName(), 'UTF-8', 'UTF7-IMAP');
|
||||
}
|
||||
return $foldersArr;
|
||||
}
|
||||
|
||||
|
||||
public function createEntity($data)
|
||||
{
|
||||
$entity = parent::createEntity($data);
|
||||
if ($entity) {
|
||||
$entity->set('assignedUserId', $this->getUser()->id);
|
||||
if (!$this->getUser()->isAdmin()) {
|
||||
$entity->set('assignedUserId', $this->getUser()->id);
|
||||
}
|
||||
$this->getEntityManager()->saveEntity($entity);
|
||||
}
|
||||
return $entity;
|
||||
}
|
||||
|
||||
|
||||
public function fetchFromMailServer(Entity $emailAccount)
|
||||
{
|
||||
if ($emailAccount->get('status') != 'Active') {
|
||||
throw new Error();
|
||||
}
|
||||
|
||||
|
||||
$importer = new \Espo\Core\Mail\Importer($this->getEntityManager(), $this->getFileManager(), $this->getConfig());
|
||||
|
||||
|
||||
$maxSize = $this->getConfig()->get('emailMessageMaxSize');
|
||||
|
||||
|
||||
$user = $this->getEntityManager()->getEntity('User', $emailAccount->get('assignedUserId'));
|
||||
|
||||
|
||||
if (!$user) {
|
||||
throw new Error();
|
||||
}
|
||||
|
||||
|
||||
$userId = $user->id;
|
||||
$teamId = $user->get('defaultTeam');
|
||||
|
||||
$teamId = $user->get('defaultTeam');
|
||||
|
||||
$fetchData = json_decode($emailAccount->get('fetchData'), true);
|
||||
if (empty($fetchData)) {
|
||||
$fetchData = array();
|
||||
$fetchData = array();
|
||||
}
|
||||
if (!array_key_exists('lastUID', $fetchData)) {
|
||||
$fetchData['lastUID'] = array();
|
||||
@@ -131,31 +133,31 @@ class EmailAccount extends Record
|
||||
if (!array_key_exists('lastUID', $fetchData)) {
|
||||
$fetchData['lastDate'] = array();
|
||||
}
|
||||
|
||||
|
||||
$imapParams = array(
|
||||
'host' => $emailAccount->get('host'),
|
||||
'port' => $emailAccount->get('port'),
|
||||
'user' => $emailAccount->get('username'),
|
||||
'password' => $this->getCrypt()->decrypt($emailAccount->get('password')),
|
||||
);
|
||||
|
||||
|
||||
if ($emailAccount->get('ssl')) {
|
||||
$imapParams['ssl'] = 'SSL';
|
||||
}
|
||||
|
||||
|
||||
$storage = new \Espo\Core\Mail\Storage\Imap($imapParams);
|
||||
|
||||
$monitoredFolders = $emailAccount->get('monitoredFolders');
|
||||
|
||||
$monitoredFolders = $emailAccount->get('monitoredFolders');
|
||||
if (empty($monitoredFolders)) {
|
||||
throw new Error();
|
||||
throw new Error();
|
||||
}
|
||||
|
||||
$monitoredFoldersArr = explode(',', $monitoredFolders);
|
||||
|
||||
$monitoredFoldersArr = explode(',', $monitoredFolders);
|
||||
foreach ($monitoredFoldersArr as $folder) {
|
||||
$folder = mb_convert_encoding(trim($folder), 'UTF7-IMAP', 'UTF-8');
|
||||
|
||||
|
||||
$storage->selectFolder($folder);
|
||||
|
||||
|
||||
$lastUID = 0;
|
||||
$lastDate = 0;
|
||||
if (!empty($fetchData['lastUID'][$folder])) {
|
||||
@@ -175,32 +177,32 @@ class EmailAccount extends Record
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if ((count($ids) == 1) && !empty($lastUID)) {
|
||||
if ($storage->getUniqueId($ids[0]) == $lastUID) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
$k = 0;
|
||||
foreach ($ids as $i => $id) {
|
||||
|
||||
$k = 0;
|
||||
foreach ($ids as $i => $id) {
|
||||
if ($k == count($ids) - 1) {
|
||||
$lastUID = $storage->getUniqueId($id);
|
||||
}
|
||||
|
||||
|
||||
if ($maxSize) {
|
||||
if ($storage->getSize($id) > $maxSize * 1024 * 1024) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$message = $storage->getMessage($id);
|
||||
|
||||
|
||||
$email = $importer->importMessage($message, $userId, array($teamId));
|
||||
|
||||
|
||||
if ($k == count($ids) - 1) {
|
||||
$lastUID = $storage->getUniqueId($id);
|
||||
|
||||
|
||||
if ($message) {
|
||||
$dt = new \DateTime($message->date);
|
||||
if ($dt) {
|
||||
@@ -209,22 +211,22 @@ class EmailAccount extends Record
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if ($k == self::PORTION_LIMIT - 1) {
|
||||
$lastUID = $storage->getUniqueId($id);
|
||||
break;
|
||||
}
|
||||
$k++;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$fetchData['lastUID'][$folder] = $lastUID;
|
||||
$fetchData['lastDate'][$folder] = $lastDate;
|
||||
|
||||
|
||||
$emailAccount->set('fetchData', json_encode($fetchData));
|
||||
$this->getEntityManager()->saveEntity($emailAccount);
|
||||
$this->getEntityManager()->saveEntity($emailAccount);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<div class="page-header">
|
||||
<h3>EspoCRM - Open Source CRM application</h3>
|
||||
<h3>EspoCRM - Open Source CRM application</h3>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
@@ -8,19 +8,19 @@
|
||||
<strong>Version {{version}}</strong>
|
||||
</p>
|
||||
<p>
|
||||
Copyright © 2014 EspoCRM: Yuri Kuznetsov, Taras Machyshyn, Oleksiy Avramenko.
|
||||
Copyright © 2014-2015 EspoCRM: Yuri Kuznetsov, Taras Machyshyn, Oleksiy Avramenko.
|
||||
<br>
|
||||
Website: <a href="http://www.espocrm.com">http://www.espocrm.com</a>.
|
||||
</p>
|
||||
</p>
|
||||
<p>
|
||||
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.
|
||||
</p>
|
||||
</p>
|
||||
<p>
|
||||
EspoCRM is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty
|
||||
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.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
/************************************************************************
|
||||
* 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/.
|
||||
************************************************************************/
|
||||
|
||||
Espo.define('Controllers.EmailAccount', 'Controllers.Record', function (Dep) {
|
||||
|
||||
return Dep.extend({
|
||||
|
||||
list: function (options) {
|
||||
var userId = (options || {}).userId;
|
||||
if (!userId) {
|
||||
Dep.prototype.list.call(this, options);
|
||||
} else {
|
||||
this.getCollection(function (collection) {
|
||||
collection.where = [{
|
||||
type: 'equals',
|
||||
field: 'assignedUserId',
|
||||
value: userId
|
||||
}];
|
||||
|
||||
this.main(this.getViewName('list'), {
|
||||
scope: this.name,
|
||||
collection: collection,
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
});
|
||||
});
|
||||
@@ -78,7 +78,12 @@ Espo.define('Views.Admin.Integrations.Index', 'View', function (Dep) {
|
||||
|
||||
renderDefaultPage: function () {
|
||||
$('#integration-header').html('').hide();
|
||||
$('#integration-content').html(this.translate('selectIntegration', 'messages', 'Integration'));
|
||||
if (this.integrationList.length) {
|
||||
var msg = this.translate('selectIntegration', 'messages', 'Integration');
|
||||
} else {
|
||||
var msg = '<p class="lead">' + this.translate('noIntegrations', 'messages', 'Integration') + '</p>';
|
||||
}
|
||||
$('#integration-content').html(msg);
|
||||
},
|
||||
|
||||
renderHeader: function () {
|
||||
|
||||
@@ -17,22 +17,22 @@
|
||||
*
|
||||
* 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.Admin.Integrations.OAuth2', 'Views.Admin.Integrations.Edit', function (Dep) {
|
||||
|
||||
|
||||
return Dep.extend({
|
||||
|
||||
|
||||
template: 'admin.integrations.oauth2',
|
||||
|
||||
|
||||
data: function () {
|
||||
|
||||
|
||||
return _.extend({
|
||||
// TODO fetch from server
|
||||
redirectUri: this.getConfig().get('siteUrl') + '/oauthcallback'
|
||||
redirectUri: this.getConfig().get('siteUrl') + '?entryPoint=oauthCallback'
|
||||
}, Dep.prototype.data.call(this));
|
||||
},
|
||||
|
||||
},
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
@@ -17,19 +17,29 @@
|
||||
*
|
||||
* 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.EmailAccount.Record.Edit', ['Views.Record.Edit', 'Views.EmailAccount.Record.Detail'], function (Dep, Detail) {
|
||||
|
||||
return Dep.extend({
|
||||
|
||||
return Dep.extend({
|
||||
|
||||
afterRender: function () {
|
||||
Dep.prototype.afterRender.call(this);
|
||||
|
||||
Detail.prototype.initSslFieldListening.call(this);
|
||||
Dep.prototype.afterRender.call(this);
|
||||
|
||||
Detail.prototype.initSslFieldListening.call(this);
|
||||
|
||||
if (this.getUser().isAdmin()) {
|
||||
var fieldView = this.getFieldView('assignedUser');
|
||||
if (fieldView) {
|
||||
fieldView.readOnly = false;
|
||||
fieldView.setMode('edit');
|
||||
fieldView.render();
|
||||
}
|
||||
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
Espo.define('Views.Email.List', 'Views.List', function (Dep) {
|
||||
|
||||
return Dep.extend({
|
||||
|
||||
|
||||
createButton: false,
|
||||
|
||||
setup: function () {
|
||||
@@ -34,7 +34,6 @@ Espo.define('Views.Email.List', 'Views.List', function (Dep) {
|
||||
"link": "#EmailAccount"
|
||||
});
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
actionComposeEmail: function () {
|
||||
@@ -48,7 +47,7 @@ Espo.define('Views.Email.List', 'Views.List', function (Dep) {
|
||||
view.notify(false);
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
getSearchDefaultData: function () {
|
||||
return {
|
||||
bool: {
|
||||
@@ -56,7 +55,7 @@ Espo.define('Views.Email.List', 'Views.List', function (Dep) {
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -40,38 +40,38 @@ Espo.define('Views.ExternalAccount.Index', 'View', function (Dep) {
|
||||
},
|
||||
},
|
||||
|
||||
setup: function () {
|
||||
setup: function () {
|
||||
this.externalAccountList = this.collection.toJSON();
|
||||
|
||||
|
||||
this.userId = this.getUser().id;
|
||||
this.id = this.options.id || null;
|
||||
this.id = this.options.id || null;
|
||||
if (this.id) {
|
||||
this.userId = this.id.split('__')[1];
|
||||
}
|
||||
|
||||
this.on('after:render', function () {
|
||||
this.renderHeader();
|
||||
|
||||
this.on('after:render', function () {
|
||||
this.renderHeader();
|
||||
if (!this.id) {
|
||||
this.renderDefaultPage();
|
||||
} else {
|
||||
this.openExternalAccount(this.id);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
openExternalAccount: function (id) {
|
||||
this.id = id;
|
||||
|
||||
|
||||
var integration = this.integration = id.split('__')[0];
|
||||
this.userId = id.split('__')[1];
|
||||
|
||||
this.getRouter().navigate('#ExternalAccount/edit/' + id, {trigger: false});
|
||||
|
||||
var viewName =
|
||||
this.getMetadata().get('integrations.' + integration + '.userView') ||
|
||||
'ExternalAccount.' + this.getMetadata().get('integrations.' + integration + '.authMethod');
|
||||
|
||||
this.notify('Loading...');
|
||||
this.getRouter().navigate('#ExternalAccount/edit/' + id, {trigger: false});
|
||||
|
||||
var viewName =
|
||||
this.getMetadata().get('integrations.' + integration + '.userView') ||
|
||||
'ExternalAccount.' + this.getMetadata().get('integrations.' + integration + '.authMethod');
|
||||
|
||||
this.notify('Loading...');
|
||||
this.createView('content', viewName, {
|
||||
el: '#external-account-content',
|
||||
id: id,
|
||||
@@ -79,10 +79,10 @@ Espo.define('Views.ExternalAccount.Index', 'View', function (Dep) {
|
||||
}, function (view) {
|
||||
this.renderHeader();
|
||||
view.render();
|
||||
this.notify(false);
|
||||
this.notify(false);
|
||||
$(window).scrollTop(0);
|
||||
}.bind(this));
|
||||
},
|
||||
},
|
||||
|
||||
renderDefaultPage: function () {
|
||||
$('#external-account-header').html('').hide();
|
||||
|
||||
@@ -17,18 +17,18 @@
|
||||
*
|
||||
* 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.ExternalAccount.OAuth2', 'View', function (Dep) {
|
||||
|
||||
|
||||
return Dep.extend({
|
||||
|
||||
|
||||
template: 'external-account.oauth2',
|
||||
|
||||
|
||||
events: {
|
||||
|
||||
},
|
||||
|
||||
|
||||
data: function () {
|
||||
return {
|
||||
integration: this.integration,
|
||||
@@ -36,9 +36,9 @@ Espo.define('Views.ExternalAccount.OAuth2', 'View', function (Dep) {
|
||||
isConnected: this.isConnected
|
||||
};
|
||||
},
|
||||
|
||||
|
||||
isConnected: false,
|
||||
|
||||
|
||||
events: {
|
||||
'click button[data-action="cancel"]': function () {
|
||||
this.getRouter().navigate('#ExternalAccount', {trigger: true});
|
||||
@@ -50,26 +50,25 @@ Espo.define('Views.ExternalAccount.OAuth2', 'View', function (Dep) {
|
||||
this.connect();
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
setup: function () {
|
||||
this.integration = this.options.integration;
|
||||
this.id = this.options.id;
|
||||
|
||||
|
||||
|
||||
this.helpText = false;
|
||||
if (this.getLanguage().has(this.integration, 'help', 'ExternalAccount')) {
|
||||
this.helpText = this.translate(this.integration, 'help', 'ExternalAccount');
|
||||
}
|
||||
|
||||
|
||||
this.fieldList = [];
|
||||
|
||||
this.dataFieldList = [];
|
||||
|
||||
|
||||
this.dataFieldList = [];
|
||||
|
||||
this.model = new Espo.Model();
|
||||
this.model.id = this.id;
|
||||
this.model.name = 'ExternalAccount';
|
||||
this.model.urlRoot = 'ExternalAccount';
|
||||
|
||||
|
||||
this.model.defs = {
|
||||
fields: {
|
||||
enabled: {
|
||||
@@ -77,32 +76,32 @@ Espo.define('Views.ExternalAccount.OAuth2', 'View', function (Dep) {
|
||||
type: 'bool'
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
this.wait(true);
|
||||
|
||||
};
|
||||
|
||||
this.wait(true);
|
||||
|
||||
this.model.populateDefaults();
|
||||
|
||||
|
||||
this.listenToOnce(this.model, 'sync', function () {
|
||||
this.createFieldView('bool', 'enabled');
|
||||
|
||||
|
||||
$.ajax({
|
||||
url: 'ExternalAccount/action/getOAuth2Info?id=' + this.id,
|
||||
dataType: 'json'
|
||||
}).done(function (respose) {
|
||||
this.clientId = respose.clientId;
|
||||
this.redirectUri = respose.redirectUri;
|
||||
if (respose.isConnected) {
|
||||
this.redirectUri = respose.redirectUri;
|
||||
if (respose.isConnected) {
|
||||
this.isConnected = true;
|
||||
}
|
||||
this.wait(false);
|
||||
}
|
||||
this.wait(false);
|
||||
}.bind(this));
|
||||
|
||||
|
||||
}, this);
|
||||
|
||||
this.model.fetch();
|
||||
|
||||
this.model.fetch();
|
||||
},
|
||||
|
||||
|
||||
hideField: function (name) {
|
||||
this.$el.find('label.field-label-' + name).addClass('hide');
|
||||
this.$el.find('div.field-' + name).addClass('hide');
|
||||
@@ -111,7 +110,7 @@ Espo.define('Views.ExternalAccount.OAuth2', 'View', function (Dep) {
|
||||
view.enabled = false;
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
showField: function (name) {
|
||||
this.$el.find('label.field-label-' + name).removeClass('hide');
|
||||
this.$el.find('div.field-' + name).removeClass('hide');
|
||||
@@ -120,12 +119,12 @@ Espo.define('Views.ExternalAccount.OAuth2', 'View', function (Dep) {
|
||||
view.enabled = true;
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
afterRender: function () {
|
||||
if (!this.model.get('enabled')) {
|
||||
this.$el.find('.data-panel').addClass('hidden');
|
||||
}
|
||||
|
||||
|
||||
this.listenTo(this.model, 'change:enabled', function () {
|
||||
if (this.model.get('enabled')) {
|
||||
this.$el.find('.data-panel').removeClass('hidden');
|
||||
@@ -134,7 +133,7 @@ Espo.define('Views.ExternalAccount.OAuth2', 'View', function (Dep) {
|
||||
}
|
||||
}, this);
|
||||
},
|
||||
|
||||
|
||||
createFieldView: function (type, name, readOnly, params) {
|
||||
this.createView(name, this.getFieldManager().getViewName(type), {
|
||||
model: this.model,
|
||||
@@ -148,45 +147,45 @@ Espo.define('Views.ExternalAccount.OAuth2', 'View', function (Dep) {
|
||||
});
|
||||
this.fieldList.push(name);
|
||||
},
|
||||
|
||||
|
||||
save: function () {
|
||||
this.fieldList.forEach(function (field) {
|
||||
var view = this.getView(field);
|
||||
if (!view.readOnly) {
|
||||
view.fetchToModel();
|
||||
}
|
||||
}, this);
|
||||
|
||||
}, this);
|
||||
|
||||
var notValid = false;
|
||||
this.fieldList.forEach(function (field) {
|
||||
notValid = this.getView(field).validate() || notValid;
|
||||
}, this);
|
||||
|
||||
|
||||
if (notValid) {
|
||||
this.notify('Not valid', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
this.listenToOnce(this.model, 'sync', function () {
|
||||
}
|
||||
|
||||
this.listenToOnce(this.model, 'sync', function () {
|
||||
this.notify('Saved', 'success');
|
||||
if (!this.model.get('enabled')) {
|
||||
if (!this.model.get('enabled')) {
|
||||
this.setNotConnected();
|
||||
}
|
||||
}, this);
|
||||
|
||||
|
||||
this.notify('Saving...');
|
||||
this.model.save();
|
||||
},
|
||||
|
||||
|
||||
popup: function (options, callback) {
|
||||
options.windowName = options.windowName || 'ConnectWithOAuth';
|
||||
options.windowOptions = options.windowOptions || 'location=0,status=0,width=800,height=400';
|
||||
options.callback = options.callback || function(){ window.location.reload(); };
|
||||
|
||||
|
||||
var self = this;
|
||||
|
||||
|
||||
var path = options.path;
|
||||
|
||||
|
||||
var arr = [];
|
||||
var params = (options.params || {});
|
||||
for (var name in params) {
|
||||
@@ -195,17 +194,17 @@ Espo.define('Views.ExternalAccount.OAuth2', 'View', function (Dep) {
|
||||
}
|
||||
}
|
||||
path += '?' + arr.join('&');
|
||||
|
||||
|
||||
var parseUrl = function (str) {
|
||||
var code = null;
|
||||
var error = null;
|
||||
|
||||
|
||||
str = str.substr(str.indexOf('?') + 1, str.length);
|
||||
str.split('&').forEach(function (part) {
|
||||
var arr = part.split('=');
|
||||
var name = decodeURI(arr[0]);
|
||||
var value = decodeURI(arr[1] || '');
|
||||
|
||||
|
||||
if (name == 'code') {
|
||||
code = value;
|
||||
}
|
||||
@@ -223,14 +222,14 @@ Espo.define('Views.ExternalAccount.OAuth2', 'View', function (Dep) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
popup = window.open(path, options.windowName, options.windowOptions);
|
||||
interval = window.setInterval(function () {
|
||||
if (popup.closed) {
|
||||
window.clearInterval(interval);
|
||||
} else {
|
||||
var res = parseUrl(popup.location.href.toString());
|
||||
if (res) {
|
||||
if (res) {
|
||||
callback.call(self, res);
|
||||
popup.close();
|
||||
window.clearInterval(interval);
|
||||
@@ -238,9 +237,8 @@ Espo.define('Views.ExternalAccount.OAuth2', 'View', function (Dep) {
|
||||
}
|
||||
}, 500);
|
||||
},
|
||||
|
||||
|
||||
connect: function () {
|
||||
this.notify('Please wait...');
|
||||
this.popup({
|
||||
path: this.getMetadata().get('integrations.' + this.integration + '.params.endpoint'),
|
||||
params: {
|
||||
@@ -250,7 +248,7 @@ Espo.define('Views.ExternalAccount.OAuth2', 'View', function (Dep) {
|
||||
response_type: 'code',
|
||||
access_type: 'offline',
|
||||
approval_prompt: 'force'
|
||||
}
|
||||
}
|
||||
}, function (res) {
|
||||
if (res.errror) {
|
||||
this.notify(false);
|
||||
@@ -268,35 +266,35 @@ Espo.define('Views.ExternalAccount.OAuth2', 'View', function (Dep) {
|
||||
dataType: 'json',
|
||||
error: function () {
|
||||
this.$el.find('[data-action="connect"]').removeClass('disabled');
|
||||
}.bind(this)
|
||||
}.bind(this)
|
||||
}).done(function (response) {
|
||||
this.notify(false);
|
||||
if (response === true) {
|
||||
this.setConneted();
|
||||
} else {
|
||||
this.setNotConneted();
|
||||
this.setNotConneted();
|
||||
}
|
||||
this.$el.find('[data-action="connect"]').removeClass('disabled');
|
||||
this.$el.find('[data-action="connect"]').removeClass('disabled');
|
||||
}.bind(this));
|
||||
|
||||
|
||||
} else {
|
||||
this.notify('Error occured', 'error');
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
setConneted: function () {
|
||||
this.isConnected = true;
|
||||
this.$el.find('[data-action="connect"]').addClass('hidden');;
|
||||
this.$el.find('.connected-label').removeClass('hidden');
|
||||
},
|
||||
|
||||
|
||||
setNotConnected: function () {
|
||||
this.isConnected = false;
|
||||
this.$el.find('[data-action="connect"]').removeClass('hidden');;
|
||||
this.$el.find('.connected-label').addClass('hidden');
|
||||
},
|
||||
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
@@ -40,18 +40,23 @@ Espo.define('Views.List', ['Views.Main', 'SearchManager'], function (Dep, Search
|
||||
|
||||
searchPanel: true,
|
||||
|
||||
searchManager: true,
|
||||
searchManager: null,
|
||||
|
||||
createButton: true,
|
||||
|
||||
setup: function () {
|
||||
this.setupSearchManager();
|
||||
this.setupSorting();
|
||||
this.collection.maxSize = this.getConfig().get('recordsPerPage') || this.collection.maxSize;
|
||||
|
||||
if (this.getMetadata().get('clientDefs.' + this.scope + '.disableSearchPanel')) {
|
||||
this.searchPanel = false;
|
||||
}
|
||||
|
||||
if (this.searchPanel) {
|
||||
this.setupSearchManager();
|
||||
}
|
||||
|
||||
this.setupSorting();
|
||||
|
||||
if (this.searchPanel) {
|
||||
this.createView('search', 'Record.Search', {
|
||||
collection: this.collection,
|
||||
@@ -86,8 +91,6 @@ Espo.define('Views.List', ['Views.Main', 'SearchManager'], function (Dep, Search
|
||||
var searchManager = new SearchManager(collection, 'list', this.getStorage(), this.getDateTime(), this.getSearchDefaultData());
|
||||
searchManager.loadStored();
|
||||
collection.where = searchManager.getWhere();
|
||||
collection.maxSize = this.getConfig().get('recordsPerPage') || collection.maxSize;
|
||||
|
||||
this.searchManager = searchManager;
|
||||
},
|
||||
|
||||
|
||||
@@ -279,7 +279,7 @@ Espo.define('Views.Record.Detail', 'View', function (Dep) {
|
||||
getFieldView: function (name) {
|
||||
var view = this.getView('record').getView(name) || null;
|
||||
if (!view && this.getView('side')) {
|
||||
view = this.getView('side').getView(name);
|
||||
view = (this.getView('side').getFields() || {})[name];
|
||||
}
|
||||
return view || null;
|
||||
},
|
||||
|
||||
@@ -18,26 +18,26 @@
|
||||
* 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.User.Record.Detail', 'Views.Record.Detail', function (Dep) {
|
||||
|
||||
return Dep.extend({
|
||||
|
||||
|
||||
sideView: 'User.Record.DetailSide',
|
||||
|
||||
|
||||
editModeEnabled: false,
|
||||
|
||||
|
||||
setup: function () {
|
||||
Dep.prototype.setup.call(this);
|
||||
this.buttons = _.clone(this.buttons);
|
||||
|
||||
|
||||
if (this.model.id == this.getUser().id || this.getUser().isAdmin()) {
|
||||
this.buttons.push({
|
||||
name: 'preferences',
|
||||
label: 'Preferences',
|
||||
style: 'default'
|
||||
});
|
||||
|
||||
|
||||
if (!this.model.get('isAdmin')) {
|
||||
this.buttons.push({
|
||||
name: 'access',
|
||||
@@ -45,7 +45,7 @@ Espo.define('Views.User.Record.Detail', 'Views.Record.Detail', function (Dep) {
|
||||
style: 'default'
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
if (this.model.id == this.getUser().id) {
|
||||
this.buttons.push({
|
||||
name: 'changePassword',
|
||||
@@ -53,40 +53,64 @@ Espo.define('Views.User.Record.Detail', 'Views.Record.Detail', function (Dep) {
|
||||
style: 'default'
|
||||
});
|
||||
}
|
||||
|
||||
if ((this.getAcl().check('EmailAccountScope') && this.model.id == this.getUser().id) || this.getUser().isAdmin()) {
|
||||
this.buttons.push({
|
||||
name: 'emailAccounts',
|
||||
label: "Email Accounts",
|
||||
style: 'default'
|
||||
});
|
||||
}
|
||||
|
||||
if (this.model.id == this.getUser().id) {
|
||||
this.buttons.push({
|
||||
name: 'externalAccounts',
|
||||
label: 'External Accounts',
|
||||
style: 'default'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (this.model.id == this.getUser().id) {
|
||||
this.listenTo(this.model, 'after:save', function () {
|
||||
this.getUser().set(this.model.toJSON());
|
||||
}.bind(this));
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
actionChangePassword: function () {
|
||||
this.notify('Loading...');
|
||||
|
||||
|
||||
this.createView('changePassword', 'Modals.ChangePassword', {
|
||||
userId: this.model.id
|
||||
}, function (view) {
|
||||
view.render();
|
||||
this.notify(false);
|
||||
|
||||
|
||||
this.listenToOnce(view, 'changed', function () {
|
||||
setTimeout(function () {
|
||||
this.getBaseController().logout();
|
||||
}.bind(this), 2000);
|
||||
}, this);
|
||||
|
||||
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
|
||||
actionPreferences: function () {
|
||||
this.getRouter().navigate('#Preferences/edit/' + this.model.id, {trigger: true});
|
||||
},
|
||||
|
||||
|
||||
actionEmailAccounts: function () {
|
||||
this.getRouter().navigate('#EmailAccount/list/userId=' + this.model.id, {trigger: true});
|
||||
},
|
||||
|
||||
actionExternalAccounts: function () {
|
||||
this.getRouter().navigate('#ExternalAccount', {trigger: true});
|
||||
},
|
||||
|
||||
actionAccess: function () {
|
||||
this.notify('Loading...');
|
||||
|
||||
|
||||
$.ajax({
|
||||
url: 'User/action/acl',
|
||||
type: 'GET',
|
||||
@@ -102,11 +126,11 @@ Espo.define('Views.User.Record.Detail', 'Views.Record.Detail', function (Dep) {
|
||||
view.render();
|
||||
}.bind(this));
|
||||
}.bind(this));
|
||||
|
||||
|
||||
|
||||
},
|
||||
|
||||
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
|
||||
|
||||
@@ -34,13 +34,26 @@ return array(
|
||||
'name' => 'Check Inbound Emails',
|
||||
'job' => 'CheckInboundEmails',
|
||||
'status' => 'Active',
|
||||
'scheduling' => '/10 * * * *',
|
||||
'scheduling' => '/5 * * * *',
|
||||
),
|
||||
1 => array(
|
||||
'name' => 'Check Personal Email Accounts',
|
||||
'job' => 'CheckEmailAccounts',
|
||||
'status' => 'Active',
|
||||
'scheduling' => '/10 * * * *',
|
||||
),
|
||||
2 => array(
|
||||
'name' => 'Send Email Reminders',
|
||||
'job' => 'SendEmailReminders',
|
||||
'status' => 'Active',
|
||||
'scheduling' => '/2 * * * *',
|
||||
),
|
||||
3 => array(
|
||||
'name' => 'Clean-up',
|
||||
'job' => 'Cleanup',
|
||||
'status' => 'Active',
|
||||
'scheduling' => '1 1 * * 0',
|
||||
),
|
||||
|
||||
),
|
||||
);
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "espocrm",
|
||||
"version": "2.9.0",
|
||||
"version": "2.9.2",
|
||||
"description": "",
|
||||
"main": "index.php",
|
||||
"repository": {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<?php
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
@@ -28,24 +28,24 @@ use tests\ReflectionHelper;
|
||||
class CronManagerTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
protected $object;
|
||||
|
||||
|
||||
protected $objects;
|
||||
|
||||
protected $filesPath= 'tests/testData/EntryPoints';
|
||||
|
||||
protected function setUp()
|
||||
{
|
||||
{
|
||||
$this->objects['container'] = $this->getMockBuilder('\Espo\Core\Container')->disableOriginalConstructor()->getMock();
|
||||
|
||||
$this->objects['serviceFactory'] = $this->getMockBuilder('\Espo\Core\ServiceFactory')->disableOriginalConstructor()->getMock();
|
||||
$this->objects['config'] = $this->getMockBuilder('\Espo\Core\Utils\Config')->disableOriginalConstructor()->getMock();
|
||||
$this->objects['fileManager'] = $this->getMockBuilder('\Espo\Core\Utils\File\Manager')->disableOriginalConstructor()->getMock();
|
||||
|
||||
|
||||
$this->objects['fileManager'] = $this->getMockBuilder('\Espo\Core\Utils\File\Manager')->disableOriginalConstructor()->getMock();
|
||||
|
||||
|
||||
$map = array(
|
||||
array('config', $this->objects['config']),
|
||||
array('fileManager', $this->objects['fileManager']),
|
||||
array('serviceFactory', $this->objects['serviceFactory']),
|
||||
array('serviceFactory', $this->objects['serviceFactory']),
|
||||
);
|
||||
|
||||
$this->objects['container']
|
||||
@@ -55,7 +55,7 @@ class CronManagerTest extends \PHPUnit_Framework_TestCase
|
||||
|
||||
$this->object = new \Espo\Core\CronManager( $this->objects['container'] );
|
||||
|
||||
$this->reflection = new ReflectionHelper($this->object);
|
||||
$this->reflection = new ReflectionHelper($this->object);
|
||||
}
|
||||
|
||||
protected function tearDown()
|
||||
@@ -68,50 +68,50 @@ class CronManagerTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
$this->objects['fileManager']
|
||||
->expects($this->once())
|
||||
->method('getContents')
|
||||
->method('getPhpContents')
|
||||
->will($this->returnValue(false));
|
||||
|
||||
$this->objects['config']
|
||||
->expects($this->exactly(2))
|
||||
->method('get')
|
||||
->will($this->returnValue(50));
|
||||
|
||||
$this->assertTrue( $this->reflection->invokeMethod('checkLastRunTime', array()) );
|
||||
}
|
||||
|
||||
$this->assertTrue( $this->reflection->invokeMethod('checkLastRunTime', array()) );
|
||||
}
|
||||
|
||||
|
||||
function testCheckLastRunTime()
|
||||
{
|
||||
$this->objects['fileManager']
|
||||
->expects($this->once())
|
||||
->method('getContents')
|
||||
->method('getPhpContents')
|
||||
->will($this->returnValue(time()-60));
|
||||
|
||||
$this->objects['config']
|
||||
->expects($this->once())
|
||||
->method('get')
|
||||
->will($this->returnValue(50));
|
||||
|
||||
$this->assertTrue( $this->reflection->invokeMethod('checkLastRunTime', array()) );
|
||||
}
|
||||
|
||||
$this->assertTrue( $this->reflection->invokeMethod('checkLastRunTime', array()) );
|
||||
}
|
||||
|
||||
|
||||
function testCheckLastRunTimeTooFrequency()
|
||||
{
|
||||
$this->objects['fileManager']
|
||||
->expects($this->once())
|
||||
->method('getContents')
|
||||
->method('getPhpContents')
|
||||
->will($this->returnValue(time()-49));
|
||||
|
||||
$this->objects['config']
|
||||
->expects($this->once())
|
||||
->method('get')
|
||||
->will($this->returnValue(50));
|
||||
|
||||
$this->assertFalse( $this->reflection->invokeMethod('checkLastRunTime', array()) );
|
||||
}
|
||||
|
||||
|
||||
$this->assertFalse( $this->reflection->invokeMethod('checkLastRunTime', array()) );
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<?php
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
@@ -150,7 +150,7 @@ class ConfigTest extends \PHPUnit_Framework_TestCase
|
||||
|
||||
public function testSystemConfigMerge()
|
||||
{
|
||||
$configDataWithoutSystem = $this->objects['fileManager']->getContents($this->configPath);
|
||||
$configDataWithoutSystem = $this->objects['fileManager']->getPhpContents($this->configPath);
|
||||
$this->assertArrayNotHasKey('systemItems', $configDataWithoutSystem);
|
||||
$this->assertArrayNotHasKey('adminItems', $configDataWithoutSystem);
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<?php
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
@@ -89,7 +89,7 @@ class FieldManagerTest extends \PHPUnit_Framework_TestCase
|
||||
->method('get')
|
||||
->will($this->returnValue($data));
|
||||
|
||||
$this->assertTrue($this->object->update('name', $data, 'Account'));
|
||||
$this->object->update('name', $data, 'Account');
|
||||
}
|
||||
|
||||
public function testUpdateCustomFieldIsNotChanged()
|
||||
@@ -138,10 +138,9 @@ class FieldManagerTest extends \PHPUnit_Framework_TestCase
|
||||
"isCustom" => true,
|
||||
);
|
||||
|
||||
$this->assertTrue($this->object->update('varName', $data, 'CustomEntity'));
|
||||
$this->object->update('varName', $data, 'CustomEntity');
|
||||
}
|
||||
|
||||
|
||||
public function testRead()
|
||||
{
|
||||
$data = array(
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<?php
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
@@ -80,7 +80,6 @@ class LanguageTest extends \PHPUnit_Framework_TestCase
|
||||
$this->object->setLanguage($originalLang);
|
||||
}
|
||||
|
||||
|
||||
public function testGetLangCacheFile()
|
||||
{
|
||||
$cacheFile = $this->cacheFile;
|
||||
@@ -96,7 +95,6 @@ class LanguageTest extends \PHPUnit_Framework_TestCase
|
||||
$this->object->setLanguage($originalLang);
|
||||
}
|
||||
|
||||
|
||||
public function testGetData()
|
||||
{
|
||||
$result = array (
|
||||
@@ -142,7 +140,6 @@ class LanguageTest extends \PHPUnit_Framework_TestCase
|
||||
$this->assertEquals($result, $this->reflection->invokeMethod('getData', array()));
|
||||
}
|
||||
|
||||
|
||||
public function testGet()
|
||||
{
|
||||
$result = array (
|
||||
@@ -204,7 +201,6 @@ class LanguageTest extends \PHPUnit_Framework_TestCase
|
||||
$this->assertEquals($result, $this->object->translate('language', 'options', 'Global', $requiredOptions));
|
||||
}
|
||||
|
||||
|
||||
public function testTranslateArray()
|
||||
{
|
||||
$input = array(
|
||||
@@ -227,20 +223,124 @@ class LanguageTest extends \PHPUnit_Framework_TestCase
|
||||
public function testSet()
|
||||
{
|
||||
$label = 'TEST';
|
||||
$this->object->set('label', $label, 'fields', 'User');
|
||||
$this->object->set('User', 'fields', 'label', $label);
|
||||
$this->assertEquals($label, $this->object->translate('label', 'fields', 'User'));
|
||||
|
||||
$result = array(
|
||||
'User' => array(
|
||||
'fields' => array(
|
||||
'label' => 'TEST',
|
||||
),
|
||||
),
|
||||
);
|
||||
$this->assertEquals($result, $this->reflection->getProperty('changedData'));
|
||||
|
||||
$label2 = 'TEST2';
|
||||
$this->object->set('name', $label2, 'fields', 'User');
|
||||
$this->object->set('User', 'fields', 'name', $label2);
|
||||
$this->assertEquals($label2, $this->object->translate('name', 'fields', 'User'));
|
||||
|
||||
$result = array(
|
||||
'User' => array(
|
||||
'fields' => array(
|
||||
'label' => 'TEST',
|
||||
'name' => 'TEST2',
|
||||
),
|
||||
),
|
||||
);
|
||||
$this->assertEquals($result, $this->reflection->getProperty('changedData'));
|
||||
|
||||
$label3 = 'TEST3';
|
||||
$this->object->set('name', $label3, 'fields', 'Account');
|
||||
$this->object->set('Account', 'fields', 'name', $label3);
|
||||
$this->assertEquals($label3, $this->object->translate('name', 'fields', 'Account'));
|
||||
|
||||
$this->reflection->invokeMethod('init', array(true));
|
||||
$result = array(
|
||||
'User' => array(
|
||||
'fields' => array(
|
||||
'label' => 'TEST',
|
||||
'name' => 'TEST2',
|
||||
),
|
||||
),
|
||||
'Account' => array(
|
||||
'fields' => array(
|
||||
'name' => 'TEST3',
|
||||
),
|
||||
),
|
||||
);
|
||||
$this->assertEquals($result, $this->reflection->getProperty('changedData'));
|
||||
|
||||
$this->object->clearChanges();
|
||||
|
||||
$this->assertEquals(array(), $this->reflection->getProperty('changedData'));
|
||||
$this->assertNotEquals('TEST', $this->object->get('User', 'fields', 'label'));
|
||||
}
|
||||
|
||||
public function testDelete()
|
||||
{
|
||||
$this->object->delete('User', 'fields', 'label');
|
||||
$this->assertNull($this->object->get('User.fields.label'));
|
||||
|
||||
$result = array(
|
||||
'User' => array(
|
||||
'fields' => array(
|
||||
'label',
|
||||
),
|
||||
),
|
||||
);
|
||||
$this->assertEquals($result, $this->reflection->getProperty('deletedData'));
|
||||
|
||||
$this->object->delete('User', 'fields', 'name');
|
||||
$this->assertNull($this->object->get('User.fields.name'));
|
||||
|
||||
$result = array(
|
||||
'User' => array(
|
||||
'fields' => array(
|
||||
'label',
|
||||
'name',
|
||||
),
|
||||
),
|
||||
);
|
||||
$this->assertEquals($result, $this->reflection->getProperty('deletedData'));
|
||||
|
||||
$this->object->clearChanges();
|
||||
|
||||
$this->assertNotNull($this->object->get('User.fields.label'));
|
||||
$this->assertNotNull($this->object->get('User.fields.name'));
|
||||
|
||||
$this->assertEquals(array(), $this->reflection->getProperty('deletedData'));
|
||||
}
|
||||
|
||||
public function testUndelete()
|
||||
{
|
||||
$this->object->delete('User', 'fields', 'label');
|
||||
$this->assertNull($this->object->get('User.fields.label'));
|
||||
|
||||
$this->object->delete('User', 'fields', 'name');
|
||||
$this->assertNull($this->object->get('User.fields.name'));
|
||||
|
||||
$label = 'TEST';
|
||||
$this->object->set('User', 'fields', 'label', $label);
|
||||
$this->assertEquals($label, $this->object->translate('label', 'fields', 'User'));
|
||||
|
||||
$result = array(
|
||||
'User' => array(
|
||||
'fields' => array(
|
||||
1 => 'name',
|
||||
),
|
||||
),
|
||||
);
|
||||
$this->assertEquals($result, $this->reflection->getProperty('deletedData'));
|
||||
|
||||
$label2 = 'TEST2';
|
||||
$this->object->set('User', 'fields', 'name', $label2);
|
||||
$this->assertEquals($label2, $this->object->translate('name', 'fields', 'User'));
|
||||
|
||||
$result = array(
|
||||
'User' => array(
|
||||
'fields' => array(
|
||||
),
|
||||
),
|
||||
);
|
||||
$this->assertEquals($result, $this->reflection->getProperty('deletedData'));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<?php
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
@@ -24,52 +24,209 @@ namespace tests\Espo\Core\Utils;
|
||||
|
||||
use tests\ReflectionHelper;
|
||||
|
||||
|
||||
class MetadataTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
protected $object;
|
||||
|
||||
|
||||
protected $objects;
|
||||
|
||||
protected $reflection;
|
||||
|
||||
protected $defaultCacheFile = 'tests/testData/Utils/Metadata/metadata.php';
|
||||
|
||||
protected $cacheFile = 'tests/testData/cache/metadata.php';
|
||||
protected $ormCacheFile = 'tests/testData/Utils/Metadata/ormMetadata.php';
|
||||
|
||||
protected function setUp()
|
||||
{
|
||||
{
|
||||
/*copy defaultCacheFile file to cache*/
|
||||
if (!file_exists($this->cacheFile)) {
|
||||
copy($this->defaultCacheFile, $this->cacheFile);
|
||||
}
|
||||
|
||||
$this->objects['config'] = $this->getMockBuilder('\Espo\Core\Utils\Config')->disableOriginalConstructor()->getMock();
|
||||
$this->objects['fileManager'] = new \Espo\Core\Utils\File\Manager();
|
||||
$this->objects['fileManager'] = new \Espo\Core\Utils\File\Manager();
|
||||
$this->objects['Unifier'] = $this->getMockBuilder('\Espo\Core\Utils\File\Unifier')->disableOriginalConstructor()->getMock();
|
||||
|
||||
//set to use cache
|
||||
$this->objects['config']
|
||||
->expects($this->any())
|
||||
->method('get')
|
||||
->will($this->returnValue(true));
|
||||
|
||||
->will($this->returnValue(true));
|
||||
|
||||
|
||||
$this->object = new \Espo\Core\Utils\Metadata($this->objects['config'], $this->objects['fileManager']);
|
||||
|
||||
$this->reflection = new ReflectionHelper($this->object);
|
||||
$this->reflection->setProperty('cacheFile', 'tests/testData/Utils/Metadata/metadata.php');
|
||||
$this->reflection->setProperty('ormCacheFile', 'tests/testData/Utils/Metadata/ormMetadata.php');
|
||||
$this->reflection = new ReflectionHelper($this->object);
|
||||
$this->reflection->setProperty('cacheFile', $this->cacheFile);
|
||||
$this->reflection->setProperty('ormCacheFile', $this->ormCacheFile);
|
||||
}
|
||||
|
||||
protected function tearDown()
|
||||
{
|
||||
$this->object = NULL;
|
||||
}
|
||||
|
||||
|
||||
function testGet()
|
||||
{
|
||||
|
||||
public function testGet()
|
||||
{
|
||||
$result = 'System';
|
||||
$this->assertEquals($result, $this->object->get('app.adminPanel.system.label'));
|
||||
$this->assertEquals($result, $this->object->get('app.adminPanel.system.label'));
|
||||
|
||||
$result = 'fields';
|
||||
$this->assertArrayHasKey($result, $this->object->get('entityDefs.User'));
|
||||
}
|
||||
$this->assertArrayHasKey($result, $this->object->get('entityDefs.User'));
|
||||
}
|
||||
|
||||
public function testSet()
|
||||
{
|
||||
$data = array (
|
||||
'fields' =>
|
||||
array (
|
||||
'name' =>
|
||||
array (
|
||||
'required' => false,
|
||||
'maxLength' => 150,
|
||||
'view' => 'Views.Test.Custom',
|
||||
),
|
||||
),
|
||||
);
|
||||
$this->object->set('entityDefs', 'Attachment', $data);
|
||||
$this->assertEquals('Views.Test.Custom', $this->object->get('entityDefs.Attachment.fields.name.view'));
|
||||
$this->assertEquals(150, $this->object->get('entityDefs.Attachment.fields.name.maxLength'));
|
||||
|
||||
$result = array(
|
||||
'entityDefs' => array(
|
||||
'Attachment' => $data
|
||||
),
|
||||
);
|
||||
$this->assertEquals($result, $this->reflection->getProperty('changedData'));
|
||||
|
||||
}
|
||||
$data = array (
|
||||
'fields' =>
|
||||
array (
|
||||
'name' =>
|
||||
array (
|
||||
'maxLength' => 200,
|
||||
),
|
||||
),
|
||||
);
|
||||
$this->object->set('entityDefs', 'Attachment', $data);
|
||||
$this->assertEquals(200, $this->object->get('entityDefs.Attachment.fields.name.maxLength'));
|
||||
$this->assertEquals('Views.Test.Custom', $this->object->get('entityDefs.Attachment.fields.name.view'));
|
||||
|
||||
?>
|
||||
$result = array(
|
||||
'entityDefs' => array(
|
||||
'Attachment' => array (
|
||||
'fields' =>
|
||||
array (
|
||||
'name' =>
|
||||
array (
|
||||
'required' => false,
|
||||
'maxLength' => 200,
|
||||
'view' => 'Views.Test.Custom',
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
$this->assertEquals($result, $this->reflection->getProperty('changedData'));
|
||||
|
||||
$this->object->clearChanges();
|
||||
|
||||
$this->assertEquals(array(), $this->reflection->getProperty('changedData'));
|
||||
$this->assertNull($this->object->get('entityDefs.Attachment.fields.name.view'));
|
||||
}
|
||||
|
||||
public function testDelete()
|
||||
{
|
||||
$data = array (
|
||||
'fields.name.type',
|
||||
);
|
||||
$this->object->delete('entityDefs', 'Attachment', $data);
|
||||
$this->assertNull($this->object->get('entityDefs.Attachment.fields.name.type'));
|
||||
|
||||
$result = array(
|
||||
'entityDefs' => array(
|
||||
'Attachment' => array(
|
||||
'fields.name.type',
|
||||
),
|
||||
),
|
||||
);
|
||||
$this->assertEquals($result, $this->reflection->getProperty('deletedData'));
|
||||
|
||||
$data = array (
|
||||
'fields.name.required',
|
||||
);
|
||||
$this->object->delete('entityDefs', 'Attachment', $data);
|
||||
$this->assertNull($this->object->get('entityDefs.Attachment.fields.name.required'));
|
||||
|
||||
$result = array(
|
||||
'entityDefs' => array(
|
||||
'Attachment' => array(
|
||||
'fields.name.type',
|
||||
'fields.name.required',
|
||||
),
|
||||
),
|
||||
);
|
||||
$this->assertEquals($result, $this->reflection->getProperty('deletedData'));
|
||||
|
||||
$this->object->init(false);
|
||||
|
||||
$this->assertNotNull($this->object->get('entityDefs.Attachment.fields.name.type'));
|
||||
$this->assertNotNull($this->object->get('entityDefs.Attachment.fields.name.required'));
|
||||
|
||||
$this->object->clearChanges();
|
||||
$this->assertEquals(array(), $this->reflection->getProperty('deletedData'));
|
||||
}
|
||||
|
||||
public function testUndelete()
|
||||
{
|
||||
$data = array (
|
||||
'fields.name.type',
|
||||
'fields.name.required',
|
||||
);
|
||||
$this->object->delete('entityDefs', 'Attachment', $data);
|
||||
$this->assertNull($this->object->get('entityDefs.Attachment.fields.name.type'));
|
||||
|
||||
$data = array (
|
||||
'fields' =>
|
||||
array (
|
||||
'name' =>
|
||||
array (
|
||||
'type' => 'enum',
|
||||
),
|
||||
),
|
||||
);
|
||||
$this->object->set('entityDefs', 'Attachment', $data);
|
||||
$this->assertEquals('enum', $this->object->get('entityDefs.Attachment.fields.name.type'));
|
||||
|
||||
$result = array(
|
||||
'entityDefs' => array(
|
||||
'Attachment' => array(
|
||||
1 => 'fields.name.required',
|
||||
),
|
||||
),
|
||||
);
|
||||
$this->assertEquals($result, $this->reflection->getProperty('deletedData'));
|
||||
|
||||
$data = array (
|
||||
'fields' =>
|
||||
array (
|
||||
'name' =>
|
||||
array (
|
||||
'required' => true,
|
||||
),
|
||||
),
|
||||
);
|
||||
$this->object->set('entityDefs', 'Attachment', $data);
|
||||
$this->assertEquals(true, $this->object->get('entityDefs.Attachment.fields.name.required'));
|
||||
|
||||
$result = array(
|
||||
'entityDefs' => array(
|
||||
'Attachment' => array(
|
||||
),
|
||||
),
|
||||
);
|
||||
$this->assertEquals($result, $this->reflection->getProperty('deletedData'));
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user