<?php
/**
 * @package         Articles Anywhere
 * @version         6.0.6
 * 
 * @author          Peter van Westen <info@regularlabs.com>
 * @link            http://www.regularlabs.com
 * @copyright       Copyright © 2017 Regular Labs All Rights Reserved
 * @license         http://www.gnu.org/licenses/gpl-2.0.html GNU/GPL
 */

namespace RegularLabs\ArticlesAnywhere;

defined('_JEXEC') or die;

use ArticlesAnywhereArticleModel;
use ArticlesAnywhereArticleView;
use ContentHelperRoute;
use JAccess;
use JComponentHelper;
use JFactory;
use JFile;
use JFolder;
use JHelperTags;
use JHtml;
use JLayoutFile;
use JLayoutHelper;
use Joomla\Registry\Registry;
use JPath;
use JRoute;
use JText;
use JUri;
use RegularLabs\Library\Date as RL_Date;
use RegularLabs\Library\Language as RL_Language;
use RegularLabs\Library\PluginTag as RL_PluginTag;
use RegularLabs\Library\RegEx as RL_RegEx;
use RegularLabs\Library\StringHelper as RL_String;
use TagsHelperRoute;

class DataTags
{
	protected $tables    = [];
	protected $field_ids = [];

	public function handleIfStatements(&$string, &$article)
	{
		list($tag_start, $tag_end) = Params::getTagCharacters();

		RL_RegEx::matchAll(
			RL_RegEx::quote($tag_start) . 'if[\: ].*?' . RL_RegEx::quote($tag_start) . '/if' . RL_RegEx::quote($tag_end),
			$string,
			$matches
		);

		if (empty($matches))
		{
			return;
		}

		Article::set($article);

		if (strpos($string, 'text') !== false)
		{
			$article->text = (isset($article->introtext) ? $article->introtext : '')
				. (isset($article->introtext) ? $article->fulltext : '');
		}

		foreach ($matches as $match)
		{
			RL_RegEx::matchAll(
				$tag_start . '(if|else ?if|else)(?:[\: ](.+?))?' . $tag_end . '(.*?)(?=' . $tag_start . '(?:else|\/if))',
				$match['0'],
				$ifs
			);

			if (empty($ifs))
			{
				continue;
			}

			$replace = $this->getIfResult($ifs);

			// replace if block with the IF value
			$string = RL_String::replaceOnce($match['0'], $replace, $string);
		}

		$article = Article::get();
	}

	private function getIfResult(&$matches)
	{
		foreach ($matches as $if)
		{
			if (!$this->passIfStatements($if))
			{
				continue;
			}

			return $if['3'];
		}

		return '';
	}

	private function passIfStatements($if)
	{
		$statement = trim($if['2']);

		if (trim($if['1']) == 'else' && $statement == '')
		{
			return true;
		}

		if ($statement == '')
		{
			return false;
		}

		$statement = RL_String::html_entity_decoder($statement);
		$statement = str_replace(
			[' AND ', ' OR '],
			[' && ', ' || '],
			$statement
		);

		$ands = explode(' && ', $statement);

		$pass = false;
		foreach ($ands as $statement)
		{
			$ors = explode(' || ', $statement);
			foreach ($ors as $statement)
			{
				if ($pass = $this->passIfStatement($statement))
				{
					break;
				}
			}

			if (!$pass)
			{
				break;
			}
		}

		return $pass;
	}

