2014-11-18

Sending email via mail function in base64

$messsage = base64_encode($message);
$send = mail($address, "=?utf-8?B?".base64_encode($subject)."?=", $message, "Content-type:text/plain; charset=UTF-8\r\nContent-Transfer-Encoding: base64");

How to set subject with "ru" locale in mail function (for utf-8)

$subject = "Тема сообщения";
$subject = "=?utf-8?B?".base64_encode($subject)."?=";

2014-11-09

Access control list (ACL) in zend framework 1

For implementing ACL you have to create class, in which will be created roles, resources and settings permissions. For this reason add to application/configs/application.ini:
autoloadernamespaces.app = "App_"
And create file library/App/Acl.php:
/**
 * Class App_Acl
 */
class App_Acl extends Zend_Acl
{
    function __construct()
    {
        $defaultResource = new Zend_Acl_Resource('default');
        $adminResource = new Zend_Acl_Resource('admin');

        $this->addResource($adminResource);
        $this->addResource($defaultResource);

        // guest/client resources
        $this->addResource(new Zend_Acl_Resource('index'), $defaultResource);
        $this->addResource(new Zend_Acl_Resource('payment'), $defaultResource);
        $this->addResource(new Zend_Acl_Resource('panel'), $defaultResource);
        $this->addResource(new Zend_Acl_Resource('user'), $defaultResource);

        // admin resources
        $this->addResource(new Zend_Acl_Resource('admin_user'), $adminResource);
        $this->addResource(new Zend_Acl_Resource('admin_exchange'), $adminResource);
        $this->addResource(new Zend_Acl_Resource('admin_rate'), $adminResource);
        $this->addResource(new Zend_Acl_Resource('admin_eps'), $adminResource);
        $this->addResource(new Zend_Acl_Resource('admin_page'), $adminResource);

        $this->addRole(new Zend_Acl_Role(App_Acl_Roles::GUEST));
        $this->addRole(new Zend_Acl_Role(App_Acl_Roles::CLIENT), App_Acl_Roles::GUEST);
        $this->addRole(new Zend_Acl_Role(App_Acl_Roles::ADMIN), App_Acl_Roles::CLIENT);

        $this->deny();

        $this->allow(App_Acl_Roles::GUEST, 'index');
        $this->allow(App_Acl_Roles::GUEST, 'payment');
        $this->allow(App_Acl_Roles::CLIENT, 'user');
        $this->allow(App_Acl_Roles::CLIENT, 'panel');

        // Allow all to administrator
        $this->allow(App_Acl_Roles::ADMIN);
    }

    /**
     * Check if user has permission to the requested resource
     *
     * @param null $resource
     * @param null $privilege
     *
     * @return bool Return true if user has permission
     */
    public static function checkPermissions($resource = null, $privilege = null)
    {
        $acl = new App_Acl();

        $auth = Zend_Auth::getInstance()->getIdentity();

        $role = App_Acl_Roles::GUEST;

        if (isset($auth->role) && $auth->role) {
            $role = $auth->role;
        }

        return $acl->isAllowed($role, $resource, $privilege);
    }
}
Note: your auth instance must contains 'role' property.
Then create file library/App/Acl/Roles.php:
/**
 * Class App_Acl_Roles
 */
class App_Acl_Roles
{
    const ADMIN = 'admin';
    const CLIENT = 'client';
    const GUEST = 'guest';
}
From this moment you can add to your controller:
public function preDispatch()
{
  parent::preDispatch();

  if (!App_Acl::checkPermissions($this->getRequest()->getModuleName())) {
      $this->redirect('/login');
  }
}
And if user doesn't have permissions to the controller - it will be redirected to the login page or whatever you want.

2014-11-08

Writing errors/exceptions to a log file in zend framework 1

Add to application.ini
resources.log.stream.writerName = "Stream"
resources.log.stream.writerParams.stream = APPLICATION_PATH "/data/logs/application.log"
resources.log.stream.writerParams.mode = "a"
resources.log.stream.filterName = "Priority"
resources.log.stream.filterParams.priority = 5
Add method to application/Bootstrap.php:
/**
* Error handler
*
* @param $errno
* @param $errstr
* @param $errfile
* @param $errline
*
* @throws ErrorException
*/
public function exceptionErrorHandler($errno, $errstr, $errfile, $errline)
{
  throw new ErrorException($errstr, $errno, 0, $errfile, $errline);
}
and to constructor in the same file:
set_error_handler([$this, 'exceptionErrorHandler']);
Then in application/controllers/ErrorController.php. Find method "errorAction" and replace:
$log->log($this->view->message, $priority, $errors->exception);
$log->log('Request Parameters', $priority, $errors->request->getParams());
with
$logMessage = $errors->exception->getMessage() . PHP_EOL .
    $errors->exception->getTraceAsString() . PHP_EOL .
    'Request Parameters:' . PHP_EOL .
    var_export($errors->request->getParams(), true) . PHP_EOL .
    str_repeat('-', 50) . PHP_EOL;
