<?php
namespace Croogo\Core\View\Helper;
use Cake\Core\Configure;
use Cake\Log\LogTrait;
use Cake\ORM\Entity;
use Cake\Routing\Exception\MissingRouteException;
use Cake\Routing\Router;
use Cake\Utility\Hash;
use Cake\Utility\Inflector;
use Cake\View\Helper;
use Croogo\Core\Croogo;
/**
* Layout Helper
*
* @category Helper
* @package Croogo.Croogo.View.Helper
* @version 1.0
* @author Fahad Ibnay Heylaal <contact@fahad19.com>
* @license http://www.opensource.org/licenses/mit-license.php The MIT License
* @link http://www.croogo.org
*/
class LayoutHelper extends Helper
{
use LogTrait;
/**
* Other helpers used by this helper
*
* @var array
* @access public
*/
public $helpers = [
'Croogo/Core.Croogo',
'Croogo/Core.Theme',
'Html',
'Form',
'Flash'
];
/**
* Core helpers
*
* Extra supported callbacks, like beforeNodeInfo() and afterNodeInfo(),
* won't be called for these helpers.
*
* @var array
* @access public
*/
public $coreHelpers = [
// CakePHP
'Cache',
'Form',
'Html',
'Number',
'Paginator',
'Rss',
'Text',
'Time',
'Xml',
// Croogo
'Filemanager',
'Image',
'Layout',
'Recaptcha',
];
/**
* Javascript variables
*
* Shows croogo.js file along with useful information like the applications's basePath, etc.
*
* Also merges Configure::read('Js') with the Croogo js variable.
* So you can set javascript info anywhere like Configure::write('Js.my_var', 'my value'),
* and you can access it like 'Croogo.my_var' in your javascript.
*
* @return string
*/
public function js()
{
$croogo = $this->_mergeThemeSettings();
$request = $this->getView()->getRequest();
if ($request->getParam('locale')) {
$croogo['basePath'] = Router::url('/' . $request->getParam('locale') . '/');
} else {
$croogo['basePath'] = Router::url('/');
}
$validKeys = [
'plugin' => null,
'controller' => null,
'action' => null,
'prefix' => null,
'pass' => null,
'?' => null,
];
$croogo['params'] = array_intersect_key(
array_merge($validKeys, $request->getAttribute('params')),
$validKeys
);
if (is_array(Configure::read('Js'))) {
$croogo = Hash::merge($croogo, Configure::read('Js'));
}
return $this->Html->scriptBlock('var Croogo = ' . json_encode($croogo) . ';');
}
/**
* Merge helper and prefix specific settings
*
* @param array $croogoSetting Croogo JS settings
* @return array Merged settings
*/
protected function _mergeThemeSettings($croogoSetting = [])
{
$themeSettings = $this->Theme->settings();
if (empty($themeSettings)) {
return $croogoSetting;
}
$validKeys = [
'css' => null,
'icons' => null,
'iconDefaults' => null,
'select2Defaults' => null,
];
$croogoSetting['themeSettings'] = array_intersect_key(
array_merge($validKeys, $themeSettings),
$validKeys
);
if ($this->_View->helpers()->has('Html') && $this->_View->helpers()->Html instanceof HtmlHelper) {
unset($validKeys['css']);
$croogoSetting['themeSettings'] = Hash::merge(
$croogoSetting['themeSettings'],
array_intersect_key(
array_merge($validKeys, $this->_View->Html->getConfig()),
$validKeys
)
);
}
return $croogoSetting;
}
/**
* Status
*
* instead of 0/1, show tick/cross
*
* @param int $value or 1
* @return string formatted img tag
*/
public function status($value)
{
$icons = $this->Theme->settings('icons');
if (empty($icons)) {
$icons = ['check-mark' => 'ok', 'x-mark' => 'remove'];
}
if ($value == 1) {
$icon = $icons['check-mark'];
$class = 'green';
} else {
$icon = $icons['x-mark'];
$class = 'red';
}
if (method_exists($this->Html, 'icon')) {
return $this->Html->icon($icon, compact('class'));
} else {
if (empty($this->_View->Html)) {
$this->_View->Helpers->load('Croogo/Core.Html');
}
return $this->_View->Html->icon($icon, compact('class'));
}
}
/**
* Display value from $item array
*
* @param $item array of values
* @param $model string model alias
* @param $field string field name
* @param $options array
* @return string
*/
public function displayField(Entity $item, $model, $field, $options = [])
{
extract(array_intersect_key($options, [
'type' => null,
'url' => [],
'options' => [],
]));
switch ($type) {
case 'boolean':
$out = $this->status($item->{$field});
break;
default:
$out = h((!isset($item->{$model})) ? $item->{$field} : $item->{$model}->{$field});
break;
}
if (!empty($url)) {
if (isset($url['pass'])) {
$passVars = is_string($url['pass']) ? [$url['pass']] : $url['pass'];
foreach ($passVars as $passField) {
$url[] = $item->get($passField);
}
unset($url['pass']);
}
if (isset($url['named'])) {
foreach ((array)$url['named'] as $namedField => $namedRoute) {
if (is_numeric($namedField)) {
$namedField = $namedRoute;
}
$url[$namedRoute] = $item->get($namedField);
}
unset($url['named']);
}
try {
$out = $this->Html->link($out, $url, $options);
} catch (MissingRouteException $e) {
$out = $out; //If the route doesn't exist then act gracefully
}
}
return $out;
}
/**
* Show flash message
*
* @return string
*/
public function sessionFlash()
{
$messages = $this->getView()->getRequest()->getSession()->read('Flash');
$output = '';
if (is_array($messages)) {
foreach (array_keys($messages) as $key) {
$output .= $this->Flash->render($key);
}
}
return $output;
}
/**
* isLoggedIn
*
* if User is logged in
*
* @return bool
*/
public function isLoggedIn()
{
if ($this->getView()->getRequest()->getSession()->check('Auth.User.id')) {
return true;
} else {
return false;
}
}
/**
* Feed
*
* RSS feeds
*
* @param bool $returnUrl true, only the URL will be returned
* @return string
*/
public function feed($returnUrl = false)
{
if (Configure::read('Site.feed_url')) {
$url = Configure::read('Site.feed_url');
} else {
/*$url = Router::url(array(
'controller' => 'Nodes',
'action' => 'index',
'type' => 'blog',
'ext' => 'rss',
));*/
$url = '/promoted.rss';
}
if ($returnUrl) {
$output = $url;
} else {
$url = Router::url($url);
$output = '<link href="' . $url . '" type="application/rss+xml" rel="alternate" title="RSS 2.0" />';
return $output;
}
return $output;
}
/**
* Get Role ID
*
* @return int
*/
public function getRoleId()
{
if ($this->isLoggedIn()) {
$roleId = $this->getView()->getRequest()->getSession()->read('Auth.User.role_id');
} else {
// Public
$roleId = 2;
}
return $roleId;
}
/**
* Filter content
*
* Replaces bbcode-like element tags
*
* @param string $content content
* @return string
*/
public function filter($content, $options = [])
{
Croogo::dispatchEvent('Helper.Layout.beforeFilter', $this->_View, [
'content' => &$content,
'options' => $options,
]);
$content = $this->filterElements($content, $options);
Croogo::dispatchEvent('Helper.Layout.afterFilter', $this->_View, [
'content' => &$content,
'options' => $options,
]);
return $content;
}
/**
* Filter content for elements
*
* Original post by Stefan Zollinger: http://bakery.cakephp.org/articles/view/element-helper
* [element:element_name] or [e:element_name]
*
* @param string $content
* @return string
*/
public function filterElements($content, $options = [])
{
preg_match_all('/\[(element|e|cell|c):([A-Za-z0-9_\-\/]*)(.*?)\]/i', $content, $tagMatches);
$validOptions = ['plugin', 'cache', 'callbacks'];
for ($i = 0, $ii = count($tagMatches[1]); $i < $ii; $i++) {
$regex = '/([\w-]+)=[\'"]?((?:.(?![\'"]?\s+(?:\S+)=|[>\'"]))*.)[\'"]?/i';
preg_match_all($regex, $tagMatches[3][$i], $attributes);
$element = $tagMatches[2][$i];
$data = $options = [];
for ($j = 0, $jj = count($attributes[0]); $j < $jj; $j++) {
if (in_array($attributes[1][$j], $validOptions)) {
$options = Hash::merge($options, [$attributes[1][$j] => $attributes[2][$j]]);
} else {
$data[$attributes[1][$j]] = $attributes[2][$j];
}
}
if (!empty($this->_View->viewVars['block'])) {
$data['block'] = $this->_View->viewVars['block'];
}
if (!empty($options['plugin'])) {
$element = $options['plugin'] . '.' . $element;
unset($options['plugin']);
}
if ($tagMatches[1][$i] === 'cell' || $tagMatches[1][$i] === 'c') {
$element = $this->_View->cell($element, $data, $options);
} else {
$element = $this->_View->element($element, $data, $options);
}
$content = str_replace($tagMatches[0][$i], $element, $content);
}
return $content;
}
/**
* Hook
*
* Used for calling hook methods from other HookHelpers
*
* @param string $methodName
* @return string
*/
public function hook($methodName)
{
$output = '';
foreach ($this->_View->helpers() as $helper => $settings) {
if (!is_string($helper) || in_array($helper, $this->coreHelpers)) {
continue;
}
list(, $helper) = pluginSplit($helper);
if (isset($this->_View->{$helper}) && method_exists($this->_View->{$helper}, $methodName)) {
$output .= $this->_View->{$helper}->$methodName();
}
}
return $output;
}
/**
* Gets a value of view variables based on path
*
* @param string $name Variable name to retrieve from View::viewVars
* @param string $path Extraction path following the Hash path syntax
* @return array
*/
public function valueOf($name, $path, $options = [])
{
if (!isset($this->_View->viewVars[$name])) {
$this->log(sprintf('Invalid viewVars "%s"', $name));
return [];
}
$result = Hash::extract($this->_View->viewVars[$name], $path);
$result = isset($result[0]) ? $result[0] : $result;
return $result;
}
/**
* Compute default options for snippet()
*
* @param string $type Type
* @return array Array of options
*/
private function __snippetDefaults($type)
{
$plural = Inflector::pluralize($type);
$varName = strtolower($plural) . 'ForLayout';
$modelAlias = Inflector::camelize($plural);
$checkField = 'alias';
$valueField = 'body';
$filter = true;
$format = '{s}.{n}[%s=%s].%s';
switch ($type) {
case 'type':
$valueField = 'description';
$format = '{s}.%s[%s=%s].%s';
break;
case 'vocabulary':
$valueField = 'title';
$format = '{s}.%s[%s=%s].%s';
break;
case 'menu':
$valueField = 'title';
$format = '{s}.%s[%s=%s].%s';
break;
case 'node':
$checkField = 'slug';
break;
}
return compact('checkField', 'filter', 'format', 'modelAlias', 'valueField', 'varName');
}
/**
* Simple method to retrieve value from view variables using Hash path format
*
* Example:
*
* // display the 'about' block
* echo $this->Layout->snippet('about');
* // display the 'hello world' node
* echo $this->Layout->snippet('hello-world', 'node');
*
* You can customize the return value by supplying a custom path:
* // display the 'main' menu array
* echo $this->Layout->snippet('main', 'menu', array(
* 'format' => '{s}.%s[%s=%s].%s',
* ));
* // display the 'main' menu description field
* echo $this->Layout->snippet('main', 'menu', array(
* 'valueField' => 'description',
* 'format' => '{s}.%s[%s=%s].%s',
* ));
*
* Options:
* - checkField Field name that will be checked against $name
* - filter Filter view data. Defaults to true
* - format Hash path format
* - modelAlias Model alias
* - valueField Field name that will be returned if data is found
* - varName Variable name as it is stored in viewVars
*
* @param string $name Identifier
* @param string $type String of `block`, `nodes`, `node`
* @param array $options Options array
* @return string
*/
public function snippet($name, $type = 'block', $options = [])
{
$options = array_merge($this->__snippetDefaults($type), $options);
extract($options);
$path = sprintf($format, $checkField, $name, $valueField);
$result = $this->valueOf($options['varName'], $path);
if ($result) {
if ($options['filter'] === true && is_string($result)) {
return $this->filter($result, $options);
} else {
return $result;
}
} else {
return null;
}
}
}