Creating a routing-based menu in symfony 1.2, part 4: the full package
April 6th, 2009 | by David |During the last 3 articles I showed you how to define the menu structure, generate sfMenuItem objects and create a builder that does all this for you. Today I want to present you the full package of my sfMenuBuilder and describe all the additional options I included.
Owing to a comment by Hugo HAMON, I decided to remove all external dependencies, so now the routing and i18n have to be injected, making i18n optional.
Download the package
For trying the sfMenuBuilder conveniently I have prepared a zip package containing all the classes and some examples (yes, that’s the download link). Feel free to use it any way you like to!
The package contains the following files:
config/
example_app.yml
lib/
menu_builder/
sfMenuBuilder.class.php
sfMenuItem.class.php
resolver/
sfObjectResolver.class.php
sfObjectResolverInterface.class.php
examples.php
LICENSEconfig/example_app.yml contains the markup and comprehensive documentation of a menu item in YAML – of course the parameters have to be the same wherever your menu structure is generated. In examples.php I placed some code showing how to create an sfMenuBuilder instance and how to render the menu either in tab levels or as a tree.
New options in sfMenuBuilder
sfMenuBuilder now works as a multi-instance holder for sfMenuBuilder objects. It’s like a singleton with multiple named singletons, allowing you to create several menu definitions in parallel and at the same time holding them in a static container – this is especially important for helper functions where you would have to manually store the menus somewhere else.
13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 | class sfMenuBuilder { protected static $instances = array(); protected $menuItems = array(), $route = null, $routeName = null, $routeObject = null, $i18n = null; /** * Initialize an instance of sfMenuBuilder with the given data and name. * If there is an existing instance with the same name it will be overwritten. * * Since sfMenuBuilder needs the current sfContext to access the routing and * request objects the context has to be provided as parameter. * * @param array $itemsData Array of data to create sfMenuItem objects * @param sfRoute $route The current sfRoute * @param string $routeName The name of the current sfRoute * @param string $name The name of the instance, optional, defaults to "default" * @param sfI18n $i18n An instance of sfI18n to perform translations using __() * * @return sfMenuBuilder The newly created instance */ public static function initInstance(array $itemsData, sfRoute $route, $routeName, $name = 'default', sfI18n $i18n = null) { self::$instances[$name] = new sfMenuBuilder($itemsData, $route, $routeName, $i18n); return self::$instances[$name]; } /** * Get the instance for the given name parameter. * * Warning: this does not create instances automatically! If you * try to get an instance that doesn't exist, null will be returned! * * @param string $name The name of the instance to fetch * * @return sfMenuBuilder An sfMenuBuilder instance for the given name. */ public static function getInstance($name = 'default') { if (isset(self::$instances[$name])) { return self::$instances[$name]; } return null; } /** * Set to protected to prevent access from outside the singleton instance holder. * * @param array $itemsData Array of data to create sfMenuItem objects * @param sfRoute $route The current sfRoute * @param string $routeName The name of the current sfRoute * @param sfI18n $i18n An instance of sfI18n to perform translations using __() */ protected function __construct(array &$itemsData, sfRoute $route, $routeName, sfI18n $i18n = null) { $this->routeName = $routeName; $this->route = $route; $this->i18n = $i18n; if ($this->route instanceof sfObjectRoute) { $options = $this->route->getOptions(); if ($options['type'] == 'object') { $this->routeObject = $this->route->getObject(); } } foreach ($itemsData as $routeName => $itemData) { $this->menuItems[$routeName] = new sfMenuItem($routeName, $itemData); $this->menuItems[$routeName]->initialize($this->routeName, $this->route, $this->routeObject, $this->i18n); } } /** * Render all items of a given level of the menu. * * By default, only items inside the current route branch will be rendered. * To render all items regardless of their branch set the $forceAll * parameter to true. * * @param int $level Level to render. 0 = top level, 1 = second level and so on * @param boolean $forceAll Set to true to render all items even if their part of the tree is inactive * * @return array Array of items' XHTML code grouped by position */ public function renderLevel($level = 0, $forceAll = false) { $rendered = array(); foreach ($this->menuItems as $item) { $item->renderLevel($level, $rendered, $forceAll); } return $rendered; } /** * Render all menu items into a tree structure. * * @param string $container The XHTML container tag to use. Defaults to 'ul'. * * @return string Rendered items and child items, wrapped in container tag */ public function renderTree($container = 'ul') { $rendered = ''; foreach ($this->menuItems as $item) { $rendered .= $item->renderTree($container); } return content_tag($container, $rendered); } } |
As you can see now the sfRoute, its name and the sfI18n instance have to be injected into the initInstance() method. The constructor cannot be called from outside the class, so you have to use initInstance() and getInstance() in combination with $name (which defaults to “default”) to access your builder instances.
sfMenuItem revisited
The constructor then creates all the sfMenuItem objects and injects the routing and i18n information. Let’s take a look at the full sfMenuItem class next:
14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 | class sfMenuItem { protected $myRouteName = null, $children = array(), $allRouteNames = array(), $title = '', $position = 'default', $container = 'li', $requireObjectClass = null, $customFunction = null, $customUrl = null, $route = null, $routeName = null, $routeObject = null, $i18n = null; /** * Generate a new sfMenuItem object using its own route name and the node * data from the structural schema. * * @param string $myRouteName This item's sfRoute name (as in routing.yml) * @param array $itemData The data definition for this item */ public function __construct($myRouteName, array $itemData) { $this->myRouteName = $myRouteName; $this->allRouteNames[] = $myRouteName; if (isset($itemData['title'])) { $this->title = $itemData['title']; } if (isset($itemData['position'])) { $this->position = $itemData['position']; } if (isset($itemData['container'])) { $this->container = $itemData['container']; } if (isset($itemData['alias_routes'])) { $this->allRouteNames = array_merge($this->allRouteNames, (array) $itemData['alias_routes']); } if (isset($itemData['require_object_class'])) { $this->requireObjectClass = $itemData['require_object_class']; } if (isset($itemData['custom_function'])) { $this->customFunction = new sfCallable($itemData['custom_function']); } if (isset($itemData['custom_url'])) { $this->customUrl = $itemData['custom_url']; } if (isset($itemData['children']) && is_array($itemData['children'])) { foreach ($itemData['children'] as $subRouteName => $subItemData) { $this->children[$subRouteName] = new sfMenuItem($subRouteName, $subItemData); $this->allRouteNames = array_merge($this->allRouteNames, $this->children[$subRouteName]->getAllRouteNames()); } } } /** * Initialize the menu item and all child menu items using the current routing * status of the application. * By passing the routing information you can either use routes from outside * the context (e.g. testing data) or just improve performance because this * would have to be repeated for all the child menu items. * * @param string $routeName Name of the currently active route * @param sfRoute $route Currently active route object * @param mixed $routeObject Object that is transported through the route if it is an sfObjectRoute * @param sfI18n $i18n An instance of sfI18n to perform translations using __() */ public function initialize($routeName, sfRoute $route, $routeObject, sfI18n $i18n = null) { $this->routeName = $routeName; $this->route = $route; $this->routeObject = $routeObject; $this->i18n = $i18n; foreach ($this->children as $child) { $child->initialize($this->routeName, $this->route, $this->routeObject, $this->i18n); } } /** * Get this menu item's route name. * * @return string Route name of this single menu item */ public function getMyRouteName() { return $this->myRouteName; } /** * Get all child sfMenuItem objects. * * @return array Array of sfMenuItem objects, using the route names as keys. */ public function getChildren() { return $this->children; } /** * Get an array of all route names and alias route names this menu item * might use. Also includes all sub items' routes recursively. * * @return array Array of route names */ public function getAllRouteNames() { return $this->allRouteNames; } /** * Get the title that will be used to render this item. * * @return string Title text */ public function getTitle() { return $this->title; } /** * Get the position this item will be rendered at. * * @return string Position. Defaults to "default" */ public function getPosition() { return $this->position; } /** * Get this menu item's wrapper container. * * @return string HTML wrapper Container name */ public function getContainer() { return $this->container; } /** * Get the object class that is required for this item to * be rendered as "enabled". * * @return string Object class name */ public function getRequireObjectClass() { return $this->requireObjectClass; } /** * Get this menu item's custom URL. * * @return string Custom URL */ public function getCustomUrl() { return $this->customUrl; } /** * If an i18n instance was provided, this will do the translation. * @see sfI18n::__() * * @param string $string The string to translate * * @return string Translated string */ public function __($string) { if (is_null($this->i18n)) { return $string; } return $this->i18n->__($string); } /** * Render the current menu item's content text. * * The content always contains an 'a' tag containing either a href * link (if the item is enabled) or the item title only. * * All titles are translated using i18n. * * @return string Rendered menu item content HTML code */ public function renderContent() { if ($this->customFunction instanceof sfCallable) { return $this->customFunction->call($this); } if (!$this->isEnabled()) { return content_tag('a', $this->__($this->title)); } else { if (!is_null($this->customUrl)) { return link_to($this->__($this->title), $this->customUrl); } elseif (is_null($this->requireObjectClass)) { return link_to($this->__($this->title), '@'.$this->myRouteName); } else { return link_to($this->__($this->title), $this->myRouteName, sfObjectResolver::resolve($this->routeObject, $this->requireObjectClass)); } } } /** * Check if the item is enabled or not. * An item will be disabled if it requires an object class * that cannot be found by the resolver. * * @return boolean True if the item is enabled */ public function isEnabled() { return is_null($this->requireObjectClass) || !is_null(sfObjectResolver::resolve($this->routeObject, $this->requireObjectClass)); } /** * Check if the item is currently selected. * An item will be selected if the current routeName matches * any route in the items allRouteNames property. * * @return boolean True if the item is currently selected in the menu */ public function isCurrent() { return in_array($this->routeName, $this->allRouteNames); } /** * Wrap an item with the container that is set. If no container * is defined, just return the item. * * @param string $text Text content * @param array $options Options array passed to content_tag() helper * * @return string Rendered content tag */ protected function containerTag($text, $options = array()) { if ($this->container != '') { return content_tag($this->container, $text, $options); } return $text; } /** * Render the current menu item. * * The item is rendered inside the element given as "container" parameter. * The container defaults to the HTML list element ("li"). * * Items that require an object route without existing object of that type * have their class attribute set to "disabled". Items that match the current * route have their class attribute set to "current". * * @return string Rendered menu item HTML code */ public function render() { $text = $this->renderContent(); if (!$this->isEnabled()) { return $this->containerTag($text, array('class' => 'disabled')); } elseif ($this->isCurrent()) { return $this->containerTag($text, array('class' => 'current')); } return $this->containerTag($text); } /** * Get the sfRoute object associated with all the menu items. * * @return sfRoute sfRoute object */ public function getRoute() { return $this->route; } /** * Get the route name associated with all the menu items. * * @return string Route name */ public function getRouteName() { return $this->routeName; } /** * Get the route object out of sfObjectRoute. * * @return mixed Route object generated by sfObjectRoute */ public function getRouteObject() { return $this->routeObject; } /** * Render all menu items of a given child level related to this item. * * The rendered items are added to the $rendered array parameter grouped * by their position. * * @param int $level Level to render. 0 will return the current item, 1 all items of the first child level and so on. * @param array $rendered Contains all items rendered so far. Rendered items will be appended to the array. * @param boolean $forceAll Force the rendering of all sub items, even if their tree is not in the current route */ public function renderLevel($level, array &$rendered, $forceAll = false) { if ($level == 0) { $rendered[$this->position][] = $this->render(); } elseif ($level > 0 && ($forceAll || in_array($this->routeName, $this->allRouteNames))) { $level--; foreach ($this->children as $child) { $child->renderLevel($level, $rendered, $forceAll); } } } /** * Render all menu items into a tree structure. * * @param string $container The HTML container tag to use. Defaults to 'ul'. * * @return string Rendered item and child items, wrapped in container tag */ public function renderTree($container = 'ul') { $children = ''; foreach ($this->children as $child) { $children .= $child->renderTree($container); } $text = $this->renderContent(); if ($children != '') { $text .= content_tag($container, $children); } if (!$this->isEnabled()) { return $this->containerTag($text, array('class' => 'disabled')); } elseif ($this->isCurrent()) { return $this->containerTag($text, array('class' => 'current')); } return $this->containerTag($text); } } |
All the configuration options
Now you can see the full parameter set of sfMenuItem – but what do all those values actually mean? I think looking at the commented parameter definitions in my sample yaml file will make it more understandable:
all:
menu:
items:
# Each item must use its route name as in routing.yml as its index.
# If you provide the custom_url parameter, the route name will be
# ignored, so you may provide a name that does not exist.
first_item_route_name:
# The title is the text that is displayed for the item.
# This text will automatically be translated if an i18n instance is passed
# to the sfMenuItem's initialize() method.
title: My first item title
# Alias routes are route names that should set the same menu item as current.
# If your applications uses different routes for the same page (e.g. "update"
# routes in form actions) you must provide them here, otherwise the menu
# will not display correctly.
alias_routes: [first_item_update_route_name, ...]
# Using the position option items can be grouped into separate arrays.
# This is used for the rendering of tab levels, where the level contains
# an array with the positions as indexes.
# Typical applications might use options like "left" and "right" to separate
# the tabs of a level into different positions. If no position is provided it
# defaults to 'default'.
position: default
# This is the HTML container to use for rendering the item's content. If no container
# is provided, it defaults to 'li'. You may also provide an empty container (''),
# which will make the item content return without an outer tag. Beware though, that
# without this tag the options "disabled" and "current" have no element to be applied to.
container: li
container: ''
# This option indicates that this item requires an object of a specific type.
# The sfMenuItem will try to get this object through the sfObjectRoute
# and (if the route object is different) through sfObjectResolver.
# If no object of this type is found the item will be set to "disabled".
require_object_class: SomeClass
# If you provide a custom function, it will be called instead of rendering the
# item's content using the title. You can provide any callable, either a globally
# accessible function name or an array holding a class name and a static method name.
# The sfMenuItem object is passed to the function, so the item's public methods can
# be used to access nearly all the parameters and settings.
#
# The function footprint should be something like this:
#
# function some_function_name(sfMenuItem $item) {
# // do custom rendering here
# return $itemContent;
# }
custom_function: some_function_name
custom_function: [SomeClass, someMethod]
# Providing a custom url you can generate menu items that link somewhere else.
# It will be used in an url_for() call, so everything that works in there will be fine.
custom_url: http://www.symfony-project.org/my_custom_url
# In the children option you can provide child items of the given menu item.
# Each child item may have children itself, recursively. Of course all the options
# as for this item may be defined for any child item.
children:
child1_route_name:
child1_title: Some title
child2_route_name:
child2_title: Some other title
children:
# I think you get it...Any questions?
I think those comments should explain the options quite well. Since I can’t think of another example at the moment and also may not publish what the menu I use at work actually looks like I can only ask for user feedback if you don’t understand something or just can’t see its usage. If I see that something is still unclear I will try to make it more specific. I can only tell you that at the moment every single option except custom_url is in use in my application and I’m glad for having all of them (and I may need custom_url in the future myself).
Dynamic menus
As promised in part 3, I want to think about how sfMenuBuilder could be used dynamically. The first thing that comes to mind is having a tree-like database structure (if you need nested menus), using nestedSet features provided (AFAIK) both by Propel and Doctrine. The basic schema for a dynamic menu could look like this:
propel:
menu_item:
id:
route_name: { type: varchar(255), required: true }
title: { type: varchar(255), required: true }
alias_routes: { type: varchar(255), required: false }
position: { type: varchar(10), required: false, default: 'default' }
container: { type: varchar(10), required: false, default: 'li' }
require_object_class: { type: varchar(255), required: false }
custom_function: { type: varchar(255), required: false }
custom_url: { type: varchar(255), required: false }Just add the code for transforming these database objects into the arrays needed for sfMenuItem and you’re done!
You could also extend statically defined menu arrays by merging with dynamically created arrays. Due to the simple array syntax nearly anything can be used to describe a menu.
That’s it!
Finally we have come to an end and the sfMenuBuilder is ready to use. I hope my articles were not too bulky and rather helpful for you guys out there. When designing something as large as this and writing about it, I always want my readers to understand what goes through my mind and how I arrived at my destination. In the end, my articles shouldn’t be about the final code but more about designing things and working them out.
What started as a proof of concept for sfObjectResolver has in the end become a fully usable little library that will hopefully evolve and be used in various applications, and at the same time teach people about OOp and clean design. I know, I always exaggerate, but that’s just me…
Have fun!
19 Responses to “Creating a routing-based menu in symfony 1.2, part 4: the full package”
By lloyd27 on Apr 6, 2009 | Reply
Have you ever thought of releasing it as a plugin?
By David on Apr 7, 2009 | Reply
Actually I have, but 3 reasons have held me back so far:
I think the place for sfMenuBuilder might be somewhere inside one of the CMS plugins, although I haven’t had a look what means of navigation generation they already provide. Since symfony’s 1.2 routing system is still quite young it will take some time until the new technique finds its way into more projects.
By Maksym on May 12, 2009 | Reply
I am just a beginner in Symfony, and I do not understand something here – for example, how to call examples.php? I’ve put everything according to structure in ZIP, but for now I do not know how to call examples.php.
Another thing – am I correct to put all the structure inside the /apps/frontend/ folder?
By admin on May 14, 2009 | Reply
The structure is intended for the lib/ folder, the example.php file states how the code could be called – I’m doing this actually inside a helper (MenuHelper.php) in my app’s lib/helper folder.
By Hugo on Jun 17, 2009 | Reply
Hello,
in Example.php I need make 2 updates, for my use.
The error sfBuilder not founded!!
I change sfBuilder for sfMenuBuilder and the finishing…
Thanks
I Install
Project
apps
config
example_apps.yml (change name for app.yml)
cache
config
data
doc
lib
menu_builder
resolver
log
plugins
test
web
And the code of example.php I use in my codes for make the layouts and templates.
By David on Jun 20, 2009 | Reply
Thanks for pointing this out, I will fix it in the package!
By Łukasz Wojciechowski on Jun 22, 2009 | Reply
Hi
I like Your articles very much and I decided to use Your solution to implement my 3levels menu.
However I run into a problem.
Lets assume menu defined that way.
menu:
items:
homepage:
title: home page
children:
about_us:
title: about us
children:
peoples:
title: peoples
Now I want to display only level 0 menu (home page link) in my top horizontal navigation (that is easy) and level1 and level2 menu (about us and nested peoples) in my left side vertical navigation.
How can I accomplish that ?
thanks in advance
–
best regards
Łukasz Wojciechowski
By kevin on Jul 24, 2009 | Reply
This is GREAT stuff,
but i’m also newbie to symfony and could not get it to work just yet… because i don’t where to put the files!
“The structure is intended for the lib/ folder, the example.php file states how the code could be called – I’m doing this actually inside a helper (MenuHelper.php) in my app’s lib/helper folder.”
-> What’s in MenuHelper.php exactly? confused there…
Anyway, imho this should definitely be a plugin for the community to use!
so useful!
By kevin on Jul 24, 2009 | Reply
Got it to work… more or less!
So for the REAL newbies like me :
mkdir -p apps/backend/lib/helper/
then move the content of “lib” from the archive to this directory.
Config files :
— apps/backend/config/app.yml
all:
menu:
items:
submissions:
title: Submissions
reports:
title: Reports
children:
reports:
title: Default report
reports_summary:
title: Summary report
— apps/backend/config/routing.yml
# default rules
(…)
submissions:
class: sfDoctrineRouteCollection
options:
model: Submissions
module: submissions
prefix_path: submissions
column: id
with_wildcard_routes: true
reports:
url: /reports
param: { module: reports, action: index }
reports_summary:
url: /reports/summary
param: { module: reports, action: summary }
Added a dummy function in myUser so i can get the menu anytime from anywhere in the app
— apps/backend/lib/myUser.class.php
getRequest()->getAttribute(‘sf_route’);
$routeName = sfContext::getInstance()->getRouting()->getCurrentRouteName();
$builder = sfMenuBuilder::initInstance(sfConfig::get(‘app_menu_items’), $route, $routeName, ‘default’);
return $builder->renderTree();
}
}
Because i want this menu to be “global”
— apps/backend/templates/_menu.php
getMenu(); ?>
— apps/backend/templates/layout.php
(…)
(…)
et voila! Well almost, i have an issue : the html is escaped so the menu is still unusable, any idea why?
By kevin on Jul 24, 2009 | Reply
Sorry for the spam,
simple solution : forget about the myUser bit, and sick the content of the getMenu() function directly into the partial:
getRequest()->getAttribute(‘sf_route’);
$routeName = sfContext::getInstance()->getRouting()->getCurrentRouteName();
$builder = sfMenuBuilder::initInstance(sfConfig::get(‘app_menu_items’), $route, $routeName, ‘default’);
echo $builder->renderTree(); // echo $sf_user->getMenu();
what’s with the myUser escape the html tags?
By Sean on Jul 30, 2009 | Reply
Hey, the sfMenuBuilder is still referred to as sfBuilder.
Fatal error: Class ‘sfBuilder’ not found in …/apps/frontend/modules/main/actions/menuComponent.class.php on line 12
By Tin Nguyen on May 12, 2010 | Reply
I have just look through your post about routing-based menu in symfony. May I ask you if your way support internationization. I mean menu’s language could be changed with user. thanks by advanced.
By Christian Ducrot on Mar 7, 2011 | Reply
Great article. It’s the only solution which supports ‘alias_routes’.
Any plans for making it symfony 1.4 ready?
For example there is an exception when you click on “new” in an admin generated form:
Unable to find the sfGuardUserPeer object with the following parameters “array ( ‘sf_format’ => ‘html’,)”).
stack trace:
1. at ()
in SF_ROOT_DIR/lib/vendor/symfony/lib/routing/sfObjectRoute.class.php line 111
2. t sfObjectRoute->getObject()
in SF_ROOT_DIR/apps/backend/modules/helper/lib/menu_builder/sfMenuBuilder.class.php line 77
I think the problem is that there is no object in the route at the time this form is generated.
By David on Mar 7, 2011 | Reply
I’ve been heavily modifying the menu system since it has been released here. It is now an sf1.4 compatible standalone plugin and even supports multiple menus in separate definition files (menu.yml), breadcrumb rendering, custom item classes and much more. I can’t afford the time to re-publish it as a package, but I think I will upload the code to my github. Just give me something like 2 weeks and it will be public.
By Christian Ducrot on Apr 5, 2011 | Reply
I’m looking forward to the github version of the menu system
By Gayatri on Apr 20, 2011 | Reply
What’s the name you’re planning for the plugin?
Have you made it public yet? Can you give the github link?
By Gayatri on Apr 20, 2011 | Reply
Will this work for cross application links in the menu? I suppose (from the code you’ve posted), two instances of sfMenuBuilder will have to be created …?
By digital audio 2000 speakers on Apr 23, 2013 | Reply
I was curious if you ever thought of changing the layout of your site?
Its very well written; I love what youve got to say.
But maybe you could a little more in the way of content so people
could connect with it better. Youve got an awful
lot of text for only having one or 2 pictures. Maybe you could space
it out better?
By More information on Apr 25, 2013 | Reply
A Japanese study examined nine patients who were not
their partners. Men in the ads also look much younger than Pfizer’s earlier farmacia on line pitchman, former Kansas Sen. Many collectors in Dolpa returned without any,” said Shrestha, referring to the popular anti-impotence treatment.