Skip to content
This repository was archived by the owner on Dec 1, 2020. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
320 changes: 217 additions & 103 deletions blocktopmenu.php

Large diffs are not rendered by default.

61 changes: 40 additions & 21 deletions blocktopmenu.tpl
Original file line number Diff line number Diff line change
@@ -1,24 +1,43 @@
{if $MENU != ''}

<!-- Menu -->
<div class="sf-contener clearfix">
<ul class="sf-menu clearfix">
{$MENU}
{if $MENU_SEARCH}
<li class="sf-search noBack" style="float:right">
<form id="searchbox" action="{$link->getPageLink('search')|escape:'html'}" method="get">
<p>
<input type="hidden" name="controller" value="search" />
<input type="hidden" value="position" name="orderby"/>
<input type="hidden" value="desc" name="orderway"/>
<input type="text" name="search_query" value="{if isset($smarty.get.search_query)}{$smarty.get.search_query|escape:'html':'UTF-8'}{/if}" />
</p>
</form>
</li>
{/if}
{function name="blocktopmenu_menu" items=array()}
{foreach $items as $item}
<li {if $item.selected}class="sfHover"{/if}>
<a href="{$item.data.link}" title="{$item.data.name}">{$item.data.name}</a>
{if $item|count > 0}
<ul>
{blocktopmenu_menu items=$item}
{if $item.type == 'category-thumbnails'}
<li class="category-thumbnail">
{foreach $item.images as $image}
<div>
<img class="imgm" src="{$image.src}" alt="{$image.alt}" title="{$image.title}" />
</div>
{/foreach}
</li>
{/if}
</ul>
</div>
<div class="sf-right">&nbsp;</div>
{/if}
</li>
{/foreach}
{/function}

<!--/ Menu -->
{if $items|count > 0}
<div class="sf-contener clearfix">
<ul class="sf-menu clearfix">
{$MENU}
{* {blocktopmenu_menu items=$items} *}
{if $MENU_SEARCH}
<li class="sf-search noBack" style="float:right">
<form id="searchbox" action="{$link->getPageLink('search')|escape:'html'}" method="get">
<p>
<input type="hidden" name="controller" value="search" />
<input type="hidden" value="position" name="orderby"/>
<input type="hidden" value="desc" name="orderway"/>
<input type="text" name="search_query" value="{if isset($smarty.get.search_query)}{$smarty.get.search_query|escape:'html':'UTF-8'}{/if}" />
</p>
</form>
</li>
{/if}
</ul>
</div>
<div class="sf-right">&nbsp;</div>
{/if}
3 changes: 3 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@
"require": {
"php": ">=5.3.2"
},
"require-dev": {
"phpunit/phpunit": "~4.5"
},
"config": {
"preferred-install": "dist"
},
Expand Down
7 changes: 7 additions & 0 deletions phpunit.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<phpunit>
<testsuites>
<testsuite name="blocktopmenu">
<directory>tests</directory>
</testsuite>
</testsuites>
</phpunit>
6 changes: 6 additions & 0 deletions src/BlocktopMenuNodeVisitor.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<?php

interface BlocktopMenuNodeVisitor
{
public function visit(BlocktopMenuNode $node);
}
102 changes: 102 additions & 0 deletions src/BlocktopmenuNode.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
<?php

