Spreadsheets error " String could not be parsed as XML" - google-sheets-api

When I try to get all titles of spreadsheets from Google drive, I got this message The string could not be parsed as XML:
ERROR -> SimpleXMLElement::__construct(): Entity: line 1: parser error : Start tag expected, '<' not found in C:\....
ERROR-> SimpleXMLElement::__construct(): { in C:\....
ERROR-> SimpleXMLElement::__construct(): ^ in C:\...
I try to connect on Gooogle drive, which I success, but I need to get the names of spreadsheets on Google drive, I previously use API v3 now I need to use V4.
this is code for call method getSpreadsheets();
$serviceRequest = new DefaultServiceRequest($token->access_token, $token->token_type);
ServiceRequestFactory::setInstance($serviceRequest);
$spreadsheetService = new Google\Spreadsheet\SpreadsheetService();
$spreadsheetFeed = $spreadsheetService->getSpreadsheets();
This is the code of method getSpreadsheets:
public function getSpreadsheets()
{
return new SpreadsheetFeed(
ServiceRequestFactory::getInstance()->get('v4/spreadsheets/1u7WYzJOYMX7uH3AIM70yVLOaHBy8p_uifuJe_Saa2T4?fields=sheets.properties.title')
);
}
And this is SpreadsheetFeed class, where is error causes:
namespace Google\Spreadsheet;
use ArrayIterator;
use SimpleXMLElement;
/**
* Spreadsheet feed.
*
* #package Google
* #subpackage Spreadsheet
* #author Asim Liaquat <asimlqt22#gmail.com>
*/
class SpreadsheetFeed extends ArrayIterator
{
/**
* The spreadsheet feed xml object
*
* #var \SimpleXMLElement
*/
protected $xml;
/**
* Initializes the the spreadsheet feed object
*
* #param string $xml the raw xml string of a spreadsheet feed
*/
public function __construct($xml)
{
$this->xml = new SimpleXMLElement($xml);
$spreadsheets = array();
foreach ($this->xml->entry as $entry) {
$spreadsheets[] = new Spreadsheet($entry);
}
parent::__construct($spreadsheets);
}
/**
* Gets a spreadhseet from the feed by its title. i.e. the name of
* the spreadsheet in google drive. This method will return only the
* first spreadsheet found with the specified title.
*
* #param string $title
*
* #return \Google\Spreadsheet\Spreadsheet|null
*/
public function getByTitle($title)
{
foreach($this->xml->entry as $entry) {
if($entry->title->__toString() == $title) {
return new Spreadsheet($entry);
}
}
return null;
}
}
I try to find a solution how to pass this error!
Any Help, how to solve this error?
Thanks!

This is solution for my app, migrate from v3 to v4 spreadsheets API:
$this->service_drive = new Google_Service_Drive($this->client);
$optParams = array('q'=> 'mimeType="application/vnd.google-apps.spreadsheet"');
$this->files = $this->service_drive->files->listFiles($optParams);
}
// $this->service_drive = new Google_Service_Drive($this->client);
//$optParams = array('q'=> 'mimeType="application/vnd.google-apps.spreadsheet"');
//$this->files = $this->service_drive->files->listFiles($optParams);
//$gdata_spreadsheets=$this->files;//my test
if($this->files !==null){
if (count($this->files->getFiles()) == 0) {
$gdata_spreadsheets=array("No Spreadsheets");
} else {
foreach ($this->files->getFiles() as $file) {
$gdata_spreadsheets[$file->getId()]=$file->getName();
}
}
}
//trenuto ispod code rjesavam
if($gdata->spreadsheet_id != '' && isset( $gdata_spreadsheets[$gdata->spreadsheet_id] ) && $this->files !== null){
$this->service = new Google_Service_Sheets($this->client);
$this->response = $this->service->spreadsheets->get($gdata->spreadsheet_id);
$this->sheets=$this->response->getSheets();
// $spreadsheet = $this->service_drive($gdata_spreadsheets[$gdata->spreadsheet_id]);
// $worksheetFeed = $spreadsheet->getWorksheets();
foreach ( $this->sheets as $sheet ){
$gdata_worksheets[$sheet->properties->sheetId]= $sheet->properties->title;
//$gdata_worksheets[]=$this->sheets;
}
//$gdata_worksheets[]=$gdata->worksheet_id;
if($gdata->worksheet_id != '' && isset( $gdata_worksheets[$gdata->worksheet_id] )){
$range = 'List 2!A1:Z';
$worksheet = $this->service->spreadsheets_values->get($gdata->spreadsheet_id, $range);
$cellFeed = $worksheet->getValues();
foreach( $cellFeed As $row) {
//$row = $cellEntry->getRow();
//$col = $cellEntry;
// $gdata_worksheets=$this->service;
if( $row > 1 ){
$gdata_columns=$row;
break;
}
}
}
}
} catch(Exception $e){
$error = $e->getMessage();
}
This is one part of my code, where is described how to migrate from v3 to v4 spreadsheets API.

