Wednesday, March 8, 2017

Using inotify to detect file chages and send a notification by PHP

First of all we need to increase the inotify watchers, by default in Linux environments this value is very low. Check this Increase inotify watchers. Next let's create a watcher to always keep watching a folder(/temp/watch), save the change in a log(watchlog.log) and send an email by PHP. By default when inotify find a change the program just close until this is run again.
There is an option -d (demon but I haven't tested)
1. Create a folder called "watchlog"(/path/to/watchlog) and create the script to watch in the folder "/temp/watch"

inotify_folder.sh
#!/bin/bash

log_dir="log/"
log_file="watchlog.log"

while true
do
ts=$(date +"%C%y%m%d")
OUTPUT="$(inotifywait -r -e modify,create,delete --format '%T %:e %w%f' --timefmt '%c' /temp/watch  | awk '{print $4" "$6" "$7}' )"
#$4 time of change
#$6 event
#$7 file path
echo "${OUTPUT}" >> $log_dir$ts$log_file
php inotify_php.php $OUTPUT
done
inotifywait is using the parameter -r to watch recursive, if there are a lot of level and files inotify can take a while to start.
the paremter -e specify wich actions we want to watch, there more actions by default.
2. Create the folder /log in the same level to save the logs. 3. Create the script that will be call by the cron to check if the script 'inotify_folder.sh' is running

inotify_cron.sh
#!/bin/bash

running=`ps auxf | grep inotify_folder | grep -v grep`
if [ -z "$running" ]
then
    /bin/bash inotify_folder.sh
fi
4. Create the PHP script to send the email

  inotify_php.php
<?php
$receivers = array('user@mail.com');
$time = $argv[1];
$event = $argv[2];
$file = $argv[3];

foreach($receivers as $receive){
	mail($receive,'Alert on file '.$file.' with a '.$event,$event.' '.$file);
}

5. Set a cron to check every one minute
*/1 * * * * root cd /path/to/watchlog; sh inotify_cron.sh

Friday, May 6, 2016

Magento Multiple Database Connection

Working with Magento 1.9 I was asked to make multiple read and write connections.
Magento has the possibility to configure read and write connections in the /app/etc/local.xml. Just set the tag use to let Magento know which one is available.

<!-- MASTER SERVER -->
<default_setup>
     <connection>
         <host>master</host>
         <username>user</username>
         <password>123456</password>
         <dbname>magento</dbname>
         <initStatements><![CDATA[SET NAMES utf8]]></initStatements>
         <model><![CDATA[mysql4]]></model>
         <type><![CDATA[pdo_mysql]]></type>
         <pdoType><![CDATA[]]></pdoType>
         <active>1</active>
     </connection>
</default_setup>
<default_write>
     <connection>
          <use>default_setup</use>
      </connection>
</default_write>
<!-- SLAVE SERVER -->
<slave_connection>
     <connection>
           <host>slave</host>
           <username>user</username>
           <password>123456</password>
           <dbname>magento</dbname>
           <initStatements><![CDATA[SET NAMES utf8]]></initStatements>
           <model><![CDATA[mysql4]]></model>
           <type><![CDATA[pdo_mysql]]></type>
           <pdoType><![CDATA[]]></pdoType>
           <active>1</active>
      </connection>
</slave_connection>
<default_read>
      <connection>
          <use>slave_connection</use>
      </connection>
</default_read>

We can define n connections in the same config file like this test example

 
   
   test_server
   root
   123456
   magento_db
   SET NAMES utf8
   mysql4
   pdo_mysql
   
   1
 

The limit is that the connections are applied to the whole system but my idea is to set only for certain resources.
In this case I have a custom report module where I only want to make read connections in the Order table.
After overriding the Order resource Mage/Sales/Model/Resource/Order.php
Just make 3 updates

  1. Make a flag to know if its time to change the connection $reportConnection.
  2. Update function _construct() to create the custom connection and add it to the array of resources.
  3. Update function _getConnection() to decide if use the custom connection.

//flag
public $reportConnection = false;

/**
*Just add the connection defined in the local.xml 'test_read'
*/
protected function _construct(){
    $this->_init('sales/order', 'entity_id');
    $this->_resources->getConnection('test_read');
}

/**
*Make the connection if the flag is set
*/
protected function _getConnection($connectionName){
 if (isset($this->_connections[$connectionName])) {
   return $this->_connections[$connectionName];
    }

   if($connectionName == 'read' && $this->reportConnection)
        $this->_connections[$connectionName] = $this->_resources->getConnection('test_read');
   else{
   if (!empty($this->_resourcePrefix)) {
      $this->_connections[$connectionName] = $this->_resources->getConnection(
      $this->_resourcePrefix . '_' . $connectionName);
  } else {
   $this->_connections[$connectionName] = $this->_resources->getConnection($connectionName);
  }
   }
   return $this->_connections[$connectionName];
}

Last step is to make call an Order collection but using the test_read connection.
//Get the Order model
$model = Mage::getModel('sales/order');
//set the flag
$model->getResource()->reportConnection = true;
//get the collection
$collection = $model->getCollection();

Friday, May 30, 2014

How to catch emails sent with PHP on your local server


Taking the idea from Martin Valasek.
I decided to make a small change like add the extension "*.eml" for opening faster the saved emails with thunderbird at the time they are created.
Create a folder for your outgoing mail. We will create it in under /var/log/ where all the logs are stored:
$ sudo mkdir /var/log/mail

Create file /usr/local/bin/sendmail using your favorite text editor
$ sudo nano /usr/local/bin/sendmail
Add following PHP script to this new "sendmail" file:
#!/usr/bin/php
<?php
$input = file_get_contents('php://stdin');
$timeCreated = explode(" ",microtime());
$filename = tempnam('/var/log/mail',$timeCreated[1].'_'.$timeCreated[0].'_');
file_put_contents($filename.'.eml', $input);
unlink($filename);
shell_exec('thunderbird '.$filename.'.eml > /dev/null 2>/dev/null &');

 We need to link our PHP script to PHP's sendmail functionality. Edit your "php.ini" file and set the sendmail_path setting as following:
sendmail_path = /usr/local/bin/sendmail
In Ubuntu the "php.ini" is located for Apache
/etc/php5/apache2
and for Cli
/etc/php5/cli

Now we need to set permissions for new files/folders:
$ sudo chmod 755 /usr/local/bin/sendmail
$ sudo chmod 777 /var/log/mail

Restart apache:
$ sudo /etc/init.d/apache2 restart
You can now try to send an email using PHP's mail() function and check the /var/log/mail folder.
And the email will open automatically with Thunderbird when they are created.

Friday, February 7, 2014

Another way to get all stores names with their id in Magento


/**
 * Return all the stores
 * 
 * @author abelbm
 * @return array
 */
function getAllStores(){
    $stores = array();
    
    $storesData = Mage::getSingleton('adminhtml/system_store')->getStoreValuesForForm(); 
    foreach ($storesData as $storeGrandParent)
        foreach ($storeGrandParent as $storeParent)
            if(is_array($storeParent) && !empty($storeParent)){
                foreach ($storeParent as $store)
                    $stores[$store['value']] = strip_tags(trim(iconv('UTF-8', 'ASCII//TRANSLIT', $store['label'])));
            }
            
    return $stores;
}

Friday, August 16, 2013

Script to refresh Cache and Index in Magento

Let's place a file in the Magento root called "maintanance.php"
$path = '/your/magento/root');
echo("Refresh the Cache \n");
$return = shell_exec("php shell/refreshCache.php");
echo $return."\n";
echo 'Refreshing Cache';
#php shell/refreshCache.php
echo("Reindexing...this could take a while  \n");
$return = shell_exec("php shell/indexer.php --reindexall");
echo $return."\n";
#php shell/indexer.php --reindexall
Create in shell folder refreshCache.php
$magePath = 'app/Mage.php';
require_once($magePath);


