Magento2 Export Button (CSv) in custom grid - magento2

How to add CSV export button in Custom grid in magento2. I have created a grid and form. Need to add csv export function in magento2.

create your controller
<?php
namespace Yourpackage\Yourmodule\Controller\Adminhtml\Sample;
class ExportCsv extends \Magento\Backend\App\Action
{
protected $_fileFactory;
protected $_response;
protected $_view;
protected $directory;
protected $converter;
protected $resultPageFactory ;
protected $directory_list;
public function __construct( \Magento\Backend\App\Action\Context $context,
\Magento\Framework\View\Result\PageFactory $resultPageFactory
) {
$this->resultPageFactory = $resultPageFactory;
parent::__construct($context);
}
public function execute()
{
$fileName = 'yourfilename.csv';
$resultPage = $this->resultPageFactory ->create();
$content = $resultPage->getLayout()->getBlock('yourblockname')->getCsv();;
$this->_sendUploadResponse($fileName, $content);
}
protected function _sendUploadResponse($fileName, $content, $contentType='application/octet-stream') {
$this->_response->setHttpResponseCode(200)
->setHeader('Pragma', 'public', true)
->setHeader('Cache-Control', 'must-revalidate, post-check=0, pre-check=0', true)
->setHeader('Content-type', $contentType, true)
->setHeader('Content-Length', strlen($content), true)
->setHeader('Content-Disposition', 'attachment; filename="' . $fileName . '"', true)
->setHeader('Last-Modified', date('r'), true)
->setBody($content)
->sendResponse();
die;
}
}
create your layout xml
yourmodule_yourcontroller_exportcsv
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
<update handle="yourmodule_yourcontroller_grid"/>
</page>

Just by adding this line under listingToolbar opening tag did the trick for me
<listingToolbar name="listing_top">
<exportButton name="export_button"/>

Since I couldn't found complete answers. Here is how you should proceed so you can filter data from admin grid.
Pls remove unwanted lines from execute method. I was in the middle of working on this code when I so this question
First create your UI Component
<listingToolbar name="listing_top">
<exportButton class="Magento\Ui\Component\ExportButton" component="Magento_Ui/js/grid/export" displayArea="dataGridActions">
<settings>
<options>
<option name="cvs" xsi:type="array">
<item name="value" xsi:type="string">csv</item>
<item name="label" xsi:type="string" translate="true">CSV</item>
<item name="url" xsi:type="string">controller/results/export</item>
</option>
<option name="xml" xsi:type="array">
<item name="value" xsi:type="string">xml</item>
<item name="label" xsi:type="string" translate="true">Excel XML</item>
<item name="url" xsi:type="string">skininc/results/export</item>
</option>
</options>
</settings>
</exportButton>
</listingToolbar>
Next create your controller for skininc/results/export
<?php
/**
* #category Magento 2 Module
* #package Theiconnz\Frontendflow
* #author Don Nuwinda
*/
namespace MyModule\Frontendflow\Controller\Adminhtml\Results;
use Magento\Framework\App\Filesystem\DirectoryList;
use Magento\Framework\Filesystem;
use Magento\Framework\Filesystem\Directory\WriteInterface;
use Magento\Ui\Component\MassAction\Filter;
use Magento\Ui\Model\Export\ConvertToCsv;
use Magento\Framework\App\Response\Http\FileFactory;
use MyModule\Frontendflow\Model\ResourceModel\MyData\CollectionFactory;
class Export extends \Magento\Backend\App\Action
{
/**
* #var \Magento\Backend\Model\View\Result\ForwardFactory
*/
protected $resultForwardFactory;
/**
* Massactions filter
*
* #var Filter
*/
protected $filter;
/**
* #var MetadataProvider
*/
protected $metadataProvider;
/**
* #var WriteInterface
*/
protected $directory;
/**
* #var ConvertToCsv
*/
protected $converter;
/**
* #var FileFactory
*/
protected $fileFactory;
public function __construct(
\Magento\Backend\App\Action\Context $context,
\Magento\Backend\Model\View\Result\ForwardFactory $resultForwardFactory,
Filter $filter,
Filesystem $filesystem,
ConvertToCsv $converter,
FileFactory $fileFactory,
\Magento\Ui\Model\Export\MetadataProvider $metadataProvider,
\MyModule\Frontendflow\Model\ResourceModel\MyData $resource,
CollectionFactory $collectionFactory
) {
$this->resources = $resource;
$this->filter = $filter;
$this->_connection = $this->resources->getConnection();
$this->directory = $filesystem->getDirectoryWrite(DirectoryList::VAR_DIR);
$this->metadataProvider = $metadataProvider;
$this->converter = $converter;
$this->fileFactory = $fileFactory;
parent::__construct($context);
$this->resultForwardFactory = $resultForwardFactory;
$this->collectionFactory = $collectionFactory;
}
/**
* export.
*
* #return \Magento\Backend\Model\View\Result\Forward
*/
public function execute()
{
$collection = $this->filter->getCollection($this->collectionFactory->create());
$component = $this->filter->getComponent();
$this->filter->prepareComponent($component);
$dataProvider = $component->getContext()->getDataProvider();
$dataProvider->setLimit(0, false);
$ids = [];
foreach ($collection as $document) {
$ids[] = (int)$document->getId();
}
$searchResult = $component->getContext()->getDataProvider()->getSearchResult();
$fields = $this->metadataProvider->getFields($component);
$options = $this->metadataProvider->getOptions();
$name = md5(microtime());
$file = 'export/'. $component->getName() . $name . '.csv';
$this->directory->create('export');
$stream = $this->directory->openFile($file, 'w+');
$stream->lock();
$stream->writeCsv($this->metadataProvider->getHeaders($component));
foreach ($searchResult->getItems() as $document) {
if( in_array( $document->getId(), $ids ) ) {
$this->metadataProvider->convertDate($document, $component->getName());
$stream->writeCsv($this->metadataProvider->getRowData($document, $fields, $options));
}
}
$stream->unlock();
$stream->close();
return $this->fileFactory->create('export.csv', [
'type' => 'filename',
'value' => $file,
'rm' => true // can delete file after use
], 'var');
}
}