Related

TYPO3 Repository outside controller

I am using TYPO3 10.4.15 and try to write a TcaProcFunc for my own extension.
namespace HGA\Album\UserFunc;
use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Object\ObjectManager;
use HGA\Album\Domain\Repository\AlbumRepository;
use HGA\Album\Domain\Model\Album;
use TYPO3\CMS\Extbase\Persistence\PersistenceManagerInterface;
class TcaProcFunc
{
/**
* #param array $config
*
*/
public function getLink(&$config)
{
$ret = [];
$objectManager = GeneralUtility::makeInstance(ObjectManager::class);
$albumRepository= $objectManager->get(AlbumRepository::class);
$albums = $albumRepository->findAll();
foreach ($albums as $album){
$ret[] = ["Test1","Test1"];
}
$i = count($albums);
error_log("Album: " . $i, 0 );
$ret[] = ["Test", "Test"];
$config['items'] = $ret;
// error_log("UserFunc: " . var_export($config, true), 0);
}
}
The problem is, that findall() does not provide any result. $albumsis empty, but there is one record inside the database.
HGA is my vendor name and Album the extension name. The TcaProcFunc is in generell is working, but I don't have any access to the extension database.
Can anybody tell me, what is wrong with my code or how I what I can do to findthe problem?
Thank you in advance.
Finally I did it with ConnectionPool
namespace HGA\Album\UserFunc;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Object\ObjectManager;
use TYPO3\CMS\Core\Database\ConnectionPool;
class TcaProcFunc
{
/**
* #param array $config
*
*/
public function getLink(&$config)
{
$ret = [];
$albums = GeneralUtility::makeInstance(ConnectionPool::class)
->getConnectionForTable('tx_album_domain_model_album')
->select(
['uid', 'album_artist', 'album'],
'tx_album_domain_model_album',
[]
)->fetchAll();
foreach ($albums as $album){
$ret[] = [$album['album_artist'] . ": " . $album['album'], $album['uid']];
}
$config['items'] = $ret;
}
}

Prestashop 1.7 - City field as dropdown or autocomplete

