Monday, December 30, 2013

Installing Subversion And Configuring Access Through Different Protocols On Ubuntu

Subversion is an open source version control system. Using Subversion, you can record the history of source files and documents. It manages files and directories over time. A tree of files is placed into a central repository. The repository is much like an ordinary file server, except that it remembers every change ever made to files and directories.

Installation

To access Subversion repository using the HTTP protocol, you must install and configure a web server. Apache2 is proven to work with Subversion. Please refer to the HTTP subsection in the Apache2 section to install and configure Apache2. To access the Subversion repository using the HTTPS protocol, you must install and configure a digital certificate in your Apache 2 web server. Please refer to the HTTPS subsection in the Apache2 section to install and configure the digital certificate.
To install Subversion, run the following command from a terminal prompt:

sudo apt-get install subversion libapache2-svn

Server Configuration

This step assumes you have installed above mentioned packages on your system. This section explains how to create a Subversion repository and access the project.

Create Subversion Repository

The Subversion repository can be created using the following command from a terminal prompt:

svnadmin create /path/to/repos/project

Importing Files

Once you create the repository you can import files into the repository. To import a directory, enter the following from a terminal prompt:
svn import /path/to/import/directory file:///path/to/repos/project

Access Methods

Subversion repositories can be accessed (checked out) through many different methods --on local disk, or through various network protocols. A repository location, however, is always a URL. The table describes how different URL schemes map to the available access methods.
Table 16.1. Access Methods
SchemaAccess Method
file://direct repository access (on local disk)
http://Access via WebDAV protocol to Subversion-aware Apache2 web server
https://Same as http://, but with SSL encryption
svn://Access via custom protocol to an svnserve server
svn+ssh://Same as svn://, but through an SSH tunnel

In this section, we will see how to configure Subversion for all these access methods. Here, we cover the basics. For more advanced usage details, refer to the svn book.

