Related
My files so far are:
system.xml
<field id="generate_csv" translate="label" type="button" sortOrder="2" showInDefault="1" showInWebsite="1" showInStore="1">
<label>Generate Categories CSV</label>
<frontend_model>Vendor\Module\Block\Adminhtml\System\Config\Button</frontend_model>
</field>
button.php
<?php
namespace [Vendor]\[Module]\Block\Adminhtml\System\Config;
use Magento\Config\Block\System\Config\Form\Field;
use Magento\Backend\Block\Template\Context;
use Magento\Framework\Data\Form\Element\AbstractElement;
class Button extends Field
{
protected $_template = 'Vendor_Module::system/config/button.phtml';
public function __construct(
Context $context,
array $data = []
){
parent::__construct($context, $data);
}
public function render(AbstractElement $element)
{
$element->unsScope()->unsCanUseWebsiteValue()->unsCanUseDefaultValue();
return parent::render($element);
}
protected function _getElementHtml(AbstractElement $element)
{
return $this->_toHtml();
}
public function getAjaxUrl()
{
return $this->getUrl('[router]/Export/CategoryExport');
}
public function getButtonHtml()
{
$button = $this->getLayout()->createBlock(
'Magento\Backend\Block\Widget\Button'
)->setData(
[
'id' => 'generate_csv',
'label' => __('Generate CSV')
]
);
return $button->toHtml();
}
}
button.phtml
<script>
require([
'jquery',
'prototype'
], function ($) {
$('#generate_csv').click(function () {
var params = {};
new Ajax.Request('<?php echo $block->getAjaxUrl() ?>', {
parameters: params,
loaderArea: false,
asynchronous: true,
onCreate: function () {
$('#custom_button_response_message').text('');
},
onSuccess: function (transport) {
var resultText = '';
if (transport.status > 200) {
resultText = transport.statusText;
} else {
var response = JSON.parse(transport.responseText);
resultText = response.message
}
$('#generate_csv_response_message').text(resultText);
}
});
});
});
</script>
<?php echo $block->getButtonHtml(); ?>
<p>
<span id="generate_csv_response_message"></span>
</p>
controller
<?php
namespace [Vendor]\[Module]\Controller\Adminhtml\Export;
use Magento\Framework\App\Action\Action;
use Magento\Framework\App\Action\Context;
use Magento\Framework\Controller\Result\Json;
use Magento\Framework\Controller\Result\JsonFactory;
use Magento\Framework\Filesystem;
use Magento\Framework\App\Filesystem\DirectoryList;
use Magento\Catalog\Model\ResourceModel\Category\CollectionFactory;
use Magento\Catalog\Model\CategoryFactory;
class CategoryExport extends Action
{
protected $resultJsonFactory;
/**
* #param Context $context
* #param JsonFactory $resultJsonFactory
*/
public function __construct(
Context $context,
JsonFactory $resultJsonFactory,
Filesystem $filesystem,
DirectoryList $directoryList,
CollectionFactory $categoryCollectionFactory,
CategoryFactory $categoryFactory
){
$this->resultJsonFactory = $resultJsonFactory;
$this->filesystem = $filesystem;
$this->directory = $filesystem->getDirectoryWrite(DirectoryList::VAR_DIR);
$this->categoryCollectionFactory = $categoryCollectionFactory;
$this->categoryFactory = $categoryFactory;
parent::__construct($context);
}
/**
* #return Json
*/
public function execute()
{
$message = 'Success';
$data[] = [
'id' => __('Entity ID'),
'name' => __('Name'),
'level' => __('Level')
];
$categoryCollection = $this->categoryCollectionFactory->create();
$categoryCollection->addAttributeToSelect('*');
$filepath = 'export/customerlist.csv';
$this->directory->create('export');
$stream = $this->directory->openFile($filepath, 'w+');
$stream->lock();
$header = ['Id', 'Name', 'Level'];
$stream->writeCsv($header);
//$categoryArray = array();
foreach ($categoryCollection as $category) {
$data = [];
$data[] = $category->getId();
$data[] = $category->getName();
$data[] = $category->getLevel();
$stream->writeCsv($data);
}
/** #var Json $result */
$result = $this->resultJsonFactory->create();
return $result->setData(['message' => $message]);
}
}
This creates a csv file in var/export directory but I cannot download it
Can anyone help explain how to modify the code so that it will automatically create and down the csv.
I have tried following various examples on the web but none of them have worked.
Please help me out
I have this public function below but I will have to write a similar code in about 60 other places, I don't want to repeat myself rather, I want to be able to write a single function such that all I need change is 'Dailysaving::', and '00:00:00' each time I use the function. And please note that I will be creating several other schedule commands which this function should work for. How do I go about this please and where am I supposed to place the function I write And how do I access different models from the function. Thanks in advance for anyone that will help me out.
public function handle()
{
$users= Dailysaving::where('debit_time', '00:00:00')->where('status', 'Active')->get();
//die($users);
foreach ($users as $user) {
$email = $user->email;
$amount = $user->amount_daily * 100;
//Let's know where the payment is on the db
$user_id = $user->user_id;
$savings_id = $user->id;
$auth_code= $user->authorization_code;
//
$metastring = '{"custom_fields":[{"user_id":'. $user_id. '}, {"action": "activatedaily"},{"savings_id": '.$savings_id.'},{"savingstype": "dailysavings"}]}';
$curl = curl_init();
curl_setopt_array($curl, array(
CURLOPT_URL => "https://api.paystack.co/transaction/charge_authorization",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'amount'=>$amount,
'email'=>$email,
'authorization_code' =>$auth_code,
'metadata' => $metastring,
]),
CURLOPT_HTTPHEADER => [
"authorization:Bearer sk_test_656456h454545",
"content-type: application/json",
"cache-control: no-cache"
],
));
$response = curl_exec($curl);
$err = curl_error($curl);
if($err){
$failedtranx = new Failedtransaction;
$failedtranx->error = $err;
$failedtranx->save();
}
if($response) {
$tranx = json_decode($response);
if (!$tranx->status) {
// there was an error contacting the Paystack API
//register in failed transaction table
$failedtranx = new Failedtransaction;
$failedtranx->error = $err;
$failedtranx->save();
}
if ('success' == $tranx->data->status) {
$auth_code = $tranx->data->authorization->authorization_code;
$amount = ($tranx->data->amount) / 100;
$last_transaction = $tranx->data->transaction_date;
$payment_ref = $tranx->data->reference;
$record = new Transactionrecord;
$record->payment_ref = $payment_ref;
$record->save();
//saving complete
//die('saved');
$item = Dailysaving::find($savings_id);
$total_deposit = $item->total_deposit + $amount;
$item->total_deposit = $total_deposit;
$item->last_transaction_date = date('Y-m-d H:i:s');
$target = $item->day_target;
if ($target == $total_deposit) {
$item->status = 'Completed';
}
$item->save();
}
echo 'done';
}
else{
echo 'failed';
}
}
}
As I understand you are trying to make custom helper functions.
So you need create helpers.php with your functions and follow instructions in the below answer: https://stackoverflow.com/a/28290359/5407558
How can i upload csv file and save data in to my mysql database.
according to id.in cakephp 3
i am unable to do that. can any one help me.
my controller
public function import() {
if(isset($_POST["submit"])){
if($_FILES['file']['csv']){
$filename = explode('.', $_FILES['file']['csv']);
debug($filename);
if($filename[1]=='csv'){
$handle = fopen($_FILES['file']['csv'], "r");
while ($data = fgetcsv($handle)){
$item1 = $data[0];
// $item2 = $data[1];
// $item3 = $data[2];
// $item4 = $data[3];
$Applicants = $this->Applicants->patchEntity($Applicants, $item1);
$this->Applicants->save($Applicants);
}
fclose($handle);
}
}
}
$this->render(FALSE);
}
my view:
<div class="col-md-8">
<?= $this->Form->create('Applicants',['type' => 'file','url' => ['controller'=>'Applicants','action' => 'import'],'class'=>'form-inline','role'=>'form',]) ?>
<div class="form-group">
<label class="sr-only" for="csv"> CSV </label>
<?php echo $this->Form->input('csv', ['type'=>'file','class' => 'form-control', 'label' => false, 'placeholder' => 'csv upload',]); ?>
</div>
<button type="submit" class="btn btn-default"> Upload </button>
<?= $this->Form->end() ?>
</div>
Your question is a bit unclear what do you want to do in the controller do you want to update the existing records or save new data. If you want to update then only you need to use patchEntity.
The patchEntity should have a database entity fetched where in you can change or update the data as per your need, so in case if your first column contains the id of the Applications table then below code can work and in $data you can write whatever fields you want to update or add
So you can use the below code block instead
public function import() {
if(isset($_POST["submit"])){
if($_FILES['file']['csv']){
$filename = explode('.', $_FILES['file']['csv']);
debug($filename);
if($filename[1]=='csv'){
$handle = fopen($_FILES['file']['csv'], "r");
while ($data = fgetcsv($handle)){
$item1 = $data[0];
$data = array(
'fieldName' => $item1
);
// $item2 = $data[1];
// $item3 = $data[2];
// $item4 = $data[3];
$Applicant = $this->Applicants->newEntity($data);
$this->Applicants->save($Applicant);
}
fclose($handle);
}
}
}
$this->render(FALSE);
}
If you have more specific code/requirement then please share, so that I can help you out accordingly.
Here is my Solution to upload csv file and save database
public function import($id = NULL) {
$data = $this->request->data['csv'];
$file = $data['tmp_name'];
$handle = fopen($file, "r");
while (($row = fgetcsv($handle, 1000, ",")) !== FALSE) {
if($row[0] == 'id') {
continue;
}
$Applicants = $this->Applicants->get($row[0]);
$columns = [
'written_mark' => $row[1],
'written_comments' => $row[2],
'viva_mark' => $row[3],
'viva_comments' => $row[4]
];
$Applicant = $this->Applicants->patchEntity($Applicants, $columns);
$this->Applicants->save($Applicant);
}
fclose($handle);
$this->set('title','Upload Student CSV File Input Number and others');
return $this->redirect($this->referer());
}
I have requirement in which user need to edit/update the s3 file metadata that are uploaded in the previous sessions. I have implemented Initial File List, but I need to make file metadata (filename, caption - new field in my case) editable in the display list. Can it be accomplished?
I see edit files feature, but that is limited to before file gets uploaded. Looks like my requirement not easily supported out of the box FU. I have followed below approach.
In template I have a button with text 'Update Caption', which has onclick="captionUpdate()",that will set JS variable(isCaptionUpdate) to true.
Caption update will trigger DeleteFile endpoint except that it will set param data for caption value from text field that is defines in template
In server side code, the process checks for Caption param, and then call function updateObjectWithCaption()
All of the above works seamlessly with following challenges.Please see the screenshot.
When user click on 'Update Caption', it follows DELETE steps and since I am passing Caption param, it updates S3 file. But problem is in the file list, I will see a status text called 'Deleting.....' appears for brief time. How can I change status to 'Updating Caption....' or something similar
Another issue with #1 is that as soon as S3 update, the File in file list gets removed. UI part still thinks that it is DELETE step for some reason, how can I say to UI that it not really delete?
As you can see in the deleteFile section of JS, caption is taken from document.getElementById('caption').value; that means, even if I click 'Update Caption' of 2nd or 3rd or 4th files, it is taking first occurrence of Caption element. How can I get the caption of the specific file ?
Last but not least, how can I show 'Update Caption' button only for previously uploaded file. I do not want show this button on fresh upload.
Sorry for too many question. I could not separate these question as they are all related to S3 file metadata update topic.
Template:
<div class="qq-uploader-selector qq-uploader" qq-drop-area-text="Drop files here">
<div class="qq-total-progress-bar-container-selector qq-total-progress-bar-container">
<div role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" class="qq-total-progress-bar-selector qq-progress-bar qq-total-progress-bar"></div>
</div>
<div class="qq-upload-drop-area-selector qq-upload-drop-area" qq-hide-dropzone>
<span class="qq-upload-drop-area-text-selector"></span>
</div>
<div class="buttons">
<div class="qq-upload-button-selector qq-upload-button">
<div>Select files</div>
</div>
<button type="button" id="trigger-upload-section1" class="btn btn-primary">
<i class="icon-upload icon-white"></i> Upload
</button>
</div>
<span class="qq-drop-processing-selector qq-drop-processing">
<span>Processing dropped files...</span>
<span class="qq-drop-processing-spinner-selector qq-drop-processing-spinner"></span>
</span>
<ul class="qq-upload-list-selector qq-upload-list" aria-live="polite" aria-relevant="additions removals">
<li>
<div class="qq-progress-bar-container-selector">
<div role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" class="qq-progress-bar-selector qq-progress-bar"></div>
</div>
<span class="qq-upload-spinner-selector qq-upload-spinner"></span>
<img class="qq-thumbnail-selector" qq-max-size="100" qq-server-scale>
<span class="qq-upload-file-selector qq-upload-file"></span>
<span class="qq-edit-filename-icon-selector qq-edit-filename-icon qq-editable" aria-label="Edit filename"></span>
<input class="qq-edit-filename-selector qq-edit-filename" tabindex="0" type="text">
<span class="qq-upload-caption-selector qq-upload-caption"></span>
<span class="qq-edit-caption-icon-selector qq-edit-caption-icon qq-editable" aria-label="Edit caption"></span>
<input class="qq-edit-caption-selector qq-edit-caption qq-editing" placeholder="Caption here ..." tabindex="0" type="text" id="caption">
<span class="qq-upload-size-selector qq-upload-size"></span>
<button type="button" class="qq-btn qq-upload-cancel-selector qq-upload-cancel">Cancel</button>
<button type="button" class="qq-btn qq-upload-retry-selector qq-upload-retry">Retry</button>
<button type="button" class="qq-btn qq-upload-delete-selector qq-upload-delete">Delete</button>
<button type="button" class="qq-btn qq-upload-delete-selector qq-upload-delete" onclick="captionUpdate();">Update Caption</button>
<span role="status" class="qq-upload-status-text-selector qq-upload-status-text"></span>
</li>
</ul>
<dialog class="qq-alert-dialog-selector">
<div class="qq-dialog-message-selector"></div>
<div class="qq-dialog-buttons">
<button type="button" class="qq-cancel-button-selector">Close</button>
</div>
</dialog>
<dialog class="qq-confirm-dialog-selector">
<div class="qq-dialog-message-selector"></div>
<div class="qq-dialog-buttons">
<button type="button" class="qq-cancel-button-selector">No</button>
<button type="button" class="qq-ok-button-selector">Yes</button>
</div>
</dialog>
<dialog class="qq-prompt-dialog-selector">
<div class="qq-dialog-message-selector"></div>
<input type="text">
<div class="qq-dialog-buttons">
<button type="button" class="qq-cancel-button-selector">Cancel</button>
<button type="button" class="qq-ok-button-selector">Ok</button>
</div>
</dialog>
</div>
JS
var isCaptionUpdate = false;
function captionUpdate(){
isCaptionUpdate = true;
};
var manualUploaderSection1 = new qq.s3.FineUploader({
element: document.getElementById('fine-uploader-manual-trigger-section1'),
template: 'qq-template-manual-trigger-section1',
autoUpload: false,
debug: true,
request: {
endpoint: "http://xx_my_bucket_xx.s3.amazonaws.com",
accessKey: "AKIAIAABIA",
},
signature: {
endpoint: "http://localhost/app/ci/php-s3-server/endpoint-cors.php"
},
uploadSuccess: {
endpoint: "http://localhost/app/ci/php-s3-server/endpoint-cors.php?success",
params: {
isBrowserPreviewCapable: qq.supportedFeatures.imagePreviews
}
},
session: {
endpoint: "http://localhost/app/ci/php-s3-server/endpoint-cors.php?filelist"
},
iframeSupport: {
localBlankPagePath: "success.html"
},
cors: {
expected: true
},
chunking: {
enabled: true
},
resume: {
enabled: true
},
deleteFile: {
enabled: true,
method: "POST",
endpoint: "http://localhost/app/ci/php-s3-server/endpoint-cors.php",
params: {
caption: function() {
if (isCaptionUpdate === true) {
isCaptionUpdate = false;
return document.getElementById('caption').value;
}
}
}
},
validation: {
itemLimit: 5,
sizeLimit: 15000000
},
thumbnails: {
placeholders: {
notAvailablePath: "http://localhost/app/ci/s3.fine-uploader/placeholders/not_available-generic.png",
waitingPath: "http://localhost/app/ci/s3.fine-uploader/placeholders/waiting-generic.png"
}
},
callbacks: {
onComplete: function(id, name, response) {
var previewLink = qq(this.getItemByFileId(id)).getByClass('preview-link')[0];
if (response.success) {
previewLink.setAttribute("href", response.tempLink)
}
},
onUpload: function(id, fileName) {
var caption = document.getElementById('caption').value;
this.setParams({'caption':caption});
}
}
});
qq(document.getElementById("trigger-upload-section1")).attach("click", function() {
manualUploaderSection1.uploadStoredFiles();
});
Server side code:
require '/vendor/autoload.php';
use Aws\S3\S3Client;
$clientPrivateKey = 'LB7r54Rgh9sCuTAC8V5F';
$serverPublicKey = 'AKIAU2ZEQ';
$serverPrivateKey = '8Xu6lxcDfKifHfn4pdELnM1E';
$expectedBucketName = 'xx_my_bucket_xx';
$expectedHostName = 'http://s3.amazonaws.com'; // v4-only
$expectedMaxSize = 15000000;
$method = getRequestMethod();
// This first conditional will only ever evaluate to true in a
// CORS environment
if ($method == 'OPTIONS') {
handlePreflight();
}
// This second conditional will only ever evaluate to true if
// the delete file feature is enabled
else if ($method == "DELETE") {
handleCorsRequest();
if (isset($_REQUEST['caption'])) {
updateObjectWithCaption();
} else {
deleteObject();
}
}
// This is all you really need if not using the delete file feature
// and not working in a CORS environment
else if ($method == 'POST') {
handleCorsRequest();
// Assumes the successEndpoint has a parameter of "success" associated with it,
// to allow the server to differentiate between a successEndpoint request
// and other POST requests (all requests are sent to the same endpoint in this example).
// This condition is not needed if you don't require a callback on upload success.
if (isset($_REQUEST["success"])) {
verifyFileInS3(shouldIncludeThumbnail());
}
else {
signRequest();
}
}
//filelist - this is to list already uploaded files
else if ($method == 'GET') {
if (isset($_REQUEST["filelist"])) {
getFileList('test/');
}
}
function getFileList($filePrefix) {
global $expectedBucketName;
$objects = getS3Client()->getIterator('ListObjects', array(
//$objects = getS3Client()->ListObjects(array(
'Bucket' => $expectedBucketName,
'Prefix' => $filePrefix //must have the trailing forward slash "/"
));
$object_list = array();
foreach ($objects as $object) {
//echo $object['Key'] . "<br>";
$object_metadata = getHeadObject($expectedBucketName, $object['Key']);
if (isset($object_metadata['Metadata']['qqfilename'])) {
$keyArr = explode("/", $object['Key']);
$posOfLastString = sizeof($keyArr) - 1;
$uuidArry = explode(".", $keyArr[$posOfLastString]);
$link = getTempLink($expectedBucketName, $object['Key']);
$object_new = array();
$object_new['name'] = $object_metadata['Metadata']['qqfilename'];
$object_new['uuid'] = $uuidArry[0];
$object_new['s3Key'] = $object['Key'];
$object_new['size'] = $object['Size'];
$object_new['s3Bucket'] = $expectedBucketName;
$object_new['thumbnailUrl'] = $link;
array_push($object_list, (object)$object_new);
}
}
echo json_encode($object_list);
}
// This will retrieve the "intended" request method. Normally, this is the
// actual method of the request. Sometimes, though, the intended request method
// must be hidden in the parameters of the request. For example, when attempting to
// send a DELETE request in a cross-origin environment in IE9 or older, it is not
// possible to send a DELETE request. So, we send a POST with the intended method,
// DELETE, in a "_method" parameter.
function getRequestMethod() {
global $HTTP_RAW_POST_DATA;
// This should only evaluate to true if the Content-Type is undefined
// or unrecognized, such as when XDomainRequest has been used to
// send the request.
if(isset($HTTP_RAW_POST_DATA)) {
parse_str($HTTP_RAW_POST_DATA, $_POST);
}
if (isset($_REQUEST['_method'])) {
return $_REQUEST['_method'];
}
return $_SERVER['REQUEST_METHOD'];
}
// Only needed in cross-origin setups
function handleCorsRequest()
// If you are relying on CORS, you will need to adjust the allowed domain here.
header('Access-Control-Allow-Origin: http://localhost');
}
// Only needed in cross-origin setups
function handlePreflight() {
handleCorsRequest();
header('Access-Control-Allow-Methods: POST');
header('Access-Control-Allow-Headers: Content-Type');
}
function getS3Client() {
global $serverPublicKey, $serverPrivateKey;
return S3Client::factory(array(
'key' => $serverPublicKey,
'secret' => $serverPrivateKey
));
}
// Only needed if the delete file feature is enabled
function deleteObject() {
getS3Client()->deleteObject(array(
'Bucket' => $_REQUEST['bucket'],
'Key' => $_REQUEST['key']
));
}
function getHeadObject($bucket, $key) {
$object_metadata = getS3Client()->headObject(array('Bucket' => $bucket,'Key' => $key));
$object_metadata = $object_metadata->toArray();
return $object_metadata;
}
function updateObjectWithCaption() {
$bucket = $_REQUEST['bucket'];
$key = $_REQUEST['key'];
$caption = $_REQUEST['caption'];
$object_metadata = getHeadObject($bucket, $key);
$filename = $object_metadata['Metadata']['qqfilename'];
$fileType = getFileType($key);
getS3Client()->copyObject(array(
'Bucket' => $bucket,
'Key' => $key,
'CopySource' => urlencode($_REQUEST['bucket'] . '/' . $key),
'MetadataDirective' => 'REPLACE',
//'CacheControl' => 'max-age=31536000',
//'Expires' => gmdate('D, d M Y H:i:s T', strtotime('+1 years')), // Set EXPIRES and CACHE-CONTROL headers to +1 year (RFC guidelines max.)
'ContentType' => $fileType,
'Metadata'=>array(
'qqcaption' => $caption,
'qqfilename' => $filename,
),
));
}
function getFileType($key) {
$file_parts = pathinfo($key);
$filetype = "";
switch($file_parts['extension'])
{
case "jpg":
$filetype = "image/jpeg";
break;
case "jpeg":
$filetype = "image/jpeg";
break;
case "png":
$filetype = "image/png";
break;
case "gif":
$filetype = "image/gif";
break;
case "tif":
$filetype = "image/tiff";
break;
case "tiff":
$filetype = "image/tiff";
break;
case "bmp":
$filetype = "image/bmp";
break;
}
return $filetype;
}
function signRequest() {
header('Content-Type: application/json');
$responseBody = file_get_contents('php://input');
$contentAsObject = json_decode($responseBody, true);
$jsonContent = json_encode($contentAsObject);
if (!empty($contentAsObject["headers"])) {
signRestRequest($contentAsObject["headers"]);
}
else {
signPolicy($jsonContent);
}
}
function signRestRequest($headersStr) {
$version = isset($_REQUEST["v4"]) ? 4 : 2;
if (isValidRestRequest($headersStr, $version)) {
if ($version == 4) {
$response = array('signature' => signV4RestRequest($headersStr));
}
else {
$response = array('signature' => sign($headersStr));
}
echo json_encode($response);
}
else {
echo json_encode(array("invalid" => true));
}
}
function isValidRestRequest($headersStr, $version) {
if ($version == 2) {
global $expectedBucketName;
$pattern = "/\/$expectedBucketName\/.+$/";
}
else {
global $expectedHostName;
$pattern = "/host:$expectedHostName/";
}
preg_match($pattern, $headersStr, $matches);
return count($matches) > 0;
}
function signPolicy($policyStr) {
$policyObj = json_decode($policyStr, true);
if (isPolicyValid($policyObj)) {
$encodedPolicy = base64_encode($policyStr);
if (isset($_REQUEST["v4"])) {
$response = array('policy' => $encodedPolicy, 'signature' => signV4Policy($encodedPolicy, $policyObj));
}
else {
$response = array('policy' => $encodedPolicy, 'signature' => sign($encodedPolicy));
}
echo json_encode($response);
}
else {
echo json_encode(array("invalid" => true));
}
}
function isPolicyValid($policy) {
global $expectedMaxSize, $expectedBucketName;
$conditions = $policy["conditions"];
$bucket = null;
$parsedMaxSize = null;
for ($i = 0; $i < count($conditions); ++$i) {
$condition = $conditions[$i];
if (isset($condition["bucket"])) {
$bucket = $condition["bucket"];
}
else if (isset($condition[0]) && $condition[0] == "content-length-range") {
$parsedMaxSize = $condition[2];
}
}
return $bucket == $expectedBucketName && $parsedMaxSize == (string)$expectedMaxSize;
}
function sign($stringToSign) {
global $clientPrivateKey;
return base64_encode(hash_hmac(
'sha1',
$stringToSign,
$clientPrivateKey,
true
));
}
function signV4Policy($stringToSign, $policyObj) {
global $clientPrivateKey;
foreach ($policyObj["conditions"] as $condition) {
if (isset($condition["x-amz-credential"])) {
$credentialCondition = $condition["x-amz-credential"];
}
}
$pattern = "/.+\/(.+)\\/(.+)\/s3\/aws4_request/";
preg_match($pattern, $credentialCondition, $matches);
$dateKey = hash_hmac('sha256', $matches[1], 'AWS4' . $clientPrivateKey, true);
$dateRegionKey = hash_hmac('sha256', $matches[2], $dateKey, true);
$dateRegionServiceKey = hash_hmac('sha256', 's3', $dateRegionKey, true);
$signingKey = hash_hmac('sha256', 'aws4_request', $dateRegionServiceKey, true);
return hash_hmac('sha256', $stringToSign, $signingKey);
}
function signV4RestRequest($rawStringToSign) {
global $clientPrivateKey;
$pattern = "/.+\\n.+\\n(\\d+)\/(.+)\/s3\/aws4_request\\n(.+)/s";
preg_match($pattern, $rawStringToSign, $matches);
$hashedCanonicalRequest = hash('sha256', $matches[3]);
$stringToSign = preg_replace("/^(.+)\/s3\/aws4_request\\n.+$/s", '$1/s3/aws4_request'."\n".$hashedCanonicalRequest, $rawStringToSign);
$dateKey = hash_hmac('sha256', $matches[1], 'AWS4' . $clientPrivateKey, true);
$dateRegionKey = hash_hmac('sha256', $matches[2], $dateKey, true);
$dateRegionServiceKey = hash_hmac('sha256', 's3', $dateRegionKey, true);
$signingKey = hash_hmac('sha256', 'aws4_request', $dateRegionServiceKey, true);
return hash_hmac('sha256', $stringToSign, $signingKey);
}
// This is not needed if you don't require a callback on upload success.
function verifyFileInS3($includeThumbnail) {
global $expectedMaxSize;
$bucket = $_REQUEST["bucket"];
$key = $_REQUEST["key"];
// If utilizing CORS, we return a 200 response with the error message in the body
// to ensure Fine Uploader can parse the error message in IE9 and IE8,
// since XDomainRequest is used on those browsers for CORS requests. XDomainRequest
// does not allow access to the response body for non-success responses.
if (isset($expectedMaxSize) && getObjectSize($bucket, $key) > $expectedMaxSize) {
// You can safely uncomment this next line if you are not depending on CORS
header("HTTP/1.0 500 Internal Server Error");
deleteObject();
echo json_encode(array("error" => "File is too big!", "preventRetry" => true));
}
else {
$link = getTempLink($bucket, $key);
$response = array("tempLink" => $link);
if ($includeThumbnail) {
$response["thumbnailUrl"] = $link;
}
echo json_encode($response);
}
}
// Provide a time-bombed public link to the file.
function getTempLink($bucket, $key) {
$client = getS3Client();
$url = "{$bucket}/{$key}";
$request = $client->get($url);
return $client->createPresignedUrl($request, '+15 minutes');
}
function getObjectSize($bucket, $key) {
$objInfo = getS3Client()->headObject(array(
'Bucket' => $bucket,
'Key' => $key
));
return $objInfo['ContentLength'];
}
// Return true if it's likely that the associate file is natively
// viewable in a browser. For simplicity, just uses the file extension
// to make this determination, along with an array of extensions that one
// would expect all supported browsers are able to render natively.
function isFileViewableImage($filename) {
$ext = strtolower(pathinfo($filename, PATHINFO_EXTENSION));
$viewableExtensions = array("jpeg", "jpg", "gif", "png", "tif", "tiff");
return in_array($ext, $viewableExtensions);
}
// Returns true if we should attempt to include a link
// to a thumbnail in the uploadSuccess response. In it's simplest form
// (which is our goal here - keep it simple) we only include a link to
// a viewable image and only if the browser is not capable of generating a client-side preview.
function shouldIncludeThumbnail() {
$filename = $_REQUEST["name"];
$isPreviewCapable = $_REQUEST["isBrowserPreviewCapable"] == "true";
$isFileViewableImage = isFileViewableImage($filename);
return !$isPreviewCapable && $isFileViewableImage;
}
When user click on 'Update Caption', it follows DELETE steps and since I am passing Caption param, it updates S3 file. But problem is in the file list, I will see a status text called 'Deleting.....' appears for brief time. How can I change status to 'Updating Caption....' or something similar
There are various text options you can set for the delete file feature, such as deleteFile.deletingStatusText.
As you can see in the deleteFile section of JS, caption is taken from document.getElementById('caption').value; that means, even if I click 'Update Caption' of 2nd or 3rd or 4th files, it is taking first occurrence of Caption element. How can I get the caption of the specific file ?
Your markup/template is flawed in that you will end up with multiple elements with an ID of "caption". This is not allowed in HTML. You'll need to restructure your markup accordingly. An ID is not appropriate here. Instead, use a class. You can always get the container element for a file using Fine Uploader's getItemByFileId API method. With this, you can query the descendant elements to look for one with a specific attribute.
Last but not least, how can I show 'Update Caption' button only for previously uploaded file. I do not want show this button on fresh upload.
Files submitted by the user (non-canned/initial files) will result in a call to your onSubmitted callback handler after they are represented in the DOM. At this point, you can use the previously mentioned getItemByFileId to retrieve the container element and hide the button.
I'm making a "Download" controller using Symfony 2, that has the sole purpose of sending headers so that I can force a .csv file download, but it isn't working properly.
$response = new Response();
$response->headers->set('Content-Type', "text/csv");
$response->headers->set('Content-Disposition', 'attachment; filename="'.$fileName.'"');
$response->headers->set('Pragma', "no-cache");
$response->headers->set('Expires', "0");
$response->headers->set('Content-Transfer-Encoding', "binary");
$response->headers->set('Content-Length', filesize($fileName));
$response->prepare();
$response->sendHeaders();
$response->setContent(readfile($fileName));
$response->sendContent();
$fileName is a "info.csv" string. Such are my actions inside my controller, there's no return statement. When I tried returning the Response Object, the contents of the file were displayed in the browser, not my intended result.
The problem I've found is that in some pages I do get my info.csv file, but in anothers all I get is a message:
No webpage was found for the web address: http://mywebpage.com/download
Error 6 (net::ERR_FILE_NOT_FOUND): The file or directory could not be found.
I'm completely sure the file exists, so there must be another thing wrong. Also, routing.yml is working correctly, since I do get the file from other pages that also link to that path.
The Apache error log doesn't show anything about it.
Has anyone forced the download of a .csv file on Symfony 2 before? If so, what am I doing wrong?
Here is a minimal example that works just fine in production:
class MyController
public function myAction()
$response = $this->render('ZaysoAreaBundle:Admin:Team/list.csv.php',$tplData);
$response->headers->set('Content-Type', 'text/csv');
$response->headers->set('Content-Disposition', 'attachment; filename="teams.csv"');
return $response;
You can replace the render call with new response and response->setContent if you like.
Your comment about no return statement inside a controller is puzzling. Controllers return a response. Let the framework take care of sending the stuff to the browser.
I realize this post is kind of old and that there is, oddly enough, practically no good resources on how to do a CSV Export in symfony 2 besides this post at stackoverflow.
Anyways I used the example above for a client contest site and it worked quite well. But today I received an e-mail and after testing it myself, the code had broken - I was able to get the download working with a small amount of results, but the database now exporting over 31,000 rows it either simply showed the text or with chrome, just basically did nothing.
For anyone having issue with a large data export, this is what I manged to get to work, basically doing what Cerad suggested as an alternate way:
$filename = "export_".date("Y_m_d_His").".csv";
$response = $this->render('AppDefaultBundle:Default:csvfile.html.twig', array('data' => $data));
$response->setStatusCode(200);
$response->headers->set('Content-Type', 'text/csv');
$response->headers->set('Content-Description', 'Submissions Export');
$response->headers->set('Content-Disposition', 'attachment; filename='.$filename);
$response->headers->set('Content-Transfer-Encoding', 'binary');
$response->headers->set('Pragma', 'no-cache');
$response->headers->set('Expires', '0');
$response->prepare();
$response->sendHeaders();
$response->sendContent();
EDIT: After more testing and upping the max seconds allowed, I realized the previous code was printing out the headers at the top so I've updated the code.
THis worked for me to export CSV and JSON.
Twig files are named : export.csv.twig, export.json.twig
The Controller :
class PrototypeController extends Controller {
public function exportAction(Request $request) {
$data = array("data" => "test");
$format = $request->getRequestFormat();
if ($format == "csv") {
$response = $this->render('PrototypeBundle:Prototype:export.' . $format . '.twig', array('data' => $data));
$filename = "export_".date("Y_m_d_His").".csv";
$response->headers->set('Content-Type', 'text/csv');
$response->headers->set('Content-Disposition', 'attachment; filename='.$filename);
return $response;
} else if ($format == "json") {
return new Response(json_encode($data));
}
}
}
The Routing :
prototype_export:
pattern: /export/{_format}
defaults: { _controller: PrototypeBundle:Prototype:export, _format: json }
requirements:
_format: csv|json
The Twigs:
export.csv.twig (do your comma seperated thing here, this is just a test)
{% for row in data %}
{{ row }}
{% endfor %}
export.json.twig (data is sent json_encoded, this file is empty)
Hope this helps!
This is how I managed to get Silex to return a csv:
// $headers in an array of strings
// $results are the records returned by a PDO query
$stream = function() use ($headers, $results) {
$output = fopen('php://output', 'w');
fputcsv($output, $headers);
foreach ($results as $rec)
{
fputcsv($output, $rec);
}
fclose($output);
};
return $app->stream($stream, 200, array(
'Content-Type' => 'text/csv',
'Content-Description' => 'File Transfer',
'Content-Disposition' => 'attachment; filename="test.csv"',
'Expires' => '0',
'Cache-Control' => 'must-revalidate',
'Pragma' => 'public',
));
You may also need to do some Jiggery Pokery with Javascript (I was downloading Via AJAX) but this post was all I needed to get it working.
simple function you can use for every case to export an csv for download...
public function getResponse(array $data, $filename, $headers = array())
{
if(substr(strtolower($filename), -4) == '.csv') {
$filename = substr($filename, 0, -4);
}
$tmpFile = $this
->_getContainer()
->get('kernel')
->getRootDir()
. '/../var/tmp_'.substr(md5(time()),0,5);
if(file_exists($tmpFile)) unlink($tmpFile);
$handle = fopen($tmpFile, 'w');
foreach ($data as $i => $row) {
$row = (array) $row;
if($i == 0) fputcsv($handle, array_keys($row));
fputcsv($handle, $row);
}
fclose($handle);
$Response = new Response(file_get_contents($tmpFile));
unlink($tmpFile);
$filename = preg_replace('[^a-z0-9A-Z_]', '', $filename);
$headers = array_merge([
'Expires' => 'Tue, 01 Jul 1970 06:00:00 GMT',
'Cache-Control' => 'max-age=0, no-cache, must-revalidate, proxy-revalidate',
'Content-Disposition' => 'attachment; filename='.$filename.'.csv',
'Content-Type' => 'text/csv',
'Content-Transfer-Encoding' => 'binary',
], $headers);
foreach ($headers as $key => $val) {
$Response->headers->set($key, $val);
}
return $Response;
}
How about using Sonata's Exporter:
use Exporter\Writer\CsvWriter;
/**
* #param array $orders
*/
public function exportToCsv($orders)
{
$rootdir = $this->get('kernel')->getRootDir();
$filename = $rootdir . '/data/orders.csv';
unlink($filename);
$csvExport = new CsvWriter($filename);
$csvExport->open();
foreach ($orders as $order)
{
$csvExport->write($order);
}
$csvExport->close();
return;
}
It crashes if the file already exists, thus the unlink-command.