$log->log($logMessage, $priority, $errors->exception);
And in the same file change $priority = Zend_Log::CRIT; to $priority = Zend_Log::NOTICE;
Don't forget to create application/data/logs/application.log and set writing permissions to it. Exceptions will be written to this file.

2014-11-06

How to enable ssl/https in zend framework 1

Add this to application/Bootstrap.php:
protected function _initForceSSL() {
    if($_SERVER['SERVER_PORT'] != '443') {
        header('Location: https://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']);
        exit();
    }
}

2014-11-04

Localization in zend framework 1

In zf1 you can localize text, date, numbers and many other.
First of all you have to create folders for each locale. E.g. for russian locale: application/languages/ru_RU/, where ru - locale, RU - country.
Then you can create files with translations. As example create FormLabel.csv in your ru_RU folder:
"Login:";"Логин:"
"Password:";"Пароль:"
"Password confirmation:";"Подтверждение пароля:"
"First name:";"Имя:"
"Last name:";"Фамилия:"
Also you need to add locale initialization to your bootstrap file (application/Bootstrap.php):
protected function _initTranslate()
{
  $locale = new Zend_Locale('ru_RU');
  $registry = Zend_Registry::getInstance();
  $registry->set('Zend_Locale', $locale);

  $translate = new Zend_Translate(Zend_Translate::AN_CSV, APPLICATION_PATH . '/languages', null,
      ['scan' => Zend_Translate::LOCALE_DIRECTORY]);

  $registry->set('Zend_Translate', $translate);
}
From this moment localization will be enabled and if you create form with label "Login:" it'll be displayed as "Логин:".
But if you want to handy translate text in your templates add this function
function __()
{
    return Zend_Registry::get('Zend_Translate')->translate(func_get_args());
}
after
require_once 'Zend/Application.php';
to public/index.php.
And then in your phtml you can translate text:
<?= __('Password:') ?>

2014-10-30

Creating pagination in zf1

For creating pagination in zend framework 1 you have to perform several steps:
1) Set up pagination template.
Create file application/views/scripts/pagination.phtml:
<div>
    <ul class="pagination">
        <?php if (isset($this->previous)): ?>
            <li><a href="<?= $this->url(array('page' => $this->first)); ?>"><span>&laquo;</span></a>
        <?php else: ?>
            <li class="disabled"><span>&laquo;</span></li>
        <?php endif; ?>

        <?php if (isset($this->previous)): ?>
            <li><a href="<?= $this->url(array('page' => $this->previous)); ?>">&larr;</a></li>
        <?php else: ?>
            <li class="disabled"><span>&larr;</span></li>
        <?php endif; ?>
        <!-- Numbered page links -->
        <?php foreach ($this->pagesInRange as $page): ?>
            <?php if ($page != $this->current): ?>
                <li><a href="<?= $this->url(array('page' => $page)); ?>"><?= $page; ?></a></li>
            <?php else: ?>
                <li class="active"><span><?= $page; ?></span></li>
            <?php endif; ?>
        <?php endforeach; ?>
        <!-- Next page link -->
        <?php if (isset($this->next)): ?>
            <li><a href="<?= $this->url(array('page' => $this->next)); ?>">&rarr;</a></li>
        <?php else: ?>
            <li class="disabled"><span>&rarr;</span></li>
        <?php endif; ?>
        <!-- Last page link -->
        <?php if (isset($this->next)): ?>
            <li><a href="<?= $this->url(array('page' => $this->last)); ?>">&raquo;</a></li>
        <?php else: ?>
            <li class="disabled"><span>&raquo;</span></li>
        <?php endif; ?>
    </ul>
</div>
2) Create an instance of Zend_Paginator with paginator adapter.
For instance, you can create method getPaginator in your table model:
/**
 * Class Application_Model_DbTable_Task
 */
class Application_Model_DbTable_Task extends Zend_Db_Table_Abstract
{
    protected $_name = 'task';
    protected $_rowClass = 'Application_Model_Task';