	private function passIfStatement($statement)
	{
		$statement = trim($statement);

		/*
		* In array syntax
		* 'bar' IN foo
		* 'bar' !IN foo
		* 'bar' NOT IN foo
		*/
		if (RL_RegEx::match('^[\'"]?(?P<val>.*?)[\'"]?\s+(?P<operator>(?:NOT\s+)?\!?IN)\s+(?P<key>[a-zA-Z0-9-_]+)$', $statement, $match))
		{
			$reverse = ($match['operator'] == 'NOT IN' || $match['operator'] == '!NOT');

			return $this->passIfStatementArray(
				$this->getValueFromData($match['key']),
				$this->getValueFromData($match['val'], $match['val']),
				$reverse
			);
		}

		/*
		* String comparison syntax:
		* foo = 'bar'
		* foo != 'bar'
		*/
		if (RL_RegEx::match('^(?P<key>[a-z0-9-_]+)\s*(?P<operator>\!?=)=*\s*[\'"]?(?P<val>.*?)[\'"]?$', $statement, $match))
		{
			$reverse = ($match['operator'] == '!=');

			return $this->passIfStatementArray(
				$this->getValueFromData($match['key']),
				$this->getValueFromData($match['val'], $match['val']),
				$reverse
			);
		}

		/*
		* Lesser/Greater than comparison syntax:
		* foo < bar
		* foo > bar
		* foo <= bar
		* foo >= bar
		*/
		if (RL_RegEx::match('^(?P<key>[a-z0-9-_]+)\s*(?P<operator>>=?|<=?)=*\s*[\'"]?(?P<val>.*?)[\'"]?$', $statement, $match))
		{
			return $this->passIfStatementCompare(
				$this->getValueFromData($match['key']),
				$this->getValueFromData($match['val'], $match['val']),
				$match['operator']
			);
		}

		/*
		* Variable check syntax:
		* foo (= not empty)
		* !foo (= empty)
		*/
		if (RL_RegEx::match('^(?P<operator>\!?)(?P<key>[a-z0-9-_]+)$', $statement, $match))
		{
			$reverse = ($match['operator'] == '!');

			return $this->passIfStatementSimple(
				$this->getValueFromData($match['key']),
				$reverse
			);
		}

		return $this->passIfStatementPHP($statement);
	}

	public function getValueFromData($key, $default = null)
	{
		if (!is_string($key))
		{
			return $default;
		}

		$key = trim($key);

		if (in_array(
			$key,
			array('NOW', 'now()', 'date()', 'JFactory::getDate()')
		))
		{
			return JFactory::getDate()->toSql();
		}

		if (Numbers::exists($key))
		{
			return Numbers::get($key);
		}

		$article = Article::get();
		if (isset($article->{$key}))
		{
			return $article->{$key};
		}


		return $default;
	}


	private function passIfStatementSimple($haystack, $reverse = 0)
	{
		if (is_null($haystack))
		{
			return false;
		}

		$pass = !empty($haystack);

		return $reverse ? !$pass : $pass;
	}

	private function passIfStatementCompare($haystack, $needle, $operator)
	{
		switch ($operator)
		{
			case '<':
				return $haystack < $needle;

			case '<=':
				return $haystack <= $needle;

			case '>':
				return $haystack > $needle;

			case '>=':
				return $haystack >= $needle;
		}

		return false;
	}

	private function passIfStatementArray($haystack, $needle, $reverse = 0)
	{
		if (is_null($haystack))
		{
			return false;
		}

		if (!is_array($haystack))
		{
			$haystack = explode(',', str_replace(', ', ',', $haystack));
		}

		if (!is_array($haystack))
		{
			return false;
		}

		$pass = false;
		foreach ($haystack as $string)
		{
			if ($pass = $this->passString($string, $needle))
			{
				break;
			}
		}

		return $reverse ? !$pass : $pass;
	}