I am using Prestashop 1.7 and I need to change the city input field.
I have a billing software that only allows me to use predefined cities. So I have created a table ps_cities with the entries (id an city name).
I know how to write a dropdown or a autocomplete script, but I do not know where to change the input type in the Prestashop files.
On the 1.6 version you have the input field in a theme file, but somehow I fail to find in the new version.
In PrestaShop 1.7.7.X I've created a module that includes some new (and cool!) hooks like showing below. I consider this one a good option because it will be easier to maintain in the next PrestaShop releases.
Some assumptions here: I created a relationship model CityAddress with two fields id_city and id_address and a City model with fields like name, id_state, id_country, also I continued using Address::city string name for compatibility.
/**
* #see /classes/form/CustomerAddressFormatter.php#L156
* #param array $param [
* #var array $fields
* ]
*/
public function hookAdditionalCustomerAddressFields($params)
{
($params['fields']['city'])->setType('hidden');
// New field
$formField = $params['fields'];
$formField = (new FormField())
->setName('id_city')
->setLabel($this->l('City'))
->setRequired(true)
->setType('select')
;
// If an address already exits, select the default city
if (Tools::getIsset('id_address')) {
$address = new Address(Tools::getValue('id_address'));
if (!empty($address->id_state)) {
$cities = City::getCitiesByIdState((int) $address->id_state);
if (!empty($cities)) {
foreach ($cities as $city) {
$formField->addAvailableValue(
$city['id_city'],
$city['name']
);
}
$id_city = CityAddress::getIdCityByIdAddress((int) $address->id);
$formField->setValue($id_city);
}
}
}
// Add the id_city field in the position of the city field
$keys = array_keys($params['fields']);
$search = 'city';
foreach ($keys as $key => $value) {
if ($value == $search) {
break;
}
}
$part1 = array_slice($params['fields'], 0, $key + 1);
$part2 = array_slice($params['fields'], $key + 1);
$part1['id_city'] = $formField;
$params['fields'] = array_merge($part1, $part2);
}
This one to validate the field:
/**
* #see /classes/form/CustomerAddressForm.php#L123
* #param array $param [
* #var CustomerAddressForm $form
* ]
*/
public function hookActionValidateCustomerAddressForm($params)
{
if (empty(Tools::getValue('id_city'))
|| empty(Tools::getValue('city'))) {
return false;
}
$form = $params['form'];
$idCityField = $form->getField('id_city');
$idCity = (int) Tools::getValue('id_city');
$cityObj = new City($idCity);
$city = pSQL(Tools::getValue('city'));
if ($cityObj->name !== $city) {
$idCityField->addError(sprintf(
$this->l('Invalid name in field id_city %s and city %s'),
$cityObj->name,
$city
));
return false;
}
return true;
}
And the submitted field:
/**
* #see /classes/form/CustomerAddressForm.php#L153
* #param array $param [
* #var Address $address
* ]
*/
public function hookActionSubmitCustomerAddressForm($params)
{
/** #var Address */
$address = $params['address'];
$address->save();
if (!Validate::isLoadedObject($address)) {
throw new PrestaShopException($this->l('Address object error while trying to save city'));
}
// If address has a previous value then update it
$cityAddress = CityAddress::getCityAddressByIdAddress((int) $address->id);
$city = City::getCityByNameAndIdState($address->city, $address->id_state);
$cityAddress->id_city = $city->id;
$cityAddress->id_address = $address->id;
$cityAddress->save();
}
It is possible if you have this line in the additionalCustomerAddressFields hook:
https://github.com/PrestaShop/PrestaShop/blob/develop/classes/form/CustomerAddressFormatter.php#L150
For previous version I included ['fields' => &$format] as a parameter.
You can find all form fields of the Front Office in your theme's /templates/_partials/form-fields.tpl file

Doesn't save the image in the database Symfony 4

I'm writing applications in symfony and I would like to add an image to the article. I can't save the image to the database. I am trying to make an example from the documentation: https://symfony.com/doc/current/controller/upload_file.html
The image is uploaded to the directory on the server and isn't saved to the database.
$post->setThumbnail($fileName); sets the image name correctly
Entity
/**
* #Assert\Image(
* minWidth = 500,
* minHeight = 300,
* maxWidth = 1920,
* maxHeight = 1080,
* maxSize = "1M"
* )
*/
private $thumbnail;
/**
* Set thumbnail.
*
* #param string $thumbnail
*
* #return Post
*/
public function setThumbnail($thumbnail)
{
$this->thumbnail = $thumbnail;
return $this;
}
/**
* Get thumbnail.
*
* #return string
*/
public function getThumbnail()
{
return $this->thumbnail;
}
Action in Controller
/**
* Add and Edit page Post.
*
* #Route(
* {"pl": "/artykyl/{slug}"},
* name="panel_post",
* defaults={"slug"=NULL}
* )
*
* #param Request $request
* #param string|null $slug
* #param TranslatorInterface $translator
*
* #return \Symfony\Component\HttpFoundation\RedirectResponse|\Symfony\Component\HttpFoundation\Response
*/
public function post(Request $request, string $slug = null, TranslatorInterface $translator, FileUploader $fileUploader)
{
if (null === $slug) {
$post = new Post();
$newPostForm = true;
} else {
$post = $this->getDoctrine()->getRepository('App\Entity\Post')->findOneBy(['slug' => $slug]);
}
$form = $this->createForm(PostType::class, $post);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
if ($post->getThumbnail()) {
$file = $post->getThumbnail();
$fileName = $fileUploader->upload($file);
$post->setThumbnail($fileName);
}
$em = $this->getDoctrine()->getManager();
$em->persist($post);
$em->flush();
$this->addFlash('success', $translator->trans('Changes have been saved'));
return $this->redirectToRoute('panel_post', ['slug' => $post->getSlug()]);
} elseif ($form->isSubmitted() && false === $form->isValid()) {
$this->addFlash('danger', $translator->trans('Corrects form'));
}
return $this->render('backend/blog/post.html.twig', [
'currPage' => 'posts',
'form' => $form->createView(),
'post' => $post,
]);
}
File Uploader
class FileUploader
{
private $targetDirectory;
public function __construct($targetDirectory)
{
$this->targetDirectory = $targetDirectory;
}
public function upload(UploadedFile $file)
{
$fileName = md5(uniqid()).'.'.$file->guessExtension();
try {
$file->move($this->getTargetDirectory(), $fileName);
} catch (FileException $e) {
// ... handle exception if something happens during file upload
}
return $fileName;
}
public function getTargetDirectory()
{
return $this->targetDirectory;
}
}
Listener ThumbnailUploadListener
class ThumbnailUploadListener
{
private $uploader;
public function __construct(FileUploader $uploader)
{
$this->uploader = $uploader;
}
public function prePersist(LifecycleEventArgs $args)
{
$entity = $args->getEntity();
$this->uploadFile($entity);
}
public function preUpdate(PreUpdateEventArgs $args)
{
$entity = $args->getEntity();
$this->uploadFile($entity);
}
private function uploadFile($entity)
{
// upload only works for Post entities
if (!$entity instanceof Post) {
return;
}
$file = $entity->getThumbnail();
// only upload new files
if ($file instanceof UploadedFile) {
$fileName = $this->uploader->upload($file);
$entity->setThumbnail($fileName);
} elseif ($file instanceof File) {
// prevents the full file path being saved on updates
// as the path is set on the postLoad listener
$entity->setThumbnail($file->getFilename());
}
}
public function postLoad(LifecycleEventArgs $args)
{
$entity = $args->getEntity();
if (!$entity instanceof Post) {
return;
}
if ($fileName = $entity->getThumbnail()) {
$entity->setPost(new File($this->uploader->getTargetDirectory().'/'.$fileName));
}
}
}
services.yaml
App\EventListener\ThumbnailUploadListener:
tags:
- { name: doctrine.event_listener, event: prePersist }
- { name: doctrine.event_listener, event: preUpdate }
- { name: doctrine.event_listener, event: postLoad }
You forgot about mapping the thumbnail field
#ORM\Column(type="string")