Direct repository access (file://)

This is the simplest of all access methods. It does not require any Subversion server process to be running. This access method is used to access Subversion from the same machine. The syntax of the command, entered at a terminal prompt, is as follows:

svn co file:///path/to/repos/project

or

svn co file://localhost/path/to/repos/project

[Note]
If you do not specify the hostname, there are three forward slashes (///) -- two for the protocol (file, in this case) plus the leading slash in the path. If you specify the hostname, you must use two forward slashes (//).
The repository permissions depend on filesystem permissions. If the user has read/write permission, he can checkout from and commit to the repository.

Access via WebDAV protocol (http://)

To access the Subversion repository via WebDAV protocol, you must configure your Apache 2 web server. Add the following snippet between the <VirtualHost> and </VirtualHost> elements in /etc/apache2/sites-available/default, or another VirtualHost file:
 <Location /svn>
  DAV svn
  SVNPath /home/svn
  AuthType Basic
  AuthName "Your repository name"
  AuthUserFile /etc/subversion/passwd
  Require valid-user
 </Location> 
[Note]
The above configuration snippet assumes that Subversion repositories are created under /home/svn/directory using svnadmin command. They can be accessible usinghttp://hostname/svn/repos_name url.
To import or commit files to your Subversion repository over HTTP, the repository should be owned by the HTTP user. In Ubuntu systems, normally the HTTP user is www-data. To change the ownership of the repository files enter the following command from terminal prompt:
sudo chown -R www-data:www-data /path/to/repos
[Note]
By changing the ownership of repository as www-data you will not be able to import or commit files into the repository by running svn import file:/// command as any user other than www-data.
Next, you must create the /etc/subversion/passwd file that will contain user authentication details. To create a file issue the following command at a command prompt (which will create the file and add the first user):
sudo htpasswd -c /etc/subversion/passwd user_name
To add additional users omit the "-c" option as this option replaces the old file. Instead use this form:
sudo htpasswd /etc/subversion/password user_name
This command will prompt you to enter the password. Once you enter the password, the user is added. Now, to access the repository you can run the following command:
                    
                      svn co http://servername/svn
                    
                  
[Warning]
The password is transmitted as plain text. If you are worried about password snooping, you are advised to use SSL encryption. For details, please refer next section.

Access via WebDAV protocol with SSL encryption (https://)

Accessing Subversion repository via WebDAV protocol with SSL encryption (https://) is similar to http:// except that you must install and configure the digital certificate in your Apache2 web server. To use SSL with Subversion add the above Apache2 configuration to /etc/apache2/sites-available/default-ssl. For more information on setting up Apache2 with SSL see the section called “HTTPS Configuration”.
You can install a digital certificate issued by a signing authority. Alternatively, you can install your own self-signed certificate.
This step assumes you have installed and configured a digital certificate in your Apache 2 web server. Now, to access the Subversion repository, please refer to the above section! The access methods are exactly the same, except the protocol. You must use https:// to access the Subversion repository.

Access via custom protocol (svn://)

Once the Subversion repository is created, you can configure the access control. You can edit the/path/to/repos/project/conf/svnserve.conf file to configure the access control. For example, to set up authentication, you can uncomment the following lines in the configuration file:
# [general]
# password-db = passwd
After uncommenting the above lines, you can maintain the user list in the passwd file. So, edit the file passwd in the same directory and add the new user. The syntax is as follows:
username = password
For more details, please refer to the file.
Now, to access Subversion via the svn:// custom protocol, either from the same machine or a different machine, you can run svnserver using svnserve command. The syntax is as follows:
$ svnserve -d --foreground -r /path/to/repos
# -d -- daemon mode
# --foreground -- run in foreground (useful for debugging)
# -r -- root of directory to serve

For more usage details, please refer to:
$ svnserve --help
Once you run this command, Subversion starts listening on default port (3690). To access the project repository, you must run the following command from a terminal prompt:

svn co svn://hostname/project project --username user_name

Based on server configuration, it prompts for password. Once you are authenticated, it checks out the code from Subversion repository. To synchronize the project repository with the local copy, you can run the update sub-command. The syntax of the command, entered at a terminal prompt, is as follows:

cd project_dir ; svn update

For more details about using each Subversion sub-command, you can refer to the manual. For example, to learn more about the co (checkout) command, please run the following command from a terminal prompt:
                    
                      svn co help
                    
                  

Access via custom protocol with SSL encryption (svn+ssh://)

The configuration and server process is same as in the svn:// method. For details, please refer to the above section. This step assumes you have followed the above step and started the Subversion server using svnserve command.
It is also assumed that the ssh server is running on that machine and that it is allowing incoming connections. To confirm, please try to login to that machine using ssh. If you can login, everything is perfect. If you cannot login, please address it before continuing further.
The svn+ssh:// protocol is used to access the Subversion repository using SSL encryption. The data transfer is encrypted using this method. To access the project repository (for example with a checkout), you must use the following command syntax:

svn co svn+ssh://hostname/var/svn/repos/project

[Note]
You must use the full path (/path/to/repos/project) to access the Subversion repository using this access method.
Based on server configuration, it prompts for password. You must enter the password you use to login via ssh. Once you are authenticated, it checks out the code from the Subversion repository.

Thursday, December 5, 2013

Magento : Display product count in Subcategory menu

Magento is the most popular use for ecommerce solutions in the web, because there have already useful built in functions and modules such a newsletter sending, credit card payments, strong securities and more and make life easy as a magento developer. In some our work projects, we need to add extra functionally in Magento Store, such a creating custom magento modules, creating custom widgets, add custom pages. In this tutorial, I would like to share on how get the count of products in Subcategories in navigation and append the count in sub category name. In the screenshot below, we appended the count of products in corresponding subcategories, this is very easy to add. This works in Magento Community Edition 1.7.0.2. However if Magento Community release the new version 1.8.x and up, I will update this post and test the compatibility of the module.

What will you do is go to the core directory '\app\code\core\Mage\Page\Block\Html\Topmenu.php' and copy the Topmenu.php, and create a override copy in local folder like '\app\code\local\Mage\Page\Block\Html\Topmenu.php' and modify it. The Modify code shown below.

<?php

class Mage_Page_Block_Html_Topmenu extends Mage_Core_Block_Template {
    /**
     * Top menu data tree
     *
     * @var Varien_Data_Tree_Node
     */
    protected $_menu;

    /**
     * Init top menu tree structure
     */
    public function _construct()
    {
        $this->_menu = new Varien_Data_Tree_Node(array(), 'root', new Varien_Data_Tree());
    }

    /**
     * Get top menu html
     *
     * @param string $outermostClass
     * @param string $childrenWrapClass
     * @return string
     */
    public function getHtml($outermostClass = '', $childrenWrapClass = '')
    {
        Mage::dispatchEvent('page_block_html_topmenu_gethtml_before', array(
            'menu' => $this->_menu
        ));

        $this->_menu->setOutermostClass($outermostClass);
        $this->_menu->setChildrenWrapClass($childrenWrapClass);

        $html = $this->_getHtml($this->_menu, $childrenWrapClass);

        Mage::dispatchEvent('page_block_html_topmenu_gethtml_after', array(
            'menu' => $this->_menu,
            'html' => $html
        ));

        return $html;
    }

    /**
     * Recursively generates top menu html from data that is specified in $menuTree
     *
     * @param Varien_Data_Tree_Node $menuTree
     * @param string $childrenWrapClass
     * @return string
     */
    protected function _getHtml(Varien_Data_Tree_Node $menuTree, $childrenWrapClass)
    {
        $html = '';

        $children = $menuTree->getChildren();
        $parentLevel = $menuTree->getLevel();
        $childLevel = is_null($parentLevel) ? 0 : $parentLevel + 1;

        $counter = 1;
        $childrenCount = $children->count();

        $parentPositionClass = $menuTree->getPositionClass();
        $itemPositionClassPrefix = $parentPositionClass ? $parentPositionClass . '-' : 'nav-';

        foreach ($children as $child) {

            $child->setLevel($childLevel);
            $child->setIsFirst($counter == 1);
            $child->setIsLast($counter == $childrenCount);
            $child->setPositionClass($itemPositionClassPrefix . $counter);

            $outermostClassCode = '';
            $outermostClass = $menuTree->getOutermostClass();

            if ($childLevel == 0 && $outermostClass) {
                $outermostClassCode = ' class="' . $outermostClass . '" ';
                $child->setClass($outermostClass);
            }

   /* custom code */
   $category = Mage::getModel('catalog/category')->load(str_replace('category-node-', '', $child->getId()));
   $prodCollection = Mage::getResourceModel('catalog/product_collection')->addCategoryFilter($category);
            /* cuatom code end */

   $html .= '<li ' . $this->_getRenderedMenuItemAttributes($child) . '>';

   /* original */
   // $html .= '<a href="' . $child->getUrl() . '" ' . $outermostClassCode . '><span>' . $this->escapeHtml($child->getName()) . '</span></a>';

   /* custom count products */
   $html .= '<a href="' . $child->getUrl() . '" ' . $outermostClassCode . '><span>' . $this->escapeHtml($child->getName()) . (is_null($parentLevel) ? '' : ' ('.$prodCollection->count().')') . '</span>' . '</a>';

            if ($child->hasChildren()) {
                if (!empty($childrenWrapClass)) {
                    $html .= '<div class="' . $childrenWrapClass . '">';
                }
                $html .= '<ul class="level' . $childLevel . '">';
                $html .=  $this->_getHtml($child , $childrenWrapClass);
                $html .= '</ul>';

                if (!empty($childrenWrapClass)) {
                    $html .= '</div>';
                }
            }
            $html .= '</li>';

            $counter++;
        }

        return $html;
    }

    /**
     * Generates string with all attributes that should be present in menu item element
     *
     * @param Varien_Data_Tree_Node $item
     * @return string
     */
    protected function _getRenderedMenuItemAttributes(Varien_Data_Tree_Node $item)
    {
        $html = '';
        $attributes = $this->_getMenuItemAttributes($item);

        foreach ($attributes as $attributeName => $attributeValue) {
            $html .= ' ' . $attributeName . '="' . str_replace('"', '\"', $attributeValue) . '"';
        }

        return $html;
    }

    /**
     * Returns array of menu item's attributes
     *
     * @param Varien_Data_Tree_Node $item
     * @return array
     */
    protected function _getMenuItemAttributes(Varien_Data_Tree_Node $item)
    {
        $menuItemClasses = $this->_getMenuItemClasses($item);
        $attributes = array(
            'class' => implode(' ', $menuItemClasses)
        );

        return $attributes;
    }

    /**
     * Returns array of menu item's classes
     *
     * @param Varien_Data_Tree_Node $item
     * @return array
     */
    protected function _getMenuItemClasses(Varien_Data_Tree_Node $item)
    {
        $classes = array();

        $classes[] = 'level' . $item->getLevel();
        $classes[] = $item->getPositionClass();

        if ($item->getIsFirst()) {
            $classes[] = 'first';
        }

        if ($item->getIsActive()) {
            $classes[] = 'active';
        }

        if ($item->getIsLast()) {
            $classes[] = 'last';
        }

        if ($item->getClass()) {
            $classes[] = $item->getClass();
        }

        if ($item->hasChildren()) {
            $classes[] = 'parent';
        }

        return $classes;
    }
}

Magento: Add additional product/item attributes in order, invoice and shippment View Information (Admin End)

To add below code in /app/design/Adminhtml/default/default/template/sales/items/column/name.phtml


 "

 <?php 
echo $Author = Mage::getModel('catalog/product')->load($_item['product_id'])->getData('author') 

?>

"
 -------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------

<?php if ($_item = $this->getItem()): ?>
    <h5 class="title"><span id="order_item_<?php echo $_item->getId() ?>_title"><?php echo $this->htmlEscape($_item->getName()) ?></span></h5>
    <div><strong><?php echo $this->helper('sales')->__('SKU') ?>:</strong> <?php echo implode('<br />', Mage::helper('catalog')->splitSku($this->htmlEscape($this->getSku()))); ?>
<?php echo "<p><b>Author:</b>&nbsp;"; ?>
 <?php echo $Author = Mage::getModel('catalog/product')->load($_item['product_id'])->getData('author') ?>
<?php echo "</p>"; ?>

<?php echo "<p><b>Publisher:</b>&nbsp;"; ?>
 <?php echo $Author = Mage::getModel('catalog/product')->load($_item['product_id'])->getData('publishers') ?>
<?php echo "</p>"; ?>

<?php echo "<p><b>Published Year:</b>&nbsp;"; ?>
 <?php echo $Author = Mage::getModel('catalog/product')->load($_item['product_id'])->getData('publication_year') ?>
<?php echo "</p>"; ?>

<?php echo "<p><b>Edition:</b>&nbsp;"; ?>
 <?php echo $Author = Mage::getModel('catalog/product')->load($_item['product_id'])->getData('edition') ?>
<?php echo "</p>"; ?>

<?php echo "<p><b>Language:</b>&nbsp;"; ?>
 <?php echo $Author = Mage::getModel('catalog/product')->load($_item['product_id'])->getData('language') ?>
<?php echo "</p>"; ?>

<?php echo "<p><b>ISBN 10:</b>&nbsp;"; ?>
 <?php echo $Author = Mage::getModel('catalog/product')->load($_item['product_id'])->getData('isbn10') ?>
<?php echo "</p>"; ?>


<?php echo "<p><b>ISBN 13:</b>&nbsp;"; ?>
 <?php echo $Author = Mage::getModel('catalog/product')->load($_item['product_id'])->getData('isbn13') ?>
<?php echo "</p>"; ?>


</div>

Magento: Add additional product/item attributes in order and invoice emails

Magento provides basic information in the default email templates, but each store has their unique requirement to show additional information.
I will show you how to add extra product attribute values, along with order item options and custom options in order emails and invoice emails.
Here is the code that should work for order and invoice emails to get additional PRODUCT ATTRIBUTES displayed:

$productId = $_item->getProduct()->getId(); //for order emails
//$productId = $_item->getProductId(); //for invoice emails
$product = Mage::getModel('catalog/product')->load($productId);
$attributes = $product->getAttributes();

//Get a list of all PRODUCT ATTRIBUTES you want to show in this array...
$dispAttribs = array('hardrive', 'memory', 'processor'); 

foreach ($attributes as $attribute) {    
        $attributeCode = $attribute->getAttributeCode();
        if(!in_array($attributeCode, $dispAttribs)) continue;
        $label = $attribute->getFrontend()->getLabel($product);
        $value = $attribute->getFrontend()->getValue($product); 
        echo "<br /><strong>" . $label . ":</strong> " . $value;
}




For displaying CUSTOM OPTIONS and/or ITEM OPTIONS from the item, use this:


foreach($this->getItemOptions() as $opt) {
    if(isset($opt['option_id'])) { //for CUSTOM OPTIONS
            echo "<strong>" . $opt['label'] . ":</strong> ". $opt['option_value'] . "<br />";
    } else { //for ITEM OPTIONS
            echo "<strong>" . $opt['label'] . ":</strong> ". $opt['value'] . "<br />";
    }
}



For adding code to ORDER emails, the file where the code should go is:
app/design/frontend/base/default/template/email/order/items/order/default.phtml
For adding code to INVOICE emails, the file where the code should go is:
app/design/frontend/base/default/template/email/order/items/invoice/default.phtml
Instead of base/default, you can put it in your custom theme location which is obvious.

example:


<?php
$productId = $_item->getProduct()->getId(); //for order emails
$product = Mage::getModel('catalog/product')->load($productId);
$attributes = $product->getAttributes();
$dispAttribs = array('author', 'publishers', 'publication_year', 'language', 'isbn10', 'isbn13');
foreach ($attributes as $attribute) {   
        $attributeCode = $attribute->getAttributeCode();
        if(!in_array($attributeCode, $dispAttribs)) continue;
        $label = $attribute->getFrontend()->getLabel($product);
        $value = $attribute->getFrontend()->getValue($product);
    if($value!='')
    {
        echo "<p><strong>" . $label . ":</strong> " . $value."</p>";
    }
}
?>

Thursday, October 17, 2013

Custom admin theme in Magento


Featured Image
As mentioned on Magento forums the easiest way to achieve this is with overriding adminhtml config with your local custom one and activate it as module.
This is just a small example of different approach with Admin Theme config option in admin panel, to show you how things can be done in different ways in Magento.

Since this is one of those “code talks, talk walks” examples, here it is: admintheme_example.rar.

It’s great example of small Magento module with simple event hooking and adding configuration fields through system.xml.
Follow directory structure, copy files to their place and you will notice new “Admin Theme” option in System->Configuration->General->Design (Default Config scope). Your theme goes in app/design/adminhtml/default/yourthemename folder. It doesn’t need to be whole theme of course, just the files you’re changing.

How to Use Custom Admin Path in Magento 1.7.0.2

              Last couple of days I am straggling with Magento. I am using the latest version of magento which is 1.7.0.2 at this time.

              Magento is a quite smart e-commerce solution for small/mediam/large business. That’s why I choose this for our little start-up business. But sad news is, there is not enough resource for learning this nice CMS. Also maximum resource is out of date.

              For security reason I need to change the admin login URL so that no one can try to login my admin panel unless s/he know the URL. I searched a lot for change the URL path and get a lot of blog/suggestion article but none is working for me. Some suggest to change the config.xml from app/code/core/adminhtml/etc but that is not working as well.
But after examine the admin panel I found an easy solution for this issue. Magento 1.7.0.2 obviously make a vast change than 1.4 (maximum resource is available for 1.4) Login to admin panel then go to System > Configuration. From Left bar navigate Advance Section admin menu. There you find something like following

Magento Admin Base URL

Magento Admin Base URL
Click over Admin Base URL tab and in Custom Admin Path text box write your desire admin Path (ex. adminlogin) After save config you will ask to login again. Check your login URL, you will find your desire custom path (in this case adminlogin) there.

How to activate new adminhtml theme in Magento?

Within config.xml under etc folder of your module, add the following


    <stores>
        <admin>
            <design>
                <package>
                    <name>NameSpace</name>
                </package>
                <theme>
                    <default>theme</default>
                </theme>
            </design>
        </admin>
    </stores> 
 
 
Assuming you created your theme under app/design/adminhtml/NameSpace/theme

Magento: Include Layered Navigation In A CMS Page (Home Page)

This is another popular question. Often you want to show items on a CMS page rather than a regular category page. But you still want to have the layered navigation to filter results like it appears by default with Magento at the category level (when listing products).
To add Magento’s layered navigation to a CMS page where you’re showing items simply edit your CMS page in the admin – under the “Design” tab put this code:

<reference name="left">
    <block type="catalog/layer_view" name="catalog.leftnav" before="-" template="catalog/layer/view.phtml"/>
</reference>

 
That will show the layered navigation at the very top of the left column on your CMS page. Of course make sure your CMS page layout is “2column with left bar” (under “layout” in the “Design” tab).

<reference name="left">
    <block type="catalog/layer_view" name="catalog.leftnav" before="-" template="catalog/layer/view.phtml"/>
</reference>

<reference name="left">
    <block type="checkout/cart_sidebar" name="cart_sidebar" template="checkout/cart/sidebar.phtml" before="-">
<action method="addItemRender"><type>simple</type><block>checkout/cart_item_renderer</block><template>checkout/cart/sidebar/default.phtml</template></action>
        <action method="addItemRender"><type>grouped</type><block>checkout/cart_item_renderer_grouped</block><template>checkout/cart/sidebar/default.phtml</template></action>
        <action method="addItemRender"><type>configurable</type><block>checkout/cart_item_renderer_configurable</block><template>checkout/cart/sidebar/default.phtml</template></action>
        <block type="core/text_list" name="cart_sidebar.extra_actions" as="extra_actions" translate="label" module="checkout">
            <label>Shopping Cart Sidebar Extra Actions</label>
        </block>
    </block>
   
    <block type="poll/activePoll" name="left.poll" after="left.permanent.callout">
        <action method="setPollTemplate"><template>poll/active.phtml</template><type>poll</type></action>
        <action method="setPollTemplate"><template>poll/result.phtml</template><type>results</type></action>
    </block>
</reference>
<reference name="content">
   <block type="catalog/product_new" name="product_new" template="catalog/product/list.phtml">
      <action method="setColumnCount"><column_count>4</column_count></action>
      <action method="setProductsCount"><count>0</count></action>
      <block type="catalog/product_list_toolbar" name="product_list_toolbar" template="catalog/product/list/toolbar.phtml">
         <block type="page/html_pager" name="product_list_toolbar_pager" />
         <action method="setDefaultGridPerPage"><limit>12</limit></action>          
      </block>    
   </block>
</reference>

Wednesday, October 16, 2013

Displaying New Products in Magento with Pagination

Just recently we ran into an issue where we wanted to display all new products added to a specific category in our Magento site, with pagination. To make things more complicated, we wanted to display this by using a CMS page. The default functionality that Magento provides for new products just didn’t cut it in our case. We had to come up with something different and I am documenting it here for everyone’s benefit. This method will allow you to add new products to a Magento CMS page and pull up new products for any category you choose, or for the entire site. Whether you want to add new products to your CMS page by category or for the entire site, it’s up to you.

Adding New Products with Built in Magento Tools

First, let’s set the record straight. It is possible to display a list of new products in Magento using a default block. You can do this without any coding changes or modifications to Magento itself. This code will allow you to bring up a list of new products on one of your CMS pages. It is also possible to use this to bring new products up within a template file or on your homepage, though we are not covering that here.

{{block type="catalog/product_new" column_count="6" products_count="400" name="home.catalog.product.new" alias="product_homepage" template="catalog/product/new.phtml"}}

The above code will bring up your 400 newest products in 6 columns. You can change the column_count and products_count variable so that any number of columns or products is shown. You can display all of your newest products by changing the products_count variable to zero. OK, so this is all good, but it’s not what we want. We want to display an entire page of our new products with the pagination toolbar, and we want to display the products with a Magento CMS page. It’s quite simple to accomplish this, and here is how you can do it.

Display New Products in Magento with Pagination

The following steps will help you to add new products to a Magento CMS page and show pagination for the products.
  1. Create all of the folders in the following path if they do not already exist.
    app/code/local/Mage/Catalog/Block/Product
    Note: We do not want to modify core Magento code so we create our own path in the “local” folder
  2. Create a New.php file and place it in the following location:
    app/code/local/Mage/Catalog/Block/Product/New.php
    Note: We named our file New.php which worked for our purposes.
    Note: If your file is named “New.php” it will overwrite Magento’s default New.php page located in app/code/core/Mage/Catalog/Block/Product. If you are going to be using the default New.php file included with Magento in other parts of your site you may want to name your file differently.
  3. Add the following code to your New.php file (The key to the pagination is extending the List class):
    //Code
    <?php

    class Mage_Catalog_Block_Product_New extends Mage_Catalog_Block_Product_List
    {
       function get_prod_count()
       {
          //unset any saved limits
          Mage::getSingleton('catalog/session')->unsLimitPage();
          return (isset($_REQUEST['limit'])) ? intval($_REQUEST['limit']) : 12;
       }// get_prod_count

       function get_cur_page()
       {
          return (isset($_REQUEST['p'])) ? intval($_REQUEST['p']) : 1;
       }// get_cur_page

       /**
        * Retrieve loaded category collection
        *
        * @return Mage_Eav_Model_Entity_Collection_Abstract
       **/
       protected function _getProductCollection()
       {
          $todayDate  = Mage::app()->getLocale()->date()->toString(Varien_Date::DATETIME_INTERNAL_FORMAT);

          $collection = Mage::getResourceModel('catalog/product_collection');
          $collection->
    setVisibility(Mage::getSingleton('catalog/product_visibility')->getVisibleInCatalogIds());

          $collection = $this->_addProductAttributesAndPrices($collection)
             ->addStoreFilter()
             ->addAttributeToFilter('news_from_date', array('date' => true, 'to' => $todayDate))
             ->addAttributeToFilter('news_to_date', array('or'=> array(
                0 => array('date' => true, 'from' => $todayDate),
                1 => array('is' => new Zend_Db_Expr('null')))
             ), 'left')
             ->addAttributeToSort('news_from_date', 'desc')
             ->setPageSize($this->get_prod_count())
             ->setCurPage($this->get_cur_page());

          $this->setProductCollection($collection);

          return $collection;
       }// _getProductCollection
    }// Mage_Catalog_Block_Product_New
    ?>
  4. Alright – save your New.php file. Now it’s time to add your Magento CMS page and get it to show the new products.
Now, I’m assuming you already know how to add a CMS page to your Magento site so we will not be covering it in this post. Once you have added your CMS page, there are a couple of ways to get your new products to show up. I plan on going through both of them below.

Route 1 – Add New Products to a Magento Page with the Magento Block

Remember that good old block code we used to get our products to show up at first? Well we can use that again, however this time we will modify it to take advantage of the list.phtml template file. Take the following block and put it in the Content area of your CMS page.
{{block type="catalog/product_new" column_count="6" products_count="0" name="home.catalog.product.new" alias="product_homepage" template="catalog/product/list.phtml"}}
You’ll notice that the only real difference between the code shown at the beginning of this post and the code shown above is the usage of the list.phtml template file. This file gives us access to the Magento toolbar and pager options. Some of you may have noticed a slight issue with this approach. We are displaying 6 columns of products, but the number of products per page is set to 10. Also – you may see that you actually have 15 products for sale but only one page of 12 products shows. This leads us to the second option of displaying new products with Magento.

Route 2 – Add New Products to a Magento page using Layout XML

Adding new products to a Magento page using Layout XML gives us a bit more control over what is displayed on our CMS page. In order to add Layout XML you will need to open the Design tab of your CMS page. You’ll notice a textarea entitled Layout Update XML. This box is where we can put our XML to display new products. Copy and paste the following XML into your Layout Update XML box.
<reference name="content">
   <block type="catalog/product_new" name="product_new" template="catalog/product/list.phtml">
      <action method="setCategoryId"><category_id>10</category_id></action>
      <action method="setColumnCount"><column_count>6</column_count></action>
      <action method="setProductsCount"><count>0</count></action>
      <block type="catalog/product_list_toolbar" name="product_list_toolbar" template="catalog/product/list/toolbar.phtml">
         <block type="page/html_pager" name="product_list_toolbar_pager" />
         <action method="setDefaultGridPerPage"><limit>12</limit></action>
         <action method="addPagerLimit"><mode>grid</mode><limit>12</limit></action>
         <action method="addPagerLimit"><mode>grid</mode><limit>24</limit></action>
         <action method="addPagerLimit"><mode>grid</mode><limit>36</limit></action>
         <action method="addPagerLimit"><mode>grid</mode><limit>48</limit></action>
         <action method="addPagerLimit" translate="label"><mode>grid</mode><limit>all</limit><label>All</label></action>
      </block>
      <action method="addColumnCountLayoutDepend"><layout>one_column</layout><count>6</count></action>
      <action method="setToolbarBlockName"><name>product_list_toolbar</name></action>
   </block>
</reference>
Let’s quickly go over what this does.
  1. <block type="catalog/product_new" name="product_new" template="catalog/product/list.phtml">
    First we set the block type and the template we are using. In our case we set the block type to “catalog/product_new” which pulls the block from the New.php file we placed in our local directory at the beginning of the post.
  2. The template we are using is “list.phtml” which gives us access to the Pager and Toolbar.
  3. <action method="setCategoryId"><category_id>10</category_id></action>
    setCategoryId – Next we set the Category ID. In our situation we had two Magento stores running off of two different root categories. We changed the category ID depending on which category we wanted to pull new products for. You can set this to whatever category you want, or remove the line altogether.
    Note: You can put this XML on multiple pages. So you could have a page for new Children clothing and a page for new Adult clothing. You would simply need to put the same XML on each page and change the Category ID to reflect the children and adult clothing categories.
  4. <action method="setColumnCount"><column_count>6</column_count></action>
    setColumnCount – This allows us to show six products in one row on the page. We set the column count to six because this fit our design. Feel free to set it to whatever suits your purposes.
  5. <action method="setProductsCount"><count>0</count></action>
    setProductsCount – Setting the products count. Leave this set to zero in order to display all new products – change it if you only wish to limit display to a certain number.
  6. <block type="catalog/product_list_toolbar" name="product_list_toolbar" template="catalog/product/list/toolbar.phtml">
             <block type="page/html_pager" name="product_list_toolbar_pager" />
    Adds the toolbar and pager.
  7. <action method="setDefaultGridPerPage"><limit>12</limit></action>
    setDefaultGridPerPage – Configure the toolbar to show what the number of products you wish. By default we are showing 12 products per page, which is two rows of six columns.
  8. <action method="addPagerLimit"><mode>grid</mode><limit>12</limit></action>
    addPagerLimit – These all add options to the toolbar which will allow your customers to choose how many products they want to see on one page. The XML given will allow the customer to choose to display 12, 24, 36, 48, or All products on one page.
  9. <action method="addColumnCountLayoutDepend"><layout>one_column</layout><count>6</count></action>
    Finally, in our design, the one_column layout has to be selected in order for 6 columns to show up (otherwise there isn’t enough room).
  10. <action method="setToolbarBlockName"><name>product_list_toolbar</name></action>
    Set the toolbar name.
  11. You may notice one caveat with this approach. You need to make sure there is content in the Content area of the CMS page. Any easy way around this is to enter a “<br />” in the html portion of the Content entry section.
  12. Save your page!

Tuesday, October 15, 2013

Magento – Displaying Products in a Static Block

This is one of those ones that can be a bit frustrating, there’s many a tutorial out there that suggests you have to create a new template file and call that. It’s possible to call the standard catalog/product/view.phtml template from a static block but also pass parameters which dictate how it’s displayed. So you can add the following;

{{block type="catalog/product_list" name="product_list" category_id="3" column_count="6" 
count="6" limit="4" mode="grid" template="catalog/product/list.phtml"}}


And it will display the products using the default list template, with product numbers pulled from the System -> Configuration -> Catalog -> Frontend config.
Better still, you can copy that template and rename it as a custom template, take out a lot of the grid/list and toolbar conditionals and style it up the way you want, then just upload the new template file and apply that. You can use the template multiple times within the same static block, calling a catalogue category each time;
 
 
<h2>First Category</h2>
{{block type="catalog/product_list" name="product_list" category_id="1" column_count="6" 
count="6" limit="4" mode="grid" template="catalog/product/custom_list.phtml"}}

<h2>Second Category</h2>
{{block type="catalog/product_list" name="product_list" category_id="2" column_count="6" 
count="6" limit="4" mode="grid" template="catalog/product/custom_list.phtml"}}

<h2>Third Category</h2>
{{block type="catalog/product_list" name="product_list" category_id="2" column_count="6" 
count="6" limit="4" mode="grid" template="catalog/product/custom_list.phtml"}}


A really easy way to display all or portions of the full catalogue in a page, and control exactly how it’s displayed. Worked well for the site I’ve just finished, it had a ProNav mega menu so I used this technique to display the parent ‘Product’ page.

Tuesday, October 8, 2013

Reading message contents PHP

Some web applications might require features of an email client to be made available the users. In these situations, we can either write our own or customize opensource clients like SquirrelMail or Roundcube. Regardless of what you choose, knowledge of working with the PHP IMAP mail extension will be helpful.
In this two-part series I’ll explain how to work with the IMAP extension. This is the first part which discusses the necessary functions for connecting to mail servers and reading messages. The second part will talk about actions for working with email, like deleting messages, downloading attachments, etc.
To demonstrate functionality, I’ll use code samples that can be used to start scripting your own mail client. I’ll assume a single web script which uses these URL parameters to call the necessary functions:
  • func – the type of functionality required (ex: read email, view inbox, delete message)
  • folder – the name of the mailbox folder to connect to (ex: inbox, sent, spam)
  • uid – the unique ID of the email targeted
The parameters can be retrieved using $_GET and a switch statement can be used to invoke the appropriate actions.

<?php
$func = (!empty($_GET["func"])) ? $_GET["func"] : "view";
$folder = (!empty($_GET["folder"])) ? $_GET["folder"] : "INBOX";
$uid = (!empty($_GET["uid"])) ? $_GET["uid"] : 0;
// connect to IMAP
// ...
switch ($func) {
    case "delete":
        deleteMail($imap, $folder, $uid);
        break;
    case "read":
        deleteMail($imap, $folder, $uid);
        break;
    case "view":
    default:
        viewMail($imap, $folder);
        break;
}

Connecting to IMAP

To establish a connection to the IMAP server, we use the imap_connect() function as shown here:

<?php
$imap = imap_open($mailboxPath, $username, $password);


The mailbox path, username, and password strings are required parameters to connect to the server. You can learn about the optional parameters in the manual.
The mailbox path is a string that identifies server and port information in braces followed by the name of the desired mail folder. Here are a few strings for the inbox folder for popular mail providers:
  • Gmail {imap.gmail.com:993/imap/ssl}INBOX
  • Yahoo {imap.mail.yahoo.com:993/imap/ssl}INBOX
  • AOL {imap.aol.com:993/imap/ssl}INBOX
Some servers do not have SSL enabled, in which case you would omit “SSL” from the string. Other servers might use self-signed certificates, in which you would include “novalidate-cert”.


 
<?php
$imap = imap_open("{localhost:993/imap/ssl/novalidate-cert}", "username", "password");


With an open connection to the mail server, now we can look at the functions used for the following activities:
  • Displaying the list of mailbox folders in your email account
  • Displaying the list of email messages in the folder
  • Viewing the email’s content

Listing the Folders

Inbox, sent, trash, and spam folders are seen in pretty much every email account, and users can often create custom folders as well. In order to view messages in these folders, we need to change our connection string. For example, I used “INBOX” in the path string earlier. If I wanted to connect to the spam folder, I might want to use something like “Spam” instead. But even though it might be listed as Spam in your email account when viewed through a mail client, the real folder name might be different behind the scenes. We can list all of the available folders in an account using imap_list().


<?php
$folders = imap_list($imap, "{imap.gmail.com:993/imap/ssl}", "*");
echo "<ul>";
foreach ($folders as $folder) {
    $folder = str_replace("{imap.gmail.com:993/imap/ssl}", "", imap_utf7_decode($folder));
    echo '<li><a href="mail.php?folder=' . $folder . '&func=view">' . $folder . '</a></li>';
}
echo "</ul>";


We have to pass the connection handle obtained with imap_open() as the initial parameter to imap_list(). We also need to pass a bare path sting (without the folder, e.g. “INBOX”). The star as the third parameter requests all of the available folders.

Listing Email Messages

Each folder has a list of available email messages, so let’s see how we can generate a listing of the messages in our inbox.
We need to first get the number of messages available using imap_num_msg(). Then we can use the imap_header() function to get the header information for each message.
Let’s say if we wanted to the last 20 emails:


<?php
$numMessages = imap_num_msg($imap);
for ($i = $numMessages; $i > ($numMessages - 20); $i--) {
    $header = imap_header($imap, $i);
    $fromInfo = $header->from[0];
    $replyInfo = $header->reply_to[0];
    $details = array(
        "fromAddr" => (isset($fromInfo->mailbox) && isset($fromInfo->host))
            ? $fromInfo->mailbox . "@" . $fromInfo->host : "",
        "fromName" => (isset($fromInfo->personal))
            ? $fromInfo->personal : "",
        "replyAddr" => (isset($replyInfo->mailbox) && isset($replyInfo->host))
            ? $replyInfo->mailbox . "@" . $replyInfo->host : "",
        "replyName" => (isset($replyTo->personal))
            ? $replyto->personal : "",
        "subject" => (isset($header->subject))
            ? $header->subject : "",
        "udate" => (isset($header->udate))
            ? $header->udate : ""
    );
    $uid = imap_uid($imap, $i);
    echo "<ul>";
    echo "<li><strong>From:</strong>" . $details["fromName"];
    echo " " . $details["fromAddr"] . "</li>";
    echo "<li><strong>Subject:</strong> " . $details["subject"] . "</li>";
    echo '<li><a href="mail.php?folder=' . $folder . '&uid=' . $uid . '&func=read">Read</a>';
    echo " | ";
    echo '<a href="mail.php?folder=' . $folder . '&uid=' . $uid . '&func=delete">Delete</a></li>';
    echo "</ul>";
}


The $imap connection should be open to the desired folder. We can then traverse through the last 20 emails using the number of messages received by imap_num_msg(). The connection and email number are given to imap_header() to retrieve the header information, which can then be parsed for the interesting details like the sender’s email address and name, subject, etc.
Note that the email number we get from using the total message count is not a unique ID for the message. If you have 100 emails in your inbox, then the last number will be 100, the previous will be 99, and so on. But its not unique. If you delete message number 100 and then receive a new message, it’s number will also will be 100.
We have to get the unique ID for an email in order to proceed with other actions. Each email does have an unique ID, called UID, which we can get by providing the email number to the imap_uid() function. The UID is unique and will not change over time.

Viewing Message Contents

Reading email is not really as simple as the previous tasks, so I’m going to use the Receive Mail class developed by Mitul Koradia as a starting point to help make things easier. From the class I’ve extracted and adapted the following three functions for our example here:


<?php
function getBody($uid, $imap) {
    $body = get_part($imap, $uid, "TEXT/HTML");
    // if HTML body is empty, try getting text body
    if ($body == "") {
        $body = get_part($imap, $uid, "TEXT/PLAIN");
    }
    return $body;
}
function get_part($imap, $uid, $mimetype, $structure = false, $partNumber = false) {
    if (!$structure) {
           $structure = imap_fetchstructure($imap, $uid, FT_UID);
    }
    if ($structure) {
        if ($mimetype == get_mime_type($structure)) {
            if (!$partNumber) {
                $partNumber = 1;
            }
            $text = imap_fetchbody($imap, $uid, $partNumber, FT_UID);
            switch ($structure->encoding) {
                case 3: return imap_base64($text);
                case 4: return imap_qprint($text);
                default: return $text;
           }
       }
        // multipart
        if ($structure->type == 1) {
            foreach ($structure->parts as $index => $subStruct) {
                $prefix = "";
                if ($partNumber) {
                    $prefix = $partNumber . ".";
                }
                $data = get_part($imap, $uid, $mimetype, $subStruct, $prefix . ($index + 1));
                if ($data) {
                    return $data;
                }
            }
        }
    }
    return false;
}
function get_mime_type($structure) {
    $primaryMimetype = array("TEXT", "MULTIPART", "MESSAGE", "APPLICATION", "AUDIO", "IMAGE", "VIDEO", "OTHER");
    if ($structure->subtype) {
       return $primaryMimetype[(int)$structure->type] . "/" . $structure->subtype;
    }
    return "TEXT/PLAIN";
}


The getBody() function gets the email’s content by passing its UID and the IMAP connection. Inside the function, we call the get_part() function with the content type as TEXT/HTML. Plain text emails are much easier to read. So we first try to find the HTML content inside the email.
Then we read the structure of the email using imap_fetchstructure() function. This function would have taken the email number by default, but we changed the library function to use the UID instead by passing the FT_UID constant.
Then we get the mime type of the email message using the get_mime_type() function. There are eight mime types returned by this function as integers:
  • 0 – TEXT
  • 1 – MULTIPART
  • 2 – MESSAGE
  • 3 – APPLICATION
  • 4 – AUDIO
  • 5 – IMAGE
  • 6 – VIDEO
  • 7 – OTHER
We convert the returned integer into actual mime type string using the mime types array.
Multipart messages can have a large number of subtypes, so we traverse recursively through all of the subtypes using the part numbers and retrieve the email content using imap_fetchBody() when the correct part is found using the mime type.
Then, we use an appropriate decode function according to the encoding type of the message and return the content. A complete list of available encoding types is shown here:
  • 0 – 7BIT
  • 1 – 8BIT
  • 2 – BINARY
  • 3 – BASE64
  • 4 – QUOTED-PRINTABLE
  • 5 – OTHER