%PDF- %PDF-
| Direktori : /proc/self/root/home/bitrix/www/bitrix/modules/sale/lib/tradingplatform/vk/api/ |
| Current File : //proc/self/root/home/bitrix/www/bitrix/modules/sale/lib/tradingplatform/vk/api/apihelper.php |
<?php
namespace Bitrix\Sale\TradingPlatform\Vk\Api;
use Bitrix\Main\ArgumentNullException;
use Bitrix\Main\SystemException;
use Bitrix\Main\Web\HttpClient;
use Bitrix\Main\Web\Json;
use Bitrix\Main\IO;
use Bitrix\Sale\TradingPlatform\Timer;
use Bitrix\Sale\TradingPlatform\Vk\Logger;
use Bitrix\Sale\TradingPlatform\Vk\Vk;
use Bitrix\Sale\TradingPlatform\TimeIsOverException;
use Bitrix\Main\Localization\Loc;
Loc::loadMessages(__FILE__);
/**
* Class ApiHelper - formatted and run requests to VK Api. Provide utility functions for help.
* @package Bitrix\Sale\TradingPlatform\Vk\Api
*/
class ApiHelper
{
private $vk;
private $api;
private $executer;
private $exportId;
private $logger;
/**
* ApiHelper constructor.
* @param $exportId - int, ID of export profile
*/
public function __construct($exportId)
{
if (empty($exportId))
throw new ArgumentNullException('exportId');
$this->exportId = $exportId;
$this->vk = Vk::getInstance();
$this->api = $this->vk->getApi($exportId);
$this->executer = $this->vk->getExecuter($exportId);
$this->logger = new Logger($this->exportId);
}
/**
* Extract specified elements from array. Need to decrease of array size to post
*
* @param array $data - source array
* @param array $keys - array of keys, thst needed in new array
* @return array - array of extracted items
*/
public static function extractItemsFromArray($data = array(), $keys = array())
{
if (!isset($keys) || empty($keys))
return $data;
$newArr = array();
foreach ($data as $value)
{
if (!is_array($value))
{
$newArr[] = $value;
}
else
{
$currArr = array();
foreach ($keys as $k)
{
$currArr[$k] = $value[$k];
}
$newArr[] = $currArr;
}
}
return $newArr;
}
/**
* Merge to arrays by reference key
*
* @param array $data
* @param array $result
* @param $referenceKey - main key in both arrays
* @return array
*/
public static function addResultToData($data = array(), $result = array(), $referenceKey)
{
if (empty($result) || !isset($referenceKey))
return $data;
foreach ($result as $item)
{
if (isset($data[$item[$referenceKey]]))
$data[$item[$referenceKey]] += $item;
}
return $data;
}
/**
* Reformat array - change main (top level) key.
*
* @param array $data
* @param $mainKey
* @param string $keyRename - if isset, new main key will be rename
* @return array
*/
public static function changeArrayMainKey($data = array(), $mainKey, $keyRename = '')
{
if (!isset($mainKey))
return $data;
$result = array();
foreach ($data as $item)
{
$result[$item[$mainKey]] = $item;
if ($keyRename)
{
$result[$item[$mainKey]][$keyRename] = $result[$item[$mainKey]][$mainKey];
unset($result[$item[$mainKey]][$mainKey]);
}
}
return $result;
}
/**
* Check photo size, get upload server, upload photo and save them
*
* @param $data
* @param $vkGroupId
* @param $uploadType - type of photo. For other types used other params and methods
* @param null $timer - timer for control time of upload
* @return array - array of save photos results
* @throws SystemException
*/
public function uploadPhotos($data, $vkGroupId, $uploadType, Timer $timer = NULL)
{
// todo: this is a little kostyl. In cool variant we must separately do http-upload,
// todo: and photo save run through execute method
// todo: but now VK can't run savePhotoMethod through execute. Sadness ((
// PARAMS set
$photoSaveResults = array();
switch ($uploadType)
{
case 'PRODUCT_MAIN_PHOTO':
$uploadServerMethod = 'photos.getMarketUploadServer';
$saveMethod = 'photos.saveMarketPhoto';
$keyReference = 'BX_ID';
$keyPhotoVk = 'PHOTO_MAIN_VK_ID';
$keyPhotoBx = 'PHOTO_MAIN_BX_ID';
break;
case 'PRODUCT_PHOTOS':
$uploadServerMethod = 'photos.getMarketUploadServer';
$saveMethod = 'photos.saveMarketPhoto';
$keyReference = 'PHOTO_BX_ID';
$keyPhotoVk = 'PHOTO_VK_ID';
$keyPhotoBx = 'PHOTO_BX_ID';
break;
case 'ALBUM_PHOTO':
$uploadServerMethod = 'photos.getMarketAlbumUploadServer';
$saveMethod = 'photos.saveMarketAlbumPhoto';
$keyReference = 'SECTION_ID';
$keyPhotoVk = 'PHOTO_VK_ID';
$keyPhotoBx = 'PHOTO_BX_ID';
break;
default:
throw new SystemException("Wrong photo upload type");
break;
}
// PROCESSED
foreach ($data as $item)
{
// check EXISTING photo
if (!array_key_exists($keyPhotoBx, $item) || empty($item[$keyPhotoBx]))
continue;
// GET upload server by type
$getServerParams = array("group_id" => str_replace("-", "", $vkGroupId));
if ($uploadType == 'PRODUCT_MAIN_PHOTO')
$getServerParams += self::setUploadServerMainPhotoParams($item[$keyPhotoBx]);
$uploadServer = $this->api->run($uploadServerMethod, $getServerParams);
// todo: may be this error in upload server response
$this->logger->addLog("Get photo upload server", [
'PARAMS' => $getServerParams,
'RESULT' => $uploadServer,
]);
$uploadServer = $uploadServer["upload_url"];
// UPLOAD photo by http
$this->logger->addLog("Upload photo HTTP before", array(
"UPLOAD_TYPE" => $uploadType,
"ITEM" => array_key_exists("BX_ID", $item) ?
$item["BX_ID"].': '.$item["NAME"] :
$item["SECTION_ID"].': '.$item["TITLE"],
"PHOTO_BX_ID" => array_key_exists("PHOTO_MAIN_BX_ID", $item) ? $item["PHOTO_MAIN_BX_ID"] : $item["PHOTO_BX_ID"],
"PHOTO_URL" => array_key_exists("PHOTO_MAIN_URL", $item) ? $item["PHOTO_MAIN_URL"] : $item["PHOTO_URL"],
"PHOTOS" => $item["PHOTOS"] //only for products
));
$responseHttp = $this->uploadPhotoHttp($item, $uploadServer, $uploadType, $timer);
$responseHttp = Json::decode($responseHttp);
// SAVE upload result
$photoSaveParams = array(
"group_id" => str_replace('-', '', $vkGroupId),
"photo" => $responseHttp["photo"],
"server" => $responseHttp["server"],
"hash" => $responseHttp["hash"],
);
// for product photo we need more params
if ($saveMethod == "photos.saveMarketPhoto")
{
if (isset($responseHttp["crop_hash"]) && $responseHttp["crop_hash"])
$photoSaveParams["crop_hash"] = $responseHttp["crop_hash"];
if (isset($responseHttp["crop_data"]) && $responseHttp["crop_data"])
$photoSaveParams["crop_data"] = $responseHttp["crop_data"];
}
$responsePhotoSave = $this->api->run($saveMethod, $photoSaveParams);
// RESULT
$photoSaveResults[] = array(
$keyReference => $item[$keyReference],
$keyPhotoVk => $responsePhotoSave[0]["id"],
);
// todo: photo mapping. po odnomu, navernoe, ved timer
}
return $photoSaveResults;
}
/**
* Formatted params and run http-upload process
*
* @param $data
* @param $uploadServer
* @param $uploadType
* @param null $timer
* @return bool|string
* @throws SystemException
* @throws TimeIsOverException
*/
private function uploadPhotoHttp($data, $uploadServer, $uploadType, Timer $timer = NULL)
{
switch ($uploadType)
{
case 'ALBUM_PHOTO':
$postParams = array(
"url" => $data["PHOTO_URL"],
"filename" => IO\Path::getName($data["PHOTO_URL"]),
"param_name" => 'file',
"timer" => $timer,
);
break;
case 'PRODUCT_MAIN_PHOTO':
$postParams = array(
"url" => $data["PHOTO_MAIN_URL"],
"filename" => IO\Path::getName($data["PHOTO_MAIN_URL"]),
"param_name" => 'file',
"timer" => $timer,
);
break;
case 'PRODUCT_PHOTOS':
$postParams = array(
"url" => $data["PHOTO_URL"],
"filename" => IO\Path::getName($data["PHOTO_URL"]),
"param_name" => 'file',
"timer" => $timer,
);
break;
default:
throw new SystemException("Wrong upload type");
break;
}
return $this->uploadHttp($uploadServer, $postParams);
}
/**
* Execute http requst
*
* @param $uploadServer
* @param $params
* @return bool|string - result of http request
* @throws TimeIsOverException
*/
private function uploadHttp($uploadServer, $params)
{
$http = new HttpClient();
$boundary = md5(rand() . time());
$file = $http->get($params["url"]);
$data = '';
$data .= '--' . $boundary . "\r\n";
$data .= 'Content-Disposition: form-data; name="' . $params["param_name"] . '"; filename="' . $params["filename"] . '"' . "\r\n";
$data .= 'Content-Type: application/octet-stream' . "\r\n\r\n";
$data .= $file . "\r\n";
$data .= '--' . $boundary . "--\r\n";
$http->setHeader('Content-type', 'multipart/form-data; boundary=' . $boundary);
$http->setHeader('Content-length', \Bitrix\Main\Text\BinaryString::getLength($data));
$this->logger->addLog("Upload photo HTTP params", [
'SERVER' => $uploadServer,
'PARAMS' => $params,
'FILE_OK' => $file ? 'Y' : 'N',
]);
$result = $http->post($uploadServer, $data);
$this->logger->addLog("Upload photo HTTP response", $result);
// check TIMER if set
if (array_key_exists("timer", $params))
{
$timer = $params["timer"];
if ($timer !== NULL && !$timer->check())
throw new TimeIsOverException();
}
return $result;
}
public function getUserGroupsSelector($selectedValue = null, $name = null, $id = null)
{
// todo: maybe cached this values
$groupsSelector = false;
$gpoups = $this->getUserGroups();
if(is_array($gpoups) && !empty($gpoups))
{
$groupsSelector = '<option value="-1">['.Loc::getMessage('SALE_VK_CHANGE_GROUP').']</option>';
$selectedValue = str_replace('-', '', $selectedValue);
$name = $name ? ' name="' . $name . '"' : '';
$id = $id ? ' id="' . $id . '"' : '';
foreach ($gpoups as $group)
{
$selected = $selectedValue == $group["id"] ? ' selected' : '';
$groupsSelector .=
'<option' . $selected . ' value="' . $group['id'] . '">' . $group['name'] . '</option>';
}
$groupsSelector =
'<select id="vk_export_groupselector" onchange="BX.Sale.VkAdmin.changeVkGroupLink();"' . $id . $name . '>' .
$groupsSelector .
'</select>';
$groupsSelector.=
'<span style="padding-left:10px">
<a href="https://vk.com/club'. $selectedValue .'" id="vk_export_groupselector__link">
<img src="/bitrix/images/sale/vk/vk_icon.png">
</a>
</span>';
}
return $groupsSelector;
}
private function getUserGroups($offset = null)
{
$userGroups = array();
$stepCount = 0;
// max 1000 in one step.Check this value and run api again if needed
while(true)
{
$params = array(
'extended' => 1,
'filter' => 'editor',
'offset' => $stepCount,
'count' => Vk::GROUP_GET_STEP,
);
$apiResult = $this->api->run('groups.get', $params);
foreach($apiResult['items'] as $group)
{
$userGroups[$group['id']] = array(
'id' => $group['id'],
'name' => $group['name']
);
}
// increment step items counter
if($apiResult['count'] > Vk::GROUP_GET_STEP + $stepCount)
$stepCount += Vk::GROUP_GET_STEP;
else
break;
}
return $userGroups;
}
/**
* Get list of VK albums from VK API
*
* @param $vkGroupId
* @param bool $flip
* @return array - list of VK albums
*/
public function getALbumsFromVk($vkGroupId, $flip = true)
{
// todo: so slow api request. Try cached this data or other acceleration techniques
$albumsFromVk = $this->executer->executeMarketAlbumsGet(array(
"owner_id" => $vkGroupId,
"offset" => 0,
"count" => Vk::MAX_ALBUMS,
));
$albumsFromVk = $albumsFromVk["items"]; // get only items from response
foreach ($albumsFromVk as &$item) // get only IDs from response
{
$item = $item["id"];
}
if ($flip)
$albumsFromVk = array_flip($albumsFromVk); // we need albumID as keys
return $albumsFromVk;
}
/**
* Get list of VK products from VK API
*
* @param $vkGroupId
* @return array - list of VK products
*/
public function getProductsFromVk($vkGroupId)
{
$productsFromVk = array();
$prodGetStep = 0;
while ($prodGetStep < Vk::MAX_PRODUCTS_IN_ALBUM)
{
$productsFromVk += $this->executer->executeMarketProductsGet(array(
"owner_id" => $vkGroupId,
"offset" => $prodGetStep,
"step" => Vk::PRODUCTS_GET_STEP)
);
$prodGetStep += Vk::PRODUCTS_GET_STEP;
// exit from loop, if we reach end of VK-products
if ($productsFromVk["end_products"])
{
unset($productsFromVk["end_products"]);
break;
}
}
$result = array();
foreach($productsFromVk as $productFromVk)
$result[$productFromVk] = array("VK_ID" => $productFromVk);
return $result;
}
/**
* Check params for save products data.
* Check photos, description, vk-category
*
* @param $data
* @return array - prepared to save data array
*/
public static function prepareProductsDataToVk($data)
{
$result = array();
foreach ($data as $item)
{
// check PHOTOS and formatted
if (isset($item["PHOTOS"]) && is_array($item["PHOTOS"]))
{
$photosIds = array();
foreach ($item["PHOTOS"] as $photo)
{
if (is_numeric($photo["PHOTO_VK_ID"]))
$photosIds[] = $photo["PHOTO_VK_ID"];
}
if (!empty($photosIds))
$item["PHOTOS"] = implode(",", $photosIds);
else
unset($item["PHOTOS"]);
}
// check VK_CATEGORY
if (!(isset($item["CATEGORY_VK"]) && intval($item["CATEGORY_VK"]) > 0))
{
$item["CATEGORY_VK"] = Vk::VERY_DEFAULT_VK_CATEGORY;
} // we need some category
$result[] = $item;
}
return $result;
}
/**
* Build params for http photo upload
*
* @param $photoId
* @return array
*/
private static function setUploadServerMainPhotoParams($photoId)
{
$result = array();
$result["main_photo"] = 1;
$photoParams = \CFile::GetFileArray($photoId);
$w = $photoParams["WIDTH"];
$h = $photoParams["HEIGHT"];
if ($w >= $h)
{
$result["crop_x"] = ceil(($w + $h) / 2);
$result["crop_y"] = 0;
$result["crop_width"] = $h;
}
else
{
$result["crop_x"] = 0;
$result["crop_y"] = ceil(($w + $h) / 2);
$result["crop_width"] = $w;
}
return $result;
}
/**
* Get list of VK product categories from VK API
*
* @param int $count
* @param int $offset
* @return array - Get list of VK product categories. Return false if error
*/
public function getVkCategories($count = Vk::MAX_VK_CATEGORIES, $offset = 0)
{
$vkCats = $this->api->run('market.getCategories', array("count" => $count, "offset" => $offset));
if (!empty($vkCats))
return $vkCats["items"];
else
return false;
}
}