	private function passIfStatementPHP($statement)
	{
		$php = RL_String::html_entity_decoder($statement);
		$php = RL_RegEx::replace('([^<>])=([^<>])', '\1==\2', $php);

		// replace keys with $article->key
		$php = '$article->' . RL_RegEx::replace('\s*(&&|&&|\|\|)\s*', ' \1 $article->', $php);

		// fix negative keys from $article->!key to !$article->key
		$php = str_replace('$article->!', '!$article->', $php);

		$numbers = Numbers::getAll();

		// replace back data variables
		foreach ($numbers as $key => $val)
		{
			$php = str_replace('$article->' . $key, (int) $val, $php);
		}

		$php = str_replace('$article->empty', (int) (Numbers::get('count') > 0), $php);

		// Place statement in return check
		$php = 'return ( ' . $php . ' ) ? true : false;';

		// Trim the text that needs to be checked and replace weird spaces
		$php = RL_RegEx::replace(
			'(\$article->[a-z0-9-_]*)',
			'trim(str_replace(chr(194) . chr(160), " ", \1))',
			$php
		);

		// Fix extra-1 field syntax: $article->extra-1 to $article->{'extra-1'}
		$php = RL_RegEx::replace(
			'->(extra-[a-z0-9]+)',
			'->{\'\1\'}',
			$php
		);

		$temp_PHP_func = create_function('&$article', $php);

		// evaluate the script
		// but without using the the evil eval
		ob_start();
		$pass = $temp_PHP_func(Article::get());
		unset($temp_PHP_func);
		ob_end_clean();

		return $pass;
	}

	private function passString($haystack, $needle)
	{
		if (!is_string($haystack) && !is_string($needle)
			&& !is_numeric($haystack)
			&& !is_numeric($needle)
		)
		{
			return false;
		}

		// Simple string comparison
		if (strpos($needle, '*') === false && strpos($needle, '+') === false)
		{
			return strtolower($haystack) == strtolower($needle);
		}

		// Using wildcards
		$needle = RL_RegEx::quote($needle);
		$needle = str_replace(
			['\\\\\\*', '\\*', '[:asterisk:]', '\\\\\\+', '\\+', '[:plus:]'],
			['[:asterisk:]', '.*', '\\*', '[:plus:]', '.+', '\\+'],
			$needle
		);

		return RL_RegEx::match($needle, $haystack);
	}

	public function replaceTags(&$text, &$matches, &$article)
	{
		Article::set($article);

		foreach ($matches as $match)
		{
			$string = $this->processTag($match['1']);
			if ($string === false)
			{
				continue;
			}

			$text = str_replace($match['0'], $string, $text);
		}
	}

	public function getTagValues($string)
	{
		$tag = $this->getTagValuesFromString($string);

		$key_aliases = [
			'limit'      => ['letters', 'letter_limit', 'characters', 'character_limit'],
			'words'      => ['word', 'word_limit'],
			'paragraphs' => ['paragraph', 'paragraph_limit'],
			'class'      => ['classes'],
		];

		RL_PluginTag::replaceKeyAliases($tag, $key_aliases);

		return $tag;
	}

	public function getTagValuesFromString($string)
	{
		if (RL_RegEx::match('^layout[ \:]([^=]+)$', $string, $match))
		{
			$string = 'layout layout="' . trim($match['1']) . '"';
		}

		if (strpos($string, ':') !== false
			&& RL_RegEx::match('^([a-z]+ )?[a-z]+\s*:\s*[a-z0-9\|]', $string)
		)
		{
			return $this->getTagValuesFromOldSyntax($string);
		}

		$string = RL_RegEx::replace('^(.*?) ', 'type="\1" ', $string);

		return RL_PluginTag::getAttributesFromString($string, 'type');
	}

