1350 lines
30 KiB
PHP
1350 lines
30 KiB
PHP
<?php
|
|
namespace Bitrix\Rest;
|
|
|
|
use Bitrix\Main;
|
|
use Bitrix\Main\ArgumentException;
|
|
use Bitrix\Main\Data\Cache;
|
|
use Bitrix\Main\Engine\CurrentUser;
|
|
use Bitrix\Main\Event;
|
|
use Bitrix\Main\EventManager;
|
|
use Bitrix\Main\EventResult;
|
|
use Bitrix\Main\Localization\Loc;
|
|
use Bitrix\Main\ObjectPropertyException;
|
|
use Bitrix\Main\SystemException;
|
|
use Bitrix\Main\Web\HttpClient;
|
|
use Bitrix\Main\Web\Uri;
|
|
use Bitrix\Rest\Engine\Access;
|
|
use Bitrix\Rest\Event\Sender;
|
|
use Bitrix\Rest\FormConfig\EventType;
|
|
use Bitrix\Rest\Internals\FreeAppTable;
|
|
use Bitrix\Rest\Marketplace\Client;
|
|
use Bitrix\Main\ORM\Fields\BooleanField;
|
|
use Bitrix\Main\ORM\Fields\Relations\OneToMany;
|
|
use Bitrix\Rest\Preset\EventController;
|
|
|
|
Loc::loadMessages(__FILE__);
|
|
|
|
|
|
/**
|
|
* Class AppTable
|
|
*
|
|
* Fields:
|
|
* <ul>
|
|
* <li> ID int mandatory
|
|
* <li> CLIENT_ID string(128) mandatory
|
|
* <li> CODE string(128) mandatory
|
|
* <li> ACTIVE bool optional default 'Y'
|
|
* <li> INSTALLED bool optional default 'N'
|
|
* <li> URL string(1000) mandatory
|
|
* <li> URL_DEMO string(1000) optional
|
|
* <li> URL_INSTALL string(1000) optional
|
|
* <li> VERSION string(4) mandatory
|
|
* <li> SCOPE string(2000) mandatory
|
|
* <li> STATUS string(1) mandatory default 'F'
|
|
* <li> DATE_FINISH date optional
|
|
* <li> IS_TRIALED bool optional default 'N'
|
|
* <li> SHARED_KEY string(32) optional
|
|
* <li> CLIENT_SECRET string(100) optional
|
|
* <li> APP_NAME string(1000) optional
|
|
* <li> ACCESS string(2000) optional
|
|
* </ul>
|
|
*
|
|
* @package Bitrix\Rest
|
|
*
|
|
* DO NOT WRITE ANYTHING BELOW THIS
|
|
*
|
|
* <<< ORMENTITYANNOTATION
|
|
* @method static EO_App_Query query()
|
|
* @method static EO_App_Result getByPrimary($primary, array $parameters = [])
|
|
* @method static EO_App_Result getById($id)
|
|
* @method static EO_App_Result getList(array $parameters = [])
|
|
* @method static EO_App_Entity getEntity()
|
|
* @method static \Bitrix\Rest\EO_App createObject($setDefaultValues = true)
|
|
* @method static \Bitrix\Rest\EO_App_Collection createCollection()
|
|
* @method static \Bitrix\Rest\EO_App wakeUpObject($row)
|
|
* @method static \Bitrix\Rest\EO_App_Collection wakeUpCollection($rows)
|
|
*/
|
|
class AppTable extends Main\Entity\DataManager
|
|
{
|
|
const ACTIVE = 'Y';
|
|
const INACTIVE = 'N';
|
|
const INSTALLED = 'Y';
|
|
const NOT_INSTALLED = 'N';
|
|
const TRIALED = 'Y';
|
|
const NOT_TRIALED = 'N';
|
|
|
|
const TYPE_STANDARD = 'N';
|
|
const TYPE_ONLY_API = 'A';
|
|
const TYPE_CONFIGURATION = 'C';
|
|
const TYPE_SMART_ROBOTS = 'R';
|
|
const TYPE_BIC_DASHBOARD = 'B';
|
|
|
|
const MODE_SITE = 'S';
|
|
|
|
const STATUS_LOCAL = 'L';
|
|
const STATUS_FREE = 'F';
|
|
const STATUS_PAID = 'P';
|
|
const STATUS_DEMO = 'D';
|
|
const STATUS_TRIAL = 'T';
|
|
const STATUS_SUBSCRIPTION = 'S';
|
|
|
|
const PAID_NOTIFY_DAYS = 5;
|
|
const PAID_GRACE_PERIOD = -14;
|
|
|
|
const CACHE_TTL = 86400;
|
|
const CACHE_PATH = '/rest/app/';
|
|
|
|
private static $skipRemoteUpdate = false;
|
|
|
|
protected static $licenseLang = null;
|
|
|
|
protected static $applicationCache = array();
|
|
|
|
protected static $localAppDeniedScope = array(
|
|
'landing_cloud', 'rating',
|
|
);
|
|
|
|
/**
|
|
* Returns DB table name for entity.
|
|
*
|
|
* @return string
|
|
*/
|
|
public static function getTableName()
|
|
{
|
|
return 'b_rest_app';
|
|
}
|
|
|
|
/**
|
|
* Returns entity map definition.
|
|
*
|
|
* @return array
|
|
*/
|
|
public static function getMap()
|
|
{
|
|
return array(
|
|
'ID' => array(
|
|
'data_type' => 'integer',
|
|
'primary' => true,
|
|
'autocomplete' => true,
|
|
),
|
|
'CLIENT_ID' => array(
|
|
'data_type' => 'string',
|
|
'required' => true,
|
|
'validation' => array(__CLASS__, 'validateClientId'),
|
|
),
|
|
'CODE' => array(
|
|
'data_type' => 'string',
|
|
'required' => true,
|
|
'validation' => array(__CLASS__, 'validateCode'),
|
|
),
|
|
'ACTIVE' => array(
|
|
'data_type' => 'boolean',
|
|
'values' => array(static::INACTIVE, static::ACTIVE),
|
|
),
|
|
'INSTALLED' => array(
|
|
'data_type' => 'boolean',
|
|
'values' => array(static::NOT_INSTALLED, static::INSTALLED),
|
|
),
|
|
'URL' => array(
|
|
'data_type' => 'string',
|
|
'validation' => array(__CLASS__, 'validateUrl'),
|
|
),
|
|
'URL_DEMO' => array(
|
|
'data_type' => 'string',
|
|
'validation' => array(__CLASS__, 'validateUrlDemo'),
|
|
),
|
|
'URL_INSTALL' => array(
|
|
'data_type' => 'string',
|
|
'validation' => array(__CLASS__, 'validateUrlInstall'),
|
|
),
|
|
'URL_SETTINGS' => array(
|
|
'data_type' => 'string',
|
|
'validation' => array(__CLASS__, 'validateUrl'),
|
|
),
|
|
'VERSION' => array(
|
|
'data_type' => 'string',
|
|
'validation' => array(__CLASS__, 'validateVersion'),
|
|
),
|
|
'SCOPE' => array(
|
|
'data_type' => 'string',
|
|
'required' => true,
|
|
'validation' => array(__CLASS__, 'validateScope'),
|
|
),
|
|
'STATUS' => array(
|
|
'data_type' => 'enum',
|
|
'required' => true,
|
|
'values' => array(
|
|
static::STATUS_LOCAL,
|
|
static::STATUS_FREE,
|
|
static::STATUS_PAID,
|
|
static::STATUS_DEMO,
|
|
static::STATUS_TRIAL,
|
|
static::STATUS_SUBSCRIPTION,
|
|
),
|
|
),
|
|
'DATE_FINISH' => array(
|
|
'data_type' => 'date',
|
|
),
|
|
'IS_TRIALED' => array(
|
|
'data_type' => 'boolean',
|
|
'values' => array(static::NOT_TRIALED, static::TRIALED),
|
|
),
|
|
'SHARED_KEY' => array(
|
|
'data_type' => 'string',
|
|
'validation' => array(__CLASS__, 'validateSharedKey'),
|
|
),
|
|
'CLIENT_SECRET' => array(
|
|
'data_type' => 'string',
|
|
'validation' => array(__CLASS__, 'validateClientSecret'),
|
|
),
|
|
'APP_NAME' => array(
|
|
'data_type' => 'string',
|
|
'validation' => array(__CLASS__, 'validateAppName'),
|
|
),
|
|
'ACCESS' => array(
|
|
'data_type' => 'string',
|
|
'validation' => array(__CLASS__, 'validateAccess'),
|
|
),
|
|
'APPLICATION_TOKEN' => array(
|
|
'data_type' => 'string',
|
|
),
|
|
'MOBILE' => array(
|
|
'data_type' => 'boolean',
|
|
'values' => array(static::INACTIVE, static::ACTIVE),
|
|
),
|
|
'USER_INSTALL' => array(
|
|
'data_type' => 'boolean',
|
|
'values' => array(static::INACTIVE, static::ACTIVE),
|
|
),
|
|
'LANG' => array(
|
|
'data_type' => 'Bitrix\Rest\AppLangTable',
|
|
'reference' => array(
|
|
'=this.ID' => 'ref.APP_ID',
|
|
'=ref.LANGUAGE_ID' => new Main\DB\SqlExpression('?s', LANGUAGE_ID),
|
|
),
|
|
),
|
|
'LANG_DEFAULT' => array(
|
|
'data_type' => 'Bitrix\Rest\AppLangTable',
|
|
'reference' => array(
|
|
'=this.ID' => 'ref.APP_ID',
|
|
'=ref.LANGUAGE_ID' => new Main\DB\SqlExpression('?s', Loc::getDefaultLang(LANGUAGE_ID)),
|
|
),
|
|
),
|
|
'LANG_LICENSE' => array(
|
|
'data_type' => 'Bitrix\Rest\AppLangTable',
|
|
'reference' => array(
|
|
'=this.ID' => 'ref.APP_ID',
|
|
'=ref.LANGUAGE_ID' => new Main\DB\SqlExpression('?s', static::getLicenseLanguage()),
|
|
),
|
|
),
|
|
(new OneToMany('LANG_ALL', AppLangTable::class, 'APP'))
|
|
->configureJoinType('left'),
|
|
new Main\ORM\Fields\Relations\Reference(
|
|
'FREE_APP',
|
|
FreeAppTable::class,
|
|
Main\ORM\Query\Join::on('this.CODE', 'ref.APP_CODE'),
|
|
),
|
|
new Main\ORM\Fields\ExpressionField(
|
|
'IS_FREE',
|
|
'CASE WHEN %s IS NOT NULL THEN 1 ELSE 0 END',
|
|
['FREE_APP.APP_CODE'],
|
|
),
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Holds sending changed data to oauth.
|
|
*
|
|
* @param $v bool
|
|
*/
|
|
public static function setSkipRemoteUpdate($v)
|
|
{
|
|
static::$skipRemoteUpdate = $v;
|
|
}
|
|
|
|
/**
|
|
* Event on before add application.
|
|
*
|
|
* @param Main\Entity\Event $event
|
|
* @return Main\Entity\EventResult
|
|
*/
|
|
public static function onBeforeAdd(Main\Entity\Event $event)
|
|
{
|
|
$result = new Main\Entity\EventResult();
|
|
$data = $event->getParameters();
|
|
|
|
if($data['fields']['STATUS'] == static::STATUS_LOCAL && !$data['fields']['CLIENT_ID'])
|
|
{
|
|
$rnd = Main\Security\Random::getString(8);
|
|
$dummyClientId = 'no_client_id_'.$rnd;
|
|
$dummyClientSecret = 'no_client_secret_'.$rnd;
|
|
|
|
$result->modifyFields(array(
|
|
"CLIENT_ID" => $dummyClientId,
|
|
"CLIENT_SECRET" => $dummyClientSecret,
|
|
"CODE" => $dummyClientId,
|
|
));
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* Event on after add application.
|
|
*
|
|
* @param Main\Entity\Event $event
|
|
* @return bool
|
|
* @throws OAuthException
|
|
*/
|
|
public static function onAfterAdd(Main\Entity\Event $event)
|
|
{
|
|
Main\Application::getInstance()->getCache()->cleanDir('rest/market_subscription');
|
|
EventController::onAddApp($event);
|
|
$data = $event->getParameters();
|
|
if(!static::$skipRemoteUpdate)
|
|
{
|
|
if(
|
|
$data['fields']['STATUS'] === static::STATUS_LOCAL
|
|
&& OAuthService::getEngine()->isRegistered()
|
|
)
|
|
{
|
|
try
|
|
{
|
|
$appFields = array(
|
|
'TITLE' => $data['fields']['APP_NAME'],
|
|
'REDIRECT_URI' => $data['fields']['URL'],
|
|
'SCOPE' => $data['fields']['SCOPE'],
|
|
);
|
|
|
|
$clientData = OAuthService::getEngine()
|
|
->getClient()
|
|
->addApplication($appFields);
|
|
}
|
|
catch(Main\SystemException $e)
|
|
{
|
|
$clientData = array(
|
|
"error" => $e->getCode(),
|
|
"error_description" => $e->getMessage(),
|
|
);
|
|
}
|
|
|
|
if(is_array($clientData))
|
|
{
|
|
if($clientData['result'])
|
|
{
|
|
static::$skipRemoteUpdate = true;
|
|
|
|
static::clearClientCache($clientData['result']['client_id']);
|
|
|
|
$updateResult = static::update($data['id'], array(
|
|
'CLIENT_ID' => $clientData['result']['client_id'],
|
|
'CLIENT_SECRET' => $clientData['result']['client_secret'],
|
|
'STATUS' => static::STATUS_LOCAL,
|
|
'SHARED_KEY' => md5(\CRestUtil::getMemberId().$clientData['result']['client_secret']),
|
|
'CODE' => $clientData['result']['client_id'],
|
|
));
|
|
static::$skipRemoteUpdate = false;
|
|
|
|
if($updateResult->isSuccess())
|
|
{
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
$clientData = array('error' => $updateResult->getErrorMessages());
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
$clientData = array('error' => 'Unknown error');
|
|
}
|
|
|
|
static::$skipRemoteUpdate = true;
|
|
static::delete($data['id']);
|
|
static::$skipRemoteUpdate = false;
|
|
|
|
throw new OAuthException($clientData);
|
|
}
|
|
}
|
|
|
|
if($data['fields']['STATUS'] !== static::STATUS_LOCAL)
|
|
{
|
|
\Bitrix\Rest\Engine\Access::getActiveEntity(true);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Event on after update application.
|
|
*
|
|
* @param Main\Entity\Event $event
|
|
* @return bool
|
|
* @throws OAuthException
|
|
*/
|
|
public static function onAfterUpdate(Main\Entity\Event $event)
|
|
{
|
|
Main\Application::getInstance()->getCache()->cleanDir('rest/market_subscription');
|
|
$data = $event->getParameters();
|
|
static::clearClientCache($data['primary']['ID']);
|
|
|
|
if(!static::$skipRemoteUpdate)
|
|
{
|
|
if(
|
|
isset($data['fields']['STATUS'])
|
|
&& $data['fields']['STATUS'] === static::STATUS_LOCAL
|
|
&& OAuthService::getEngine()->isRegistered()
|
|
)
|
|
{
|
|
$app = static::getByClientId($data['primary']['ID']);
|
|
|
|
try
|
|
{
|
|
$updateResult = OAuthService::getEngine()
|
|
->getClient()
|
|
->updateApplication(array(
|
|
"CLIENT_ID" => $app['CLIENT_ID'],
|
|
'TITLE' => $data['fields']['APP_NAME'],
|
|
'REDIRECT_URI' => $data['fields']['URL'],
|
|
'SCOPE' => $data['fields']['SCOPE'],
|
|
));
|
|
|
|
if($updateResult['result'])
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
catch(Main\SystemException $e)
|
|
{
|
|
$updateResult = array(
|
|
"error" => $e->getCode(),
|
|
"error_description" => $e->getMessage(),
|
|
);
|
|
}
|
|
|
|
throw new OAuthException($updateResult);
|
|
}
|
|
}
|
|
|
|
if(isset($data['fields']['STATUS']) && $data['fields']['STATUS'] !== static::STATUS_LOCAL)
|
|
{
|
|
\Bitrix\Rest\Engine\Access::getActiveEntity(true);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Event on before delete application.
|
|
*
|
|
* @param Main\Entity\Event $event
|
|
*/
|
|
public static function onDelete(Main\Entity\Event $event)
|
|
{
|
|
if(!static::$skipRemoteUpdate)
|
|
{
|
|
$data = $event->getParameters();
|
|
$app = static::getByClientId($data['primary']['ID']);
|
|
|
|
if($app['STATUS'] == AppTable::STATUS_LOCAL)
|
|
{
|
|
if(OAuthService::getEngine()->isRegistered())
|
|
{
|
|
\CRestUtil::cleanApp($app["ID"], true);
|
|
|
|
try
|
|
{
|
|
OAuthService::getEngine()
|
|
->getClient()
|
|
->deleteApplication(array(
|
|
'CLIENT_ID' => $app['CLIENT_ID'],
|
|
));
|
|
}
|
|
catch(\Bitrix\Main\SystemException $e)
|
|
{
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Event on after delete application.
|
|
*
|
|
* @param Main\Entity\Event $event
|
|
*/
|
|
public static function onAfterDelete(Main\Entity\Event $event)
|
|
{
|
|
Main\Application::getInstance()->getCache()->cleanDir('rest/market_subscription');
|
|
$data = $event->getParameters();
|
|
|
|
static::clearClientCache($data['primary']['ID']);
|
|
AppLangTable::deleteByApp($data['primary']['ID']);
|
|
}
|
|
|
|
public static function isInstalled(int $appId): bool
|
|
{
|
|
$row = self::getRowById($appId);
|
|
if (!$row)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return $row['INSTALLED'] === self::INSTALLED;
|
|
}
|
|
|
|
public static function install($appId)
|
|
{
|
|
$appInfo = static::getByClientId($appId);
|
|
if($appInfo)
|
|
{
|
|
$eventFields = array(
|
|
'APP_ID' => $appId,
|
|
'VERSION' => $appInfo['VERSION'],
|
|
'ACTIVE' => $appInfo['ACTIVE'],
|
|
'INSTALLED' => $appInfo['INSTALLED'],
|
|
);
|
|
|
|
if ($appInfo['ACTIVE'] === self::ACTIVE && $appInfo['INSTALLED'] === self::INSTALLED)
|
|
{
|
|
$res = PlacementTable::getList(
|
|
[
|
|
'filter' => [
|
|
'=APP_ID' => $appInfo['ID'],
|
|
],
|
|
'select' => [
|
|
'ID',
|
|
'PLACEMENT',
|
|
'USER_ID',
|
|
],
|
|
]
|
|
);
|
|
while ($item = $res->fetch())
|
|
{
|
|
$event = new Event(
|
|
'rest',
|
|
PlacementTable::PREFIX_EVENT_ON_AFTER_ADD . $item['PLACEMENT'],
|
|
[
|
|
'ID' => $item['ID'],
|
|
'PLACEMENT' => $item['PLACEMENT'],
|
|
'USER_ID' => $item['USER_ID'],
|
|
]
|
|
);
|
|
EventManager::getInstance()->send($event);
|
|
}
|
|
}
|
|
|
|
foreach(GetModuleEvents("rest", "OnRestAppInstall", true) as $eventHandler)
|
|
{
|
|
ExecuteModuleEventEx($eventHandler, array($eventFields));
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Uninstalls application.
|
|
*
|
|
* @param string|int $appId
|
|
* @param int $clean
|
|
*/
|
|
public static function uninstall($appId, $clean = 0)
|
|
{
|
|
$appInfo = static::getByClientId($appId);
|
|
if($appInfo)
|
|
{
|
|
\CRestUtil::cleanApp($appId, $clean);
|
|
|
|
if($appInfo['STATUS'] !== static::STATUS_LOCAL)
|
|
{
|
|
OAuthService::getEngine()->getClient()->unInstallApplication(array(
|
|
'CLIENT_ID' => $appInfo['CLIENT_ID']
|
|
));
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Checks opportunity of deleting application.
|
|
*
|
|
* @param int $appId
|
|
* @param int $clean
|
|
* @return Main\ErrorCollection
|
|
*/
|
|
public static function checkUninstallAvailability($appId, $clean = 0)
|
|
{
|
|
$event = new Main\Event('rest', 'onBeforeApplicationUninstall', [
|
|
'ID' => $appId,
|
|
'CLEAN' => $clean
|
|
]);
|
|
$event->send();
|
|
|
|
$result = new Main\ErrorCollection();
|
|
if ($event->getResults())
|
|
{
|
|
/** @var \Bitrix\Main\EventResult $eventResult */
|
|
foreach ($event->getResults() as $eventResult)
|
|
{
|
|
if($eventResult->getType() === EventResult::ERROR)
|
|
{
|
|
$eventResultData = $eventResult->getParameters();
|
|
if ($eventResultData instanceof Main\Error)
|
|
{
|
|
$result->add([$eventResultData]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* Updates applications status from OAuth.
|
|
*
|
|
* @throws Main\ArgumentException
|
|
* @throws Main\ObjectPropertyException
|
|
* @throws Main\SystemException
|
|
*/
|
|
public static function updateAppStatusInfo()
|
|
{
|
|
$appList = OAuthService::getEngine()->getClient()->getApplicationList();
|
|
|
|
if(is_array($appList) && is_array($appList['result']))
|
|
{
|
|
$dbApps = static::getList(array(
|
|
'filter' => array(
|
|
'!=STATUS' => static::STATUS_LOCAL,
|
|
),
|
|
'select' => array(
|
|
'ID', 'CLIENT_ID', 'STATUS', 'DATE_FINISH',
|
|
)
|
|
));
|
|
|
|
$localApps = array();
|
|
while($app = $dbApps->fetch())
|
|
{
|
|
$localApps[$app['CLIENT_ID']] = $app;
|
|
}
|
|
|
|
foreach($appList['result'] as $app)
|
|
{
|
|
if(array_key_exists($app['client_id'], $localApps))
|
|
{
|
|
$dateFinishLocal = $localApps[$app['client_id']]['DATE_FINISH']
|
|
? $localApps[$app['client_id']]['DATE_FINISH']->getTimestamp()
|
|
: '';
|
|
$dateFinishRemote = ($app['date_finish'] ?? null) ? Main\Type\Date::createFromTimestamp($app['date_finish'])->getTimestamp() : '';
|
|
|
|
if(
|
|
$localApps[$app['client_id']]['STATUS'] !== $app['status']
|
|
|| $dateFinishRemote != $dateFinishLocal
|
|
)
|
|
{
|
|
$appFields = array(
|
|
'STATUS' => $app['status'],
|
|
'DATE_FINISH' => $app['date_finish']
|
|
? Main\Type\Date::createFromTimestamp($app['date_finish'])
|
|
: '',
|
|
);
|
|
|
|
static::setSkipRemoteUpdate(true);
|
|
$result = static::update($localApps[$app['client_id']]['ID'], $appFields);
|
|
static::setSkipRemoteUpdate(false);
|
|
|
|
if(
|
|
$result->isSuccess()
|
|
&& $appFields['STATUS'] === static::STATUS_PAID
|
|
)
|
|
{
|
|
static::callAppPaymentEvent($localApps[$app['client_id']]['ID']);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
$appFields = array(
|
|
'CLIENT_ID' => $app['client_id'],
|
|
'CODE' => $app['code'],
|
|
'ACTIVE' => $app['active'] ? static::ACTIVE : static::INACTIVE,
|
|
'INSTALLED' => static::NOT_INSTALLED,
|
|
'VERSION' => $app['version'],
|
|
'STATUS' => $app['status'],
|
|
'SCOPE' => $app['scope'],
|
|
);
|
|
|
|
if(!empty($app['date_finish']))
|
|
{
|
|
$appFields['DATE_FINISH'] = Main\Type\Date::createFromTimestamp($app['date_finish']);
|
|
}
|
|
|
|
$result = static::add($appFields);
|
|
|
|
if($result->isSuccess() && $appFields['STATUS'] === static::STATUS_PAID)
|
|
{
|
|
static::callAppPaymentEvent($result->getId());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Sends event applications payment information.
|
|
*
|
|
* @param $appId
|
|
*/
|
|
public static function callAppPaymentEvent($appId)
|
|
{
|
|
// for compatibility purpose module_id is bitrix24 here
|
|
foreach(GetModuleEvents('bitrix24', 'OnAfterAppPaid', true) as $event)
|
|
{
|
|
ExecuteModuleEventEx($event, array($appId));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns applications information.
|
|
*
|
|
* @param mixed $app
|
|
* @param string $detailUrl
|
|
* @return array
|
|
*/
|
|
public static function getAppStatusInfo($app, $detailUrl)
|
|
{
|
|
$res = array();
|
|
|
|
if (
|
|
!empty($app)
|
|
&& (
|
|
is_string($app)
|
|
|| is_integer($app)
|
|
)
|
|
)
|
|
{
|
|
$appInfo = $app = static::getByClientId($app);
|
|
}
|
|
elseif (isset($app['CODE']))
|
|
{
|
|
$appInfo = static::getByClientId($app['CODE']);
|
|
}
|
|
|
|
if(is_array($app))
|
|
{
|
|
$res['STATUS'] = $app['STATUS'];
|
|
$res['PAYMENT_NOTIFY'] = 'N';
|
|
$res['PAYMENT_EXPIRED'] = 'N';
|
|
$res['PAYMENT_ALLOW'] = 'Y';
|
|
|
|
if ($app['STATUS'] === self::STATUS_SUBSCRIPTION)
|
|
{
|
|
if (!\Bitrix\Rest\Marketplace\Client::isSubscriptionAvailable())
|
|
{
|
|
$res['MESSAGE_REPLACE'] = array(
|
|
'#DETAIL_URL#' => $detailUrl,
|
|
'#DAYS#' => 0,
|
|
'#CODE#' => urlencode($app['CODE'])
|
|
);
|
|
$res['PAYMENT_NOTIFY'] = 'Y';
|
|
$res['PAYMENT_EXPIRED'] = 'Y';
|
|
$res['PAYMENT_ALLOW'] = 'N';
|
|
}
|
|
else
|
|
{
|
|
$dateFinish = \Bitrix\Rest\Marketplace\Client::getSubscriptionFinalDate();
|
|
if (!is_null($dateFinish))
|
|
{
|
|
$res['DAYS_LEFT'] = floor(($dateFinish->getTimestamp() - \CTimeZone::getOffset() - time()) / 86400);
|
|
if($res['DAYS_LEFT'] < 0)
|
|
{
|
|
$res['MESSAGE_REPLACE'] = array(
|
|
'#DETAIL_URL#' => $detailUrl,
|
|
'#DAYS#' => $res['DAYS_LEFT'],
|
|
'#CODE#' => urlencode($app['CODE'])
|
|
);
|
|
$res['PAYMENT_NOTIFY'] = 'Y';
|
|
$res['PAYMENT_EXPIRED'] = 'Y';
|
|
$res['PAYMENT_ALLOW'] = 'N';
|
|
}
|
|
elseif ($res['DAYS_LEFT'] < static::PAID_NOTIFY_DAYS)
|
|
{
|
|
$res['MESSAGE_REPLACE'] = array(
|
|
'#DETAIL_URL#' => $detailUrl,
|
|
'#DAYS#' => $res['DAYS_LEFT'],
|
|
'#CODE#' => urlencode($app['CODE'])
|
|
);
|
|
$res['PAYMENT_NOTIFY'] = 'Y';
|
|
}
|
|
}
|
|
}
|
|
}
|
|
elseif($app['DATE_FINISH'] <> '' && $app['STATUS'] != self::STATUS_FREE)
|
|
{
|
|
$res['DAYS_LEFT'] = floor(
|
|
(MakeTimeStamp($app['DATE_FINISH']) - \CTimeZone::getOffset() - time()) / 86400
|
|
);
|
|
|
|
if(
|
|
$res['DAYS_LEFT'] < static::PAID_NOTIFY_DAYS
|
|
|| $app['STATUS'] == static::STATUS_TRIAL)
|
|
{
|
|
$res['PAYMENT_NOTIFY'] = 'Y';
|
|
|
|
if($res['DAYS_LEFT'] < 0)
|
|
{
|
|
$res['PAYMENT_EXPIRED'] = 'Y';
|
|
|
|
if($app['STATUS'] == static::STATUS_TRIAL)
|
|
{
|
|
$res['PAYMENT_ALLOW'] = 'N';
|
|
}
|
|
elseif(
|
|
$app['STATUS'] == static::STATUS_PAID
|
|
&& $res['DAYS_LEFT'] < static::PAID_GRACE_PERIOD
|
|
)
|
|
{
|
|
if($app['IS_TRIALED'] == 'N' && $app['URL_DEMO'] <> '')
|
|
{
|
|
$res['STATUS'] = static::STATUS_DEMO;
|
|
}
|
|
else
|
|
{
|
|
$res['PAYMENT_ALLOW'] = 'N';
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
$res['MESSAGE_REPLACE'] = array(
|
|
"#DETAIL_URL#" => $detailUrl,
|
|
"#DAYS#" => $res['DAYS_LEFT'],
|
|
"#CODE#" => urlencode($app['CODE']),
|
|
);
|
|
}
|
|
elseif($app['STATUS'] == self::STATUS_DEMO)
|
|
{
|
|
$res['PAYMENT_NOTIFY'] = 'Y';
|
|
$res['MESSAGE_REPLACE'] = array(
|
|
"#DETAIL_URL#" => $detailUrl,
|
|
"#CODE#" => urlencode($app['CODE'])
|
|
);
|
|
}
|
|
else
|
|
{
|
|
$res['MESSAGE_REPLACE'] = array(
|
|
"#DETAIL_URL#" => $detailUrl,
|
|
"#CODE#" => urlencode($app['CODE'])
|
|
);
|
|
}
|
|
|
|
$res['MESSAGE_SUFFIX'] = '_'.$res['STATUS'].'_'.$res['PAYMENT_EXPIRED'].'_'.$res['PAYMENT_ALLOW'];
|
|
|
|
}
|
|
|
|
if (!empty($appInfo['CLIENT_ID']))
|
|
{
|
|
$isHold = \Bitrix\Rest\Engine\Access\HoldEntity::is(
|
|
\Bitrix\Rest\Engine\Access\HoldEntity::TYPE_APP,
|
|
$appInfo['CLIENT_ID']
|
|
);
|
|
if ($isHold)
|
|
{
|
|
$res['MESSAGE_SUFFIX'] = '_HOLD_OVERLOAD';
|
|
$res['PAYMENT_NOTIFY'] = 'Y';
|
|
}
|
|
}
|
|
|
|
return $res;
|
|
}
|
|
|
|
/**
|
|
* Returns message with applications status.
|
|
*
|
|
* @param string $suffix
|
|
* @param array|null $replace
|
|
* @param bool $checkAdmin
|
|
* @param string|null $language
|
|
*
|
|
* @return string
|
|
*/
|
|
public static function getStatusMessage($suffix, $replace = null, $checkAdmin = true, $language = null)
|
|
{
|
|
if ($checkAdmin && \CRestUtil::isAdmin())
|
|
{
|
|
$suffix .= '_A';
|
|
}
|
|
|
|
if (
|
|
is_array($replace)
|
|
&& array_key_exists('#DAYS#', $replace)
|
|
&& (
|
|
is_int($replace['#DAYS#'])
|
|
|| preg_match('/^(-|)\d+$/', $replace['#DAYS#'])
|
|
)
|
|
)
|
|
{
|
|
$replace['#DAYS#'] = FormatDate('ddiff', time(), time() + 24 * 60 * 60 * $replace['#DAYS#']);
|
|
}
|
|
|
|
return Loc::getMessage('PAYMENT_MESSAGE' . $suffix, $replace, $language);
|
|
}
|
|
|
|
/**
|
|
* @param string|int $appId
|
|
* @return array|false
|
|
*/
|
|
public static function getAccess($appId)
|
|
{
|
|
$appInfo = static::getByClientId($appId);
|
|
if($appInfo)
|
|
{
|
|
if($appInfo['ACCESS'] <> '')
|
|
{
|
|
$rightsList = explode(",", $appInfo["ACCESS"]);
|
|
|
|
$access = new \CAccess();
|
|
$accessNames = $access->getNames($rightsList);
|
|
|
|
$result = array();
|
|
foreach($rightsList as $right)
|
|
{
|
|
$result[$right] = array(
|
|
"id" => $right,
|
|
"provider" => $accessNames[$right]["provider_id"],
|
|
"name" => $accessNames[$right]["name"]
|
|
);
|
|
}
|
|
return $result;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* @param string|int $appId
|
|
* @param array $newRights
|
|
* @throws \Exception
|
|
*/
|
|
public static function setAccess($appId, $newRights = array())
|
|
{
|
|
$appInfo = static::getByClientId($appId);
|
|
if($appInfo)
|
|
{
|
|
$rights = '';
|
|
if(is_array($newRights) && !empty($newRights))
|
|
{
|
|
$rightIdList = array();
|
|
foreach($newRights as $key => $rightsList)
|
|
{
|
|
foreach($rightsList as $rightId => $ar)
|
|
{
|
|
$rightIdList[] = $rightId;
|
|
}
|
|
}
|
|
$rights = implode(",", $rightIdList);
|
|
}
|
|
|
|
static::update($appId, array(
|
|
'ACCESS' => $rights,
|
|
));
|
|
}
|
|
|
|
if(defined("BX_COMP_MANAGED_CACHE"))
|
|
{
|
|
global $CACHE_MANAGER;
|
|
$CACHE_MANAGER->ClearByTag('bitrix24_left_menu');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param string|int $clientId
|
|
* @return mixed
|
|
* @throws Main\ArgumentException
|
|
* @throws Main\ObjectPropertyException
|
|
* @throws Main\SystemException
|
|
*/
|
|
public static function getByClientId($clientId)
|
|
{
|
|
if(!array_key_exists($clientId, static::$applicationCache))
|
|
{
|
|
if(strval(intval($clientId)) == $clientId)
|
|
{
|
|
$filter = array('=ID' => $clientId);
|
|
}
|
|
else
|
|
{
|
|
$filter = array(
|
|
array(
|
|
'LOGIC' => 'OR',
|
|
'=CODE' => $clientId,
|
|
'=CLIENT_ID' => $clientId,
|
|
),
|
|
);
|
|
}
|
|
|
|
$dbRes = static::getList(
|
|
[
|
|
'filter' => $filter,
|
|
'select' => [
|
|
'*',
|
|
'MENU_NAME' => 'LANG.MENU_NAME',
|
|
'MENU_NAME_DEFAULT' => 'LANG_DEFAULT.MENU_NAME',
|
|
'MENU_NAME_LICENSE' => 'LANG_LICENSE.MENU_NAME',
|
|
],
|
|
'limit' => 1,
|
|
]
|
|
);
|
|
|
|
foreach ($dbRes->fetchCollection() as $app)
|
|
{
|
|
$appInfo = [
|
|
'ID' => $app->getId(),
|
|
'MENU_NAME' => !is_null($app->getLang()) ? $app->getLang()->getMenuName() : '',
|
|
'MENU_NAME_DEFAULT' => !is_null($app->getLangDefault()) ? $app->getLangDefault()->getMenuName() : '',
|
|
'MENU_NAME_LICENSE' => !is_null($app->getLangLicense()) ? $app->getLangLicense()->getMenuName() : '',
|
|
];
|
|
foreach ($app->sysGetEntity()->getScalarFields() as $field)
|
|
{
|
|
$fieldName = $field->getName();
|
|
if ($field instanceof BooleanField)
|
|
{
|
|
$appInfo[$fieldName] = $app->get($fieldName) ? 'Y' : 'N';
|
|
}
|
|
else
|
|
{
|
|
$appInfo[$fieldName] = $app->get($fieldName);
|
|
}
|
|
}
|
|
$app->fillLangAll();
|
|
if (!is_null($app->getLangAll()))
|
|
{
|
|
foreach ($app->getLangAll() as $lang)
|
|
{
|
|
$appInfo['LANG_ALL'][$lang->getLanguageId()] = [
|
|
'MENU_NAME' => $lang->getMenuName(),
|
|
];
|
|
}
|
|
}
|
|
if ($appInfo['MENU_NAME'] === '')
|
|
{
|
|
$appInfo = Lang::mergeFromLangAll($appInfo);
|
|
}
|
|
}
|
|
|
|
if (isset($appInfo) && is_array($appInfo))
|
|
{
|
|
static::$applicationCache[$appInfo['ID']] = $appInfo;
|
|
static::$applicationCache[$appInfo['CLIENT_ID']] = $appInfo;
|
|
static::$applicationCache[$appInfo['CODE']] = $appInfo;
|
|
}
|
|
}
|
|
|
|
return static::$applicationCache[$clientId] ?? null;
|
|
}
|
|
|
|
protected static function clearClientCache($clientId)
|
|
{
|
|
if(array_key_exists($clientId, static::$applicationCache))
|
|
{
|
|
$app = static::$applicationCache[$clientId];
|
|
if(is_array($app))
|
|
{
|
|
unset(static::$applicationCache[$app['ID']]);
|
|
unset(static::$applicationCache[$app['CLIENT_ID']]);
|
|
}
|
|
else
|
|
{
|
|
unset(static::$applicationCache[$clientId]);
|
|
}
|
|
}
|
|
}
|
|
|
|
protected static function getLicenseLanguage()
|
|
{
|
|
if(static::$licenseLang === null)
|
|
{
|
|
if(Main\Loader::includeModule('bitrix24'))
|
|
{
|
|
static::$licenseLang = \CBitrix24::getLicensePrefix();
|
|
}
|
|
else
|
|
{
|
|
$dbSites = \CSite::getList('sort', 'asc', array('DEFAULT' => 'Y', 'ACTIVE' => 'Y'));
|
|
$site = $dbSites->fetch();
|
|
|
|
static::$licenseLang = is_array($site) && isset($site['LANGUAGE_ID']) ? $site['LANGUAGE_ID'] : LANGUAGE_ID;
|
|
}
|
|
|
|
if(static::$licenseLang === null)
|
|
{
|
|
static::$licenseLang = 'en';
|
|
}
|
|
}
|
|
|
|
return static::$licenseLang;
|
|
}
|
|
|
|
|
|
/**
|
|
* Returns validators for CLIENT_ID field.
|
|
*
|
|
* @return array
|
|
*/
|
|
public static function validateClientId()
|
|
{
|
|
return array(
|
|
new Main\Entity\Validator\Length(null, 128),
|
|
new Main\Entity\Validator\Unique(),
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Returns validators for CODE field.
|
|
*
|
|
* @return array
|
|
*/
|
|
public static function validateCode()
|
|
{
|
|
return array(
|
|
new Main\Entity\Validator\Length(null, 128),
|
|
new Main\Entity\Validator\Unique(),
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Returns validators for URL field.
|
|
*
|
|
* @return array
|
|
*/
|
|
public static function validateUrl()
|
|
{
|
|
return array(
|
|
new Main\Entity\Validator\Length(null, 1000),
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Returns validators for URL_DEMO field.
|
|
*
|
|
* @return array
|
|
*/
|
|
public static function validateUrlDemo()
|
|
{
|
|
return array(
|
|
new Main\Entity\Validator\Length(null, 1000),
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Returns validators for URL_INSTALL field.
|
|
*
|
|
* @return array
|
|
*/
|
|
public static function validateUrlInstall()
|
|
{
|
|
return array(
|
|
new Main\Entity\Validator\Length(null, 1000),
|
|
function ($value, $primary, array $row, Main\Entity\Field $field)
|
|
{
|
|
$checkResult = true;
|
|
|
|
if($value)
|
|
{
|
|
try
|
|
{
|
|
if(!HandlerHelper::checkCallback($value, $row, false))
|
|
{
|
|
$checkResult = false;
|
|
}
|
|
}
|
|
catch(RestException $e)
|
|
{
|
|
$checkResult = false;
|
|
}
|
|
|
|
if(!$checkResult)
|
|
{
|
|
return Loc::getMessage("MP_ERROR_INCORRECT_URL_INSTALL");
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Returns validators for VERSION field.
|
|
*
|
|
* @return array
|
|
*/
|
|
public static function validateVersion()
|
|
{
|
|
return array(
|
|
new Main\Entity\Validator\Length(null, 4),
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Returns validators for SCOPE field.
|
|
*
|
|
* @return array
|
|
*/
|
|
public static function validateScope()
|
|
{
|
|
return array(
|
|
new Main\Entity\Validator\Length(null, 2000),
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Returns validators for SHARED_KEY field.
|
|
*
|
|
* @return array
|
|
*/
|
|
public static function validateSharedKey()
|
|
{
|
|
return array(
|
|
new Main\Entity\Validator\Length(null, 32),
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Returns validators for APP_SECRET_ID field.
|
|
*
|
|
* @return array
|
|
*/
|
|
public static function validateClientSecret()
|
|
{
|
|
return array(
|
|
new Main\Entity\Validator\Length(null, 100),
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Returns validators for APP_NAME field.
|
|
*
|
|
* @return array
|
|
*/
|
|
public static function validateAppName()
|
|
{
|
|
return array(
|
|
new Main\Entity\Validator\Length(null, 1000),
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Returns validators for ACCESS field.
|
|
*
|
|
* @return array
|
|
*/
|
|
public static function validateAccess()
|
|
{
|
|
return array(
|
|
new Main\Entity\Validator\Length(null, 2000),
|
|
);
|
|
}
|
|
|
|
/**
|
|
* @param array $permissionList
|
|
* @return array
|
|
*/
|
|
public static function cleanLocalPermissionList(array $permissionList)
|
|
{
|
|
foreach($permissionList as $key => $perm)
|
|
{
|
|
if(in_array($perm, static::$localAppDeniedScope))
|
|
{
|
|
unset($permissionList[$key]);
|
|
}
|
|
}
|
|
|
|
return array_values($permissionList);
|
|
}
|
|
|
|
/**
|
|
* @param string $code
|
|
* @param false $version
|
|
* @return bool
|
|
*/
|
|
public static function canUninstallByType($code, $version = false)
|
|
{
|
|
$result = true;
|
|
|
|
$type = static::getAppType($code, $version);
|
|
if($type == static::TYPE_CONFIGURATION)
|
|
{
|
|
$appList = \Bitrix\Rest\Configuration\Helper::getInstance()->getBasicAppList();
|
|
if(in_array($code, $appList))
|
|
{
|
|
$result = false;
|
|
}
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* @param $code
|
|
* @param false $version
|
|
* @return false|mixed
|
|
*/
|
|
public static function getAppType($code, $version = false)
|
|
{
|
|
$result = false;
|
|
$cache = Cache::createInstance();
|
|
if ($cache->initCache(static::CACHE_TTL, 'appType'.md5($code.$version), static::CACHE_PATH))
|
|
{
|
|
$result = $cache->getVars();
|
|
}
|
|
elseif ($cache->startDataCache())
|
|
{
|
|
$appDetailInfo = Client::getInstall($code, $version);
|
|
$result = ($appDetailInfo['ITEMS']['TYPE'])?:false;
|
|
$cache->endDataCache($result);
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
}
|
|
|
|
class App
|
|
{
|
|
public function __construct(
|
|
private string $clientId
|
|
)
|
|
{
|
|
}
|
|
|
|
/**
|
|
* @throws ObjectPropertyException
|
|
* @throws SystemException
|
|
* @throws ArgumentException
|
|
*/
|
|
public function fetchAppFormConfig(array $formData, EventType $type): string
|
|
{
|
|
$appData = AppTable::getByClientId($this->clientId);
|
|
if (empty($appData['URL_SETTINGS']))
|
|
{
|
|
throw new ArgumentException('Property "URL_SETTINGS" is empty', 'URL_SETTINGS');
|
|
}
|
|
$uri = new Uri($appData['URL_SETTINGS']);
|
|
$httpClient = new HttpClient();
|
|
$params = Sender::getDefaultEventParams();
|
|
$params['sendRefreshToken'] = true;
|
|
$event = [
|
|
'event' => $type->value,
|
|
'data' => $formData,
|
|
'auth' => Sender::getAuth(
|
|
$this->clientId,
|
|
CurrentUser::get()->getId() ?? 0,
|
|
[],
|
|
$params
|
|
)
|
|
];
|
|
|
|
return $httpClient->post($uri, $event);
|
|
}
|
|
}
|