Efficient way to extract files and meta data from Amazon S3?

Is there a more efficient way to list files from a bucket in Amazon S3 and also extract the meta data for each of those files? I'm using the AWS PHP SDK.
if ($paths = $s3->get_object_list('my-bucket')) {
foreach($paths AS $path) {
$meta = $s3->get_object_metadata('my-bucket', $path);
echo $path . ' was modified on ' . $meta['LastModified'] . '<br />';
}
}
At the moment I need to run get_object_list() to list all the files and then get_object_metadata() for each file to get its meta data.
If I have 100 files in my bucket, it makes 101 calls to get this data. It would be good if it's possible to do it in 1 call.
E.g:
if ($paths = $s3->get_object_list('my-bucket')) {
foreach($paths AS $path) {
echo $path['FileName'] . ' was modified on ' . $path['LastModified'] . '<br />';
}
}
I know this is a bit old, but I encountered this problem and to solve it I extended the Aws sdk to use the batch functionality for this type of problem. It makes a lot quicker to retrieve custom meta data for lots of files.
This is my code:
/**
* Name: Steves_Amazon_S3
*
* Extends the AmazonS3 class in order to create a function to
* more efficiently retrieve a list of
* files and their custom metadata using the CFBatchRequest function.
*
*
*/
class Steves_Amazon_S3 extends AmazonS3 {
public function get_object_metadata_batch($bucket, $filenames, $opt = null) {
$batch = new CFBatchRequest();
foreach ($filenames as $filename) {
$this->batch($batch)->get_object_headers($bucket, $filename); // Get content-type
}
$response = $this->batch($batch)->send();
// Fail if any requests were unsuccessful
if (!$response->areOK()) {
return false;
}
foreach ($response as $file) {
$temp = array();
$temp['name'] = (string) basename($file->header['_info']['url']);
$temp['etag'] = (string) basename($file->header['etag']);
$temp['size'] = $this->util->size_readable((integer) basename($file->header['content-length']));
$temp['size_raw'] = basename($file->header['content-length']);
$temp['last_modified'] = (string) date("jS M Y H:i:s", strtotime($file->header['last-modified']));
$temp['last_modified_raw'] = strtotime($file->header['last-modified']);
#$temp['creator_id'] = (string) $file->header['x-amz-meta-creator'];
#$temp['client_view'] = (string) $file->header['x-amz-meta-client-view'];
#$temp['user_view'] = (string) $file->header['x-amz-meta-user-view'];
$result[] = $temp;
}
return $result;
}
}
You need to know that list_objects function has limit. It doesn't allows to load more than 1000 objects, even if max-keys option will be set to some large number.
To fix this you need to load data several times:
private function _getBucketObjects($prefix = '', $booOneLevelOny = false)
{
$objects = array();
$lastKey = null;
do {
$args = array();
if (isset($lastKey)) {
$args['marker'] = $lastKey;
}
if (strlen($prefix)) {
$args['prefix'] = $prefix;
}
if($booOneLevelOny) {
$args['delimiter'] = '/';
}
$res = $this->_client->list_objects($this->_bucket, $args);
if (!$res->isOK()) {
return null;
}
foreach ($res->body->Contents as $object) {
$objects[] = $object;
$lastKey = (string)$object->Key;
}
$isTruncated = (string)$res->body->IsTruncated;
unset($res);
} while ($isTruncated == 'true');
return $objects;
}
As result - you'll have a full list of the objects.
What if you have some custom headers?
They will be not returned via list_objects function. In this case this will help:
foreach (array_chunk($arrObjects, 1000) as $object_set) {
$batch = new CFBatchRequest();
foreach ($object_set as $object) {
if(!$this->isFolder((string)$object->Key)) {
$this->_client->batch($batch)->get_object_headers($this->_bucket, $this->preparePath((string)$object->Key));
}
}
$response = $this->_client->batch($batch)->send();
if ($response->areOK()) {
foreach ($response as $arrHeaderInfo) {
$arrHeaders[] = $arrHeaderInfo->header;
}
}
unset($batch, $response);
}
I ended up using the list_objects function which pulled out the LastModified meta I required.
All in one call :)