	public function getTagValuesFromOldSyntax($string)
	{
		$tag = (object) [];

		if (strpos($string, ' ') !== false
			&& RL_RegEx::match('^[a-z]+ [a-z0-9\|]', $string)
		)
		{
			$data      = explode(' ', $string, 2);
			$tag->type = array_shift($data);

			$data = explode('|', array_shift($data));

			foreach ($data as $parameter)
			{
				if (strpos($parameter, ':') === false)
				{
					continue;
				}

				list($key, $val) = explode(':', $parameter, 2);
				$tag->{$key} = $val;
				unset($data[array_search($key, $data)]);
			}

			return $tag;
		}

		$data = explode(':', $string);

		$tag->type = array_shift($data);

		foreach ($data as $parameter)
		{
			if (strpos($parameter, '=') === false)
			{
				continue;
			}

			list($key, $val) = explode('=', $parameter, 2);
			$tag->{$key} = $val;
		}

		if (empty($data))
		{
			return $tag;
		}

		switch (true)
		{
			// Readmore link
			case (strpos($tag->type, 'readmore') === 0):

				$tag->text = array_shift($data);
				if (strpos($tag->text, '|') === false)
				{
					break;
				}

				list($tag->text, $tag->class) = explode('|', $tag->text, 2);
				break;

			// Title / Text
			case (
					$tag->type == 'title'
					|| strpos($tag->type, 'title:') === 0
					|| strpos($tag->type, 'text') === 0)
				|| (strpos($tag->type, 'intro') === 0)
				|| (strpos($tag->type, 'full') === 0
				):

				if (in_array('strip', $data))
				{
					$tag->strip = 1;
					unset($data[array_search('strip', $data)]);
				}
				if (in_array('noimages', $data))
				{
					$tag->noimages = 1;
					unset($data[array_search('noimages', $data)]);
				}

				if (empty($data))
				{
					break;
				}

				$limit = array_shift($data);

				if (strpos($limit, 'word') !== false)
				{
					$tag->words = (int) $limit;
					break;
				}

				$tag->limit = (int) $limit;
				break;


			// Database values
			case (RL_String::is_alphanumeric(str_replace(['-', '_'], '', $tag->type))):
				$tag->format = array_shift($data);
				break;
		}

		return $tag;
	}

	public function processTag($string)
	{
		$tag = $this->getTagValues($string);

		switch (true)
		{
			// Link closing tag
			case ($tag->type == '/link'):
				return '</a>';

			// Total count
			case ($tag->type == 'total' || $tag->type == 'totalcount'):
				return Numbers::get('total');

			// Counter
			case ($tag->type == 'count' || $tag->type == 'counter'):
				return Numbers::get('count');

			// Div closing tag
			case ($tag->type == '/div'):
				return '</div>';

			// Div
			case ($tag->type == 'div'):
				return $this->processTagDiv($tag);

			// URL
			case ($tag->type == 'url' || $tag->type == 'nonsefurl'):
				return $this->getArticleUrl();

			// SEF URL
			case ($tag->type == 'sefurl'):
				return JRoute::_($this->getArticleUrl());

			// Link tag
			case ($tag->type == 'link'):
				return $this->processTagLink();

			// Readmore link
			case ($tag->type == 'readmore'):
				return $this->processTagReadmore($tag);

			// Title
			case ($tag->type == 'title'):
				return $this->processTagTitle($tag);

			// Text
			case (in_array($tag->type, ['text', 'introtext', 'fulltext'])):
				return $this->processTagText($tag);

			// Intro image
			case ($tag->type == 'image-intro'):
				return $this->processTagImageIntro();

			// Fulltext image
			case ($tag->type == 'image-fulltext'):
				return $this->processTagImageFulltext();

			// Layout
			case ($tag->type == 'layout'):
				return $this->processTagLayout($tag);


			// Database values
			case (RL_String::is_alphanumeric(str_replace(['-', '_'], '', $tag->type))):
				return $this->processTagDatabase($tag);

			default:
				return false;
		}
	}