Below Is a Default Class it only Export that Data which are show in your Admin Grid not Export your whole data
FIND in your grid column,
and add only this code in ui contactus_grid.xml or any other.
<exportButton class="Magento\Ui\Component\ExportButton" component="Magento_Ui/js/grid/export" displayArea="dataGridActions">
<settings>
<options>
<option name="csv" xsi:type="array">
<item name="value" xsi:type="string">csv</item>
<item name="label" xsi:type="string" translate="true">CSV</item>
<item name="url" xsi:type="string">mui/export/gridToCsv</item>
</option>
<option name="xml" xsi:type="array">
<item name="value" xsi:type="string">xml</item>
<item name="label" xsi:type="string" translate="true">Excel XML</item>
<item name="url" xsi:type="string">mui/export/gridToXml</item>
</option>
</options>
</settings>
</exportButton>

To Do Export in grid using UI Components in magento2
Under Container tag add following lines in ui listing component xml
<exportButton name="export_button">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="selectProvider" xsi:type="string">vendor_listing.vendor_listing.example_blog_columns.ids</item>
</item>
</argument>
</exportButton>
This will generate csv for loaded collection in grid in magento2

Below Is a Default Class it only Export that Data which are show in your Admin Grid not Export your whole data
<exportButton class="Magento\Ui\Component\ExportButton">
If you want all Data you have create a Custom Export Button that export your all data.
edit in your Ui-Component file where Admin Grid Show
[vendor_name] \ [module_name] \view\adminhtml\ui_component
Add this code to add Button into Admin Grid
Ui-ComponentFIleName.xml
<item name="buttons" xsi:type="array">
<item name="import" xsi:type="array">
<item name="name" xsi:type="string">import</item>
<item name="label" xsi:type="string" translate="true">Import</item>
<item name="class" xsi:type="string">secondary</item>
<item name="url" xsi:type="string">*/*/importdata</item>
<item name="sortOrder" xsi:type="number">20</item>
</item>
<item name="export" xsi:type="array">
<item name="name" xsi:type="string">export</item>
<item name="label" xsi:type="string" translate="true">Export</item>
<item name="class" xsi:type="string">secondary</item>
<item name="url" xsi:type="string">*/*/exportdata</item>
<item name="sortOrder" xsi:type="number">30</item>
</item>
</item>
Now create a File that Export or Import Data. File Name must be same as you define in Ui-Component file
<item name="url" xsi:type="string">*/*/exportdata</item>
<item name="url" xsi:type="string">*/*/importdata</item>
File path must be like this
[vendor_name] \ [module_name] \ Controller \ Adminhtml \ [Controller_name] \ Exportdata.php
Exportdata.php
<?php
namespace [vendor_name]\[module_name]\Controller\Adminhtml\[Controller_name];
use Magento\Framework\App\Filesystem\DirectoryList;
class Exportdata extends \Magento\Backend\App\Action
{
protected $uploaderFactory;
protected $_locationFactory;
public function __construct(
\Magento\Backend\App\Action\Context $context,
\Magento\Framework\App\Response\Http\FileFactory $fileFactory,
\Magento\Framework\Filesystem $filesystem,
\[vendor_name]\[module_name]\Model\locatorFactory $locationFactory // This is returns Collaction of Data
) {
parent::__construct($context);
$this->_fileFactory = $fileFactory;
$this->_locationFactory = $locationFactory;
$this->directory = $filesystem->getDirectoryWrite(DirectoryList::VAR_DIR); // VAR Directory Path
parent::__construct($context);
}
public function execute()
{
$name = date('m-d-Y-H-i-s');
$filepath = 'export/export-data-' .$name. '.csv'; // at Directory path Create a Folder Export and FIle
$this->directory->create('export');
$stream = $this->directory->openFile($filepath, 'w+');
$stream->lock();
//column name dispay in your CSV
$columns = ['Col-1-name','Col-2-name','Col-3-name','Col-4-name','Col-5-name','Col-6-name','Col-7-name','Col-8-name','Col-9-name',];
foreach ($columns as $column)
{
$header[] = $column; //storecolumn in Header array
}
$stream->writeCsv($header);
$location = $this->_locationFactory->create();
$location_collection = $location->getCollection(); // get Collection of Table data
foreach($location_collection as $item){
$itemData = [];
// column name must same as in your Database Table
$itemData[] = $item->getData('col-1-name');
$itemData[] = $item->getData('col-2-name');
$itemData[] = $item->getData('col-3-name');
$itemData[] = $item->getData('col-4-name');
$itemData[] = $item->getData('col-5-name');
$itemData[] = $item->getData('col-6-name');
$itemData[] = $item->getData('col-7-name');
$itemData[] = $item->getData('col-8-name');
$itemData[] = $item->getData('col-9-name');
$stream->writeCsv($itemData);
}
$content = [];
$content['type'] = 'filename'; // must keep filename
$content['value'] = $filepath;
$content['rm'] = '1'; //remove csv from var folder
$csvfilename = 'locator-import-'.$name.'.csv';
return $this->_fileFactory->create($csvfilename, $content, DirectoryList::VAR_DIR);
}
}
Now you can Click on the Export Button and See your .csv file Downloaded below.
I Hope This Helps You.

