im generating a daily report from a php script, it is residing in a folder as follows
report_2015_02_15.csv
report_2015_02_16.csv
report_2015_02_17.csv
report_2015_02_18.csv
report_2015_02_19.csv
And im sending my users an email with link to download, once they click the download link the download script triggers and prepares the download. It currently gets all the files into an array sorts it and finds the latest for the download,
this method has a flow in it where, even when you go to a email that is 2 weeks older and clicks the download link, it gives you the latest report instead of giving the two weeks older report.
so can anybody tell me a way to send the download link in my email with a relationship to its corresponding file?
email script
$down_link = Config_Reader::readProjectConfig('tad')->base_url.'CSVDownload.php;
$mail = new Zend_Mail ();
$sentFromEmail = $config->sentFromrec;
$tr = new Zend_Mail_Transport_Sendmail ( '-f' . $sentFromEmail );
Zend_Mail::setDefaultTransport ( $tr );
$mail->setReturnPath($sentFromEmail);
$mail->setFrom ( $sentFromEmail, 'Reporting' );
$email_body = "Hi,<br /><br />Download the weekly details of adtracker projects, by clicking the link below.
<br /> Report<br /><br />Thank You.";
$mail->setBodyHtml ( $email_body );
$mail->addTo ( $weeklyReportRec);
$mail->setSubject ( "Report" );
try {
$mail->send ();
} catch ( Exception $e ) {
echo "Mail sending failed.\n";
}
download script
$basePath = "$download/dailyReport/";
$csvFiles = glob($basePath."/*.csv");
if(count($csvFiles)){
array_multisort($csvFiles, SORT_DESC);
$csvFile = $csvFiles[0];
}else{
exit("Server error!");
}
$h = fopen($csvFile, "r");
$contents = fread($h, filesize($csvFile));
You can use a query parameter indicating the report date on the download link. .../CSCDownload.php?date=2015/02/17
This will allow you to select the respective report from the available list.
You can use somthing like that as download script. I made just minum changes to get it work:
$date = $_GET['date'];
$basePath = "$download/dailyReport/";
$csvFiles = glob($basePath."/*.csv");
$file = $basePath . 'report_' . $date . '.csv';
$found = false;
foreach($csvFiles as $csvFile){
if($file === $csvFile){
$found = $csvFile;
// You have found the file, so you can stop searching
break;
}
}
if(false === $found){
exit("Server error!");
}
$h = fopen($found, "r");
$contents = fread($h, filesize($found));
Now you can call you script in your browser with "example.com/getcsvfile.php?date=2015_02_15" without worrying about injection, because you check if the file is one of the csv files.
Related
I have a WordPress issue and want to simply write log messages to a text file. I am aware that error_log exists, but want to have a more segregated log file for different messages.
I am using wp_filesystem->put_contents, and it DOES write to the file and succeeds, but it ONLY outputs the last call's data.
I have the following method:
public static function log_message($msg) {
error_log($msg);
require_once(ABSPATH . 'wp-admin/includes/file.php');
global $wp_filesystem;
if ( ! is_a( $wp_filesystem, 'WP_Filesystem_Base') ){
$creds = request_filesystem_credentials( site_url() );
wp_filesystem($creds);
}
$bt = debug_backtrace();
$caller = array_shift($bt);
$logStr = date("Y-m-d hh:ii A",time())." - ".$caller['file'].":".$caller['line']." - ".$msg;
$filePathStr = SRC_DIR.DIRECTORY_SEPARATOR.$logFileName;
$success = $wp_filesystem->put_contents(
$filePathStr,
$logStr,
FS_CHMOD_FILE // predefined mode settings for WP files
);
if(!$success) {
error_log("Writing to file \"".$filePathStr."\" failed.");
} else {
error_log("Writing to file \"".$filePathStr."\" succeeded.");
}
}
I call it using:
log_message("\nTest 1");
log_message("\nTest 2");
log_message("\nTest 3");
The output is ALWAYS ONLY Test 3 with the other invocations being ignored yet, their output appears in the debug.log as well as all the success messages.
Why would this be?
Looking at the WPCodex for the source code of this, it uses fwrite behind the scenes. The file is closed in this code, and I cannot use any "flush" technique.
Is there a way to figure this out?
I found that the source of WP_Filesystem uses file_put_contents (as the name does suggest), and I assumed this is for APPENDING to the file's data.
This is incorrect.
This function is to take data, and then WRITE it to the file, erasing prior data.
Mainly useful for creating resources, downloading a file, etc.
If I want to APPEND to a file, I need to use 'fwrite'.
This post describes that.
This is the example to APPEND to a file:
$filepath = '\path\to\file\';
$filename = 'out.log';
$fullpath = $filepath.$filename;
if(file_exists($fullpath)) {
$file = fopen($filepath.$filename, "a");//a for append -- could use a+ to create the file if it doesn't exist
$data = "test message";
fwrite($file, "\n". $data);
fclose($file);
} else {
error_log("The file \'".$fullpath."\' does not exist.");
}
The fopen docs describe this method and it's modes.
I want to open a server stored html report file on a client machine.
I want to bring back a list of all the saved reports in that folder (scandir).
This way the user can click on any of the crated reports to open them.
So id you click on a report to open it, you will need the location where the report can be opend from
This is my dilemma. Im not sure how to get a decent ip, port and folder location that the client can understand
Here bellow is what Ive been experimenting with.
Using this wont work obviously:
$path = $_SERVER['DOCUMENT_ROOT']."/reports/saved_reports/";
So I though I might try this instead.
$host= gethostname();
$ip = gethostbyname($host);
$ip = $ip.':'.$_SERVER['SERVER_PORT'];
$path = $ip."/reports/saved_reports/";
$files = scandir($path);
after the above code I loop through each file and generate a array with the name, date created and path. This is sent back to generate a list of reports in a table that the user can interact with. ( open, delete, edit)
But this fails aswell.
So im officially clueless on how to approach this.
PS. Im adding react.js as a tag, because that is my front-end and might be useful to know.
Your question may be partially answered here: https://stackoverflow.com/a/11970479/2781096
Get the file names from the specified path and hit curl or get_text() function again to save the files.
function get_text($filename) {
$fp_load = fopen("$filename", "rb");
if ( $fp_load ) {
while ( !feof($fp_load) ) {
$content .= fgets($fp_load, 8192);
}
fclose($fp_load);
return $content;
}
}
$matches = array();
// This will give you names of all the files available on the specified path.
preg_match_all("/(a href\=\")([^\?\"]*)(\")/i", get_text($ip."/reports/saved_reports/"), $matches);
foreach($matches[2] as $match) {
echo $match . '<br>';
// Again hit a cURL to download each of the reports.
}
Get list of reports:
<?php
$path = $_SERVER['DOCUMENT_ROOT']."/reports/saved_reports/";
$files = scandir($path);
foreach($files as $file){
if($file !== '.' && $file != '..'){
echo "<a href='show-report.php?name=".$file. "'>$file</a><br/>";
}
}
?>
and write second php file for showing html reports, which receives file name as GET param and echoes content of given html report.
show-report.php
<?php
$path = $_SERVER['DOCUMENT_ROOT']."/reports/saved_reports/";
if(isset($_GET['name'])){
$name = $_GET['name'];
echo file_get_contents($path.$name);
}
I have a script which put images, floor plans and video into a zip file, it can reach 500mb easily but most of the time average is 150mb.
The generation of the zip file is extremely slow and i can't figurate why. Is there any tips to improve my script?
It took me 10 min to create the zip file in the server just for 100mb.
if( !empty( $files ) ){
$random_nbr = mt_rand(1,5646866662);
$path = 'webroot/img/tmp/' . $random_nbr;
if (!file_exists(\Cake\Core\Configure::read('pathTo') . 'webroot/img/tmp')) {
mkdir(\Cake\Core\Configure::read('pathTo') . 'webroot/img/tmp', 0777, true);
}
$destination = \Cake\Core\Configure::read('pathTo') . $path . '_media.zip';
$media_url = \Cake\Core\Configure::read('websiteUrl') . '/img/tmp/' . $random_nbr . '_media.zip';
$zip = new ZipArchive();
$zip->open( $destination, ZIPARCHIVE::CREATE | ZIPARCHIVE::OVERWRITE );
// Photos
if (isset($files['photos'])):
foreach( $files['photos'] as $f ){
$context = stream_context_create(array('http' => array('header'=>'Connection: close\r\n')));
// Original
$parsed_file = $f['original_file'];
$download_file = file_get_contents($parsed_file, false,$context);
$zip->addFromString('photos/original/' . basename($parsed_file), $download_file);
// Web with or without a watermark
$web = $this->Images->state_image(1270, $f['id'], 0, '');
$web = $web->response('jpg');
$zip->addFromString('photos/web/' . $f['name'], $web);
// High Res Web with or without a watermark
$web = $this->Images->state_image(2000, $f['id'], 0, '');
$web = $web->response('jpg');
$zip->addFromString('photos/high_res_web/' . $f['name'], $web);
}
endif;
// Floor Plan
if (isset($files['floorplan'])):
foreach( $files['floorplan'] as $f ){
$parsed_file = $f['original_file'];
$context = stream_context_create(array('http' => array('header'=>'Connection: close\r\n')));
$download_file = file_get_contents($parsed_file, false,$context);
$zip->addFromString('floorplan/' . basename($parsed_file), $download_file);
}
endif;
// Video
if (isset($files['video'])):
foreach( $files['video'] as $f ){
$parsed_file = $f['original_file'];
$context = stream_context_create(array('http' => array('header'=>'Connection: close\r\n')));
$download_file = file_get_contents($parsed_file, false,$context);
$zip->addFromString('floorplan/' . basename($parsed_file), $download_file);
}
endif;
$zip->close();
echo $media_url;
die();
}
Outside of dedicated hardware, there probably is not much you'll be able to do to speed up the actual zipping process. You could try exec()'ing the system zip utility rather than using PHP to do it, but that may not change things.
What you can do though (if the host allows it) is background the process and provide a status page so users can see how long until their file is ready. I've done this in the past for similar problems.
What I did was have a table in the database that would store information about the zip file to be created, and a list of all the files to be added to the zip file. Then I'd exec() off a background script with the ID of the newly created DB record.
The background process would read the DB for all the details and begin creating the zip file. Periodically it would update the DB with a % complete. When finished it'd update the DB with the file system path to the newly generated zip file.
Then I had another page for the end user that displayed a progress bar. The page would periodically make an Ajax request to the server to get the new % complete for the file and update the bar accordingly. When the file was complete it would change to a download link for them to begin downloading the file.
There was another cron job process that would periodically go through and delete all the temp files older than 5 days. If users needed the file again they had to have it re-generated.
I've recently created a page on our site where users can upload an image and email it to an email address set up specifically to keep the uploaded documents.
I've tested this myself and it works, with the attachments arriving in gmail as expected.
However, whenever someone from outside uses this feature the attachment in the email is unavailable, or not could not be loaded, when we try to open it.
The code is split between 2 files, a controller and a helper. Here's the code (For the sake of saving some space I've removed all error checks, but in the actual code they are all still in place and not picking up any errors whatsoever):
controller
$helper = [GET HELPER];
/** Upload the file to a temp location so that we can attach it to an email */
$uploader = new Varien_File_Uploader('filename');
$uploader->setAllowedExtensions(array(
'image/jpeg',
'image/jpg',
'image/png',
'application/pdf'
))
->setAllowRenameFiles(true)
->setFilesDispersion(false);
$path = $helper->getFileStorageLocation(); // Will store files in /tmp
if (!is_dir($path))
{
mkdir($path, 0775, true);
}
$uploader->save($path, $_FILES['filename']['name']);
$result = $helper->sendMail($_FILES['filename']['name']);
if ($result)
{
$uploadSuccess = true;
/** Remove the temp file */
unlink($path . DS . $_FILES['filename']['name']);
}
helper
/** Declare variables */
$order = Mage::getModel('sales/order')->load($orderId);
$file_incremented_id = $order->getIncrementId();
$copyTo = $this->getCopyTo();
$copyFrom = $this->getCopyFrom();
$subject = 'proof of upload for ' . $file_incremented_id;
$copyTo = explode(',', $copyTo);
$body = '<span>Please see attachment</span>';
$file = $this->getFileStorageLocation() . DS . $filename; // function receives filename from whatever is calling it
$attachment = file_get_contents($file);
$extension = pathinfo($file, PATHINFO_EXTENSION);
if (!$copyTo)
{
return false;
}
$mail = Mage::getModel('core/email_template');
$mail->setSenderName('Uploader');
$mail->setSenderEmail($copyFrom);
$mail->setTemplateSubject($subject);
$mail->setTemplateText($body);
$mail->getMail()->createAttachment(
$attachement,
Zend_Mime::TYPE_OCTETSTREAM,
Zend_Mime::DISPOSITION_ATTACHMENT,
Zend_Mime::ENCODING_BASE64,
$file_incremented_id . '.' . $extension // Set order number as file name
);
try
{
$mail->send($copyTo);
return true;
}
catch (Exception $e)
{
return false;
}
Can anyone see anything that might be causing the issue, or think of what it might be based on my explanation of the setup?
So the problem, in the end, was filesize. My fault for not posting the $_FILES variable.
I saw it a bit later and the variable had error = 1, meaning that the file's size was larger than what was allowed by the max_upload_filesize in the php.ini
I am trying to read messages from an email... Depending on the subject's content I want to move it to either a "Processes" or "unauthorized" folder
save the messages in an array and then move the message from the INBOX to the Proceeded folder
Here is what I have done
// Checks the inbox
if ($messages = imap_search($this->conn,'ALL'))
{
// Sorts the messages newest first
rsort($messages);
// Loops through the messages
foreach ($messages as $id)
{
$header = imap_headerinfo($this->conn, $id);
$message = imap_fetchbody($this->conn, $id, 1);
if( !isset($header->from[0]->mailbox) || empty($header->from[0]->mailbox)
|| !isset($header->from[0]->host) || empty($header->from[0]->host)
|| !isset($header->subject) || empty($header->from[0]->host)
) {
continue;
}
$from = $header->from[0]->mailbox . '#' . $header->from[0]->host;
$subject = $header->subject;
$outlook = $this->_parseReplyExchange($message);
if($outlook !== false){
$newReply = $outlook;
} else {
$newReply = $this->_parseReplySystem($message);
}
$ticketID = $this->_parseTicketID($subject);
if($ticketID !== false){
$f = array();
$f['id'] = $id;
$f['from'] = $from;
$f['subject'] = $subject;
$f['ticketID'] = $ticketID;
$f['message'] = $newReply;
$this->replyList[] = $f;
$imapresult = imap_mail_move($this->conn, $id, $box, CP_UID);
if($imapresult == false){
echo imap_last_error();
}
}
}
}
else
{
exit('No messages on the IMAP server.');
}
I read the message with no issues, but when trying to moving the message I get an error.
.[TRYCREATE] The requested item could not be found.
Notice: Unknown: [TRYCREATE] The requested item could not be found. (errflg=2) in Unknown on line 0
I think the issue is the way how I am passing the $id to the imap_mail_move function.
I also tried to convert the message sequance number to a UID number like so $f['id'] = imap_uid($this->conn , $id ) and that did not work..
I also tried this
$imapresult = imap_mail_move($this->conn, '1:' . $id, $box);
$imapresult = imap_mail_move($this->conn, '1:' . $id, $box, CP_UID);
I even tried to copy and then delete the message and that did not work.
$imapresult = imap_mail_copy($c, '1', 'INBOX/Processed', CP_MOVE);
I can't get the message to move.
How can I correctly move the message?
I found the issue.
The issue was is that the Processed folder was not a sub folder of the INBOX folder. It was a folder setting next to INBOX.
The take away here is when using the imap_mail_move() function you will need to pass either a sequence number or a range of sequence numbers
$imapresult = imap_mail_move($this->conn, $id, $box);
Each message received have a sequence number 1,2,3,n where n is the newest message received in a giving box.
Here is an examples of the $id variable
1
1:5
1,2,5,6,7
The first example means move message 1 from current folder to a new folder defined in $box.
The second example means move messages 1,2,3,4,5 from current folder to a new folder defined in $box.
The third example means move messages 1,2,5,6,7 from current folder to a new folder defined in $box.
In addition, here are some examples of the $box variable
'INBOX/Processed'
'Unauthorized'
The first example means the Processed folder that is located under the INBOX folder.
The second example means the Unauthorized folder that is located next "same location" to the INBOX folder
To know where each folder is located in your email, you can use imap_list function.
I hope this helps other as it took me a while to find this silly issue.