	public function processTagDiv($tag)
	{
		$attributes = [];

		if (isset($tag->class))
		{
			$attributes[] = 'class="' . $tag->class . '"';
		}

		$style = [];

		if (isset($tag->width))
		{
			if (is_numeric($tag->width))
			{
				$tag->width .= 'px';
			}
			$style[] = 'width:' . $tag->width;
		}

		if (isset($tag->height))
		{
			if (is_numeric($tag->height))
			{
				$tag->height .= 'px';
			}
			$style[] = 'height:' . $tag->height;
		}

		if (isset($tag->align))
		{
			$style[] = 'float:' . $tag->align;
		}
		else if (isset($tag->float))
		{
			$style[] = 'float:' . $tag->float;
		}

		if (!empty($style))
		{
			$attributes[] = 'style="' . implode(';', $style) . ';"';
		}

		if (empty($attributes))
		{
			return '<div>';
		}

		return trim('<div ' . implode(' ', $attributes)) . '>';
	}

	public function processTagReadmore($tag)
	{
		if (!$link = $this->getArticleUrl())
		{
			return false;
		}

		// load the content language file
		RL_Language::load('com_content', JPATH_SITE);

		if (!empty($tag->class))
		{
			return '<a class="' . trim($tag->class) . '" href="' . $link . '">' . $this->getReadMoreText($tag) . '</a>';
		}

		$config = JComponentHelper::getParams('com_content');
		$config->set('access-view', true);

		$article = Article::get();

		if ($text = $this->getCustomReadMoreText($tag))
		{
			$article->alternative_readmore = $text;
			$config->set('show_readmore_title', false);
		}

		return JLayoutHelper::render('joomla.content.readmore', ['item' => $article, 'params' => $config, 'link' => $link]);
	}

	private function getCustomReadMoreText($tag)
	{
		if (empty($tag->text))
		{
			return '';
		}

		$title = trim($tag->text);
		$text  = JText::sprintf($title, Article::get('title'));

		return $text ?: $title;
	}

	public function getReadMoreText($tag)
	{
		if ($text = $this->getCustomReadMoreText($tag))
		{
			return $text;
		}

		$config  = JComponentHelper::getParams('com_content');
		$article = Article::get();

		switch (true)
		{
			case (isset($article->alternative_readmore) && $article->alternative_readmore) :
				$text = $article->alternative_readmore;
				break;
			case (!$config->get('show_readmore_title', 0)) :
				$text = JText::_('COM_CONTENT_READ_MORE_TITLE');
				break;
			default:
				$text = JText::_('COM_CONTENT_READ_MORE');
				break;
		}

		if (!$config->get('show_readmore_title', 0))
		{
			return $text;
		}

		return $text . JHtml::_('string.truncate', ($article->title), $config->get('readmore_limit'));
	}

	public function processTagLink()
	{
		if (!$link = $this->getArticleUrl())
		{
			return false;
		}

		return '<a href="' . $link . '">';
	}

	public function processTagTitle($extra)
	{
		$article = Article::get();
		$title   = isset($article->title) ? $article->title : '';

		if (empty($title) || empty($extra))
		{
			return $title;
		}

		return Text::process($title, $extra);
	}

	public function processTagText($tag)
	{
		$article = Article::get();

		switch (true)
		{
			case ($tag->type == 'introtext'):
				if (!isset($article->introtext))
				{
					return false;
				}

				$article->text = $article->introtext;
				break;

			case ($tag->type == 'fulltext'):
				if (!isset($article->fulltext))
				{
					return false;
				}

				$article->text = $article->fulltext;

				$this->hitArticle();
				break;

			case ($tag->type == 'text'):
				$article->text = (isset($article->introtext) ? $article->introtext : '')
					. (isset($article->fulltext) ? $article->fulltext : '');

				$this->hitArticle();
				break;
		}

		if ($article->text == '')
		{
			return '';
		}

		$string = $article->text;

		return Text::process($string, $tag);
	}

	public function hitArticle()
	{
		$params = Params::get();

		if (!$params->increase_hits_on_text)
		{
			return;
		}

		require_once __DIR__ . '/Helpers/article_model.php';

		$model   = new ArticlesAnywhereArticleModel;
		$article = Article::get();

		$model->hit($article->id);
	}

