| Current Path : /home/megadansyp/www/components/com_eventgallery/library/Connector/ |
| Current File : /home/megadansyp/www/components/com_eventgallery/library/Connector/GooglePhotos.php |
<?php
namespace Joomla\Component\Eventgallery\Site\Library\Connector;
use components\com_eventgallery\site\library\Data\Exif;
/**
* @package Sven.Bluege
* @subpackage com_eventgallery
*
* @copyright Copyright (C) 2005 - 2019 Sven Bluege All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
defined('_JEXEC') or die;
jimport('joomla.error.log');
require_once JPATH_ROOT.'/components/com_eventgallery/config.php';
require_once JPATH_ROOT.'/components/com_eventgallery/library/common/logger.php';
class GooglePhotos
{
/**
* This cache is necessary to avoid requesting an auth token too many times. This happens
* if the refresh token is invalid but not empty.
*
* @var array
*/
static $tokenCache = [];
public static $cachebasedir;
public static $cache_life = '86400'; //caching time, in seconds
public static $requesttimeout = 5;
/**
* @return string
*/
const COM_EVENTGALLERY_GOOGLEPHOTOS_LOGFILENAME = 'com_eventgallery_googlephotos.log.php';
public static function doRequest($cachelifetime, $method, $url, $hash, $data, $doCache)
{
\JLog::addLogger(
array(
'text_file' => self::COM_EVENTGALLERY_GOOGLEPHOTOS_LOGFILENAME,
'logger' => 'Eventgalleryformattedtext'
),
\JLog::ALL,
'com_eventgallery'
);
//\JLog::add('processing url '.$url, \JLog::INFO, 'com_eventgallery');
self::initCacheDirs();
$cachefilename = self::$cachebasedir . $hash . '.json';
if ($doCache && file_exists($cachefilename) && (time() - filemtime($cachefilename) <= $cachelifetime)) {
// no need to do anything since the cache is still valid
} else {
//\JLog::add('have writen new cache file for '.$url, \JLog::INFO, 'com_eventgallery');
/**
* @var \JHttpResponse $result
*/
try {
if ($method == 'POST') {
$result = \JHttpFactory::getHttp()->post($url, $data, ['Content-Type' => 'application/json'], self::$requesttimeout);
} else {
$result = \JHttpFactory::getHttp()->get($url, ['Content-Type' => 'application/json'], self::$requesttimeout);
}
if ($result->code < 300) {
$fh = fopen($cachefilename, 'w') or die("can't open file $cachefilename");
fwrite($fh, $result->body);
fclose($fh);
} else {
if ($result->code == 400) {
\JLog::add("Request answered with HTTP 400 for URL: $url\n". $result->body, \JLog::INFO, 'com_eventgallery');
} else {
\JLog::add('unable to connect to remote host. Make sure curl is available or allow_url_fopen=On and the server has access to the internet. url: ' . $url, \JLog::INFO, 'com_eventgallery');
}
}
// make sure we don't cause reload issues by loading elements too often. Max every minute should do.
if (strlen($result->body) == 0 || self::verifyResult($result->body) == false) {
\JLog::add('Invalid return result detected. The response body is either empty or not a valid JSON result. Please check the used credentials.', \JLog::INFO, 'com_eventgallery');
touch($cachefilename, time() - (self::$cache_life - 60)) ;
}
$result = NULL;
} catch (\RuntimeException $e) {
\JLog::add('Error for url '. $url .': '.$e->getMessage(), \JLog::INFO, 'com_eventgallery');
}
}
return $cachefilename;
}
/**
* @param $responseBody
* @return bool
*/
public static function verifyResult($responseBody) {
$json = json_decode($responseBody);
return json_last_error() === JSON_ERROR_NONE;
}
public static function initCacheDirs() {
if (!is_dir(JPATH_CACHE)) {
mkdir(JPATH_CACHE);
}
self::$cachebasedir = COM_EVENTGALLERY_GOOGLE_PHOTOS_CACHE_PATH;
if (!is_dir(self::$cachebasedir)) {
mkdir(self::$cachebasedir);
}
}
/**
* Updates the album with the database
* @param $cachelifetime
* @param $api_clientid
* @param $api_secret
* @param $refresh_token
* @param $db \JDatabaseDriver
* @return null
*/
public static function syncAlbums($cachelifetime, $api_clientid, $api_secret, $refresh_token, $db)
{
self::initCacheDirs();
$hash_prefix = md5( $api_clientid.$api_secret.$refresh_token);
$filename = $hash_prefix . '.albums.obj';
$serOBjectPath = self::$cachebasedir . $filename;
if (file_exists($serOBjectPath) && (time() - filemtime($serOBjectPath) <= $cachelifetime)) {
return null;
}
$access_token = self::getAccessToken($db, $api_clientid, $api_secret, $refresh_token);
if ($access_token == "") {
return null;
}
$url = 'https://photoslibrary.googleapis.com/v1/albums/?pageSize=50&prettyPrint=true';
$url .= "&access_token=" . $access_token;
$currentPageNumber = 1;
$cachefilename = self::doRequest($cachelifetime, 'GET', $url, $hash_prefix.'.albums.'.$currentPageNumber , null, true);
$jsonAlbums = json_decode(@file_get_contents($cachefilename));
if (json_last_error() != JSON_ERROR_NONE) {
\JLog::add('unable to load json content from file. File Name: '. $cachefilename.' for albums.', \JLog::INFO, 'com_eventgallery');
return;
}
$albums = $jsonAlbums->albums;
while (!empty($jsonAlbums->nextPageToken)) {
$currentPageNumber++;
$pagingUrl = $url . '&pageToken=' . $jsonAlbums->nextPageToken;
$cachefilename = self::doRequest($cachelifetime, 'GET', $pagingUrl, $hash_prefix.'.albums.'.$currentPageNumber, null, true);
$jsonAlbums = json_decode(file_get_contents($cachefilename));
if (json_last_error() != JSON_ERROR_NONE) {
\JLog::add('unable to load json content from file. File Name: '. $cachefilename.' for albums.', \JLog::INFO, 'com_eventgallery');
return;
}
if (isset($jsonAlbums->albums)) {
$albums = array_merge($albums, $jsonAlbums->albums);
}
}
foreach($albums as $album) {
$query = $db->getQuery(true);
$query->update('#__eventgallery_file')
->set('googlephotos_baseurl='.$db->quote("".$album->coverPhotoBaseUrl))
->where('folder='.$db->quote($album->id) .' AND ismainimage=1');
$db->setQuery($query)->execute();
}
$c = 'just dummy content';//serialize($album);
file_put_contents($serOBjectPath, $c);
return;
}
/**
* retrieved albums from Google
* @param $cachelifetime
* @param $api_clientid
* @param $api_secret
* @param $refresh_token
* @param $db \JDatabaseDriver
* @return null
*/
public static function getAlbums($cachelifetime, $api_clientid, $api_secret, $refresh_token, $db) {
self::initCacheDirs();
$hash_prefix = md5( $api_clientid.$api_secret.$refresh_token);
$access_token = self::getAccessToken($db, $api_clientid, $api_secret, $refresh_token);
$url = 'https://photoslibrary.googleapis.com/v1/albums/?pageSize=50&prettyPrint=true';
$url .= "&access_token=" . $access_token;
$currentPageNumber = 1;
$cachefilename = self::doRequest($cachelifetime, 'GET', $url, $hash_prefix.'.albums.'.$currentPageNumber , null, true);
$jsonAlbums = json_decode(@file_get_contents($cachefilename));
if (json_last_error() != JSON_ERROR_NONE) {
\JLog::add('unable to load json content from file. File Name: '. $cachefilename.' for albums.', \JLog::INFO, 'com_eventgallery');
return;
}
$albums = $jsonAlbums->albums;
while (!empty($jsonAlbums->nextPageToken)) {
$currentPageNumber++;
$pagingUrl = $url . '&pageToken=' . $jsonAlbums->nextPageToken;
$cachefilename = self::doRequest($cachelifetime, 'GET', $pagingUrl, $hash_prefix.'.albums.'.$currentPageNumber, null, true);
$jsonAlbums = json_decode(file_get_contents($cachefilename));
if (isset($jsonAlbums->albums)) {
$albums = array_merge($albums, $jsonAlbums->albums);
}
}
return $albums;
}
/**
* Updates the album with the database
* @param $cachelifetime
* @param $api_clientid
* @param $api_secret
* @param $refresh_token
* @param $db \JDatabaseDriver
* @param $albumId
* @return null
*/
public static function syncAlbum($cachelifetime, $api_clientid, $api_secret, $refresh_token, $db, $albumId)
{
$startTime = microtime(true);
self::initCacheDirs();
$serOBjectPath = self::$cachebasedir . $albumId .'.obj';
if (file_exists($serOBjectPath) && (time() - filemtime($serOBjectPath) <= $cachelifetime)) {
return null;
}
$access_token = self::getAccessToken($db, $api_clientid, $api_secret, $refresh_token);
if ($access_token == "") {
return null;
}
$hash_prefix = $albumId;
$url = 'https://photoslibrary.googleapis.com/v1/albums/'. $albumId .'?prettyPrint=true';
$url .= "&access_token=" . $access_token;
$cachefilename = self::doRequest($cachelifetime, 'GET', $url, $hash_prefix.'.album' , null, true);
$jsonAlbum = json_decode(@file_get_contents($cachefilename));
$currentPageNumber = 1;
$url = 'https://photoslibrary.googleapis.com/v1/mediaItems:search?pageSize=100&prettyPrint=true';
$url .= "&access_token=" . $access_token;
$data = json_encode(
[
'albumId' => $albumId
]);
$cachefilename = self::doRequest($cachelifetime, 'POST', $url, $hash_prefix.'.items.'.$currentPageNumber, $data, true);
$jsonMediaItems = json_decode(@file_get_contents($cachefilename));
if (json_last_error() != JSON_ERROR_NONE) {
\JLog::add('unable to load json content from file. File Name: '. $cachefilename.' for ' . $albumId, \JLog::INFO, 'com_eventgallery');
$album['photos'] = [];
$album['overallCount'] = 0;
return (object)$album;
}
$mediaItems = $jsonMediaItems->mediaItems;
while (!empty($jsonMediaItems->nextPageToken)) {
$currentPageNumber++;
$pagingUrl = $url . '&pageToken=' . $jsonMediaItems->nextPageToken;
$cachefilename = self::doRequest($cachelifetime, 'POST', $pagingUrl, $hash_prefix.'.items.'.$currentPageNumber, $data, true);
$jsonMediaItems = json_decode(file_get_contents($cachefilename));
$mediaItems = array_merge($mediaItems, $jsonMediaItems->mediaItems);
}
$album = Array();
$photos = Array();
$ordering = 0;
#echo "After DOM loaded:". memory_get_usage() . "<br>\n";
foreach ($mediaItems as $mediaItem) {
$photo = Array();
if (!isset($mediaItem->mediaMetadata->photo)) {
continue;
}
$photo['baseurl'] = $mediaItem->baseUrl;
$photo['width'] = $mediaItem->mediaMetadata->width;
$photo['height'] = $mediaItem->mediaMetadata->height;
$photo['caption'] = isset($mediaItem->description)?$mediaItem->description:'';
$photo['title'] = "";//$mediaItem->filename;
$photo['date'] = $mediaItem->mediaMetadata->creationTime;
$photo['folder'] = $albumId;
$photo['file'] = $mediaItem->id;
$photo['googlephotos_filename'] = $mediaItem->filename;
$exif = Array();
$exif['fstop'] = isset($mediaItem->mediaMetadata->photo->apertureFNumber)? $mediaItem->mediaMetadata->photo->apertureFNumber:'';
$exif['focallength'] = isset($mediaItem->mediaMetadata->photo->focalLength)?$mediaItem->mediaMetadata->photo->focalLength:'';
$exif['exposuretime'] = isset($mediaItem->mediaMetadata->photo->exposureTime)?Exif::float2rat((float)$mediaItem->mediaMetadata->photo->exposureTime):'';
$exif['model'] = (isset($mediaItem->mediaMetadata->photo->cameraModel)?$mediaItem->mediaMetadata->photo->cameraModel:'');
$exif['iso'] = isset($mediaItem->mediaMetadata->photo->isoEquivalent)?$mediaItem->mediaMetadata->photo->isoEquivalent:'';
$exif['filename'] = isset($mediaItem->filename)?$mediaItem->filename:'';
$photo['exif'] = (object)$exif;
$photo['published'] = 1;
$photo['ordering'] = $ordering++;
$photos[$photo['file']] = $photo;
unset($photo);
}
$album['folder'] = $albumId;
$album['file'] = $jsonAlbum->coverPhotoMediaItemId;
$album['photos'] = $photos;
$album['overallCount'] = $jsonAlbum->mediaItemsCount;
$album['description'] = $jsonAlbum->title;
$album['width'] = 1440;
$album['height'] = 1440;
$album['title'] = $jsonAlbum->title;
$album['baseurl'] = $jsonAlbum->coverPhotoBaseUrl;
#echo "Finally:". memory_get_usage() . "\n<br>";
#echo memory_get_peak_usage() . "\n<br>";
$start = microtime(true);
$album = (object)$album;
$db->transactionStart();
try {
$query = $db->getQuery(true);
$query->select('id, file')->from('#__eventgallery_file')
->where('folder='.$db->quote($albumId));
$db->setQuery($query);
$oldFiles = $db->loadObjectList();
$oldFileIDs = [];
if (is_iterable($oldFiles)) {
foreach ($oldFiles as $oldFile) {
$oldFileIDs[$oldFile->file] = $oldFile->id;
}
}
$query = $db->getQuery(true);
$query->delete('#__eventgallery_file')
->where('folder='.$db->quote($albumId));
$db->setQuery($query);
$db->execute();
if (count($photos)>0) {
foreach(array_chunk($photos, 100) as $photosChunk) {
self::addPhotosToDatabase($photosChunk, $albumId, $db, $oldFileIDs);
}
$query = $db->getQuery(true);
$query->update('#__eventgallery_file')
->set('ismainimage=1')
->where('folder=' . $db->quote($album->folder) . ' AND file=' . $db->quote($album->file));
$db->setQuery($query);
$db->execute();
$query = $db->getQuery(true);
$query->update('#__eventgallery_folder')
->set('googlephotostitle=' . $db->quote($album->title))
->where('folder=' . $db->quote($album->folder));
$db->setQuery($query);
$db->execute();
}
$db->transactionCommit();
} catch (\JDatabaseExceptionExecuting $e) {
\JLog::add('Catched database execution while updating Google Photos files. Error message: '. $e->getMessage(), \JLog::INFO, 'com_eventgallery');
$db->transactionRollback();
}
$c = 'just dummy content because we want to remember the last update time.';
file_put_contents($serOBjectPath, $c);
$endTime = microtime(true);
// no need to return anything since this is just an update message.
return null;
}
private static function addPhotosToDatabase($photos, $albumId, $db, $oldFileIDs) {
$query = $db->getQuery(true);
$query->insert($db->quoteName('#__eventgallery_file'))
->columns(
'id,
folder,
file,
googlephotos_filename,
width,
height,
caption,
title,
googlephotos_baseurl,
url,
exif,
ordering,
ismainimage,
ismainimageonly,
hits,
published,
userid,
modified,
created,
creation_date'
);
foreach ($photos as $photo) {
$query->values(implode(',', array(
isset($oldFileIDs[$photo['file']]) ? $db->quote((int)$oldFileIDs[$photo['file']]):0,
$db->quote($albumId),
$db->quote($photo['file']),
$db->quote($photo['googlephotos_filename']),
$db->quote($photo['width']),
$db->quote($photo['height']),
$db->quote($photo['title']),
$db->quote($photo['caption']),
$db->quote($photo['baseurl']),
$db->quote(''),
$db->quote(json_encode($photo['exif'])),
$db->quote($photo['ordering']),
0,
0,
0,
1,
0,
'now()',
'now()',
$db->quote(date('YmdHis', strtotime($photo['date'])))
)));
}
$db->setQuery($query);
$db->execute();
}
/**
* use a refresh token to create an access token.
*
* @param $db \JDatabaseDriver
* @param $api_clientid
* @param $api_secret
* @param $refresh_token
* @return mixed|string
*/
private static function getAccessToken($db, $api_clientid, $api_secret, $refresh_token)
{
\JLog::addLogger(
array(
'text_file' => self::COM_EVENTGALLERY_GOOGLEPHOTOS_LOGFILENAME,
'logger' => 'Eventgalleryformattedtext'
),
\JLog::ALL,
'com_eventgallery'
);
$refreshTokenHash = hash('sha256', $refresh_token);
if (isset(self::$tokenCache[$refreshTokenHash])) {
return self::$tokenCache[$refreshTokenHash];
}
$query = $db->getQuery(true);
$query->select('access_token')
->from('#__eventgallery_auth_token')
->where('valid_until > now()')
->andWhere('refresh_token_hash='.$db->quote($refreshTokenHash));
$db->setQuery($query);
$access_token = $db->loadResult();
if ($access_token !== null && strlen($access_token) > 0) {
self::$tokenCache[$refreshTokenHash] = $access_token;
return $access_token;
}
try {
$response = self::requestAccessToken($api_clientid, $api_secret, $refresh_token);
} catch (\Exception $e) {
\JLog::add('unable to get access token from Google. Check the refresh token for this account. Error message: ' . $e->getMessage(), \JLog::INFO, 'com_eventgallery');
}
/**
* Save the stuff to the database and set the timestamp to the future. Remove some seconds so we avoid edge cases.
*/
$access_token = "";
if (isset($response->access_token)) {
$db->transactionStart();
try {
$access_token = $response->access_token;
$validityTime = (int)$response->expires_in - 120;
$query = $db->getQuery(true);
$query->delete()->from('#__eventgallery_auth_token')
->where('refresh_token_hash=' . $db->quote($refreshTokenHash));
$db->setQuery($query);
$db->execute();
$query = $db->getQuery(true);
$query->insert('#__eventgallery_auth_token')
->columns(array('refresh_token_hash', 'access_token', 'valid_until'))
->values(implode(',', array(
$db->quote($refreshTokenHash), $db->quote($access_token), 'DATE_ADD(NOW(), INTERVAL ' . (int)$validityTime . ' SECOND)')));
$db->setQuery($query);
$db->execute();
$db->transactionCommit();
} catch (\JDatabaseExceptionExecuting $e) {
\JLog::add('Catched database execution while adding token to database. Error message: '. $e->getMessage(), \JLog::INFO, 'com_eventgallery');
$db->transactionRollback();
}
} else {
\JLog::add('unable to get access token from Google. Check the refresh token for this account. Error message: '. (isset($response)?$response->error:''). ', ' . (isset($response)?$response->error_description:''), \JLog::INFO, 'com_eventgallery');
}
self::$tokenCache[$refreshTokenHash] = $access_token;
return $access_token;
}
/**
* @param $api_clientid
* @param $api_secret
* @param $refresh_token
* @return array
*/
public static function requestAccessToken($api_clientid, $api_secret, $refresh_token)
{
$data = [
'client_id' => $api_clientid,
'client_secret' => $api_secret,
'refresh_token' => $refresh_token,
'grant_type' => 'refresh_token'
];
$response = null;
$response = \JHttpFactory::getHttp()->post('https://www.googleapis.com/oauth2/v4/token', $data, [], 2);
$response = json_decode($response->body);
return $response;
}
}