    public function getPaginator(array $where = [])
    {
        $db = $this->getAdapter();
        $select = $db->select();
        $select->from($this->_name);

        foreach ($where as $key => $value) {
            $select->where($key, $value);
        }

        $select->order('created_at DESC');

        $adapter = new Zend_Paginator_Adapter_DbSelect($select);
        $paginator = new Zend_Paginator($adapter);

        return $paginator;
    }
}
and in your controller:
$user = Application_Model_User::getCurrent();
$tableTask = new Application_Model_DbTable_Task();
$paginator = $tableTask->getPaginator(['id_user = ?' => $user->id]);

$page = $this->getRequest()->getParam('page', 1);
$paginator->setCurrentPageNumber($page);
$config = $this->getInvokeArg('bootstrap')->getOptions();
$paginator->setItemCountPerPage($config['pagination']['per_page']);

$this->view->paginator = $paginator;
3) Configure pagination in settings file and in your bootstrap.
Add to application.ini:
pagination.per_page = 10
and in bootstrap:
protected function _initPagination()
{
    Zend_Paginator::setDefaultScrollingStyle('Sliding');
    Zend_View_Helper_PaginationControl::setDefaultViewPartial(
        'pagination.phtml'
    );
}
4) Print pagination html.
Add in view template:
<?php if (count($this->paginator)): ?>
    <?php foreach ($this->paginator as $item): ?>
        <div class="item">
            <?= $item['value'] ?> (<?= $item['state'] ?>)
        </div>
    <?php endforeach; ?>
<?php else: ?>
    <div class="empty">
        Nothing is found.
    </div>
<?php endif; ?>

<?= $this->paginator ?>
where $item - a row from db

Multi checkbox or multiselect in zend framework 1

For creating a form with multi checkboxes you can add to your form:
$possibleExchange = $this->createElement('MultiCheckbox', 'possible_exchange');
$possibleExchange->setRequired(true)->setLabel('MyTitle')->addMultiOptions($items);
then in populating the form you have to do something like that:
$populateData = $epsCurrencyItem->toArray();
$populateData['possible_exchange'] = explode(',', $populateData['possible_exchange']);
$form->populate($populateData);
this is needed for selecting checkboxes.
And when you save data to db you have to:
$data['possible_exchange'] = implode(',', $data['possible_exchange']);
$epsCurrencyItem->setFromArray($data);
$epsCurrencyItem->save();
Similarly you can use multiselect. Difference will be only when you create the form:
$possibleExchange = $this->createElement('multiselect', 'possible_exchange');
$possibleExchange->setRequired(true)
   ->setLabel('MyTitle')
   ->setAttrib('size', 10)
   ->addMultiOptions($items);

2014-08-30

Sending POST/GET request with Zend Framework 1

For POST request:
$data = [
 'login' => 'admin',
 'password' => 'pass',
];
$client = new Zend_Http_Client('https://your.URL');
$client->setMethod(Zend_Http_Client::POST);
$client->setParameterPost($data);
$json = $client->request()->getBody();
For GET request:
$client = new Zend_Http_Client('https://blahblahblah.blah');
$client->setMethod(Zend_Http_Client::GET);
$client->request();

Get config in zf 1

From controller:
$config = $this->getInvokeArg('bootstrap')->getOptions();
then $config is associative array
From other places:
$config = Zend_Controller_Front::getInstance()->getParam('bootstrap')->getOptions();

2014-08-22

How to add own class to library in ZF1

You have to add to your application.ini:
autoloadernamespaces.broker = "Broker_"
where Broker is your directory into library folder. As a result you can create class Broker_Object in /library/Broker/Object.php and it will be loaded by autoloader.

2014-08-14

How to add compare link to top links

First of all you have to create helper in your own module with two methods:
/**
 * Return label for comparing products list
 *
 * @return int
 */
public function getCompareLabel()
{
    $count = Mage::helper('catalog/product_compare')->getItemCollection()->getSize();
    return $this->__('Compare (%s)', $count);
}

/**
 * Return whether visible compare link
 *
 * @return bool
 */
public function getCompareLinkIsVisibleClass()
{
    $count = Mage::helper('catalog/product_compare')->getItemCollection()->getSize();

    if ($count > 0) {
        return '';
    }

    return 'class="no-display"';
}
Then add to layout xml (in block where you want to add link):

 
Where helper="slirx_ajaxpanel - your helper, module="slirx_ajaxpanel" - your module.

And in app.js (for rwd theme, for another you have to add this code to any js file that will be loaded on pages with your compare link)
$j('body').delegate('#header-account .compare', 'click', function(event) {
 var _url = $j(this).attr('href');
 popWin(_url, 'compare', 'top:0,left:0,width=820,height=600,resizable=yes,scrollbars=yes');

 event.preventDefault();
});

2014-08-07

Filter categories by store