class BlocktopmenuNode implements ArrayAccess, Countable, IteratorAggregate
{
const TYPE_CATEGORY = 'category';
const TYPE_CATEGORY_THUMBNAILS = 'category-thumbnails';
const TYPE_PRODUCT = 'product';
const TYPE_MANUFACTURERS = 'manufacturers';
const TYPE_MANUFACTURER = 'manufacturer';
const TYPE_SUPPLIERS = 'suppliers';
const TYPE_SUPPLIER = 'supplier';
const TYPE_CMS = 'cms';
const TYPE_CMS_CATEGORY = 'cms-category';
const TYPE_SHOP = 'shop';
const TYPE_LINK = 'link';

private $type;
private $selected = false;
private $data = array();
private $children = array();

public function __construct($type, array $data = array())
{
$this->type = $type;
$this->data = $data;
}

public function getType()
{
return $this->type;
}

public function getData($name)
{
return isset($this->data[$name]) ? $this->data[$name] : null;
}

public function addChild(BlocktopmenuNode $node)
{
$this->children[] = $node;

return $this;
}

public function setSelected($selected = true)
{
$this->selected = $selected;

return $this;
}

public function isSelected()
{
return $this->selected;
}

public function getChildren()
{
return $this->children;
}

public function offsetExists($offset)
{
return null !== $this->offsetGet($offset);
}

public function offsetGet($offset)
{
switch ($offset) {
case 'selected' :
return $this->selected;
case 'children' :
return $this->children;
case 'data' :
return $this->data;
break;
case 'type' :
return $this->type;
break;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's nice that the model is flexible enough so that we can store an ID for the entities for which it makes sense.
(eg, the link ID, or the new_window attribute).
But, is the foreach loop in blocktopmenu.tpl active/relevant?
ie: I would want:
<a href="{$item.data.link}" title="{$item.data.name}" id="blocktopmenu_menu_{$item.data.type}_{$item.data.id}>{$item.data.name}</a>
Would I change it in the markup hardcoded inside SuperfishNodeVisitor.php? Would I create my own class? Or would I have to change/override the template?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SuperfishNodeVisitor.php is only here for backward compatibility. It is wrapped into a class to separate concerns and to make it testable easily.
If you want to change the HTML code, you should do it in the view.
The function in blocktopmenu.tpl is an example of how to use a recursive function to render the menu.

}
}

public function offsetSet($offset, $value)
{
// Read only
}

public function offsetUnset($offset)
{
// Read only
}

public function getIterator()
{
return new ArrayIterator($this->children);
}

public function count()
{
return count($this->children);
}
}
51 changes: 51 additions & 0 deletions src/SuperfishNodeVisitor.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<?php

class SuperfishNodeVisitor implements BlocktopMenuNodeVisitor
{
private $output;

public function reset()
{
$this->output = '';
}

public function visit(BlocktopMenuNode $node)
{
$cssClasses = array();
if ($node->isSelected()) {
$cssClasses[] = 'sfHover';
}
if ($node->getType() === BlocktopMenuNode::TYPE_CATEGORY_THUMBNAILS) {
$cssClasses[] = 'category-thumbnail';
}

$this->output .= '<li'.(!empty($cssClasses) ? ' class="'.implode(' ', $cssClasses).'"' : '').'>';
switch ($node->getType()) {
case BlocktopMenuNode::TYPE_CATEGORY_THUMBNAILS:
foreach ($node->getData('images') as $image) {
$this->output .= '<div><img src="'.$image['src'].'" alt="'.$image['alt'].'" title="'.$image['title'].'" class="imgm" /></div>';
}
break;
case BlocktopMenuNode::TYPE_LINK:
$newWindow = true === $node->getData('new_window');
$this->output .= '<a href="'.$node->getData('link').'" title="'.$node->getData('name').'"'.($newWindow ? ' onclick="return !window.open(this.href);"' : '').'>'.$node->getData('name').'</a>';
break;
default :
$this->output .= '<a href="'.$node->getData('link').'" title="'.$node->getData('name').'">'.$node->getData('name').'</a>';
}

if (count($node) > 0) {
$this->output .= '<ul>';
foreach ($node as $childNode) {
$this->visit($childNode);
}
$this->output .= '</ul>';
}
$this->output .= '</li>';
}

public function __toString()
{
return $this->output;
}
}
30 changes: 30 additions & 0 deletions tests/BlocktopmenuNodeTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php

require_once dirname(__FILE__).'/../src/BlocktopmenuNode.php';