	public function processTagImageIntro()
	{
		$article = Article::get();
		if (empty($article->image_intro))
		{
			return '';
		}

		$class = 'img-intro-' . $article->float_intro;

		return self::getImageHtml($article->image_intro, $article->image_intro_alt, $article->image_intro_caption, $class);
	}

	public function processTagImageFulltext()
	{
		$article = Article::get();
		if (empty($article->image_fulltext))
		{
			return '';
		}

		$class = 'img-fulltext-' . $article->float_fulltext;

		return self::getImageHtml($article->image_fulltext, $article->image_fulltext_alt, $article->image_fulltext_caption, $class);
	}

	public static function getImageHtml($url, $alt = '', $caption = '', $class = '', $in_div = true)
	{
		$img_class = $caption ? 'caption' : '';
		$caption   = $caption ? ' title="' . htmlspecialchars($caption) . '"' : '';

		if ($in_div)
		{
			return '<div class="' . htmlspecialchars($class) . '"><img' . $caption . ' src="' . htmlspecialchars($url) . '" alt="' . htmlspecialchars($alt) . '" class="' . $img_class . '"></div>';
		}

		$img_class = trim($img_class . ' ' . htmlspecialchars($class));

		return '<img' . $caption . ' src="' . htmlspecialchars($url) . '" alt="' . htmlspecialchars($alt) . '" class="' . $img_class . '">';
	}

	public function processTagLayout($tag)
	{
		$article = Article::get();

		if (
			JFactory::getApplication()->input->get('option') == 'com_finder'
			&& JFactory::getApplication()->input->get('format') == 'json'
		)
		{
			// Force simple layout for finder indexing, as the setParams causes errors
			return
				'<h2>' . $article->title . '</h2>'
				. $this->processTagText('text', $tag);
		}

		$params = Params::get();

		list($template, $layout) = $this->getTemplateAndLayout($tag);

		require_once __DIR__ . '/Helpers/article_view.php';

		$view = new ArticlesAnywhereArticleView;

		$view->setParams($article->id, $template, $layout, $params);

		return $view->display();
	}


	public function processTagDatabase($tag, $return_empty = false)
	{
		// Get data from data object, even, uneven, first, last
		if (is_bool(Numbers::get($tag->type)))
		{
			return Numbers::get($tag->type) ? 'true' : 'false';
		}

		// Get data from db columns
		$string = $this->getTagFromDatabase($tag);

		if ($string === false || is_array($string) || is_object($string))
		{
			return $return_empty ? '' : false;
		}

		// Convert string if it is a date
		$string = $this->convertDateToString($string, isset($tag->format) ? $tag->format : '', $tag->type);

		return $string;
	}

	private function getTagFromDatabase($tag)
	{
		$article = Article::get();

		if (isset($article->{$tag->type}))
		{
			return $article->{$tag->type};
		}



		return false;
	}


	public function convertDateToString($string, $format, $key)
	{
		// Check if string could be a date
		if (
			// These keys are never dates
			in_array($key, [
				'title', 'alias',
				'cat', 'cat_title', 'cat_alias',
				'author', 'author_name',
				'text', 'introtext', 'fulltext',
			])
			// Dates must contain a '-' and not letters
			|| (strpos($string, '-') == false)
			|| RL_RegEx::match('[a-z]', $string)
			// Check string it passes a simple strtotime
			|| !strtotime($string)
		)
		{
			return $string;
		}

		if (empty($format))
		{
			$format = JText::_('DATE_FORMAT_LC2');
		}

		if (strpos($format, '%') !== false)
		{
			$format = RL_Date::strftimeToDateFormat($format);
		}

		return JHtml::_('date', $string, $format);
	}