Getting categories from specific store:
$storeId = 5;
$categoryId = Mage::app()->getStore($storeId)->getRootCategoryId();
$categories = Mage::getModel('catalog/category')->getCollection()
->addFieldToFilter('path', array('like'=> "1/$categoryId/%"))

2014-08-01

Magento: get product qty/count/quantity

$qtyStock = (int)Mage::getModel('cataloginventory/stock_item')->loadByProduct($product)->getQty();
where $product - loaded magento product (Mage_Catalog_Model_Product);

2014-07-22

Move category

$category = Mage::getModel('catalog/category')->load($idCategoryForMoving);
$category->move($idParentCategory, $idAfterCategory);

Getting filterable attributes in category

Getting filterable attributes from layered navigation
$category = Mage::registry('current_category');
$products = $category->getProductCollection();

$appliedFilters = Mage::getSingleton('catalog/layer')->getFilterableAttributes();
foreach ($appliedFilters as $filter) {
  if ($value = $this->getRequest()->getParam($filter->getAttributeCode())) {
      $products->addFieldToFilter($filter->getAttributeCode(), $value);
  }
}
Method getFilterableAttributes returns all available attributes for filtering. Then in foreach we check if an attribute has been selected by user, we add it to the filter.

2014-07-17

Increase the size of a VDI file

You can only increase size
VBoxManage modifyhd YourFile.vdi --resize 32240

2014-07-16

Include third party libraries

For including third party libraries from lib folder
require_once(Mage::getBaseDir('lib') . '/simple_html_dom.php');

2014-06-27

Determine page type in magento

For category page (in layout xml file):

 
  
   category
  
 

And in your block class you can get value of page type:
$this->setPageType()
For other pages you can do the same - just replace catalog_category_view with your action.

2014-06-23

Add column in magento install/upgrade script

For adding a new column to table in magento installer you have to add to your install/upgrade script:
$installer = $this;
$installer->startSetup();
// ...
$installer->getConnection()->addColumn(
    $this->getTable('sales/quote_payment'),
    'billing_code', // column name
    'VARCHAR(255) NOT NULL' // type definition
);
// ...
$installer->endSetup();

2014-06-09

Rewrite controllers in magento

For rewriting controller in magento you have to add to your module config.xml:

 
  
   
    
     SliRx_Test
    
   
  
 

where Mage_Checkout - module with a controller which you are going to rewrite.
SliRx_Test - your module which contains the controller for rewrite.

The above config means that magento will search the controller in your module first and after that, if doesn't find it, in Mage_Checkout.
Then you have to create the file SliRx/Test/controllers/CartController.php:
require_once(Mage::getModuleDir('controllers', 'Mage_Checkout') . DS . 'CartController.php');

class SliRx_Test_CartController extends Mage_Checkout_CartController
{
    public function indexAction()
    {
        echo "it's cart!";
    }
}
But magento doesn't know about Mage_Checkout_CartController and you must include the file with this class by yourself. That's why before declaring SliRx_Test_CartController calls require_once;

Debug layout updates in magento

For each request magento sees all layout updates (located at design layout folder) and find the appropriate handles. To get list of handles which appropriate to the current request you have to write in controller:
$this->loadLayout();
echo '<pre>';
print_r($this->getLayout()->getUpdate()->getHandles());
echo '</pre>';
exit;
The result will be something like that:
Array
(
    [0] => default
    [1] => STORE_default
    [2] => THEME_frontend_default_default
    [3] => catalog_category_view
    [4] => customer_logged_out
)
Then magento merges all layout updates for handles in the above array to one xml file. To show this file you have to write in controller (at the beginning of method):
header('Content-type: text/xml');
$this->loadLayout();
echo $this->getLayout()->getNode()->asXML();
exit;

2014-06-08

Possible values for addAttributeToFilter

Collections in Magento have the method addAttributeToFilter. It filters entities.
This method takes an attribute code and a condition.
$products = Mage::getModel('catalog/product')->getCollection();
$products->addAttributeToFilter('sku', 'test-sku');
$products->load();
Where 'test-sku' - value of condition.
If value of condition is integer or string - exact value will be filtered with 'eq' condition).
If value of condition is array - one of the following structures is expected:
array("from" => $fromValue, "to" => $toValue)
array("eq" => $equalValue)
array("neq" => $notEqualValue)
array("like" => $likeValue)
array("in" => array($inValues))
array("nin" => array($notInValues))
array("notnull" => $valueIsNotNull)
array("null" => $valueIsNull)
array("moreq" => $moreOrEqualValue)
array("gt" => $greaterValue)
array("lt" => $lessValue)
array("gteq" => $greaterOrEqualValue)
array("lteq" => $lessOrEqualValue)
array("finset" => $valueInSet)
array("regexp" => $regularExpression)
array("seq" => $stringValue)
array("sneq" => $stringValue)