try {
 $allTypes = Mage::app()->useCache();
 foreach($allTypes as $type => $blah) {
  Mage::app()->getCacheInstance()->cleanType($type);
 }
} catch (Exception $e) {
 // do something
 error_log($e->getMessage());
 Mage::log($e->getMessage());
}

Friday, June 1, 2012

Update Magento 1.5 to 1.7

1. First of all prepare your environment for a big time of php execution, this can depend on the size of your database. 1.a Increase the time to your PHP
max_execution_time = 10800     ; Maximum execution time of each script, in seconds, 10800 = 3 hours
max_input_time = 10800 ; Maximum amount of time each script may spend parsing request data
1.b Increase also in your Apache
BusyTimeout 10800
ProcessLifeTime 10800
IPCConnectTimeout 20
IPCCommTimeout 10800
MaxRequestsPerProcess -1
2. Run the updates of Magento
chmod 755 mage
rm -rf var/cache/* downloader/pearlib/cache/* downloader/pearlib/download/*
./mage mage-setup .
./mage sync --force
./mage config-set preferred_state stable
./mage list-upgrades
./mage install http://connect20.magentocommerce.com/community Mage_All_Latest --force
rm -rf var/cache/* downloader/pearlib/cache/* downloader/pearlib/download/*
chmod 755 mage
2.a This step always occurs an error. This step is for re-index the tables.
php shell/indexer.php reindexall
This is the error but is normal, it will fix on the way. It only happen in the "Product Price"
Product Prices index process unknown error:
exception 'PDOException' with message 'SQLSTATE[42S22]: Column not found: 1054 Unknown column 'website_date' in 'field list'' in /home/erjpub/documents/public_html/lib/Zend/Db/Statement/Pdo.php:228
Stack trace:
#0 /home/erjpub/documents/public_html/lib/Zend/Db/Statement/Pdo.php(228): PDOStatement->execute(Array)
#1 /home/erjpub/documents/public_html/lib/Varien/Db/Statement/Pdo/Mysql.php(110): Zend_Db_Statement_Pdo->_execute(Array)
#2 /home/erjpub/documents/public_html/lib/Zend/Db/Statement.php(300): Varien_Db_Statement_Pdo_Mysql->_execute(Array)
#3 /home/erjpub/documents/public_html/lib/Zend/Db/Adapter/Abstract.php(479): Zend_Db_Statement->execute(Array)
#4 /home/erjpub/documents/public_html/lib/Zend/Db/Adapter/Pdo/Abstract.php(238): Zend_Db_Adapter_Abstract->query('INSERT INTO `ca...', Array)
#5 /home/erjpub/documents/public_html/lib/Varien/Db/Adapter/Pdo/Mysql.php(419): Zend_Db_Adapter_Pdo_Abstract->query('INSERT INTO `ca...', Array)
#6 /home/erjpub/documents/public_html/lib/Varien/Db/Adapter/Pdo/Mysql.php(1974): Varien_Db_Adapter_Pdo_Mysql->query('INSERT INTO `ca...', Array)
#7 /home/erjpub/documents/public_html/lib/Varien/Db/Adapter/Pdo/Mysql.php(1947): Varien_Db_Adapter_Pdo_Mysql->insertArray('catalog_product...', Array, Array)
#8 /home/erjpub/documents/public_html/app/code/core/Mage/Catalog/Model/Resource/Product/Indexer/Price.php(599): Varien_Db_Adapter_Pdo_Mysql->insertMultiple('catalog_product...', Array)
#9 /home/erjpub/documents/public_html/app/code/core/Mage/Catalog/Model/Resource/Product/Indexer/Price.php(378): Mage_Catalog_Model_Resource_Product_Indexer_Price->_prepareWebsiteDateTable()
#10 /home/erjpub/documents/public_html/app/code/core/Mage/Index/Model/Indexer/Abstract.php(143): Mage_Catalog_Model_Resource_Product_Indexer_Price->reindexAll()
#11 /home/erjpub/documents/public_html/app/code/core/Mage/Index/Model/Process.php(209): Mage_Index_Model_Indexer_Abstract->reindexAll()
#12 /home/erjpub/documents/public_html/app/code/core/Mage/Index/Model/Process.php(255): Mage_Index_Model_Process->reindexAll()
#13 /home/erjpub/documents/public_html/shell/indexer.php(158): Mage_Index_Model_Process->reindexEverything()
#14 /home/erjpub/documents/public_html/shell/indexer.php(198): Mage_Shell_Compiler->run()
#15 {main}
2.b Keep with the update
./mage upgrade-all --force
find . -type f -exec chmod 644 {} \;
find . -type d -exec chmod 777 {} \;
chmod 750 mage
chmod o+w var var/.htaccess app/etc
chmod -R o+w media
chmod -R 777 var/package var/locks var/report var/export downloader 
3. Update to 1.7 is succesfull, but the pager doesn't work as expected. This is because an missing function. 3.a Go to 'app/code/core/Mage/Catalog/Block/Product/List/Toolbar.php' line 35. Look up this line
class Mage_Catalog_Block_Product_List_Toolbar extends Mage_Core_Block_Template
Change it by this
class Mage_Catalog_Block_Product_List_Toolbar extends Mage_Page_Block_Html_Pager

Friday, March 2, 2012

Problem with DataTable and FancyBox in Internet Explorer

Working in a project I found a strange problem when I was mixing  FancyBox with DataTable.
Here was the issue, when placing a link inside a table generated with DataTable and this link was set to be handle with FancyBox for showing a 'div' tag, in Mozilla Firefox and Chrome everything worked just fine, but in all Internet Explorer  nothing worked as expected.

So, I used a link as a gateway, when you click in a link inside the DataTable it update the value for the 'gateway' link and later just trigger FancyBox.

Try it your self!
Of course, first download DataTable and FancyBox.

Here is the HTML code:
Email Name
Email Name
To:
Message:


Here is the JavaScript code:








This is what you should see at the end: