Commit a5af7609 authored by Dainis Abols's avatar Dainis Abols
Browse files

REST API Integration for TYPO3

Version 2.5.0
parents
Pipeline #121 failed with stages
.idea/
vendor
<?php
namespace Lu\LuApi\Authentication;
use Lu\LuApi\Generators\Randomizer;
use Lu\LuApi\Helpers\ReturnPretty;
use GuzzleHttp\Client as Guzzle;
use Lu\LuAuth\Models\Users;
use TYPO3\CMS\Backend\Utility\BackendUtility;
use TYPO3\CMS\Core\Resource\ResourceFactory;
use TYPO3\CMS\Core\Utility\GeneralUtility;
/**
* Class LuisLibrary. Main LUIS request library.
*
* @author Dainis Abols <dainis.abols@lu.lv>
* @owner University of Latvia
* @version 1.0
* @since 28.05.2018
*
* @package Lu\LuApi\Authentication
*/
class LuisLibrary
{
/**
* Extension configuration
*
* @array
*/
private $conf = [];
/**
* LUIS connection
*
* @var resource
*/
private $luisdb = null;
/**
* LUIS connection hostname
*
* @var string
*/
private $luis_host;
/**
* LUIS connection username
*
* @var string
*/
private $luis_user;
/**
* LUIS connection password
*
* @var string
*/
private $luis_pass;
/**
* LU University code
*
* @var int
*/
private $uni_code = 2;
/**
* Username
*
* @var string
*/
private $username;
/**
* LuisLibrary constructor.
*
* Gathers and sets connection data from environmental values. Prepares and opens connection.
*/
public function __construct()
{
$this->conf = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Configuration\ExtensionConfiguration::class)->get('lu_api');
$configFile = @include($this->conf['configFile']);
$this->luis_host = $configFile['luis_db']['luis_host'];
$this->luis_user = $configFile['luis_db']['luis_user'];
$this->luis_pass = $configFile['luis_db']['luis_pass'];
$this->conf['luis_host'] = $configFile['luis_db']['luis_host'];
$this->conf['luis_user'] = $configFile['luis_db']['luis_user'];
$this->conf['luis_pass'] = $configFile['luis_db']['luis_pass'];
$this->luisdb = $this->luisConnect();
}
/**
* LuisLibrary de-constructor.
*
* Closes connection on `unset` or script end.
*/
public function __destruct()
{
if (!empty($this->luisdb) && is_resource($this->luisdb)) {
oci_close($this->luisdb);
}
}
/**
* Creates connection with LUIS.
*
* @TODO: Add log about error connections
*
* @return bool|resource
*/
private function luisConnect()
{
// Try to connect to the external database (forcing new connection)
try {
if ($luisdb = oci_connect($this->luis_user, $this->luis_pass, $this->luis_host)) {
return $luisdb;
} else {
$data = [
'success' => false,
'error' => "Error connecting to LUIS DB host: ".$this->luis_host,
];
return ReturnPretty::json($data, 400);
}
} catch (\Exception $e) {
$data = [
'success' => false,
'error' => $e->getMessage(),
];
return ReturnPretty::json($data, 400);
}
}
/**
* Retrieve user data if possible
*
* @return string
*/
public function getUserData($username)
{
// Check for valid values
if (empty($this->luisdb) || empty($username)) {
return ReturnPretty::json([], 400);
}
$this->username = $username;
// Prepare bind data
$userData = [
'username' => $username,
'first_name' => '',
'last_name' => '',
'description' => '',
'email' => '',
'telephone' => '',
'address' => '',
];
$stmt = oci_parse($this->luisdb, 'BEGIN estudijas.dta(:lg, :va, :uzv, :apl, :epasts, :tel, :adr, :msg); END;');
oci_bind_by_name($stmt, ":lg", $userData['username']);
oci_bind_by_name($stmt, ":va", $userData['first_name'], 512);
oci_bind_by_name($stmt, ":uzv", $userData['last_name'], 512);
oci_bind_by_name($stmt, ":apl", $userData['description'], 512);
oci_bind_by_name($stmt, ":epasts", $userData['email'], 512);
oci_bind_by_name($stmt, ":tel", $userData['telephone'], 512);
oci_bind_by_name($stmt, ":adr", $userData['address'], 512);
oci_bind_by_name($stmt, ":msg", $msg, 512);
oci_execute($stmt);
// @TODO: Add log here
if (!empty($userData['first_name']) && !empty($userData['last_name'])) {
$userData['name'] = $userData['first_name'].' '.$userData['last_name'];
} else {
unset($userData['name']);
}
return ReturnPretty::json($userData, 200);
}
/**
* Retrieve user photo data from LUIS Oracle procedure
*
* @return string
*/
public function syncUserImage($username)
{
// Avatar sync is disabled currently!
return;
$this->username = $username;
// Remove image from avatar before checking if new one available.
$this->removeFileReference();
// Check if image usage set
if (!$this->conf['extraImages']) {
return null;
}
// Check for valid values
if (empty($this->luisdb) || empty($username)) {
return ReturnPretty::json([], 400);
}
// Prepare bind data
$photo = [
'id' => '',
'url' => '',
'public' => '',
'type' => '',
];
// @TODO: Add log here
// Gather user structure codes
$stmt = oci_parse($this->luisdb, 'BEGIN da_telpas.set_foto_dati(:p_au, :p_login, :o_foto_id, :o_foto_url, :o_publisks, :o_foto_type); END;');
oci_bind_by_name($stmt, ':p_au', $this->uni_code, 4);
oci_bind_by_name($stmt, ':p_login', $username, 50);
oci_bind_by_name($stmt, ':o_foto_id', $photo['id'], 100);
oci_bind_by_name($stmt, ':o_foto_url', $photo['url'], 1000);
oci_bind_by_name($stmt, ':o_publisks', $photo['public'], 4);
oci_bind_by_name($stmt, ':o_foto_type', $photo['type'], 100);
oci_execute($stmt);
// Save file. Can add public check here!
if (!empty($photo['url'])) {
$newFile = @$this->saveRemoteFile($photo['url'], $username.'.png');
if (!empty($newFile)) {
$this->addFileReference($newFile);
// $this->updateUserAvatar();
}
}
return ReturnPretty::json($photo);
}
/**
* Save remote file
*
* @param $url
* @param $newname
*
* @return \TYPO3\CMS\Core\Resource\FileInterface
*/
private function saveRemoteFile($url, $newname)
{
// @TODO: Add log here
// Pre-set used models
$client = new Guzzle();
$Randomizer = new Randomizer();
// Fetch actual image content
$result = $client->get($url, ['verify' => false]);
$imageData = $result->getBody()->getContents();
// Save it to temp
$string = $Randomizer->getRandomString(16, true);
$string = json_decode($string);
$subfolder = $this->username[0];
$imagePath = '/'.trim($this->conf['imagesTempFolder'], '/').'/userimage_'.$string->data;
file_put_contents($imagePath, $imageData);
// Check what storage to use
$resourceFactory = ResourceFactory::getInstance();
if ($this->conf['extraImagesStorage'] > 0) {
$storage = $resourceFactory->getStorageObject(4);
} else {
$storage = $resourceFactory->getDefaultStorage();
}
// Remove permission check
$storage->setEvaluatePermissions(false);
// Check if folder exists and create if needed
if (!$storage->hasFolder($subfolder)) {
$storage->createFolder($subfolder);
}
// Add the file to the folder
$newFile = $storage->addFile(
$imagePath,
$storage->getFolder($subfolder),
$newname
);
// Renew permission check
$storage->setEvaluatePermissions(true);
// Delete file, on successful save
@unlink($imagePath);
return $newFile;
}
/**
* Remove user avatar file references
*/
private function removeFileReference()
{
// @TODO: Get user object for be_users
// @TODO Get all entries from 'sys_file_reference' WHERE avatar = '1' and uid_foreign(?) user ID
// @TODO Get all entries from 'sys_file' WHERE ...
// @TODO: Delete found files
// @TODO: Delete found 'sys_file' entries
// @TODO: Delete found 'sys_file_reference' entries
// @TODO: Update be_users SET avatar = '0' WHERE username = 'username'
}
/**
* Add user avatar file references
*
* @param $newFile
*/
private function addFileReference($newFile)
{
// Pre-set models
$UserModel = new Users('be');
// @TODO: Add log here
$resourceFactory = ResourceFactory::getInstance();
if ($this->conf['extraImagesStorage'] > 0) {
$storage = $resourceFactory->getStorageObject($this->conf['extraImagesStorage']);
} else {
$storage = $resourceFactory->getDefaultStorage();
}
// Remove permission check
$storage->setEvaluatePermissions(false);
// Get user object
$user = $UserModel->getUserData($this->username);
$resourceFactory = ResourceFactory::getInstance();
$fileObject = $resourceFactory->getFileObject($newFile->getUid());
$contentElement = BackendUtility::getRecord(
'be_users',
$user['uid']
);
// Update
$reference = [
'tstamp' => time(),
'crdate' => time(),
'table_local' => 'sys_file',
'uid_local' => $fileObject->getUid(),
'tablenames' => 'be_users',
'uid_foreign' => $contentElement['uid'],
'fieldname' => 'avatar',
'pid' => $contentElement['pid'],
];
// Insert reference
$queryBuilder = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Database\ConnectionPool::class)->getQueryBuilderForTable('sys_file_reference');
$queryBuilder->insert('sys_file_reference')->values($reference)->execute();
// Renew permission check
$storage->setEvaluatePermissions(true);
}
}
<?php
namespace Lu\LuApi\Controller;
use Lu\LuApi\DataSources\FileDownloader;
use Lu\LuApi\Generators\QRCodeGenerator;
use Lu\LuApi\Helpers\ReturnPretty;
use Lu\LuApi\Models\Courses;
use Lu\LuApi\Models\CoursesContinue;
use Lu\LuApi\Models\Email;
use Lu\LuApi\Models\Lessons;
use Lu\LuApi\Models\NabaLive;
use Lu\LuApi\Models\NabaStore;
use Lu\LuApi\Models\Namedays;
use Lu\LuApi\Models\Programs;
use Lu\LuApi\Models\ProgramsContinue;
use Lu\LuApi\Models\Purchases;
use TYPO3\CMS\Extbase\Mvc\View\ViewInterface;
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
/**
* Class ExchangeController
*
* @author Dainis Abols <dainis.abols@lu.lv>
* @owner University of Latvia
* @version 1.4.0
* @since 30.07.2018
*
* @package Lu\LuApi\Controller
*/
class ExchangeController extends ActionController
{
/**
* Set up the doc header properly here
*
* @param ViewInterface $view
*
* @return void
*/
protected function initializeView(ViewInterface $view)
{
parent::initializeView($view);
}
/**
* HTTP status codes
*
* 200 OK - Response to a successful GET, PUT, PATCH or DELETE. Can also be used for a POST that doesn't result in
* a creation.
* 201 Created - Response to a POST that results in a creation. Should be combined with a Location header pointing
* to the location of the new resource
* 204 No Content - Response to a successful request that won't be returning a body (like a DELETE request)
* 304 Not Modified - Used when HTTP caching headers are in play
* 400 Bad Request - The request is malformed, such as if the body does not parse
* 401 Unauthorized - When no or invalid authentication details are provided. Also useful to trigger an auth popup
* if the API is used from a browser
* 403 Forbidden - When authentication succeeded but authenticated user doesn't have access to the resource
* 404 Not Found - When a non-existent resource is requested
* 405 Method Not Allowed - When an HTTP method is being requested that isn't allowed for the authenticated user
* 410 Gone - Indicates that the resource at this end point is no longer available. Useful as a blanket response
* for old API versions
* 415 Unsupported Media Type - If incorrect content type was provided as part of the request
* 422 Unprocessable Entity - Used for validation errors
* 429 Too Many Requests - When a request is rejected due to rate limiting
*
* @param $data
*/
private function printJson($data, $status)
{
switch ($status) {
case 200:
header("HTTP/1.1 200 OK");
header("Access-Control-Allow-Origin: *"); // This probably needs a configurration of allowed sources
break;
case 404:
header("HTTP/1.1 404 Not Found");
break;
}
header('Content-Type: application/json');
echo ReturnPretty::json($data, $status, true);
die;
}
/**
* Prints out visual data, instead of json
*
* @param $data
* @param string $view
*/
private function printView($data, string $view)
{
$this->view->assign('data', $data);
$this->view->setTemplatePathAndFilename('EXT:lu_api/Resources/Private/Templates/Modules/'.$view.'.html');
}
/**
* Main route action
*/
public function routeAction()
{
// Fetch request
$r = $_REQUEST['r'];
$visual = !empty($_REQUEST['visual']) && $_REQUEST['visual'] == 1 ? true : false;
$request = reset(explode('/', $r));
$view = ucfirst($request);
// Routes switch
switch ($request) {
default:
$data = -1;
break;
/* SELF ENDING ROUTES */
case "download": FileDownloader::fetch(); break; // Direct file download from type and id
case "qrcode": QRCodeGenerator::create(); break; // QR Code generator from URL
/* BASIC ROUTES */
case "course": $data = Courses::fetch(); break; // Study catalogue course
case "course-continue": $data = CoursesContinue::fetch(); break; // Study catalogue course continue
case "program": $data = Programs::fetch(); break; // Study catalogue program
case "program-continue": $data = ProgramsContinue::fetch(); break; // Study catalogue program continue
case "purchase": $data = Purchases::fetch(); break; // Purchase list routes
case "namedays": $data = Namedays::fetch(); break; // Namedays request
case "naba-live": $data = NabaLive::fetch(); $view = $data['view']; break; // Return live stream song
case "naba-store": $data = NabaStore::fetch(); $view = $data['view']; break; // Call live song storage
/* VIEWABLE ROUTES */
case "tornkalns-lessons":
case "dabas-maja": $data = Lessons::fetch(1859); $view = $data['view']; break; // Dabas maja's lessons and auditoriums
case "zinatnu-maja": $data = Lessons::fetch(2219); $view = $data['view']; break; // Zinatnu maja's lessons and auditoriums
/* EMAIL CHECK ROUTE */
case "email": $data = Email::fetch(); $view = $data['view']; break; // Detect show gmail or webmail
}
// Return JSON and die!
if ($data !== -1) {
if ($visual) {
$this->printView($data, $view);
} else {
$this->printJson($data, 200);
}
}
}
}
\ No newline at end of file
<?php
namespace Lu\LuApi\DataSources;
use TYPO3\CMS\Core\Utility\GeneralUtility;
/**
* Class FileDownloader
*
* @author Dainis Abols <dainis.abols@lu.lv>
* @owner University of Latvia
* @version 1.4.2
* @since 07.08.2018
*/
class FileDownloader
{
/**
* Extension configuration
*
* @array
*/
private $conf = [];
/**
* LuisContracts constructor.
*
* Gathers and sets connection data from environmental values. Prepares and opens connection.
*/
public function __construct()
{
// Read config file
$this->conf = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Configuration\ExtensionConfiguration::class)->get('lu_api');
}
/**
* Download files from server
*/
static public function fetch()
{
// Set default response
$self = new self;
// Fetch request
$r = $_REQUEST['r'];
$request = explode('/', $r);
unset($request[0]);
// Call method directly
$method = $request[1];
unset($request[1]);
$request = trim(implode("/", $request), '/');
$self->{$method}($request);
}
/**
* @param $fileId
*/
public function purchase(int $fileId)
{
// Pre-set models
$luisPurchases = new LuisPurchases();
// Set storage directory
$storage = rtrim($this->conf['storage']['lupurchases'], '/');
// Initialize data request
$luisPurchases->setRequestType('download');
$luisPurchases->setFileId($fileId);
$jsonData = $luisPurchases->fetch();
$result = json_decode($jsonData);
// Stop, if no data
if (!$result->data) {
exit;
}
// Fetch data from location and return as download
$this->downloadFile($storage.'/'.$result->data->sysname, $result->data->filename);
}
/**