	public function canEdit()
	{
		$user = JFactory::getUser();
		if ($user->get('guest'))
		{
			return false;
		}

		$article = Article::get();

		$userId = $user->get('id');
		$asset  = 'com_content.article.' . $article->id;

		// Check general edit permission first.
		if ($user->authorise('core.edit', $asset))
		{
			return true;
		}

		// Now check if edit.own is available.
		if (empty($userId) || $user->authorise('core.edit.own', $asset))
		{
			return false;
		}

		// Check for a valid user and that they are the owner.
		if ($userId != $article->created_by)
		{
			return false;
		}

		return true;
	}

	public function getArticleUrl()
	{
		$article = Article::get();

		if (isset($article->url))
		{
			return $article->url;
		}

		if (!isset($article->id))
		{
			return false;
		}

		if (!class_exists('ContentHelperRoute'))
		{
			require_once JPATH_SITE . '/components/com_content/helpers/route.php';
		}

		$article->url = ContentHelperRoute::getArticleRoute($article->id, $article->catid, $article->language);

		if (empty($article->has_access))
		{
			$article->url = $this->getRestrictedUrl($article->url);
		}

		return $article->url;
	}

	public function getRestrictedUrl($url)
	{
		$menu   = JFactory::getApplication()->getMenu();
		$active = $menu->getActive();
		$itemId = $active->id;
		$link   = new JUri(JRoute::_('index.php?option=com_users&view=login&Itemid=' . $itemId, false));

		$link->setVar('return', base64_encode(JRoute::_($url, false)));

		return (string) $link;
	}

	public function getArticleEditUrl()
	{
		$article = Article::get();

		if (isset($article->editurl))
		{
			return $article->editurl;
		}

		if (!isset($article->id))
		{
			return false;
		}

		$article->editurl = '';

		if (!$this->canEdit())
		{
			return '';
		}

		$uri = JUri::getInstance();

		$article->editurl = JRoute::_('index.php?option=com_content&task=article.edit&a_id=' . $article->id . '&return=' . base64_encode($uri));

		return $article->editurl;
	}


	public function getLayoutFile($tag)
	{
		jimport('joomla.filesystem.path');
		jimport('joomla.filesystem.file');

		$template_layout = (isset($tag->template) ? $tag->template . ':' : '')
			. (isset($tag->layout) ? $tag->layout . ':' : '');

		list($template, $layout) = $this->getTemplateAndLayout($template_layout);

		// Load the language file for the template
		$lang = JFactory::getLanguage();
		$lang->load('tpl_' . $template, JPATH_BASE, null, false, false)
		|| $lang->load('tpl_' . $template, JPATH_THEMES . '/' . $template, null, false, false)
		|| $lang->load('tpl_' . $template, JPATH_BASE, $lang->getDefault(), false, false)
		|| $lang->load('tpl_' . $template, JPATH_THEMES . '/' . $template, $lang->getDefault(), false, false);

		$paths = [
			JPATH_THEMES . '/' . $template . '/html/com_content/article',
			JPATH_SITE . '/components/com_content/views/article/tmpl',
		];

		$file = JPath::find($paths, $layout . '.php');

		// Check if layout exists
		if (JFile::exists($file))
		{
			return $file;
		}

		// Return default layout
		return JPath::find($paths, 'default.php');
	}

	public function getTemplateAndLayout($data)
	{
		$article = Article::get();

		if (!isset($data->template) && isset($data->layout) && strpos($data->layout, ':') !== false)
		{
			list($data->template, $data->layout) = explode(':', $data->layout);
		}

		$layout   = !empty($data->layout) ? $data->layout : (!empty($article->article_layout) ? $article->article_layout : 'default');
		$template = !empty($data->template) ? $data->template : JFactory::getApplication()->getTemplate();

		if (strpos($layout, ':') !== false)
		{
			list($template, $layout) = explode(':', $layout);
		}

		jimport('joomla.filesystem.folder');

		// Layout is a template, so return default layout
		if (empty($data->template) && JFolder::exists(JPATH_THEMES . '/' . $layout))
		{
			return [$layout, 'default'];
		}

		// Value is not a template, so a layout
		return [$template, $layout];
	}
}