create your controller
<?php
namespace Yourpackage\Yourmodule\Controller\Adminhtml\Report;
use Magento\Framework\App\Filesystem\DirectoryList;
class ExportCsv extends \Magento\Backend\App\Action {
protected $resultPageFactory;
protected $fileFactory;
/**
*
* #param \Magento\Backend\App\Action\Context $context
* #param \Magento\Framework\View\Result\PageFactory $resultPageFactory
*/
public function __construct(
\Magento\Backend\App\Action\Context $context,
\Magento\Framework\App\Response\Http\FileFactory $fileFactory,
\Magento\Framework\View\Result\PageFactory $resultPageFactory
) {
$this->resultPageFactory = $resultPageFactory;
$this->fileFactory = $fileFactory;
parent::__construct($context);
}
/**
*
* #return type
*/
public function execute() {
$fileName = 'salesreport.csv';
$content = $this->_view->getLayout()->createBlock(
\Ktpl\RepresentativeReport\Block\Adminhtml\Salesdata\Grid::class
)->getCsvFile();
return $this->fileFactory->create($fileName, $content, DirectoryList::VAR_DIR);
}
}

Add CSV export button to your grid block layout (XML):
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
<body>
<referenceBlock name="your.grid.container">
<block class="Yourpackage\Yourmodule\Block\Adminhtml\Sample\Grid" name="sample.grid" as="grid">
<!-- Arguments or blocks -->
<!-- Export Widget -->
<block class="Magento\Backend\Block\Widget\Grid\Export" name="sample.grid.export" as="grid.export">
<arguments>
<argument name="exportTypes" xsi:type="array">
<item name="csv" xsi:type="array">
<item name="urlPath" xsi:type="string">*/*/exportCsv</item>
<item name="label" xsi:type="string" translate="true">CSV</item>
</item>
</argument>
</arguments>
</block>
<!-- Columns block -->
</block>
</referenceBlock>
</body>
</page>
Create your controller:
<?php
namespace Yourpackage\Yourmodule\Controller\Adminhtml\Sample;
use Magento\Framework\App\ResponseInterface;
use Magento\Framework\App\Filesystem\DirectoryList;
class ExportCsv extends \Yourpackage\Yourmodule\Controller\Adminhtml\Sample
{
/**
* Export data grid to CSV format
*
* #return ResponseInterface
*/
public function execute()
{
$this->_view->loadLayout();
$fileName = 'sample_data.csv';
$content = $this->_view->getLayout()->getChildBlock('sample.grid', 'grid.export');
return $this->_fileFactory->create(
$fileName,
$content->getCsvFile($fileName),
DirectoryList::VAR_DIR
);
}
}

Related

Override _prepareSpecificInformation method of \Magento\Payment\Block\Info\Cc class