2014-06-07

Rewrite class/method in Magento

To rewrite class method you have to add rewrite config to etc/config.xml:

 
  
   
    
     SliRx_Test_Model_Category
    
   
  
 

Where tags "catalog" and "category" are assigned to Mage::getModel('catalog/category')
Than create corresponding class at app/code/local/SliRx/Test/Model/Category.php:
class SliRx_Test_Model_Category extends Mage_Catalog_Model_Category
{
    public function getChildren()
    {
        return $this->getResource()->getChildren($this, false);
    }
}

2014-06-05

Insert tabs in magento 1.9 responsive theme

For creating tabs you need to write some html:
Title 1
Content html 1
Title 2
Content html 2
Where ajax-tabs - your own class.
And include styles in your scss file:
@include bp(min-width, $bp-medium + 1) {
    .ajax-tabs {
        @include tabs;
    }
}

@include bp(max-width, $bp-medium) {
    .ajax-tabs {
        @include accordion;
        @include accordionCollapse;
    }
}

2014-05-14

Install/rescue/restore grub2

To restore grub2 run this commands:
sudo mount /dev/sda1 /mnt
where /dev/sda1 - partition for grub (with installed linux)
sudo mount --bind /dev /mnt/dev
sudo mount --bind /proc /mnt/proc
sudo chroot /mnt
sudo grub-install /dev/sda
If output has "chroot: cannot run command `/bin/bash': Exec format error" then your live CD architecture doesn't match with restorable.

2014-05-07

SSH host alias

To create ssh alias you have to create the file ~/.ssh/config with similar lines:
Host your-host
 HostName your-server.com
 Port 22
 user your-ssh-username
Then for connecting to server: ssh your-host

2014-04-23

Delete header and footer from magento custom page

For deleting header and footer from custom page add this lines to layout configuration:
<remove name="header"/>
<remove name="footer"/>

2014-03-27

Интеграция magerun с phpstorm

Для интеграции magerun (n98-magerun) открываем в phpstorm File->Settings->Command Line Tool Support.
Нажимаем плюсик. В появившемся окне в "Choose tool" выбираем "Tool based on Synfony Console". В "Visibility" выбираем "global". В следующем окне указываем путь к файлу (Например ""/home/slirx/web_dev/n98-magerun.phar") и алиас, через который мы будем обращаться к скрипту (например "m").
Сохраняем изменения и открываем Command Line Tool Console в Tools->Run Command или с помощью Ctrl+Shift+X.

2014-03-26

Создание ключей для bitbucket / github

Генерируем ключ:
ssh-keygen -t rsa -f ~/.ssh/rsa_id_bitbucket -C "your.email@gmail.com"
rsa_id_bitbucket - имя файла ключа. Для гитхаба можем использовать rsa_id_github
Добавляем ключ:
ssh-add ~/.ssh/rsa_id_bitbucket
Копируем содержимое файла ~/.ssh/rsa_id_bitbucket.pub на bitbucket/github
cat ~/.ssh/rsa_id_bitbucket.pub
Проверяем работоспособность:
ssh -T git@bitbucket.com

2014-03-22

Сохранение базы данных в git

Для сохранение дампа базы данных при каждом коммите можно использовать git hooks (почитать больше).
Для этого создаем файл pre-commit в папке .git/hooks в вашем репозитории с таким содержимым:
#!/bin/sh
mysqldump -u ПользовательБД --password=ПарольБД ИмяБД > var/backup/db.sql
git add var/backup/db.sql
exit 0
Даем файлу доступ на выполнение. И все. Теперь перед каждым коммитом будет делаться дамп и записываться в папку var/backup проекта.

2014-03-20

Google Adwords API ошибка "WebLoginRequired"

При первом логине пользователя в Google Adwords API возникает ошибка: "BadAuthentication: WebLoginRequired".

Для ее решения надо перейти по ссылке https://accounts.google.com/DisplayUnlockCaptcha и разрешить доступ, после этого API будет работать.

Также надо разрешить доступ на странице https://security.google.com/settings/security/activity

2014-03-13

Внешнее исполнение кода magento

Для использования кода magento вне рабочей директории сайта:
require_once('/path/to/Mage.php/file/../app/Mage.php');
umask(0);
Mage::app();

2014-02-28

Неправильный путь к файлам css/js

Если путь к файлам css/js выглядит как полный системный путь к файлу (например /home/slirx/web_dev/m.local/js/prototype/window.js) значит нет доступа на папки media/css (media/js).