Double log statements - cakephp

I have weird issue, it might be something silly but I can't find where the problem is.
I develop an application on cakephp 2.x and when I log data from the controller it appears twice in the log. Something like this:
2013-05-24 11:50:19 Debug: excel file uploaded
2013-05-24 11:50:19 Debug: excel file uploaded
2013-05-24 11:50:19 Debug: fire test
2013-05-24 11:50:19 Debug: fire test
Just to add some fun, it doesn't happen in all functions in that controller, only in two out of six. It annoys me a lot and I don't see what way I should to dig to get rid of it.
Any ideas?
EDIT:
OK, I found that this happens when I log to the two different files in one method.
When I change the line: CakeLog::write('time'....); to CakeLog::write('debug'....);
everything works fine. Like in the following method:
function file_upload() {
if (!$this->request->data) {
} else {
CakeLog::write('time', 'start working at: ' . date('m/d/Y', strtotime("now")));
$data = Sanitize::clean($this->request->data);
CakeLog::write('debug', 'test statement');
if ($data['Scrap']['excel_submittedfile']['type'] === 'application/vnd.ms-excel' && $data['Scrap']['csv_submittedfile']['type'] === 'text/csv') {
$tmp_xls_file = $data['Scrap']['excel_submittedfile']['tmp_name'];
$xls_file = $data['Scrap']['excel_submittedfile']['name'];
$tmp_csv_file = $data['Scrap']['csv_submittedfile']['tmp_name'];
$csv_file = $data['Scrap']['csv_submittedfile']['name'];
$upload_dir = WWW_ROOT . "/files/";
if (file_exists($upload_dir) && is_writable($upload_dir)) {
if (move_uploaded_file($tmp_xls_file, $upload_dir . $xls_file) && move_uploaded_file($tmp_csv_file, $upload_dir . $csv_file)) {
CakeLog::write('debug', 'excel file uploaded');
$this->redirect(array('action' => 'edit', $xls_file, $csv_file));
} else {
echo 'upload failed';
}
} else {
echo 'Upload directory is not writable, or does not exist.';
}
} else {
echo 'make sure the files are in correct format';
}
}
}
I guess it has something to do with declarations of log files in bootstrap.php. So it's not that big problem just annoying.

This happens because your call
CakeLog::write('time', 'start working at: ' . date('m/d/Y', strtotime("now")));
Will attempt to write a log of the type: "time". Since there is no stream configured to handle that the CakeLog will create a "default" stream for you to handle this log call.
The problem is that, from now on you will have a "default" stream configured that will catch all logs and double them for debug and error logs.
The solution is to properly configure the log in the bootstrap.php file like this:
CakeLog::config('time_stream', array(
'engine' => 'FileLog',
'types' => array( 'time' ), //<--here is the log type of 'time'
'file' => 'time', //<-- this will go to time.log
) );
Of course, that if you use other log types you will need to configure streams for those as well, otherwise the default catch-all stream will be configured for you and you will be having the same problem again.
Good luck!

Related

How to save images in cakephp 3.4.0 with cakephp-upload while being immutable

I'm working on my cakephp project
and I am currently upgrading from 3.3.16 to 3.4.0
The project uses the cakephp-upload plugin to save an image.
The Upload plugin needs an existing entity to attach a file to it. A modification of the request is done to grab the avatar, before unsetting it to save the user.
I know this is not a good practice to modify a request, but the code was made this way.
With immutable objects in version 3.4.0, it is just not possible anymore. But i dont know how to do it properly.
Here is the error message given by my unit-test,
ran by vendor/bin/phpunit --filter testAdd tests/TestCase/Controller/Api/V1/UsersControllerTest.php:
There was 1 failure:
1) App\Test\TestCase\Controller\Api\V1\UsersControllerTest::testAdd
Failed asserting that file "/home/comptoir/Comptoir-srv/webroot/img/files/Users/photo/5/avatar/correctAvatarLogo.jpg" exists.
/home/comptoir/Comptoir-srv/tests/TestCase/Controller/Api/V1/UsersControllerTest.php:208
Here is the actual code:
public function add()
{
if (!empty($this->request->data)) {
$user = $this->Users->newEntity($this->request->data);
} else {
$user = $this->Users->newEntity();
}
$message = "";
// Get the avatar before unset it to save the user.
// The Upload plugin need an existing entity to attach a file to it.
if ($this->request->is('post')) {
if (isset($this->request->data['photo']) && !$user->errors()) {
$avatar = $this->request->data['photo'];
$this->request->data['photo'] = "";
}
$user = $this->Users->patchEntity($user, $this->request->data);
if ($this->Users->save($user)) {
$user = $this->Users->get($user->id, ['contain' => []]);
isset($avatar) ? $this->request->data['photo'] = $avatar : null;
$user = $this->Users->patchEntity($user, $this->request->data);
if ($this->Users->save($user)) {
$message = "Success";
$this->Flash->success(__d("Forms", "Your are registred on the Comptoir du Libre, welcome !"));
if (!$this->request->is('json')) {
$this->Auth->setUser($this->Auth->identify());
$this->redirect([
"prefix" => false,
"controller" => "Pages",
"language" => $this->request->param("language")
]);
}
} else {
$message = "Error";
}
} else {
$message = "Error";
$this->Flash->error(__d("Forms", "Your registration failed, please follow rules in red."));
}
$message == "Error" ? $this->set('errors', $user->errors()) : null;
}
$this->ValidationRules->config('tableRegistry', "Users");
$rules = $this->ValidationRules->get();
$userTypes = $this->Users->UserTypes->find('list', ['limit' => 200]);
$this->set(compact('user', 'userTypes', 'rules', 'message'));
$this->set('_serialize', ['user', 'userTypes', 'rules', 'message', 'errors']);
}
Does anyone know how to do that respecting the immutable rule ?
Your premise is wrong.
The Upload plugin needs an existing entity to attach a file to it
That's actually not correct, uploading files alongside creating new records works fine. There's no need for this stuff in your controller, it should be possible to handle this with a single basic save, ie you should investigate the problem that you're having with that, and fix it.
However looking at your test, it should fail anyways, because the file data that you're passing is invalid, it's neither an actual uploaded file for which is_uploaded_file() would return true, nor is it acceptable for user data to be able to define the temporary file path, and the error code, ie you're not properly validating the data if that test passes as is. Accepting such data is a security vulnerability, it could allow all sorts of attacks, from path traversal to arbitrary file injections!
Ideally your whole upload validation and writing functionality would support \Psr\Http\Message\UploadedFileInterface objects, that would allow for very simply testing by being able to pass instances of that class into the test data, that might be something worth suggesting for the plugin. Without such functionality, your second best bet would probably be something like modifying the table's validation rules before issuing the test request, so that is_uploaded_file() is being skipped, or you're switching to integration tests over HTTP, instead of the simulation in CakePHP.

Links not rendering correctly in email send from console

I have the following code that sends my email:
/**
* #param array $to
* #param string $subject
* #param array $vars
* #param string $template
* #param array $from
*/
public function sendEmail(array $to, $subject, array $vars, $template = 'default', array $from = ['dev#example.com' => 'Online'])
{
$transport = 'default';
if (Configure::read('debug')) {
$transport = 'dev';
}
$mailer = new Email($transport);
if ($this->isCommandLineInterface()) {
$mailer->setDomain('http://local.peepznew.com');
}
$this->addRecipients($mailer, $to);
$mailer->setFrom($from);
$mailer->setSubject($subject);
$mailer->setTemplate($template);
if (isset($vars['preheaderText']) === false) {
$vars['preheaderText'] = '';
}
$vars['subject'] = $subject;
$mailer->setViewVars($vars);
$mailer->setEmailFormat('both');
$mailer->send();
}
This code is called from the web interface as well as from the command line. After struggling to get the full url to display in messages sent from the command line, I read the docs and came across this:
Which is why I'm doing the setDomain call. I run my code again, and it still doesn't have full Urls. So I created the exact same function in both the web interface and cli, that looks like this:
$this->sendEmail(
['my.email#example.com'],
'Test Email',
[
'title' => 'We need to select another peep',
'showFooterLinks' => true,
]
);
die;
The default template looks like so (it literally only has this one line in it):
echo $this->Html->link('test link', ['controller' => 'jobs', 'action' => 'select_staff', 1, '_full' => true]);
The emails from the web interface, using the code above, sends perfect. Full URLs and everything. However from the cli, it just sends /jobs/select_staff/1.
Why is this and how do I fix it?
Read the docs closely, they say that the domain set via setDomain() is being used when generating message IDs, ie it's being used in an E-Mail header.
Generating links is something completely different, and is affected by the App.fullBaseUrl configuration option, which is by default derived from env('HTTP_HOST') in your applications config/bootstrap.php, unless already configured in config/app.php.
It's also possible to configure the base URL separately for the CLI environment in your config/bootstrap_cli.php file, there should already be a commented snippet for doing so that looks like this:
// Set the fullBaseUrl to allow URLs to be generated in shell tasks.
// This is useful when sending email from shells.
//Configure::write('App.fullBaseUrl', php_uname('n'));
See also
Cookbook > Console Tools, Shells & Tasks > Routing in the Console Environment
Source > cakephp/app > config/bootstrap.php
Source > cakephp/app > config/bootstrap_cli.php
Source > cakephp/app > config/app.php

Debugging SQL in controller

I am trying to debug my sql but I am having a hard time. I know I can use this:
<?php echo $this->element('sql_dump'); ?>
to dump the sql but this doesnt (or at least I dont know how to use it) work if I am doing an ajax call. Because the page is not reloaded, the dump does not get refreshed. How can I run my command and debug the sql? Here is the code I have in my controller:
public function saveNewPolicy(){
$this->autoRender = false;
$policyData = $this->request->data["policyData"];
$numRows=0;
$data = array(
'employee_id' => trim($policyData[0]["employeeId"]),
'insurancetype_id'=> $policyData[0]["insuranceTypeId"],
'company' => $policyData[0]["companyName"],
'policynumber' => $policyData[0]["policyNumber"],
'companyphone' => $policyData[0]["companyPhone"],
'startdate'=> $policyData[0]["startDate"],
'enddate'=> $policyData[0]["endDate"],
'note' => $policyData[0]["notes"]
);
try{
$this->Policy->save($data);
$numRows =$this->Policy->getAffectedRows();
if($numRows>0){
$dataId = $this->Policy->getInsertID();
$response =json_encode(array(
'success' => array(
'msg' =>"Successfully Added New Policy.",
'newId' => $dataId
),
));
return $response;
}else{
throw new Exception("Unspecified Error. Data Not Save! ");
}
}catch (Exception $e){
return $this->EncodeError($e);
}
}
The problem is that if the company field in my array is empty, empty, the insert will fail without any error. I know it has failed, though, because of the numrows variable I use. I know the field accepts nulls in the database. It seems like the only way for me to debug this is to look at what SQL is being sent to MySql. Anyone know how to debug it? I am using CakePhp 2.4
I use this approach. I added this method in my AppModel class:
public function getLastQuery() {
$dbo = $this->getDatasource();
$logs = $dbo->getLog();
$lastLog = end($logs['log']);
return $lastLog['query'];
}
and then in any controller you call this like:
debug($this->{your model here}->getLastQuery());
Rather than trying to hack around in CakePHP, perhaps it would be easier to just log the queries with MySQL and do a tail -f on the log file? Here's how you can turn that on:
In MySQL, run SHOW VARIABLES; and look for general_log and general_log_file entries
If general_log is OFF, run SET GLOBAL general_log = 'ON'; to turn it on
In a terminal, run a tail -f logfile.log (log file location is in the general_log_file entry) to get a streaming view of the log as it's written to
This is very helpful for these circumstances to see what's going on behind the scenes, or if you have debug off for some reason.

Unable to upload multiple files to Google drive using PHP SDK

I am trying to upload multiple files to Google drive using the PHP SDK. For this I am calling the function below iteratively passing the required parameters:
function insertFile($driveService, $title, $description, $parentId, $fileUrl) {
global $header;
$file = new Google_DriveFile();
$file->setTitle($title);
$file->setDescription($description);
$mimeType= "application/vnd.google-apps.folder";
if ($fileUrl != null) {
$fileUrl = replaceSpaceWithHtmlCode($fileUrl);
$header = getUrlHeader($fileUrl);
$mimeType = $header['content-type'];
}
$file->setMimeType($mimeType);
$parent = new Google_ParentReference();
// Set the parent folder.
if ($parentId != null) {
$parent->setId($parentId);
$file->setParents(array($parent));
}
try {
$data = null;
if ($fileUrl != null) {
if (hasErrors($driveService, $fileUrl) == True) {
return null;
}
$data = file_get_contents($fileUrl);
}
$createdFile = $driveService->files->insert($file, array(
'data' => $data,
'mimeType' => $mimeType,
));
return $createdFile;
} catch (Exception $e) {
echo "Error: 12";
return null;
}
}
I am running this app on the Google App Engine.
However, I am unable to upload all the files I pass to it. For example, if I pass about 12-15 files, only 10-11 get uploaded, and sometimes all get uploaded, even though all parameters are correct. I have caught the exception when it fails to create a file and this says it is unable to create a file, for the files that are not uploaded. I don't see any warnings or errors in the logs on the app engine.
Am I missing something? Can someone please point me where I should be looking to correct this and make it reliable enough to upload all files given to it?
The HTTP response that I get when I try to upload 30 files is this:
PHP Fatal error: The request was aborted because it exceeded the maximum execution time
Check the http response to see the detailed reason. It might be that you are hitting the throttle limit and getting a 403 rate limit response.

Limit File Types in CakePHP File Input

I just wanted to know if there is a way to limit the file input dialog to show only certain kinds of files. My web page only can accept .bin or .gz file types, but the user can select other file types and try to upload them.
What would be the best way to prevent the wrong kind of file from being uploaded?
Here is my controller for file uploading:
public function uploadFile()
{
$this->Session->write('isFileUpload', false);
$this->Session->write('isFileLarge', false);
if($this->request->is('post'))
{
$uploadedFile = array();
// Check if the Document object is set
// If it is set, process the file for uploading,
if(isset($this->request->data['Document']))
{
$filename = $this->request->data['Document']['MyFile']['tmp_name'];
$uploadedFile['MyFile']['name'] = $this->request->data['Document']['MyFile']['name'];
$uploadedFile['MyFile']['type'] = $this->request->data['Document']['MyFile']['type'];
$uploadedFile['MyFile']['size'] = $this->request->data['Document']['MyFile']['size'];
// Move the file to the /home/spectracom folder
$filePath = DS . 'home' . DS . $uploadedFile['MyFile']['name'];
if (move_uploaded_file($filename, $filePath))
{
$this->Session->write('isFileUpload', true);
$this->Session->write('isFileLarge', false);
$this->redirect('/tools/upgradebackup');
}
else
{
$this->Session->write('isFileUpload', false);
$this->Session->write('isFileLarge', true);
$this->redirect('/tools/upgradebackup');
}
}
else
{
$this->Session->write('isFileUpload', false);
$this->Session->write('isFileLarge', true);
$this->redirect('/tools/upgradebackup');
}
}
}
I basically check that the file exists, or else it is too large, and when it returns to the main upgrade page it sets the session variables.
Thanks
You limit what the browser allows the user to select in the file selection dialog using the accept attribute, although not all browsers support it.
I think this should work for creating the input (you'll need to play around with the MIME types to see what works):
echo $this->Form->input('MyFile', array('type' => 'file', 'options' => array('accept' => 'application/gzip,application/gzipped,application/octet-stream')));
You should also validate the files once they arrive on the server by setting up validation on your model (look at extension and mimeType validation rules).
You can also use JavaScript to validate the file extension once it has been selected by the user, and clear the file input field if it has the wrong extension.
Tested with Cakephp 3.4
$this->Form->control('my_file', ['label' => 'Upload File','type' => 'file', 'accept' => 'application/msword']);

Resources