595 lines
13 KiB
PHP
595 lines
13 KiB
PHP
<?php
|
|
|
|
namespace Bitrix\Landing;
|
|
|
|
use Bitrix\Landing\Internals\FileTable;
|
|
use Bitrix\Landing\Internals\HistoryTable;
|
|
use Bitrix\Main\File\Internal\FileDuplicateTable;
|
|
use Bitrix\Main\Loader;
|
|
use Bitrix\Landing\Subtype;
|
|
use Bitrix\Landing\Internals\BlockTable;
|
|
use Bitrix\Crm\WebForm;
|
|
use Bitrix\Main\Type\DateTime;
|
|
use Bitrix\Main\Web\HttpClient;
|
|
|
|
/**
|
|
* Service class for agent functions
|
|
*/
|
|
class Agent
|
|
{
|
|
/**
|
|
* Tech method for adding new unique agent.
|
|
* @param string $funcName Function name from this class.
|
|
* @param array $params Some params for agent function.
|
|
* @param int $time Time in seconds for executing period.
|
|
* @param int|null $nextExecDelay - time in sec before next agent delay, default - exec immediately
|
|
* @return void
|
|
*/
|
|
public static function addUniqueAgent(
|
|
string $funcName,
|
|
array $params = [],
|
|
int $time = 7200,
|
|
?int $nextExecDelay = null
|
|
): void
|
|
{
|
|
if (!method_exists(__CLASS__, $funcName))
|
|
{
|
|
return;
|
|
}
|
|
|
|
$funcName = self::createFunctionName($funcName, $params);
|
|
|
|
$res = \CAgent::getList(
|
|
[],
|
|
[
|
|
'MODULE_ID' => 'landing',
|
|
'NAME' => $funcName,
|
|
]
|
|
);
|
|
if (!$res->fetch())
|
|
{
|
|
if ($nextExecDelay)
|
|
{
|
|
\CAgent::addAgent($funcName,
|
|
'landing',
|
|
'N',
|
|
$time,
|
|
'',
|
|
'Y',
|
|
\ConvertTimeStamp(time() + \CTimeZone::GetOffset() + $nextExecDelay, "FULL"));
|
|
}
|
|
else
|
|
{
|
|
\CAgent::addAgent($funcName, 'landing', 'N', $time);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Tech method for delete existing unique agent.
|
|
*/
|
|
public static function deleteUniqueAgent(string $funcName, array $params = []): void
|
|
{
|
|
if (!method_exists(__CLASS__, $funcName))
|
|
{
|
|
return;
|
|
}
|
|
|
|
$funcName = self::createFunctionName($funcName, $params);
|
|
$res = \CAgent::getList(
|
|
[],
|
|
[
|
|
'MODULE_ID' => 'landing',
|
|
'NAME' => $funcName,
|
|
]
|
|
);
|
|
if ($agent = $res->fetch())
|
|
{
|
|
\CAgent::Delete((int)$agent['ID']);
|
|
}
|
|
}
|
|
|
|
private static function createFunctionName(string $funcName, array $params = []): string
|
|
{
|
|
$funcName = __CLASS__ . '::' . $funcName . '(';
|
|
foreach ($params as $value)
|
|
{
|
|
if (is_int($value))
|
|
{
|
|
$funcName .= $value . ',';
|
|
}
|
|
elseif (is_string($value))
|
|
{
|
|
$funcName .= '\'' . $value . '\'' . ',';
|
|
}
|
|
}
|
|
$funcName = trim($funcName, ',');
|
|
$funcName .= ');';
|
|
|
|
return $funcName;
|
|
}
|
|
|
|
/**
|
|
* Agent to remove one not resolved domain. Removes agent if such domains not exists.
|
|
* @return string
|
|
*/
|
|
public static function removeBadDomain(): string
|
|
{
|
|
$maxFailCount = 7;
|
|
|
|
Rights::setGlobalOff();
|
|
|
|
// only custom domain
|
|
$filterDomains = array_map(function ($domain) {
|
|
return '%.' . $domain;
|
|
}, Domain::B24_DOMAINS);
|
|
$filterDomains[] = '%' . Manager::getHttpHost();
|
|
|
|
$customDomainExist = false;
|
|
$resDomain = Domain::getList([
|
|
'select' => [
|
|
'ID', 'DOMAIN', 'FAIL_COUNT',
|
|
],
|
|
'filter' => [
|
|
'!DOMAIN' => $filterDomains,
|
|
],
|
|
'limit' => 5,
|
|
'order' => [
|
|
'DATE_MODIFY' => 'asc',
|
|
],
|
|
]);
|
|
while ($domain = $resDomain->fetch())
|
|
{
|
|
$customDomainExist = true;
|
|
if (Domain\Register::isDomainActive($domain['DOMAIN']))
|
|
{
|
|
Domain::update($domain['ID'], [
|
|
'FAIL_COUNT' => null,
|
|
])->isSuccess();
|
|
}
|
|
else
|
|
{
|
|
// remove domain
|
|
if ($domain['FAIL_COUNT'] >= $maxFailCount - 1)
|
|
{
|
|
// wee need site for randomize domain
|
|
$resSite = Site::getList([
|
|
'select' => [
|
|
'ID', 'DOMAIN_ID', 'DOMAIN_NAME' => 'DOMAIN.DOMAIN',
|
|
],
|
|
'filter' => [
|
|
'DOMAIN_ID' => $domain['ID'],
|
|
],
|
|
]);
|
|
if ($rowSite = $resSite->fetch())
|
|
{
|
|
Debug::log('removeBadDomain-randomizeDomain', var_export($rowSite, true));
|
|
Site::randomizeDomain($rowSite['ID']);
|
|
}
|
|
// site not exist, delete domain
|
|
/*else
|
|
{
|
|
Debug::log('removeBadDomain-Domain::delete', var_export($rowSite, true));
|
|
Domain::delete($domain['ID'])->isSuccess();
|
|
}*/
|
|
}
|
|
else
|
|
{
|
|
Domain::update($domain['ID'], [
|
|
'FAIL_COUNT' => intval($domain['FAIL_COUNT']) + 1,
|
|
])->isSuccess();
|
|
}
|
|
}
|
|
}
|
|
|
|
Rights::setGlobalOn();
|
|
|
|
return $customDomainExist ? __CLASS__ . '::' . __FUNCTION__ . '();' : '';
|
|
}
|
|
|
|
/**
|
|
* Clear recycle bin for scope.
|
|
* @param string $scope Scope code.
|
|
* @param int|null $days After this time items will be deleted.
|
|
* @return string
|
|
*/
|
|
public static function clearRecycleScope(string $scope, ?int $days = null): string
|
|
{
|
|
Site\Type::setScope($scope);
|
|
|
|
self::clearRecycle($days);
|
|
|
|
return __CLASS__ . '::' . __FUNCTION__ . '(\'' . $scope . '\');';
|
|
}
|
|
|
|
/**
|
|
* Returns all sub folders for the folder.
|
|
* @param int $folderId Folder id.
|
|
* @return array
|
|
*/
|
|
protected static function getSubFolders(int $folderId): array
|
|
{
|
|
$folders = [];
|
|
$res = Folder::getList([
|
|
'select' => [
|
|
'ID',
|
|
],
|
|
'filter' => [
|
|
'PARENT_ID' => $folderId,
|
|
],
|
|
]);
|
|
while ($row = $res->fetch())
|
|
{
|
|
$folders[] = $row['ID'];
|
|
$folders = array_merge($folders, self::getSubFolders($row['ID']));
|
|
}
|
|
|
|
return $folders;
|
|
}
|
|
|
|
/**
|
|
* Clear recycle bin.
|
|
* @param int|null $days After this time items will be deleted.
|
|
* @return string
|
|
*/
|
|
public static function clearRecycle(?int $days = null): string
|
|
{
|
|
Rights::setGlobalOff();
|
|
|
|
$days = !is_null($days)
|
|
? $days
|
|
: (int)Manager::getOption('deleted_lifetime_days');
|
|
|
|
$date = new DateTime;
|
|
$date->add('-' . $days . ' days');
|
|
|
|
// check folders to delete
|
|
$foldersToDelete = [-1];
|
|
$res = Folder::getList([
|
|
'select' => [
|
|
'ID',
|
|
],
|
|
'filter' => [
|
|
'=DELETED' => 'Y',
|
|
'<DATE_MODIFY' => $date,
|
|
],
|
|
]);
|
|
while ($row = $res->fetch())
|
|
{
|
|
$foldersToDelete[] = $row['ID'];
|
|
$foldersToDelete = array_merge($foldersToDelete, self::getSubFolders($row['ID']));
|
|
}
|
|
|
|
// first delete landings
|
|
$res = Landing::getList([
|
|
'select' => [
|
|
'ID', 'FOLDER_ID',
|
|
],
|
|
'filter' => [
|
|
[
|
|
'LOGIC' => 'OR',
|
|
[
|
|
'=DELETED' => 'Y',
|
|
'<DATE_MODIFY' => $date,
|
|
],
|
|
[
|
|
'=SITE.DELETED' => 'Y',
|
|
'<SITE.DATE_MODIFY' => $date,
|
|
],
|
|
[
|
|
'FOLDER_ID' => $foldersToDelete,
|
|
],
|
|
],
|
|
'=DELETED' => ['Y', 'N'],
|
|
'=SITE.DELETED' => ['Y', 'N'],
|
|
'CHECK_PERMISSIONS' => 'N',
|
|
],
|
|
'order' => [
|
|
'DATE_MODIFY' => 'desc',
|
|
],
|
|
]);
|
|
while ($row = $res->fetch())
|
|
{
|
|
Lock::lockDeleteLanding($row['ID'], false);
|
|
Landing::delete($row['ID'], true)->isSuccess();
|
|
}
|
|
|
|
// delete folders
|
|
foreach (array_unique($foldersToDelete) as $folderId)
|
|
{
|
|
if ($folderId > 0)
|
|
{
|
|
Folder::delete($folderId)->isSuccess();
|
|
}
|
|
}
|
|
|
|
// then delete sites
|
|
$res = Site::getList([
|
|
'select' => [
|
|
'ID',
|
|
],
|
|
'filter' => [
|
|
'=DELETED' => 'Y',
|
|
'<DATE_MODIFY' => $date,
|
|
'CHECK_PERMISSIONS' => 'N',
|
|
],
|
|
'order' => [
|
|
'DATE_MODIFY' => 'desc',
|
|
],
|
|
]);
|
|
while ($row = $res->fetch())
|
|
{
|
|
Lock::lockDeleteSite($row['ID'], false);
|
|
Site::delete($row['ID'])->isSuccess();
|
|
}
|
|
|
|
Rights::setGlobalOn();
|
|
|
|
return __CLASS__ . '::' . __FUNCTION__ . '();';
|
|
}
|
|
|
|
/**
|
|
* Remove marked for deleting files.
|
|
* @param int|null $count Count of files wich will be deleted per once.
|
|
* @return string
|
|
*/
|
|
public static function clearFiles(?int $count = null): string
|
|
{
|
|
$count = !is_null($count) ? $count : 30;
|
|
|
|
File::deleteFinal($count);
|
|
|
|
return __CLASS__ . '::' . __FUNCTION__ . '(' . $count . ');';
|
|
}
|
|
|
|
/**
|
|
* Clear old history records
|
|
* @param int|null $days After this time items will be deleted.
|
|
* @return string
|
|
*/
|
|
public static function clearHistory(?int $days = null): string
|
|
{
|
|
Rights::setGlobalOff();
|
|
|
|
$newAgentName = __CLASS__ . '::' . __FUNCTION__ . '(' . ($days ?? '') . ');';
|
|
|
|
$days = $days ?: (int)Manager::getOption('history_lifetime_days');
|
|
$date = new DateTime();
|
|
$date->add('-' . $days . ' days');
|
|
|
|
$rows = HistoryTable::query()
|
|
->setSelect(['ENTITY_ID', 'ENTITY_TYPE'])
|
|
->setDistinct(true)
|
|
->where('DATE_CREATE', '<', $date)
|
|
->fetchAll()
|
|
;
|
|
foreach ($rows as $row)
|
|
{
|
|
$history = new History($row['ENTITY_ID'], $row['ENTITY_TYPE']);
|
|
$history->clearOld($days);
|
|
}
|
|
|
|
return $newAgentName;
|
|
}
|
|
|
|
/**
|
|
* Send used rest statistic.
|
|
* @return string
|
|
*/
|
|
public static function sendRestStatistic(): string
|
|
{
|
|
if (
|
|
\Bitrix\Main\Loader::includeModule('rest')
|
|
&& is_callable(['\Bitrix\Rest\UsageStatTable', 'logLanding'])
|
|
)
|
|
{
|
|
$statCode = [
|
|
\Bitrix\Landing\PublicAction::REST_USAGE_TYPE_BLOCK => 'LANDING_BLOCK',
|
|
\Bitrix\Landing\PublicAction::REST_USAGE_TYPE_PAGE => 'LANDING_PAGE',
|
|
];
|
|
$data = PublicAction::getRestStat(false, true);
|
|
foreach ($data as $type => $stat)
|
|
{
|
|
if ($statCode[$type])
|
|
{
|
|
foreach ($stat as $clientId => $count)
|
|
{
|
|
\Bitrix\Rest\UsageStatTable::logLanding($clientId, $statCode[$type], $count);
|
|
}
|
|
}
|
|
}
|
|
\Bitrix\Rest\UsageStatTable::finalize();
|
|
}
|
|
|
|
return __CLASS__ . '::' . __FUNCTION__ . '();';
|
|
}
|
|
|
|
/**
|
|
* Marks all temporary files to delete.
|
|
* @return string
|
|
*/
|
|
public static function clearTempFiles(): string
|
|
{
|
|
$dateTime = new DateTime();
|
|
|
|
$res = Internals\FileTable::getList([
|
|
'select' => [
|
|
'ID', 'FILE_ID',
|
|
],
|
|
'filter' => [
|
|
'>FILE_ID' => 0,
|
|
'=TEMP' => 'Y',
|
|
'<FILE.TIMESTAMP_X' => $dateTime->add('-60 minute'),
|
|
],
|
|
]);
|
|
while ($row = $res->fetch())
|
|
{
|
|
Internals\FileTable::update($row['ID'], [
|
|
'FILE_ID' => -1 * $row['FILE_ID'],
|
|
]);
|
|
}
|
|
|
|
return __CLASS__ . '::' . __FUNCTION__ . '();';
|
|
}
|
|
|
|
/**
|
|
* Tmp agent for rebuild form's blocks.
|
|
* @param int $lastLid Last item id.
|
|
* @return string
|
|
*/
|
|
public static function repairFormUrls(int $lastLid = 0): string
|
|
{
|
|
if (Loader::includeModule('crm'))
|
|
{
|
|
$formQuery = WebForm\Internals\LandingTable::query()
|
|
->addSelect('FORM_ID')
|
|
->addSelect('LANDING_ID')
|
|
->addOrder('LANDING_ID')
|
|
->setLimit(50)
|
|
->where('LANDING_ID', '>', $lastLid)
|
|
->exec()
|
|
;
|
|
$lastLid = 0;
|
|
while ($form = $formQuery->fetch())
|
|
{
|
|
$blocksQuery = BlockTable::query()
|
|
->addSelect('ID')
|
|
->where('LID', $form['LANDING_ID'])
|
|
->where('CODE', '66.90.form_new_default')
|
|
->exec()
|
|
;
|
|
while ($block = $blocksQuery->fetch())
|
|
{
|
|
Subtype\Form::setFormIdToBlock($block['ID'], $form['FORM_ID']);
|
|
}
|
|
$lastLid = (int)$form['LANDING_ID'];
|
|
}
|
|
|
|
if ($lastLid > 0)
|
|
{
|
|
return __CLASS__ . '::' . __FUNCTION__ . '(' . $lastLid . ');';
|
|
}
|
|
}
|
|
|
|
return '';
|
|
}
|
|
|
|
/**
|
|
* In clouds files can gone. Check existing and del invalid rows from tables
|
|
* @param int $fileId
|
|
* @return void
|
|
*/
|
|
public static function checkFileExists(int $fileId): string
|
|
{
|
|
$file = \CFile::getFileArray($fileId);
|
|
if (!$file)
|
|
{
|
|
return '';
|
|
}
|
|
if (!$file['SRC'] || !preg_match('#^(https?://)#', $file['SRC']))
|
|
{
|
|
return '';
|
|
}
|
|
$request = new HttpClient(["redirect" => false,]);
|
|
$request->query(HttpClient::HTTP_HEAD, $file['SRC']);
|
|
if ($request->getStatus() !== 200)
|
|
{
|
|
$filesToDelete = [$fileId];
|
|
|
|
// find duplicates of file
|
|
$originals = FileDuplicateTable::getList([
|
|
'select' => ['ORIGINAL_ID'],
|
|
'filter' => [
|
|
'DUPLICATE_ID' => $fileId,
|
|
],
|
|
]);
|
|
while ($original = $originals->fetch())
|
|
{
|
|
$filesToDelete[] = (int)$original['ORIGINAL_ID'];
|
|
}
|
|
|
|
$duplicates = FileDuplicateTable::getList([
|
|
'select' => ['DUPLICATE_ID'],
|
|
'filter' => [
|
|
'ORIGINAL_ID' => $filesToDelete,
|
|
],
|
|
]);
|
|
while ($duplicate = $duplicates->fetch())
|
|
{
|
|
$filesToDelete[] = (int)$duplicate['DUPLICATE_ID'];
|
|
}
|
|
|
|
$filesToDelete = array_unique($filesToDelete);
|
|
|
|
// clear LandingFile table
|
|
$landingFiles = FileTable::getList([
|
|
'select' => ['ID', 'ENTITY_ID'],
|
|
'filter' => [
|
|
'ENTITY_TYPE' => File::ENTITY_TYPE_ASSET,
|
|
'=FILE_ID' => $filesToDelete,
|
|
],
|
|
]);
|
|
$landingsToUpdate = [];
|
|
while ($landingFile = $landingFiles->fetch())
|
|
{
|
|
FileTable::delete((int)$landingFile['ID']);
|
|
$landingsToUpdate[] = (int)$landingFile['ENTITY_ID'];
|
|
}
|
|
$landingsToUpdate = array_unique($landingsToUpdate);
|
|
|
|
// find landings for drop public cache
|
|
if (Manager::isB24())
|
|
{
|
|
$sites = Landing::getList([
|
|
'select' => ['SITE_ID'],
|
|
'filter' => [
|
|
'=ID' => $landingsToUpdate,
|
|
],
|
|
]);
|
|
while ($site = $sites->fetch())
|
|
{
|
|
Site::update((int)$site['SITE_ID'], []);
|
|
}
|
|
}
|
|
|
|
foreach ($filesToDelete as $fileToDelete)
|
|
{
|
|
\CFile::delete($fileToDelete);
|
|
}
|
|
}
|
|
|
|
return '';
|
|
}
|
|
|
|
/**
|
|
* Publication landing and drop public cache if success.
|
|
* F.e. need for recovery form-loader file, that is not created at the moment of first public
|
|
* @param $landingId
|
|
* @return void
|
|
*/
|
|
public static function rePublicationLanding($landingId): void
|
|
{
|
|
$landing = Landing::createInstance($landingId);
|
|
if ($landing->publication())
|
|
{
|
|
Manager::clearCacheForSite($landing->getSiteId());
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Run copilot site generation
|
|
* @param int $generationId
|
|
* @return string
|
|
*/
|
|
public static function executeGeneration(int $generationId): string
|
|
{
|
|
$generation = new Copilot\Generation();
|
|
if ($generation->initById($generationId))
|
|
{
|
|
$generation->execute();
|
|
}
|
|
|
|
return '';
|
|
}
|
|
}
|