multidimesional array in yii

sendmail.php
This is just sample of attachment.in which i m confused...
class sendmail()
public $ATTACHMENT = array();
public function MailSend()
{
$message = new YiiMailMessage;
$message->subject = $this->SUBJECT;
$message->from = $this->FROM;
$message->setBody($this->EMAILBODY, 'text/html','utf-8');
$message->addTo($this->EMAILTO);
if (count($this->ATTACHMENT) > 0)
{
foreach ($this->ATTACHMENT as $Attachement_Array)
{
if (isset($Attachement_Array['filepath']) && $Attachement_Array['filename'] && $Attachement_Array['mimetype'] && file_exists($Attachement_Array['filepath']))
{
$message->attach(Swift_Attachment::fromPath($Attachement_Array['filepath']), $Attachement_Array['filename'], $Attachement_Array['mimetype']);
}
}
}
}
}
mycontroller.php
here in
$SendMail->ATTACHMENT = array("what to pass here");
please explain me
i want multiple dimensional array of filepath,mimetype,filename
/** SEND EMAIL USING COMMON EMAIL FUNCTIONALITY */
$SendMail = new SendMail;
$SendMail->DRAFT_ID = $id;
$SendMail->SUBJECT = $modelEmail->subject;
$SendMail->FROM = $modelEmail->email_from;
$SendMail->CMPAIGN_ID = 1;
$SendMail->TAG_ARRAY = array("[FIRSTNAME]"=>$profiledata->name,"[EMAIL]"=>$profiledata->email,"[CONTACT_NO]"=>$profiledata->contact_no,"[ADDRESS]"=>$profiledata->address,"[WEBSITE]"=>$profiledata->website);
$SendMail->EMAILTO = $profiledata->name."<".$profiledata->email.">";
$SendMail->ATTACHMENT = array('filepath'=>Yii::app()->params['var_path']."stationary.html"); /// what to write here... i want multidimension array here
$SendMail->MailSend();
/** SEND EMAIL USING COMMON EMAIL FUNCTIONALITY */
my answer is
$attach1 = array();
$attach2 = array();
$attach1 = array('filepath'=>Yii::app()->params['var_path']."stationary.html",
'filename'=>"stationary.html",
'mimetype'=>"application/html",);
$attach2 = array('filepath'=>Yii::app()->params['var_path']."pregmatch1_old.php",
'filename'=>"pregmatch1_old.php",
'mimetype'=>"application/php",);
$SendMail->ATTACHMENT = array($attach1,$attach2);