I want to override _prepareSpecificInformation method of the Magento\Payment\Block\Info\Cc class
This is Core class.
vendor/magento/module-payment/Block/Info/Cc.php
<?php
namespace Magento\Payment\Block\Info;
/**
* Credit card generic payment info
*
* #api
* #since 100.0.2
*/
class Cc extends \Magento\Payment\Block\Info
{
protected function _prepareSpecificInformation($transport = null)
{
if (null !== $this->_paymentSpecificInformation) {
return $this->_paymentSpecificInformation;
}
$transport = parent::_prepareSpecificInformation($transport);
$data = [];
if ($ccType = $this->getCcTypeName()) {
$data[(string)__('Credit Card Type')] = $ccType;
}
if ($this->getInfo()->getCcLast4()) {
$data[(string)__('Credit Card Number')] = sprintf('xxxx-%s', $this->getInfo()->getCcLast4());
}
if (!$this->getIsSecureMode()) {
if ($ccSsIssue = $this->getInfo()->getCcSsIssue()) {
$data[(string)__('Switch/Solo/Maestro Issue Number')] = $ccSsIssue;
}
$year = $this->getInfo()->getCcSsStartYear();
$month = $this->getInfo()->getCcSsStartMonth();
if ($year && $month) {
$data[(string)__('Switch/Solo/Maestro Start Date')] = $this->_formatCardDate($year, $month);
}
}
return $transport->setData(array_merge($data, $transport->getData()));
}
Following is what I have done.
Muk/OrderEmail/etc/di.xml
<?xml version="1.0" encoding="UTF-8"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
<preference for="Magento\Payment\Block\Info\Cc" type="Muk\OrderEmail\Block\Info\Cc"/>
</config>
My custom class
app/code/Muk/OrderEmail/Block/Info/Cc.php
<?php
declare(strict_types=1);
namespace Muk\OrderEmail\Block\Info;
use Magento\Framework\DataObject;
use Magento\Framework\Exception\LocalizedException;
class Cc extends \Magento\Payment\Block\Info\Cc
{
public function __construct
(
\Magento\Framework\View\Element\Template\Context $context,
\Magento\Payment\Model\Config $paymentConfig,
array $data = [])
{
parent::__construct($context, $paymentConfig, $data);
}
/**
* Prepare credit card related payment info
*
* #param DataObject|array $transport
* #return DataObject
* #throws LocalizedException
*/
protected function _prepareSpecificInformation($transport = null)
{
if (null !== $this->_paymentSpecificInformation) {
return $this->_paymentSpecificInformation;
}
$transport = parent::_prepareSpecificInformation($transport);
$data = [];
if ($ccType = $this->getCcTypeName()) {
$data[(string)__('Credit Card Type')] = $ccType;
}
if ($this->getInfo()->getCcLast4()) {
$data[(string)__('Credit Card Number')] = sprintf('xxxx-%s', $this->getInfo()->getCcLast4());
}
// Custom information
if ($ccType = $this->getCcTypeName()) {
$data[(string)__('Name on the Card:')] = $this->getInfo()->getCcOwner();
}
// End Custom information
if (!$this->getIsSecureMode()) {
if ($ccSsIssue = $this->getInfo()->getCcSsIssue()) {
$data[(string)__('Switch/Solo/Maestro Issue Number')] = $ccSsIssue;
}
$year = $this->getInfo()->getCcSsStartYear();
$month = $this->getInfo()->getCcSsStartMonth();
if ($year && $month) {
$data[(string)__('Switch/Solo/Maestro Start Date')] = $this->_formatCardDate($year, $month);
}
}
return $transport->setData(array_merge($data, $transport->getData()));
}
}
module.xml
<?xml version="1.0" encoding="UTF-8"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
<module name="Muk_OrderEmail">
<sequence>
<module name="Magento_Payment"/>
<module name="Magento_Backend"/>
<module name="Magento_Sales"/>
<module name="Magento_Quote"/>
<module name="Magento_Checkout"/>
</sequence>
</module>
</config>
registration.php
<?php
declare(strict_types=1);
\Magento\Framework\Component\ComponentRegistrar::register(
\Magento\Framework\Component\ComponentRegistrar::MODULE,
'Muk_OrderEmail',
__DIR__
);
But this override is not working for me.
It turned out that I was overriding a wrong file. Because the \Magento\Payment\Block\Info\Cc was already overridden by a custom module.
After correcting the preference it worked for me.
<?xml version="1.0" encoding="UTF-8"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
<preference for="VendorName\Paymetric\Block\Xiintercept\Info\Iframe" type="Muk\OrderEmail\Block\Info\Cc"/>
</config>
After investigating in this, we have found solution for this. You can not override _prepareSpecificInformation method directly because this method is overided into another class that name is Magento\Paypal\Block\Payment\Info.
You have to override this class into custom module than you can do whatever changes into function that you want do.
Please refer this content for more info:
https://itcruncher.blogspot.com/2020/11/override-preparespecificinformation.html
If you like please give kund

How to render product list in my custom widget

I am creating my custom module for magento 2.3 and I've faced an issue with widget.
I have created my widget type and when I include it to homepage I have to see product list of products I've selected to render.
I want to use .../magento/vendor/magento/module-catalog/view/frontend/templates/product/list.phtml as a template
The problem is that Widget class has to Extend Magento\Framework\View\Element\Template class, and to render ProductList I have to extend Magento\Catalog\Block\Product\ListProduct.
Maybe someone could give me a piece of advice how to solve this problem.
Here is a part of my code
file my-vendor/my-module/Block/Widget/MyWidget.php
class PromotedWidget extends Template implements BlockInterface
{
public function __construct(
Context $context,
\Magento\Catalog\Model\ResourceModel\Product\CollectionFactory $productCollectionFactory,
array $data = []
)
{
$this->productCollectionFactory = $productCollectionFactory;
parent::__construct($context, $data);
}
public function getLoadedProductCollection()
{
$collection = $this->productCollectionFactory->create();
$collection->addAttributeToSelect('*');
$collection->addAttributeToFilter('necessary_attribute', ['necessary_attribute' => 'attr_value']);
return $collection;
}
public function toHtml()
{
$this->setTemplate('Magento_Catalog::product/list.phtml');
return parent::_toHtml();
}
}
I think it is not good practice to extend Magento\Catalog\Block\Product\ListProduct class. If you want to load custom product collection and want it to work it same as product listing page, then I suggest you to create a plugin for Magento\CatalogSearch\Model\Search\IndexBuilder class in your custom widget as follows:
Namespace\Modulename\etc\di.xml
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
<type name="Magento\CatalogSearch\Model\Search\IndexBuilder">
<plugin name="Namespace_Modulename::custom_collection" type="Namespace\Modulename\Plugin\CatalogSearch\Model\Search\IndexBuilder" />
</type>
</config>
Namespace\Modulename\Plugin\CatalogSearch\Model\Search\IndexBuilder.php
<?php
namespace Namespace\Modulename\Plugin\CatalogSearch\Model\Search;
use Magento\Framework\Search\RequestInterface;
class IndexBuilder {
protected $_request;
protected $_categoryHelper;
public function __construct(
\Magento\Catalog\Model\ResourceModel\Product\CollectionFactory $productCollectionFactory,
\Magento\Catalog\Helper\Category $categoryHelper,
\Magento\Framework\App\Request\Http $request
) {
$this->_productCollectionFactory = $productCollectionFactory;
$this->_categoryHelper = $categoryHelper;
$this->_request = $request;
}
public function aroundBuild($subject, callable $proceed, RequestInterface $request) {
$select = $proceed($request);
$collection = $this->_productCollectionFactory->create();
$collection->addAttributeToSelect('*');
$collection->addAttributeToFilter('necessary_attribute', ['necessary_attribute' => 'attr_value']);
$getProductAllIds = $collection->getAllIds();
$productUniqueIds = array_unique($getProductAllIds);
$select->where("search_index.entity_id IN ('" . join("','", $productUniqueIds) . "')");
return $select;
}
}
In addition, you can refer this link for more details.

biml and script component and external file

I have an external file that is a csharp file. The code searches through the public folders in Outlook calendar and return a calendar data based on root folder name and calendar name. The code includes for example:
#region Namespaces
using System;
using System.Data;
using Microsoft.SqlServer.Dts.Pipeline.Wrapper;
using Microsoft.SqlServer.Dts.Runtime.Wrapper;
using Microsoft.SqlServer.Dts.Runtime;
using System.Net;
using Microsoft.Exchange.WebServices;
using Microsoft.Exchange.WebServices.Data;
using System.Linq;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Data.Common;
#endregion
/// <summary>
/// This is the class to which to add your code. Do not change the name, attributes, or parent
/// of this class.
/// </summary>
[Microsoft.SqlServer.Dts.Pipeline.SSISScriptComponentEntryPointAttribute]
public class ScriptMain : UserComponent
{
/// <summary>
/// This method is called once, before rows begin to be processed in the data flow.
///
/// You can remove this method if you don't need to do anything here.
/// </summary>
public override void PreExecute()
{
base.PreExecute();
/*
* Add your code here
*/
}
/// <summary>
/// This method is called after all the rows have passed through this component.
///
/// You can delete this method if you don't need to do anything here.
/// </summary>
public override void PostExecute()
{
base.PostExecute();
/*
* Add your code here
*/
}
byte[] GetBytes(string str)
{
byte[] bytes = new byte[str.Length * sizeof(char)];
System.Buffer.BlockCopy(str.ToCharArray(), 0, bytes, 0, bytes.Length);
return bytes;
}
public override void CreateNewOutputRows()
{
bool pbCancel = false;
Uri OutlookWebAccessUri = new Uri(#"https://outlook.office365.com/EWS/Exchange.asmx");
try
{
#region create service binding
// Create the service binding.
ExchangeService esb = new ExchangeService(ExchangeVersion.Exchange2010_SP2);
esb.Credentials = new WebCredentials("username", "password");// exchangeAccessAccount;
esb.Url = OutlookWebAccessUri;
#endregion
#region create CalendarView
// this week --SET YOUR VIEW ON WHAT TIMELINE YOU WANT TO RETRIEVE YOUR CALENDAR DATA --
//if no calendar data - start here
DateTime startDate = new DateTime(Variables.StartYear, Variables.StartMonth, 1);
DateTime endDate = new DateTime(Variables.StartYear, Variables.StartMonth, DateTime.DaysInMonth(Variables.StartYear, Variables.StartMonth));
//there is no calendar data, get all data
startDate = new DateTime(Variables.StartYear, Variables.StartMonth, 1);
endDate = new DateTime(Variables.StartYear, Variables.StartMonth, DateTime.DaysInMonth(Variables.StartYear, Variables.StartMonth));
//find the root folder
Folder rootfolder = Folder.Bind(esb, WellKnownFolderName.PublicFoldersRoot, new PropertySet());
FolderView view = new FolderView(100);
SearchFilter search = new SearchFilter.ContainsSubstring(FolderSchema.DisplayName, Variables.RootFolder); //Enter your rootfolder here that you are looking for in the public folder domain
FindFoldersResults myFolderResults = rootfolder.FindFolders(search, view);
foreach (Folder myFolder in myFolderResults.Folders)
{
foreach (CalendarFolder PublicItem in myFolder.FindFolders(view))
{
string myname = PublicItem.DisplayName;
if (myname == Variables.CalendarName) //Enter your calendar name here within the sub folder
{
CalendarFolder PublicCalendar = CalendarFolder.Bind(esb, PublicItem.Id, new PropertySet());
CalendarView cPublicView = new CalendarView(startDate, endDate, 512);
cPublicView.PropertySet = new PropertySet(AppointmentSchema.Subject, AppointmentSchema.Start, AppointmentSchema.End, AppointmentSchema.Id);
FindItemsResults<Appointment> findPublicItemResponse = PublicCalendar.FindAppointments(cPublicView);
if (findPublicItemResponse == null)
{
return;
}
//get additional properties for each item returned by view, do this as view cannot return a lot of useful stuff like attendees
ServiceResponseCollection<ServiceResponse> addPublicProperties =
esb.LoadPropertiesForItems(from Item item in findPublicItemResponse select item,
new PropertySet(
BasePropertySet.IdOnly,
AppointmentSchema.Resources,
AppointmentSchema.RequiredAttendees,
AppointmentSchema.OptionalAttendees,
AppointmentSchema.Subject,
AppointmentSchema.Start,
AppointmentSchema.End,
AppointmentSchema.IsCancelled,
AppointmentSchema.Location,
AppointmentSchema.Body,
AppointmentSchema.Categories
));
List<Appointment> additionalProperties = new List<Appointment>(addPublicProperties.Count);
if (addPublicProperties != null)
{
foreach (ServiceResponse currentResponce in addPublicProperties)
{
additionalProperties.Add(((Appointment)((GetItemResponse)currentResponce).Item));
}
}
Appointment currentAppointmentAddProps = null;
foreach (Appointment currentAppointment in findPublicItemResponse)
{
#region find additional properties for current Appointment
currentAppointmentAddProps = additionalProperties.Find(delegate (Appointment arg)
{ return arg.Id == currentAppointment.Id; });
#endregion
OutputRecordSetBuffer.AddRow();
if (currentAppointmentAddProps.RequiredAttendees.Count > 0)
{
foreach (var attendee in currentAppointmentAddProps.RequiredAttendees) //what to do if no attendee?
{
OutputRecordSetBuffer.Attendee = attendee.Name;
OutputRecordSetBuffer.ActualStartDate = currentAppointmentAddProps.Start;
OutputRecordSetBuffer.ActualEndDate = currentAppointmentAddProps.End;
OutputRecordSetBuffer.Subject = currentAppointment.Subject;
OutputRecordSetBuffer.Location = currentAppointment.Location;
OutputRecordSetBuffer.Detail.AddBlobData(GetBytes(currentAppointment.Body.Text));
OutputRecordSetBuffer.DetailType = currentAppointment.Body.BodyType.ToString();
OutputRecordSetBuffer.CalendarName = Variables.CalendarName;
//Currently no way to return category color
}
}
else
{
OutputRecordSetBuffer.ActualStartDate = currentAppointmentAddProps.Start;
OutputRecordSetBuffer.ActualEndDate = currentAppointmentAddProps.End;
OutputRecordSetBuffer.Subject = currentAppointment.Subject;
OutputRecordSetBuffer.Location = currentAppointment.Location;
if (currentAppointment.Body.Text != null)
{
OutputRecordSetBuffer.Detail.AddBlobData(GetBytes(currentAppointment.Body.Text));
}
OutputRecordSetBuffer.DetailType = currentAppointment.Body.BodyType.ToString();
OutputRecordSetBuffer.CalendarName = Variables.CalendarName;
}
}
}
}
}
#endregion
}
catch (Exception e)
{
ComponentMetaData.FireError(1, "Get empty calendar data for insert has error", e.Message, "", 0, out pbCancel);
}
}
}
My biml xml gets broken due to the (I think) types in the list. The ServiceResponseCollection and List. Biml reads the tags <> as part of biml and then don't read the rest of the file as C#. How do I reference a C# file as I'd like to keep things neat and not having to post a whole lot of code into the Scriptprojects but keep it as a separate file as below.
<ScriptProjects>
<ScriptComponentProject Name="GetData">
<AssemblyReferences>
<AssemblyReference AssemblyPath="Microsoft.SqlServer.DTSPipelineWrap" />
<AssemblyReference AssemblyPath="Microsoft.SqlServer.DTSRuntimeWrap" />
<AssemblyReference AssemblyPath="Microsoft.SqlServer.PipelineHost" />
<AssemblyReference AssemblyPath="Microsoft.SqlServer.TxScript" />
<AssemblyReference AssemblyPath="System.dll" />
<AssemblyReference AssemblyPath="System.AddIn.dll" />
<AssemblyReference AssemblyPath="System.Data.dll" />
<AssemblyReference AssemblyPath="System.Xml.dll" />
</AssemblyReferences>
<ReadOnlyVariables>
<Variable VariableName="CalendarName" DataType="String" Namespace="User" ></Variable>
<Variable VariableName="RootFolder" DataType="String" Namespace="User" ></Variable>
<Variable VariableName="StartMonth" DataType="Int32" Namespace="User" ></Variable>
<Variable VariableName="StartYear" DataType="Int32" Namespace="User" ></Variable>
<Variable VariableName="EndMonth" DataType="Int32" Namespace="User" ></Variable>
<Variable VariableName="EndYear" DataType="Int32" Namespace="User" ></Variable>
</ReadOnlyVariables>
<OutputBuffers>
<OutputBuffer Name="CalendarOutput" IsSynchronous="false">
<Columns>
<Column Name="Detail" DataType="String" ></Column>
<Column Name="Status" DataType="String" Length="50" ></Column>
<Column Name="ActualStartDate" DataType="Date" ></Column>
<Column Name="Attendee" DataType="String" Length="50" ></Column>
<Column Name="Location" DataType="String" Length="500"></Column>
<Column Name="Subject" DataType="String" Length="255" ></Column>
<Column Name="ActualEndDate" DataType="Date" ></Column>
<Column Name="Category" DataType="String" Length="50"></Column>
<Column Name="DetailType" DataType="String" Length="50"></Column>
<Column Name="CalendarName" DataType="String" Length="50"></Column>
</Columns>
</OutputBuffer>
</OutputBuffers>
<Files >
<File Path="Properties\AssemblyInfo.cs">
using System.Reflection;
using System.Runtime.CompilerServices;
[assembly: AssemblyTitle("GetData")]
[assembly: AssemblyDescription("Get calendar data from Exchange")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("HCC DataServices")]
[assembly: AssemblyProduct("GetData")]
[assembly: AssemblyCopyright("Copyright # 2017")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
[assembly: AssemblyVersion("1.0.*")]
</File>
<ExternalFile Path="main.cs" ExternalPath="ExchangeCalendarScript.cs"/>
</Files>
</ScriptComponentProject>
</ScriptProjects>
<Biml xmlns="http://schemas.varigence.com/biml.xsd">
<Packages>
<Package Name="<#=PackageName#>" ProtectionLevel="EncryptSensitiveWithUserKey">
<Tasks>
<Dataflow Name="Get Calendar data">
<Transformations>
<ScriptComponentSource Name="Get calendar data">
<ScriptComponentProjectReference ScriptComponentProjectName="GetData"></ScriptComponentProjectReference>
</ScriptComponentSource>
</Transformations>
</Dataflow>
</Tasks>
</Package>
</Packages>
</Biml>
How do I fix this?

Query filter object only working once - extbase 6.2

In my extbase repository I've built a similar query function as described in this tutorial: this and it was using a filter object, $demand.
So I created a class for my filter as well in order to work with an object in fluid.
It works, but only once - I can change something, click on "Filter" and it works.
But if I change something again and click on "Filter" it jumps back to the previous values and nothing changes.
I feel like this might have something to do with caching, but I'm not sure.
When I debug the filter object it doesn't show my the debug code after the second click on "Filter" because that's what it does when nothing has changed I noticed.
What can I do to change my filter settings as many times as I want?
My filter class:
class AppointmentFilter extends \TYPO3\CMS\Extbase\DomainObject\AbstractEntity {
/**
* future
*
* #var int
*/
protected $future = 1;
/**
* booked
*
* #var int
*/
protected $booked = 2;
/**
* student
*
* #var \vendor\extension\Domain\Model\Student
*/
protected $student = NULL;
/**
* expertise
*
* #var \vendor\extension\Domain\Model\Expertise
*/
protected $expertise = NULL;
function getFuture() {
return $this->future;
}
function getBooked() {
return $this->booked;
}
function getStudent() {
return $this->student;
}
function getExpertise() {
return $this->expertise;
}
function setFuture($future) {
$this->future = $future;
}
function setBooked($booked = NULL) {
$this->booked = $booked;
}
function setStudent(\vendor\extension\Domain\Model\Student $student = NULL) {
$this->student = $student;
}
function setExpertise(\vendor\extension\Domain\Model\Expertise $expertise = NULL) {
$this->expertise = $expertise;
}
}
And the corresponding fluid form:
<f:form action="list" name="appointmentFilter"
class="filter-form"
object="{appointmentFilter}"
arguments="{students:students,expertises:expertises}">
Termine:
<label>
<f:form.radio property="future" value="1"/> Bevorstehende
<f:form.radio property="future" value="0"/> Vergangene
</label>
<label>
<f:form.radio property="booked" value="2"/> Alle
<f:form.radio property="booked" value="1"/> Gebuchte
<f:form.radio property="booked" value="0"/> Freie
</label>
<label>
Studenten:
<f:form.select property="student" options="{students}" optionLabelField="fullName" prependOptionLabel="Alle Studenten" class="filterSelect" />
</label>
<label>
Expertise:
<f:form.select property="expertise" options="{expertises}" optionLabelField="name" prependOptionLabel="Alle" class="filterSelect" />
</label>
<f:form.submit value="Filter anwenden" class="rm-with-js" />
</f:form>
According to your description this is a caching problem. You can set your action to be noncachable by adding it in the second array in the plugin configuration in the localconf.php. Example:
\TYPO3\CMS\Extbase\Utility\ExtensionUtility::configurePlugin(
$_EXTKEY,
'Pi1',
array(
'Controller' => 'list, cachedAction',
),
// this is the array for noncachable actions
array(
'Controller' => 'list',
)
);

Symfony 2 GenemuFormBundle how to create a jQuery Select2 with Ajax

I'm trying to add a Select2 input using the GenemuFormBundle as described in the "Use jQuery Select2 with Ajax" doc. Adding a jQuery Select2 Field following the jQuery Select2 Field documentation works just fine.
But the documentation on how to implement an Ajax-loading Select2 form is very inconclusive. If I unterstand the doc correctly, it aims to create the same as mentioned in the Select2 documentation. This is exactly the thing I'd like to create. I added hidden field as well as the required JavaScript, but the only thing that I get is a Variable "id" does not exist in xBundle:x:new.html.twig at line x.
Form builder (taken directly form the mentioned doc):
...
->add('field_name', 'genemu_jqueryselect2_hidden', array(
'configs' => array(
'multiple' => true // Wether or not multiple values are allowed (default to false)
)
))
->add('field_name', 'genemu_jqueryselect2_entity', array(
'class' => 'xBundle:Entity',
'property' => 'foo',
))
View (also taken directly form the doc):
{% block stylesheets %}
{{ form_stylesheet(form) }}
{% endblock %}
{% block javascript %}
{{ form_javascript(form) }}
{% endblock %}
{% block genemu_jqueryselect2_javascript %}
<script type="text/javascript">
$field = $('#{{ id }}');
var $configs = {{ configs|json_encode|raw }};
// custom configs
$configs = $.extend($configs, {
query: function (query) {
var data = {results: []}, i, j, s;
for (i = 1; i < 5; i++) {
s = "";
for (j = 0; j < i; j++) {s = s + query.term;}
data.results.push({id: query.term + i, text: s});
}
query.callback(data);
}
});
// end of custom configs
$field.select2($configs);
</script>
{% endblock %}
I just struggled with this exact issue and thought I would throw in my own findings for anyone that happens to stumble across this. I did find a solution, but I would probably still recommend just going with the ZenStruckFormBundle recommended by Doug because it seems to actually be designed to work as a solution for a select2 field type that loads via ajax and relates back to an entity.
The real reason this doesn't work is that the the genemu_jqueryselect2_* types for the GenemuFormBundle do not implement a data transformer that will work with an entity when you need an ajax-loading select2 field. It doesn't seem like it was ever designed to work this way. When you use the genemu_jqueryselect2_hidden type and have the “multiple’ config option set to true, then it adds an ArrayToStringTransformer. This will not work with an entity.
To fix this you need to make a new form field type and define that its parent is genemu_jqueryselect2_hidden then make a few customizations. It would look something like this…
namespace Acme\DemoBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Acme\DemoBundle\Form\DataTransformer\EntityCollectionToIdTransformer;
use Doctrine\Common\Persistence\ObjectManager;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class EntityCollectionSelectorType extends AbstractType
{
/**
* #var ObjectManager
*/
protected $om;
/**
* #param ObjectManager $om
*/
public function __construct(ObjectManager $om)
{
$this->om = $om;
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$transformer = new EntityCollectionToIdTransformer($this->om, $options['configs']['entity']);
$builder->resetViewTransformers();
$builder->addModelTransformer($transformer);
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'invalid_message' => 'The selected entity does not exist',
'required' => true,
'auto_initialize' => false,
'configs' => array('multiple' => true),
'error_bubbling' => false,
));
}
public function getParent()
{
return 'genemu_jqueryselect2_hidden';
}
public function getName()
{
return 'entity_collection_selector';
}
}
Then you will also need to add the new data transformer used in the above form field type, so that it can translate the values between the form field and the entity…
namespace Acme\DemoBundle\Form\DataTransformer;
use Symfony\Component\Form\DataTransformerInterface;
use Symfony\Component\Form\Exception\TransformationFailedException;
use Doctrine\ORM\PersistentCollection;
use Doctrine\Common\Persistence\ObjectManager;
use Doctrine\Common\Collections\ArrayCollection;
class EntityCollectionToIdTransformer implements DataTransformerInterface
{
/**
* #var ObjectManager
*/
private $om;
/**
* #var string The Doctrine entity type to use
*/
private $entityType;
/**
* #param ObjectManager $om
*/
public function __construct(ObjectManager $om, $entityType)
{
$this->om = $om;
$this->entityType = $entityType;
}
/**
* Transforms a collection of entities to a comma separated string
*
* #param ArrayCollection $entities
* #return string
*/
public function transform($entities)
{
if (null == $entities || empty($entities)) {
return '';
}
$results = '';
foreach ($entities as $entity) {
$results .= $entity->getId() . ',';
}
$results = trim($results, ' ,');
return $results;
}
/**
* Transforms a string of comma separated IDs to a PersistentCollection for Doctrine
*
* #param string $values
* #return PersistentCollection|ArrayCollection
* #throws TransformationFailedException if entity is not found.
*/
public function reverseTransform($values)
{
if (!$values) {
return new ArrayCollection();
}
$values = explode(',', $values);
$collection = array();
foreach ($values as $id) {
$item = $this->om->getRepository($this->entityType)->findOneById($id);
if (!is_null($item)) {
$collection[] = $item;
}
else {
throw new TransformationFailedException(sprintf(
'An entity with ID "%s" does not exist!',
$value
));
}
}
return new PersistentCollection($this->om, $this->entityType, new ArrayCollection($collection));
}
}
Now make sure you define your new field type in your config for your services…
<?xml version="1.0" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<parameters>
...
<parameter key="acme_demo.form.type.entity_collection_selector.class">Acme\DemoBundle\Form\Type\EntityCollectionSelectorType</parameter>
...
</parameters>
<services>
...
<service id="acme_demo.form.type.entity_collection_selector"
class="%acme_demo.form.type.entity_collection_selector.class%">
<argument type="service" id="doctrine.orm.default_entity_manager" />
<tag name="form.type" alias="entity_collection_selector" />
</service>
...
</services>
</container>
Now you can use it as such…
$builder->add('customers', 'entity_collection_selector', array(
'configs' => array('entity' => 'AcmeDemoBundle:Customer')
));