class BlocktopmenuNodeTest extends PHPUnit_Framework_TestCase
{
public function testClassIsCountableAndIterable()
{
$category = new BlocktopmenuNode(BlocktopmenuNode::TYPE_CATEGORY);

$subCategory1 = new BlocktopmenuNode(BlocktopmenuNode::TYPE_CATEGORY);
$category->addChild($subCategory1);

$subCategory2 = new BlocktopmenuNode(BlocktopmenuNode::TYPE_CATEGORY);
$category->addChild($subCategory2);

$this->assertCount(2, $category);
$i = 0;
foreach ($category as $key => $subCategory) {
if ($key === 0) {
$this->assertSame($subCategory1, $subCategory);
}
if ($key === 1) {
$this->assertSame($subCategory2, $subCategory);
}
++$i;
}
$this->assertEquals(2, $i);
}
}
97 changes: 97 additions & 0 deletions tests/SuperfishNodeVisitorTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
<?php

require_once dirname(__FILE__).'/../src/BlocktopmenuNode.php';
require_once dirname(__FILE__).'/../src/BlocktopmenuNodeVisitor.php';
require_once dirname(__FILE__).'/../src/SuperfishNodeVisitor.php';

class SuperfishNodeVisitorTest extends PHPUnit_Framework_TestCase
{
public function testVisitProductNode()
{
$visitor = new SuperfishNodeVisitor();

$node = new BlocktopmenuNode(BlocktopmenuNode::TYPE_PRODUCT, array(
'name' => 'Foo',
'link' => 'http://prestashop.com',
));

$node->setSelected(false);
$visitor->visit($node);
$this->assertEquals('<li><a href="http://prestashop.com" title="Foo">Foo</a></li>', (string) $visitor);

$visitor->reset();
$node->setSelected(true);
$visitor->visit($node);
$this->assertEquals('<li class="sfHover"><a href="http://prestashop.com" title="Foo">Foo</a></li>', (string) $visitor);
}

public function testVisitLinkNode()
{
$visitor = new SuperfishNodeVisitor();

$node = new BlocktopmenuNode(BlocktopmenuNode::TYPE_LINK, array(
'name' => 'Foo',
'link' => 'http://prestashop.com',
'new_window' => true
));

$node->setSelected(false);
$visitor->visit($node);
$this->assertEquals('<li><a href="http://prestashop.com" title="Foo" onclick="return !window.open(this.href);">Foo</a></li>', (string) $visitor);
}

public function testVisitCategoryNode()
{
$category = new BlocktopmenuNode(BlocktopmenuNode::TYPE_CATEGORY, array(
'name' => 'Foo',
'link' => 'http://foo.prestashop.com',
));

$subCategory1 = new BlocktopmenuNode(BlocktopmenuNode::TYPE_CATEGORY, array(
'name' => 'Bar',
'link' => 'http://bar.prestashop.com',
));
$category->addChild($subCategory1);

$subCategory2 = new BlocktopmenuNode(BlocktopmenuNode::TYPE_CATEGORY, array(
'name' => 'Baz',
'link' => 'http://baz.prestashop.com',
));
$category->addChild($subCategory2);

$thumbnails = new BlocktopmenuNode(BlocktopmenuNode::TYPE_CATEGORY_THUMBNAILS, array(
'images' => array(
array(
'src' => 'http://prestashop.com/logo.jpg',
'alt' => 'PrestaShop',
'title' => 'PrestaShop',
),
array(
'src' => 'http://prestashop.com/logo.jpg',
'alt' => 'PrestaShop',
'title' => 'PrestaShop',
),
),
));
$category->addChild($thumbnails);

$visitor = new SuperfishNodeVisitor();
$visitor->visit($category);

$expected = <<<EXPECTED
<li>
<a href="http://foo.prestashop.com" title="Foo">Foo</a>
<ul>
<li><a href="http://bar.prestashop.com" title="Bar">Bar</a></li>
<li><a href="http://baz.prestashop.com" title="Baz">Baz</a></li>
<li class="category-thumbnail">
<div><img src="http://prestashop.com/logo.jpg" alt="PrestaShop" title="PrestaShop" class="imgm" /></div>
<div><img src="http://prestashop.com/logo.jpg" alt="PrestaShop" title="PrestaShop" class="imgm" /></div>
</li>
</ul>
</li>
EXPECTED;

$this->assertEquals(str_replace(array(PHP_EOL, ' '), array('', ''), $expected), (string) $visitor);
}
}