<?php
namespace FACTFinder\Core\Client;

use FACTFinder\Loader as FF;

/**
 * Extracts several data from the request made to the client.
 */
class RequestParser
{
    protected $clientRequestParameters;
    protected $serverRequestParameters;
    protected $requestTarget;

    /**
     * @var FACTFinder\Util\LoggerInterface
     */
    private $log;

    /**
     * @var ConfigurationInterface
     */
    protected $configuration;

    /**
     * @var AbstractEncodingConverter
     */
    protected $encodingConverter;

    /**
     * @var ParametersConverter
     */
    protected $parametersConverter;

    /**
     * @param string $loggerClass Class name of logger to use. The class should
     *                            implement FACTFinder\Util\LoggerInterface.
     * @param ConfigurationInterface $configuration
     * @param AbstractEncodingConverter $encodingConverter
     */
    function __construct(
        $loggerClass,
        \FACTFinder\Core\ConfigurationInterface $configuration,
        \FACTFinder\Core\AbstractEncodingConverter $encodingConverter = null
    ) {
        $this->log = $loggerClass::getLogger(__CLASS__);
        $this->configuration = $configuration;
        $this->encodingConverter = $encodingConverter;
        $this->parametersConverter = FF::getInstance(
            'Core\ParametersConverter',
            $loggerClass,
            $configuration
        );
    }

    /**
     * Loads parameters from the request and returns a Parameter object.
     * Also takes care of encoding conversion if necessary. Finally it will make
     * all necessary conversions according to the ignore/require/mapping
     * directives given in the configuration for the server. Use this method
     * for any other part of the library that needs the request parameters.
     *
     * @return Parameters Array of UTF-8 encoded and converted parameters
     */
    public function getRequestParameters()
    {
        if (is_null($this->serverRequestParameters))
        {
            $clientParameters = $this->getClientRequestParameters();
            $this->serverRequestParameters =
                $this->parametersConverter->convertClientToServerParameters(
                    $clientParameters
                );
        }

        return $this->serverRequestParameters;
    }

    /**
     * Loads parameters from the request and returns a Parameter object.
     * Also takes care of encoding conversion if necessary. However, the
     * parameters themselves are not converted (that is ignore, require and
     * mapping directives in the configuration are not taken into account).
     *
     * You won't usually need this method unless you really want to get access
     * to some parameters that would be ignored or mapped otherwise.
     *
     * For use with any other part of the library, use getRequestParameters()
     * instead, which converts the parameters for usage with the server.
     *
     * @return Parameters Array of UTF-8 encoded parameters
     */
    public function getClientRequestParameters()
    {
        if (is_null($this->clientRequestParameters))
        {
            if (isset($_SERVER['QUERY_STRING']))
            {
                // TODO: Respect variables_order so that conflicting variables
                //       lead to the same result as in $_REQUEST (save for
                //       $_COOKIE variables). This todo also goes for the second
                //       alternative.
                $parameters = FF::getInstance(
                    'Util\Parameters',
                    $_SERVER['QUERY_STRING']
                );

                $data = $_POST;

                if (!empty($data)) {
                    foreach ($data as $key => $value) {
                        if (is_array($value)) {
                            unset($data[$key]);
                        }
                    }
                }

                $parameters->setAll($data);
            }
            else if (isset($_GET))
            {
                $this->log->warn('$_SERVER[\'QUERY_STRING\'] is not available. '
                               . 'Using $_GET instead. This may cause problems '
                               . 'if the query string contains parameters with '
                               . 'non-[a-zA-Z0-9_] characters.');

                // Don't use $_REQUEST, because it also contains $_COOKIE.
                // Note that we don't have to URL decode here, because _GET is
                // already URL decoded.
                $parameters = FF::getInstance(
                    'Util\Parameters',
                    array_merge($_POST, $_GET)
                );
            }
            else
            {
                // For CLI use:
                $parameters = FF::getInstance('Util\Parameters');
            }

            if(isset($_SERVER['REQUEST_URI'])) {
                $path = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
                $seoPathPosition = strrpos($path, "/s/");
                if ($seoPathPosition > -1) {
                    $encodedSeoPath = substr($path, $seoPathPosition + 2);
                    $decodedSeoPath = urldecode($encodedSeoPath);
                    $parameters['seoPath'] = $decodedSeoPath;
                }
            }

            // Convert encoding and then the parameters themselves
            $this->clientRequestParameters = $this->encodingConverter != null ? $this->encodingConverter->decodeClientUrlData($parameters) : $parameters;

        }

        return $this->clientRequestParameters;
    }

    /**
     * Get target of the current request.
     *
     * @return string request target
     */
    public function getRequestTarget()
    {
        if ($this->requestTarget === null)
        {
            // Workaround for some servers (IIS) which do not provide
            // $_SERVER['REQUEST_URI']. Taken from
            // http://php.net/manual/en/reserved.variables.server.php#108186
            if(!isset($_SERVER['REQUEST_URI'])) {
                $_SERVER['REQUEST_URI'] = $_SERVER['SCRIPT_NAME'];
                if(isset($_SERVER['QUERY_STRING'])) {
                    $_SERVER['REQUEST_URI'] .= '?' . $_SERVER['QUERY_STRING'];
                }
            }

            if (strpos($_SERVER['REQUEST_URI'], '?') === false)
                $this->requestTarget = $_SERVER['REQUEST_URI'];
            else
            {
                $parts = explode('?', $_SERVER['REQUEST_URI']);
                $this->requestTarget = $parts[0];
            }

            $seoPathPosition = strrpos($this->requestTarget , "/s/");
            if ($seoPathPosition > -1) {
                 $this->requestTarget = substr($this->requestTarget , 0, $seoPathPosition);
            }

            // Use rawurldecode() so that +'s are not converted to spaces.
            $this->requestTarget = rawurldecode($this->requestTarget);
            if ($this->encodingConverter != null)
            {
                $this->requestTarget = $this->encodingConverter ->decodeClientUrlData($this->requestTarget);
            }
        }
        return $this->requestTarget;
    }
}


<?php
/**
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * @package log4php
 */

/**
 * A flexible layout configurable with a pattern string.
 *
 * Configurable parameters:
 *
 * * converionPattern - A string which controls the formatting of logging
 *   events. See docs for full specification.
 *
 * @package log4php
 * @subpackage layouts
 * @version $Revision: 1395470 $
 */
class LoggerLayoutPattern extends LoggerLayout {

	/** Default conversion pattern */
	const DEFAULT_CONVERSION_PATTERN = '%date %-5level %logger %message%newline';

	/** Default conversion TTCC Pattern */
	const TTCC_CONVERSION_PATTERN = '%d [%t] %p %c %x - %m%n';

	/** The conversion pattern. */
	protected $pattern = self::DEFAULT_CONVERSION_PATTERN;

	/** Maps conversion keywords to the relevant converter (default implementation). */
	protected static $defaultConverterMap = array(
		'c' => 'LoggerPatternConverterLogger',
		'lo' => 'LoggerPatternConverterLogger',
		'logger' => 'LoggerPatternConverterLogger',

		'C' => 'LoggerPatternConverterClass',
		'class' => 'LoggerPatternConverterClass',

		'cookie' => 'LoggerPatternConverterCookie',

		'd' => 'LoggerPatternConverterDate',
		'date' => 'LoggerPatternConverterDate',

		'e' => 'LoggerPatternConverterEnvironment',
		'env' => 'LoggerPatternConverterEnvironment',

		'ex' => 'LoggerPatternConverterThrowable',
		'exception' => 'LoggerPatternConverterThrowable',
		'throwable' => 'LoggerPatternConverterThrowable',

		'F' => 'LoggerPatternConverterFile',
		'file' => 'LoggerPatternConverterFile',

		'l' => 'LoggerPatternConverterLocation',
		'location' => 'LoggerPatternConverterLocation',

		'L' => 'LoggerPatternConverterLine',
		'line' => 'LoggerPatternConverterLine',

		'm' => 'LoggerPatternConverterMessage',
		'msg' => 'LoggerPatternConverterMessage',
		'message' => 'LoggerPatternConverterMessage',

		'M' => 'LoggerPatternConverterMethod',
		'method' => 'LoggerPatternConverterMethod',

		'n' => 'LoggerPatternConverterNewLine',
		'newline' => 'LoggerPatternConverterNewLine',

		'p' => 'LoggerPatternConverterLevel',
		'le' => 'LoggerPatternConverterLevel',
		'level' => 'LoggerPatternConverterLevel',

		'r' => 'LoggerPatternConverterRelative',
		'relative' => 'LoggerPatternConverterRelative',

		'req' => 'LoggerPatternConverterRequest',
		'request' => 'LoggerPatternConverterRequest',

		's' => 'LoggerPatternConverterServer',
		'server' => 'LoggerPatternConverterServer',

		'ses' => 'LoggerPatternConverterSession',
		'session' => 'LoggerPatternConverterSession',

		'sid' => 'LoggerPatternConverterSessionID',
		'sessionid' => 'LoggerPatternConverterSessionID',

		't' => 'LoggerPatternConverterProcess',
		'pid' => 'LoggerPatternConverterProcess',
		'process' => 'LoggerPatternConverterProcess',

		'x' => 'LoggerPatternConverterNDC',
		'ndc' => 'LoggerPatternConverterNDC',

		'X' => 'LoggerPatternConverterMDC',
		'mdc' => 'LoggerPatternConverterMDC',
	);

	/** Maps conversion keywords to the relevant converter. */
	protected $converterMap = array();

	/**
	 * Head of a chain of Converters.
	 * @var LoggerPatternConverter
	 */
	private $head;

	/** Returns the default converter map. */
	public static function getDefaultConverterMap() {
		return self::$defaultConverterMap;
	}

	/** Constructor. Initializes the converter map. */
	public function __construct() {
		$this->converterMap = self::$defaultConverterMap;
	}

	/**
	 * Sets the conversionPattern option. This is the string which
	 * controls formatting and consists of a mix of literal content and
	 * conversion specifiers.
	 * @param array $conversionPattern
	 */
	public function setConversionPattern($conversionPattern) {
		$this->pattern = $conversionPattern;
	}

	/**
	 * Processes the conversion pattern and creates a corresponding chain of
	 * pattern converters which will be used to format logging events.
	 */
	public function activateOptions() {
		if (!isset($this->pattern)) {
			throw new LoggerException("Mandatory parameter 'conversionPattern' is not set.");
		}

		$parser = new LoggerPatternParser($this->pattern, $this->converterMap);
		$this->head = $parser->parse();
	}

	/**
	 * Produces a formatted string as specified by the conversion pattern.
	 *
	 * @param LoggerLoggingEvent $event
	 * @return string
	 */
	public function format(LoggerLoggingEvent $event) {
		$sbuf = '';
		$converter = $this->head;
		while ($converter !== null) {
			$converter->format($sbuf, $event);
			$converter = $converter->next;
		}
		return $sbuf;
	}
}


<?php
/**
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements. See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 *
 *		http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * @package log4php
 */

/**
 * Abstract class that defines output logs strategies.
 *
 * @version $Revision: 1374777 $
 * @package log4php
 */
abstract class LoggerAppender extends LoggerConfigurable {

	/**
	 * Set to true when the appender is closed. A closed appender will not
	 * accept any logging requests.
	 * @var boolean
	 */
	protected $closed = false;

	/**
	 * The first filter in the filter chain.
	 * @var LoggerFilter
	 */
	protected $filter;

	/**
	 * The appender's layout. Can be null if the appender does not use
	 * a layout.
	 * @var LoggerLayout
	 */
	protected $layout;

	/**
	 * Appender name. Used by other components to identify this appender.
	 * @var string
	 */
	protected $name;

	/**
	 * Appender threshold level. Events whose level is below the threshold
	 * will not be logged.
	 * @var LoggerLevel
	 */
	protected $threshold;

	/**
	 * Set to true if the appender requires a layout.
	 *
	 * True by default, appenders which do not use a layout should override
	 * this property to false.
	 *
	 * @var boolean
	 */
	protected $requiresLayout = true;

	/**
	 * Default constructor.
	 * @param string $name Appender name
	 */
	public function __construct($name = '') {
		$this->name = $name;

		if ($this->requiresLayout) {
			$this->layout = $this->getDefaultLayout();
		}
	}

	public function __destruct() {
		$this->close();
	}

	/**
	 * Returns the default layout for this appender. Can be overriden by
	 * derived appenders.
	 *
	 * @return LoggerLayout
	 */
	public function getDefaultLayout() {
		return new LoggerLayoutSimple();
	}

	/**
	 * Adds a filter to the end of the filter chain.
	 * @param LoggerFilter $filter add a new LoggerFilter
	 */
	public function addFilter($filter) {
		if($this->filter === null) {
			$this->filter = $filter;
		} else {
			$this->filter->addNext($filter);
		}
	}

	/**
	 * Clears the filter chain by removing all the filters in it.
	 */
	public function clearFilters() {
		$this->filter = null;
	}

	/**
	 * Returns the first filter in the filter chain.
	 * The return value may be null if no is filter is set.
	 * @return LoggerFilter
	 */
	public function getFilter() {
		return $this->filter;
	}

	/**
	 * Returns the first filter in the filter chain.
	 * The return value may be null if no is filter is set.
	 * @return LoggerFilter
	 */
	public function getFirstFilter() {
		return $this->filter;
	}

	/**
	 * Performs threshold checks and invokes filters before delegating logging
	 * to the subclass' specific append() method.
	 * @see LoggerAppender::append()
	 * @param LoggerLoggingEvent $event
	 */
	public function doAppend(LoggerLoggingEvent $event) {
		if($this->closed) {
			return;
		}

		if(!$this->isAsSevereAsThreshold($event->getLevel())) {
			return;
		}

		$filter = $this->getFirstFilter();
		while($filter !== null) {
			switch ($filter->decide($event)) {
				case LoggerFilter::DENY: return;
				case LoggerFilter::ACCEPT: return $this->append($event);
				case LoggerFilter::NEUTRAL: $filter = $filter->getNext();
			}
		}
		$this->append($event);
	}

	/**
	 * Sets the appender layout.
	 * @param LoggerLayout $layout
	 */
	public function setLayout($layout) {
		if($this->requiresLayout()) {
			$this->layout = $layout;
		}
	}

	/**
	 * Returns the appender layout.
	 * @return LoggerLayout
	 */
	public function getLayout() {
		return $this->layout;
	}

	/**
	 * Configurators call this method to determine if the appender
	 * requires a layout.
	 *
	 *
If this method returns true, meaning that layout is required,
	 * then the configurator will configure a layout using the configuration
	 * information at its disposal.	 If this method returns false,
	 * meaning that a layout is not required, then layout configuration will be
	 * skipped even if there is available layout configuration
	 * information at the disposal of the configurator.

	 *
	 *
In the rather exceptional case, where the appender
	 * implementation admits a layout but can also work without it, then
	 * the appender should return true.

	 *
	 * @return boolean
	 */
	public function requiresLayout() {
		return $this->requiresLayout;
	}

	/**
	 * Retruns the appender name.
	 * @return string
	 */
	public function getName() {
		return $this->name;
	}

	/**
	 * Sets the appender name.
	 * @param string $name
	 */
	public function setName($name) {
		$this->name = $name;
	}

	/**
	 * Returns the appender's threshold level.
	 * @return LoggerLevel
	 */
	public function getThreshold() {
		return $this->threshold;
	}

	/**
	 * Sets the appender threshold.
	 *
	 * @param LoggerLevel|string $threshold Either a {@link LoggerLevel}
	 *   object or a string equivalent.
	 * @see LoggerOptionConverter::toLevel()
	 */
	public function setThreshold($threshold) {
		$this->setLevel('threshold', $threshold);
	}

	/**
	 * Checks whether the message level is below the appender's threshold.
	 *
	 * If there is no threshold set, then the return value is always true.
	 *
	 * @param LoggerLevel $level
	 * @return boolean Returns true if level is greater or equal than
	 *   threshold, or if the threshold is not set. Otherwise returns false.
	 */
	public function isAsSevereAsThreshold($level) {
		if($this->threshold === null) {
			return true;
		}
		return $level->isGreaterOrEqual($this->getThreshold());
	}

	/**
	 * Prepares the appender for logging.
	 *
	 * Derived appenders should override this method if option structure
	 * requires it.
	 */
	public function activateOptions() {
		$this->closed = false;
	}

	/**
	 * Forwards the logging event to the destination.
	 *
	 * Derived appenders should implement this method to perform actual logging.
	 *
	 * @param LoggerLoggingEvent $event
	 */
	abstract protected function append(LoggerLoggingEvent $event);

	/**
	 * Releases any resources allocated by the appender.
	 *
	 * Derived appenders should override this method to perform proper closing
	 * procedures.
	 */
	public function close() {
		$this->closed = true;
	}

	/** Triggers a warning for this logger with the given message. */
	protected function warn($message) {
		$id = get_class($this) . (empty($this->name) ? '' : ":{$this->name}");
		trigger_error("log4php: [$id]: $message", E_USER_WARNING);
	}

}


<?php
namespace FACTFinder\Adapter;

use FACTFinder\Loader as FF;

/**
 * TODO: Are there any other FF 6.8 features left which we are not making use of
 *       yet? If so: change that.
 */
class Suggest extends AbstractAdapter
{
    /**
     * @var FACTFinder\Util\LoggerInterface
     */
    private $log;

    /**
     * @var FACTFinder\Data\SuggestQuery[]
     */
    private $suggestions;

    public function __construct(
        $loggerClass,
        \FACTFinder\Core\ConfigurationInterface $configuration,
        \FACTFinder\Core\Server\Request $request,
        \FACTFinder\Core\Client\UrlBuilder $urlBuilder,
        \FACTFinder\Core\AbstractEncodingConverter $encodingConverter = null
    ) {
        parent::__construct($loggerClass, $configuration, $request,
                            $urlBuilder, $encodingConverter);

        $this->log = $loggerClass::getLogger(__CLASS__);

        $this->request->setAction('Suggest.ff');

        $this->request->setConnectTimeout($configuration->getSuggestConnectTimeout());
        $this->request->setTimeout($configuration->getSuggestTimeout());
    }

    /**
     * Get the suggestions from FACT-Finder as an array of SuggestQuery's.
     *
     * @return FACTFinder\Data\SuggestQuery[]
     */
    public function getSuggestions()
    {
        if (is_null($this->suggestions))
            $this->suggestions = $this->createSuggestions();

        return $this->suggestions;
    }

    private function createSuggestions()
    {
        $suggestions = array();

        $this->useJsonResponseContentProcessor();

        if (isset($this->parameters['format']))
            $oldFormat = $this->parameters['format'];

        $this->parameters['format'] = 'json';
        $suggestData = $this->getResponseContent();
        if (parent::isValidResponse($suggestData))
        {
            if (isset($suggestData['suggestions']))
            {
                $suggestData = $suggestData['suggestions'];
            }

            foreach ($suggestData as $suggestQueryData)
            {
                $suggestLink = $this->convertServerQueryToClientUrl(
                    $suggestQueryData['searchParams']
                );

                $suggestAttributes = null;
                if (isset($suggestQueryData['attributes'])
                    && is_array($suggestQueryData['attributes'])
                ) {
                    $suggestAttributes = $suggestQueryData['attributes'];
                } else {
                    $suggestAttributes = array();
                }

                $suggestions[] = FF::getInstance(
                    'Data\SuggestQuery',
                    $suggestQueryData['name'],
                    $suggestLink,
                    $suggestQueryData['hitCount'],
                    $suggestQueryData['type'],
                    $suggestQueryData['image'],
                    $suggestAttributes
                );
            }
        }

        if (isset($oldFormat))
            $this->parameters['format'] = $oldFormat;

        return $suggestions;
    }

    /**
     * Get the suggestions from FACT-Finder as the string returned by the
     * server.
     *
     * @param string $format Optional. Either 'json' or 'jsonp'. Use to
     *                       overwrite the 'format' parameter.
     * @param string $callback Optional name to overwrite the 'callback'
     *                         parameter, which determines the name of the
     *                         callback the response is wrapped in.
     *
     * @return string
     */
    public function getRawSuggestions($format = null, $callback = null)
    {
        $this->usePassthroughResponseContentProcessor();

        if (!is_null($format))
            $this->parameters['format'] = $format;
        if (!is_null($callback))
            $this->parameters['callback'] = $callback;

        return $this->getResponseContent();
    }
}


<?php
/**
 * Bootstrap file which should be called on every request. It defines some basic
 * constants and defines the autoloader class, which handles classloading and
 * instance creation.
 */
namespace FACTFinder;

use FACTFinder\Loader as FF;
use FACTFinder\Util\LoggerInterface;

if (!defined('DS'))
{
    /**
     * Short alias for the constant DIRECTORY_SEPARATOR
     */
    define('DS', DIRECTORY_SEPARATOR);
}

if (!defined('FF_LIB_DIR'))
{
    /**
     * Contains the absolute directory path to the library.
     */
    define('FF_LIB_DIR', dirname(dirname(__FILE__)));
}

// set as include path if this is not the case yet
$includePaths = explode(PATH_SEPARATOR, get_include_path());
if ( array_search(FF_LIB_DIR, $includePaths, true) === false )
{
    set_include_path( get_include_path() . PATH_SEPARATOR . FF_LIB_DIR);
}
spl_autoload_register(array('FACTFinder\Loader', 'autoload'));

// don't know, whether I should do that
if (function_exists('__autoload')
    && array_search('__autoload', spl_autoload_functions()) === false)
{
    spl_autoload_register('__autoload');
}

/**
 * Responsible for auto loading and instance creation. Please instantiate all
 * library classes through this loader, as it automatically takes care of
 * overriding library implementations with custom ones.
 *
 * For convenience, you can alias this class with FF in your source files like:
 * use FACTFinder\Loader as FF;
 */
class Loader
{
    protected static $classNames = array();

    protected static $loadCustomClasses = true;

    // TODO: Check parent namespaces, too?
    public static function autoload($classname)
    {
        $filename = self::getFilename($classname);
        if (file_exists($filename))
            include_once $filename;
    }

    private static function getFilename($classname)
    {
        return FF_LIB_DIR . DS . str_replace('\\', DS, $classname) . '.php';
    }

    /**
     * Creates an instance of a class taking into account classes within the
     * "FACTFinder\Custom\" namespace instead of "FACTFinder\".
     * Note that classes in the \Custom namespace should inherit the class they
     * overwrite - otherwise some of the type hinting within the library might
     * break.
     * USE THIS method instead of the PHP "new" keyword for all classes from
     * this library.
     * Eg. instead of "$obj = new myclass;", you should use
     * "$obj = FACTFinder\Loader::getInstance("myclass")"!
     * You can also pass arguments for a constructor:
     *     Loader::getInstance('myClass', $arg1, $arg2,  ..., $argN)
     *
     * @param    string $name   Class name to instantiate
     * @param    mixed optional Constructor parameters
     * @return   object         A reference to the instance
     */
    public static function getInstance($name)
    {
        if (isset(self::$classNames[$name]))
        {
            $className = self::$classNames[$name];
        }
        else
        {
            $className = self::getClassName($name);
            self::$classNames[$name] = $className;
        }

        // this snippet is from the typo3 class "t3lib_div"
        // written by Kasper Skaarhoj <kasperYYYY@typo3.com>
        if (func_num_args() > 1)
        {
            // getting the constructor arguments by removing this
            // method's first argument (the class name)
            $constructorArguments = func_get_args();
            array_shift($constructorArguments);

            $reflectedClass = new \ReflectionClass($className);
            $instance = $reflectedClass->newInstanceArgs($constructorArguments);
        }
        else
        {
            $instance = new $className;
        }

        return $instance;
    }

    /**
     * Expects a fully qualified class name. if the leading namespace is omitted
     * or "FACTFinder", we first check whether there is a custom class in
     * namespace "FACTFinder\Custom\", then we look in "FACTFinder\". If none of
     * them exist, it also checks if the name is the class name itself.
     * Note that classes in the \Custom namespace should inherit the class they
     * overwrite - otherwise some of the type hinting within the library might
     * break.
     *
     * @param string $name The class name to be resolved.
     *
     * @return string The class name to be used.
     */
    public static function getClassName($name)
    {
        $name = trim(preg_replace('/^FACTFinder\\\\/i', '', $name));

        // check whether there is a custom or lib-unrelated class
        $customClassName     = 'FACTFinder\Custom\\' . $name;
        $factfinderClassName = 'FACTFinder\\' . $name;
        $defaultClassName    = $name;

        if (self::$loadCustomClasses && class_exists($customClassName))
            $className = $customClassName;
        else if (class_exists($factfinderClassName))
            $className = $factfinderClassName;
        else if (class_exists($defaultClassName))
            $className = $defaultClassName;
        else
            throw new \Exception("class '$factfinderClassName' not found");
        return $className;
    }

    /**
     * This a convenient wrapper around instanceof or is_a() when dealing with
     * library classes.
     * Instead of a class name it expects an identifier like getClassName() or
     * getInstance() does, and checks that the object passed is actually of the
     * class that is currently in use for this identifier.
     *
     * @param object $object The object whose class to check.
     * @param string $class An identifier for the class to require the object to
     *        stem from.
     *
     * @return bool True, if $object belongs to $class.
     */
    public static function isInstanceOf($object, $class)
    {
        $className = self::getClassName($class);
        return $object instanceof $className;
    }

    /**
     * It may happen that your application's autoloader is not compatible
     * with this one due to the custom class loading feature.
     * This method disables the loading of custom classes.
     */
    public static function disableCustomClasses()
    {
        self::$loadCustomClasses = false;
    }
}


<?php
namespace FACTFinder\Core\Server;

/**
 * Data providers take care of retrieving pre-configured requests with
 * FACT-Finder data, including the network connection if necessary.
 */
abstract class AbstractDataProvider
{
    /**
     * @var \FACTFinder\Util\LoggerInterface
     */
    private $log;

    /**
     * @var \FACTFinder\Core\ConfigurationInterface
     */
    protected $configuration;

    /**
     * @var ConnectionData[] Keys are IDs to identify each connection data
     *      object.
     */
    protected $connectionData;

    /**
     * @var int
     */
    static private $nextID = 0;

    public function __construct(
        $loggerClass,
        \FACTFinder\Core\ConfigurationInterface $configuration
    ) {
        $this->log = $loggerClass::getLogger(__CLASS__);
        $this->configuration = $configuration;

        $this->connectionData = array();
    }

    /**
     * Make a connection data object known to the data provider and obtain an ID
     * for it (basically, a handle).
     *
     * @param ConnectionData $connectionData The connection data object to be
     *        registered.
     *
     * @param int The ID by which to refer to the connection data in the future.
     *
     * @throws InvalidArgumentException if the $id is already in use.
     */
    public function register(ConnectionData $connectionData)
    {
        $id = self::$nextID++;

        $this->connectionData[$id] = $connectionData;

        $this->log->debug("Registered connection data for ID $id.");

        return $id;
    }

    /**
     * Remove all references to the connection data object identified by $id.
     *
     * @param mixed $id The ID corresponding to the connection data object to be
     *        removed from the DataProvider.
     */
    public function unregister($id)
    {
        unset($this->connectionData[$id]);

        $this->log->debug("Unregistered connection data for ID $id.");
    }

    /**
     * Set the number of seconds to wait while trying to connect. Any particular
     * implementation of this abstract class is free to ignore this timeout, but
     * it should respect it if the underlying connection mechanism allows.
     *
     * @param mixed $id The ID of the connection data for which to set the
     *        timeout.
     * @param int $timeout The timeout in seconds.
     */
    abstract public function setConnectTimeout($id, $timeout);

    /**
     * Set the number of seconds to wait for the entire request to complete. Any
     * particular implementation of this abstract class is free to ignore this
     * timeout, but it should respect it if the underlying connection mechanism
     * allows.
     *
     * @param mixed $id The ID of the connection data for which to set the
     *        timeout.
     * @param int $timeout The timeout in seconds.
     */
    abstract public function setTimeout($id, $timeout);

    /**
     * Load a response based on the current state of the connection data
     * corresponding to $id and fill that ConnectionData object with this
     * response.
     *
     * @param mixed $id The ID of the connection data for which to retrieve a
     *        response.
     *
     * @return void The response is NOT returned by this function. It has to be
     *         obtained directly from the ConnectionData object.
     */
    abstract public function loadResponse($id);
}


<?php
/**
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements. See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 *
 *	   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * @package log4php
 */

/**
 * The NDC class implements nested diagnostic contexts.
 *
 * NDC was defined by Neil Harrison in the article "Patterns for Logging
 * Diagnostic Messages" part of the book "Pattern Languages of
 * Program Design 3" edited by Martin et al.
 *
 * A Nested Diagnostic Context, or NDC in short, is an instrument
 * to distinguish interleaved log output from different sources. Log
 * output is typically interleaved when a server handles multiple
 * clients near-simultaneously.
 *
 * This class is similar to the {@link LoggerMDC} class except that it is
 * based on a stack instead of a map.
 *
 * Interleaved log output can still be meaningful if each log entry
 * from different contexts had a distinctive stamp. This is where NDCs
 * come into play.
 *
 * Note that NDCs are managed on a per thread basis.
 *
 * NDC operations such as {@link push()}, {@link pop()},
 * {@link clear()}, {@link getDepth()} and {@link setMaxDepth()}
 * affect the NDC of the current thread only. NDCs of other
 * threads remain unaffected.
 *
 * For example, a servlet can build a per client request NDC
 * consisting the clients host name and other information contained in
 * the the request. Cookies are another source of distinctive
 * information. To build an NDC one uses the {@link push()}
 * operation.
 *
 * Simply put,
 *
 * - Contexts can be nested.
 * - When entering a context, call LoggerNDC::push()
 *	 As a side effect, if there is no nested diagnostic context for the
 *	 current thread, this method will create it.
 * - When leaving a context, call LoggerNDC::pop()
 * - When exiting a thread make sure to call {@link remove()}
 *
 * There is no penalty for forgetting to match each
 * push operation with a corresponding pop,
 * except the obvious mismatch between the real application context
 * and the context set in the NDC.
 *
 * If configured to do so, {@link LoggerPatternLayout} and {@link LoggerLayoutTTCC}
 * instances automatically retrieve the nested diagnostic
 * context for the current thread without any user intervention.
 * Hence, even if a servlet is serving multiple clients
 * simultaneously, the logs emanating from the same code (belonging to
 * the same category) can still be distinguished because each client
 * request will have a different NDC tag.
 *
 * Example:
 *
 * {@example ../../examples/php/ndc.php 19}

 *
 * With the properties file:
 *
 * {@example ../../examples/resources/ndc.properties 18}

 *
 * Will result in the following (notice the conn and client ids):
 *
 *

 * 2009-09-13 19:04:27 DEBUG root conn=1234: just received a new connection in src/examples/php/ndc.php at 23
 * 2009-09-13 19:04:27 DEBUG root conn=1234 client=ab23: some more messages that can in src/examples/php/ndc.php at 25
 * 2009-09-13 19:04:27 DEBUG root conn=1234 client=ab23: now related to a client in src/examples/php/ndc.php at 26
 * 2009-09-13 19:04:27 DEBUG root : back and waiting for new connections in src/examples/php/ndc.php at 29
 *

 *
 * @version $Revision: 1350602 $
 * @package log4php
 * @since 0.3
 */
class LoggerNDC {

	/** This is the repository of NDC stack */
	private static $stack = array();

	/**
	 * Clear any nested diagnostic information if any. This method is
	 * useful in cases where the same thread can be potentially used
	 * over and over in different unrelated contexts.
	 *
	 *
This method is equivalent to calling the {@link setMaxDepth()}
	 * method with a zero <var>maxDepth</var> argument.
	 */
	public static function clear() {
		self::$stack = array();
	}

	/**
	 * Never use this method directly, use the {@link LoggerLoggingEvent::getNDC()} method instead.
	 * @return array
	 */
	public static function get() {
		return implode(' ', self::$stack);
	}

	/**
	 * Get the current nesting depth of this diagnostic context.
	 *
	 * @see setMaxDepth()
	 * @return integer
	 */
	public static function getDepth() {
		return count(self::$stack);
	}

	/**
	 * Clients should call this method before leaving a diagnostic
	 * context.
	 *
	 *
The returned value is the value that was pushed last. If no
	 * context is available, then the empty string "" is returned.

	 *
	 * @return string The innermost diagnostic context.
	 */
	public static function pop() {
		if(count(self::$stack) > 0) {
			return array_pop(self::$stack);
		} else {
			return '';
		}
	}

	/**
	 * Looks at the last diagnostic context at the top of this NDC
	 * without removing it.
	 *
	 *
The returned value is the value that was pushed last. If no
	 * context is available, then the empty string "" is returned.

	 * @return string The innermost diagnostic context.
	 */
	public static function peek() {
		if(count(self::$stack) > 0) {
			return end(self::$stack);
		} else {
			return '';
		}
	}

	/**
	 * Push new diagnostic context information for the current thread.
	 *
	 *
The contents of the <var>message</var> parameter is
	 * determined solely by the client.
	 *
	 * @param string $message The new diagnostic context information.
	 */
	public static function push($message) {
		array_push(self::$stack, (string)$message);
	}

	/**
	 * Remove the diagnostic context for this thread.
	 */
	public static function remove() {
		LoggerNDC::clear();
	}

	/**
	 * Set maximum depth of this diagnostic context. If the current
	 * depth is smaller or equal to <var>maxDepth</var>, then no
	 * action is taken.
	 *
	 *
This method is a convenient alternative to multiple
	 * {@link pop()} calls. Moreover, it is often the case that at
	 * the end of complex call sequences, the depth of the NDC is
	 * unpredictable. The {@link setMaxDepth()} method circumvents
	 * this problem.
	 *
	 * @param integer $maxDepth
	 * @see getDepth()
	 */
	public static function setMaxDepth($maxDepth) {
		$maxDepth = (int)$maxDepth;
		if(LoggerNDC::getDepth() > $maxDepth) {
			self::$stack = array_slice(self::$stack, 0, $maxDepth);
		}
	}
}


<?php
/**
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements. See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 *
 *	   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * @package log4php
 */

/**
 * LoggerAppenderRollingFile writes logging events to a specified file. The
 * file is rolled over after a specified size has been reached.
 *
 * This appender uses a layout.
 *
 * ## Configurable parameters: ##
 *
 * - **file** - Path to the target file.
 * - **append** - If set to true, the appender will append to the file,
 *     otherwise the file contents will be overwritten.
 * - **maxBackupIndex** - Maximum number of backup files to keep. Default is 1.
 * - **maxFileSize** - Maximum allowed file size (in bytes) before rolling
 *     over. Suffixes "KB", "MB" and "GB" are allowed. 10KB = 10240 bytes, etc.
 *     Default is 10M.
 * - **compress** - If set to true, rolled-over files will be compressed.
 *     Requires the zlib extension.
 *
 * @version $Revision: 1394975 $
 * @package log4php
 * @subpackage appenders
 * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
 * @link http://logging.apache.org/log4php/docs/appenders/rolling-file.html Appender documentation
 */
class LoggerAppenderRollingFile extends LoggerAppenderFile {

	/** Compressing backup files is done in chunks, this determines how large. */
	const COMPRESS_CHUNK_SIZE = 102400; // 100KB

	/**
	 * The maximum size (in bytes) that the output file is allowed to reach
	 * before being rolled over to backup files.
	 *
	 * The default maximum file size is 10MB (10485760 bytes). Maximum value
	 * for this option may depend on the file system.
	 *
	 * @var integer
	 */
	protected $maxFileSize = 10485760;

	/**
	 * Set the maximum number of backup files to keep around.
	 *
	 * Determines how many backup files are kept before the oldest is erased.
	 * This option takes a positive integer value. If set to zero, then there
	 * will be no backup files and the log file will be truncated when it
	 * reaches <var>maxFileSize</var>.
	 *
	 * There is one backup file by default.
	 *
	 * @var integer
	 */
	protected $maxBackupIndex = 1;

	/**
	 * The <var>compress</var> parameter determindes the compression with zlib.
	 * If set to true, the rollover files are compressed and saved with the .gz extension.
	 * @var boolean
	 */
	protected $compress = false;

	/**
	 * Set to true in the constructor if PHP >= 5.3.0. In that case clearstatcache
	 * supports conditional clearing of statistics.
	 * @var boolean
	 * @see http://php.net/manual/en/function.clearstatcache.php
	 */
	private $clearConditional = false;

	/**
	 * Get the maximum size that the output file is allowed to reach
	 * before being rolled over to backup files.
	 * @return integer
	 */
	public function getMaximumFileSize() {
		return $this->maxFileSize;
	}

	public function __construct($name = '') {
		parent::__construct($name);
		if (version_compare(PHP_VERSION, '5.3.0') >= 0) {
			$this->clearConditional = true;
		}
	}

	/**
	 * Implements the usual roll over behaviour.
	 *
	 * If MaxBackupIndex is positive, then files File.1, ..., File.MaxBackupIndex -1 are renamed to File.2, ..., File.MaxBackupIndex.
	 * Moreover, File is renamed File.1 and closed. A new File is created to receive further log output.
	 *
	 * If MaxBackupIndex is equal to zero, then the File is truncated with no backup files created.
	 *
	 * Rollover must be called while the file is locked so that it is safe for concurrent access.
	 *
	 * @throws LoggerException If any part of the rollover procedure fails.
	 */
	private function rollOver() {
		// If maxBackups <= 0, then there is no file renaming to be done.
		if($this->maxBackupIndex > 0) {
			// Delete the oldest file, to keep Windows happy.
			$file = $this->file . '.' . $this->maxBackupIndex;

			if (file_exists($file) && !unlink($file)) {
				throw new LoggerException("Unable to delete oldest backup file from [$file].");
			}

			// Map {(maxBackupIndex - 1), ..., 2, 1} to {maxBackupIndex, ..., 3, 2}
			$this->renameArchievedLogs($this->file);

			// Backup the active file
			$this->moveToBackup($this->file);
		}

		// Truncate the active file
		ftruncate($this->fp, 0);
		rewind($this->fp);
	}

	private function moveToBackup($source) {
		if ($this->compress) {
			$target = $source . '.1.gz';
			$this->compressFile($source, $target);
		} else {
			$target = $source . '.1';
			copy($source, $target);
		}
	}

	private function compressFile($source, $target) {
		$target = 'compress.zlib://' . $target;

		$fin = fopen($source, 'rb');
		if ($fin === false) {
			throw new LoggerException("Unable to open file for reading: [$source].");
		}

		$fout = fopen($target, 'wb');
		if ($fout === false) {
			throw new LoggerException("Unable to open file for writing: [$target].");
		}

		while (!feof($fin)) {
			$chunk = fread($fin, self::COMPRESS_CHUNK_SIZE);
			if (false === fwrite($fout, $chunk)) {
				throw new LoggerException("Failed writing to compressed file.");
			}
		}

		fclose($fin);
		fclose($fout);
	}

	private function renameArchievedLogs($fileName) {
		for($i = $this->maxBackupIndex - 1; $i >= 1; $i--) {

			$source = $fileName . "." . $i;
			if ($this->compress) {
				$source .= '.gz';
			}

			if(file_exists($source)) {
				$target = $fileName . '.' . ($i + 1);
				if ($this->compress) {
					$target .= '.gz';
				}

				rename($source, $target);
			}
		}
	}

	/**
	 * Writes a string to the target file. Opens file if not already open.
	 * @param string $string Data to write.
	 */
	protected function write($string) {
		// Lazy file open
		if(!isset($this->fp)) {
			if ($this->openFile() === false) {
				return; // Do not write if file open failed.
			}
		}

		// Lock the file while writing and possible rolling over
		if(flock($this->fp, LOCK_EX)) {

			// Write to locked file
			if(fwrite($this->fp, $string) === false) {
				$this->warn("Failed writing to file. Closing appender.");
				$this->closed = true;
			}

			// Stats cache must be cleared, otherwise filesize() returns cached results
			// If supported (PHP 5.3+), clear only the state cache for the target file
			if ($this->clearConditional) {
				clearstatcache(true, $this->file);
			} else {
				clearstatcache();
			}

			// Rollover if needed
			if (filesize($this->file) > $this->maxFileSize) {
				try {
					$this->rollOver();
				} catch (LoggerException $ex) {
					$this->warn("Rollover failed: " . $ex->getMessage() . " Closing appender.");
					$this->closed = true;
				}
			}

			flock($this->fp, LOCK_UN);
		} else {
			$this->warn("Failed locking file for writing. Closing appender.");
			$this->closed = true;
		}
	}

	public function activateOptions() {
		parent::activateOptions();

		if ($this->compress && !extension_loaded('zlib')) {
			$this->warn("The 'zlib' extension is required for file compression. Disabling compression.");
			$this->compression = false;
		}
	}

	/**
	 * Set the 'maxBackupIndex' parameter.
	 * @param integer $maxBackupIndex
	 */
	public function setMaxBackupIndex($maxBackupIndex) {
		$this->setPositiveInteger('maxBackupIndex', $maxBackupIndex);
	}

	/**
	 * Returns the 'maxBackupIndex' parameter.
	 * @return integer
	 */
	public function getMaxBackupIndex() {
		return $this->maxBackupIndex;
	}

	/**
	 * Set the 'maxFileSize' parameter.
	 * @param mixed $maxFileSize
	 */
	public function setMaxFileSize($maxFileSize) {
		$this->setFileSize('maxFileSize', $maxFileSize);
	}

	/**
	 * Returns the 'maxFileSize' parameter.
	 * @return integer
	 */
	public function getMaxFileSize() {
		return $this->maxFileSize;
	}

	/**
	 * Set the 'maxFileSize' parameter (kept for backward compatibility).
	 * @param mixed $maxFileSize
	 * @deprecated Use setMaxFileSize() instead.
	 */
	public function setMaximumFileSize($maxFileSize) {
		$this->warn("The 'maximumFileSize' parameter is deprecated. Use 'maxFileSize' instead.");
		return $this->setMaxFileSize($maxFileSize);
	}

	/**
	 * Sets the 'compress' parameter.
	 * @param boolean $compress
	 */
	public function setCompress($compress) {
		$this->setBoolean('compress', $compress);
	}

	/**
	 * Returns the 'compress' parameter.
	 * @param boolean
	 */
	public function getCompress() {
		return $this->compress;
	}
}


<?php
/**
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * LoggerAppenderPDO appender logs to a database using the PHP's PDO extension.
 *
 * ## Configurable parameters: ##
 *
 * - dsn             - The Data Source Name (DSN) used to connect to the database.
 * - user            - Username used to connect to the database.
 * - password        - Password used to connect to the database.
 * - table           - Name of the table to which log entries are be inserted.
 * - insertSQL       - Sets the insert statement for a logging event. Defaults
 *                     to the correct one - change only if you are sure what you are doing.
 * - insertPattern   - The conversion pattern to use in conjuction with insert
 *                     SQL. Must contain the same number of comma separated
 *                     conversion patterns as there are question marks in the
 *                     insertSQL.
 *
 * @version $Revision: 1374546 $
 * @package log4php
 * @subpackage appenders
 * @since 2.0
 * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
 * @link http://logging.apache.org/log4php/docs/appenders/pdo.html Appender documentation
 */
class LoggerAppenderPDO extends LoggerAppender {

	// ******************************************
	// *** Configurable parameters            ***
	// ******************************************

	/**
	 * DSN string used to connect to the database.
	 * @see http://www.php.net/manual/en/pdo.construct.php
	 */
	protected $dsn;

	/** Database user name. */
	protected $user;

	/** Database password. */
	protected $password;

	/**
	 * The insert query.
	 *
	 * The __TABLE__ placeholder will be replaced by the table name from
	 * {@link $table}.
	 *
	 * The questionmarks are part of the prepared statement, and they must
	 * match the number of conversion specifiers in {@link insertPattern}.
	 */
	protected $insertSQL = "INSERT INTO __TABLE__ (timestamp, logger, level, message, thread, file, line) VALUES (?, ?, ?, ?, ?, ?, ?)";

	/**
	 * A comma separated list of {@link LoggerPatternLayout} format strings
	 * which replace the "?" in {@link $insertSQL}.
	 *
	 * Must contain the same number of comma separated conversion patterns as
	 * there are question marks in {@link insertSQL}.
 	 *
 	 * @see LoggerPatternLayout For conversion patterns.
	 */
	protected $insertPattern = "%date{Y-m-d H:i:s},%logger,%level,%message,%pid,%file,%line";

	/** Name of the table to which to append log events. */
	protected $table = 'log4php_log';

	/** The number of recconect attempts to make on failed append. */
	protected $reconnectAttempts = 3;

	// ******************************************
	// *** Private memebers                   ***
	// ******************************************

	/**
	 * The PDO instance.
	 * @var PDO
	 */
	protected $db;

	/**
	 * Prepared statement for the insert query.
	 * @var PDOStatement
	 */
	protected $preparedInsert;

	/** This appender does not require a layout. */
	protected $requiresLayout = false;

	// ******************************************
	// *** Appender methods                   ***
	// ******************************************

	/**
	 * Acquires a database connection based on parameters.
	 * Parses the insert pattern to create a chain of converters which will be
	 * used in forming query parameters from logging events.
	 */
	public function activateOptions() {
		try {
			$this->establishConnection();
		} catch (PDOException $e) {
			$this->warn("Failed connecting to database. Closing appender. Error: " . $e->getMessage());
			$this->close();
			return;
		}

		// Parse the insert patterns; pattern parts are comma delimited
		$pieces = explode(',', $this->insertPattern);
		$converterMap = LoggerLayoutPattern::getDefaultConverterMap();
		foreach($pieces as $pattern) {
			$parser = new LoggerPatternParser($pattern, $converterMap);
			$this->converters[] = $parser->parse();
		}

		$this->closed = false;
	}

	/**
	 * Connects to the database, and prepares the insert query.
	 * @throws PDOException If connect or prepare fails.
	 */
	protected function establishConnection() {
		// Acquire database connection
		$this->db = new PDO($this->dsn, $this->user, $this->password);
		$this->db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

		// Prepare the insert statement
		$insertSQL = str_replace('__TABLE__', $this->table, $this->insertSQL);
		$this->preparedInsert = $this->db->prepare($insertSQL);
	}

	/**
	 * Appends a new event to the database.
	 *
	 * If writing to database fails, it will retry by re-establishing the
	 * connection up to $reconnectAttempts times. If writing still fails,
	 * the appender will close.
	 */
	public function append(LoggerLoggingEvent $event) {

		for ($attempt = 1; $attempt <= $this->reconnectAttempts + 1; $attempt++) {
			try {
				// Attempt to write to database
				$this->preparedInsert->execute($this->format($event));
				$this->preparedInsert->closeCursor();
				break;
			} catch (PDOException $e) {
				$this->warn("Failed writing to database: ". $e->getMessage());

				// Close the appender if it's the last attempt
				if ($attempt > $this->reconnectAttempts) {
					$this->warn("Failed writing to database after {$this->reconnectAttempts} reconnect attempts. Closing appender.");
					$this->close();
				// Otherwise reconnect and try to write again
				} else {
					$this->warn("Attempting a reconnect (attempt $attempt of {$this->reconnectAttempts}).");
					$this->establishConnection();
				}
			}
		}
	}

	/**
	 * Converts the logging event to a series of database parameters by using
	 * the converter chain which was set up on activation.
	 */
	protected function format(LoggerLoggingEvent $event) {
		$params = array();
		foreach($this->converters as $converter) {
			$buffer = '';
			while ($converter !== null) {
				$converter->format($buffer, $event);
				$converter = $converter->next;
			}
			$params[] = $buffer;
		}
		return $params;
	}

	/**
	 * Closes the connection to the logging database
	 */
	public function close() {
		// Close the connection (if any)
		$this->db = null;

		// Close the appender
		$this->closed = true;
	}

	// ******************************************
	// *** Accessor methods                   ***
	// ******************************************

	/**
	 * Returns the active database handle or null if not established.
	 * @return PDO
	 */
	public function getDatabaseHandle() {
		return $this->db;
	}

	/** Sets the username. */
	public function setUser($user) {
		$this->setString('user', $user);
	}

	/** Returns the username. */
	public function getUser($user) {
		return $this->user;
	}

	/** Sets the password. */
	public function setPassword($password) {
		$this->setString('password', $password);
	}

	/** Returns the password. */
	public function getPassword($password) {
		return $this->password;
	}

	/** Sets the insert SQL. */
	public function setInsertSQL($sql) {
		$this->setString('insertSQL', $sql);
	}

	/** Returns the insert SQL. */
	public function getInsertSQL($sql) {
		return $this->insertSQL;
	}

	/** Sets the insert pattern. */
	public function setInsertPattern($pattern) {
		$this->setString('insertPattern', $pattern);
	}

	/** Returns the insert pattern. */
	public function getInsertPattern($pattern) {
		return $this->insertPattern;
	}

	/** Sets the table name. */
	public function setTable($table) {
		$this->setString('table', $table);
	}

	/** Returns the table name. */
	public function getTable($table) {
		return $this->table;
	}

	/** Sets the DSN string. */
	public function setDSN($dsn) {
		$this->setString('dsn', $dsn);
	}

	/** Returns the DSN string. */
	public function getDSN($dsn) {
		return $this->setString('dsn', $dsn);
	}
}


<?php
/**
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements. See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 *
 *	   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * @package log4php
 */

/**
 * Provides methods for reflective use on php objects
 * @package log4php
 */
class LoggerReflectionUtils {
		/** the target object */
	private $obj;

	/**
	 * Create a new LoggerReflectionUtils for the specified Object.
	 * This is done in prepartion for invoking {@link setProperty()}
	 * one or more times.
	 * @param object &$obj the object for which to set properties
	 */
	public function __construct($obj) {
		$this->obj = $obj;
	}

	/**
	 * Set the properties of an object passed as a parameter in one
	 * go. The properties are parsed relative to a
	 * prefix.
	 *
	 * @param object $obj The object to configure.
	 * @param array $properties An array containing keys and values.
	 * @param string $prefix Only keys having the specified prefix will be set.
	 */
	 // TODO: check, if this is really useful
	public static function setPropertiesByObject($obj, $properties, $prefix) {
		$pSetter = new LoggerReflectionUtils($obj);
		return $pSetter->setProperties($properties, $prefix);
	}

	/**
	 * Set the properites for the object that match the
	 * prefix passed as parameter.
	 *
	 * Example:
	 *
	 * $arr['xxxname'] = 'Joe';
 	 * $arr['xxxmale'] = true;
	 * and prefix xxx causes setName and setMale.
	 *
	 * @param array $properties An array containing keys and values.
	 * @param string $prefix Only keys having the specified prefix will be set.
	 */
	public function setProperties($properties, $prefix) {
		$len = strlen($prefix);
		reset($properties);
		while(list($key,) = each($properties)) {
			if(strpos($key, $prefix) === 0) {
				if(strpos($key, '.', ($len + 1)) > 0) {
					continue;
				}
				$value = $properties[$key];
				$key = substr($key, $len);
				if($key == 'layout' and ($this->obj instanceof LoggerAppender)) {
					continue;
				}
				$this->setProperty($key, $value);
			}
		}
		$this->activate();
	}

	/**
	 * Set a property on this PropertySetter's Object. If successful, this
	 * method will invoke a setter method on the underlying Object. The
	 * setter is the one for the specified property name and the value is
	 * determined partly from the setter argument type and partly from the
	 * value specified in the call to this method.
	 *
	 *
If the setter expects a String no conversion is necessary.
	 * If it expects an int, then an attempt is made to convert 'value'
	 * to an int using new Integer(value). If the setter expects a boolean,
	 * the conversion is by new Boolean(value).
	 *
	 * @param string $name	name of the property
	 * @param string $value	String value of the property
	 */
	public function setProperty($name, $value) {
		if($value === null) {
			return;
		}

		$method = "set" . ucfirst($name);

		if(!method_exists($this->obj, $method)) {
			throw new Exception("Error setting log4php property $name to $value: no method $method in class ".get_class($this->obj)."!");
		} else {
			return call_user_func(array($this->obj, $method), $value);
		}
	}

	public function activate() {
		if(method_exists($this->obj, 'activateoptions')) {
			return call_user_func(array($this->obj, 'activateoptions'));
		}
	}

	/**
	 * Creates an instances from the given class name.
	 *
	 * @param string $classname
	 * @return an object from the class with the given classname
	 */
	public static function createObject($class) {
		if(!empty($class)) {
			return new $class();
		}
		return null;
	}

	/**
	 * @param object $object
	 * @param string $name
	 * @param mixed $value
	 */
	public static function setter($object, $name, $value) {
		if (empty($name)) {
			return false;
		}
		$methodName = 'set'.ucfirst($name);
		if (method_exists($object, $methodName)) {
			return call_user_func(array($object, $methodName), $value);
		} else {
			return false;
		}
	}

}


<?php
namespace FACTFinder\Core\Client;

use FACTFinder\Loader as FF;

/**
 * Generates URLs to be used in requests to the client.
 */
class UrlBuilder
{
    /**
     * @var FACTFinder\Util\LoggerInterface
     */
    private $log;

    /**
     * @var FACTFinder\Core\ParametersConverter
     */
    private $parametersConverter;

    /**
     * @var FACTFinder\Core\Client\RequestParser
     */
    private $requestParser;

    /**
     * @var FACTFinder\Core\AbstractEncodingConverter
     */
    private $encodingConverter;

    public function __construct(
        $loggerClass,
        \FACTFinder\Core\ConfigurationInterface $configuration,
        \FACTFinder\Core\Client\RequestParser $requestParser,
        \FACTFinder\Core\AbstractEncodingConverter $encodingConverter = null
    ) {
        $this->log = $loggerClass::getLogger(__CLASS__);

        $this->parametersConverter = FF::getInstance(
            'Core\ParametersConverter',
            $loggerClass,
            $configuration
        );
        $this->requestParser = $requestParser;
        $this->encodingConverter = $encodingConverter;
    }

    /**
     * Generates a link to be used on the page that leads to the client from a
     * set of server parameters. Note that the link will still be UTF-8 encoded.
     * If the page uses a different encoding, conversion to that encoding has to
     * be done when actually rendering the string to the page.
     *
     * TODO: Should the signature be more similar to that of \Server\UrlBuilder?
     *
     * @param FACTFinder\Util\Parameters $parameters The server parameters that
     *        should be retrieved when the link is followed.
     * @param string $target An optional request target. If omitted, the target
     *        of the current request will be used. For instance, this parameter
     *        can be used if a product detail page needs a different target.
     *
     * @return string
     */
    public function generateUrl($parameters, $target = null)
    {
        $parameters = $this->parametersConverter
                           ->convertServerToClientParameters($parameters);

        $parameters = $this->encodingConverter != null ? $this->encodingConverter->encodeClientUrlData($parameters) : $parameters;

        if (!is_string($target))
            $target = $this->requestParser->getRequestTarget();

        if ($parameters->offsetExists('seoPath')) {
            $seoPath = $parameters['seoPath'];
            $parameters->offsetUnset('seoPath');
            $seoPathPosition = strrpos($target, "/s/");
            if ($seoPathPosition > -1) {
                $target = substr($target, 0, $seoPathPosition);
            }
            $url = rtrim($target, '/') . '/s' . urldecode($seoPath) . '?' . $parameters->toPhpQueryString();
        } else {
            $url = $target . '?' . $parameters->toPhpQueryString();
        }
        return $url;
    }
}


<?php
/**
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements. See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 *
 *	   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * @package log4php
 */

/**
 * Returns the date/time of the logging request.
 *
 * Option: the datetime format, as used by the date() function. If
 * the option is not given, the default format 'c' will be used.
 *
 * There are several "special" values which can be given for this option:
 * 'ISO8601', 'ABSOLUTE' and 'DATE'.
 *
 * @package log4php
 * @subpackage pattern
 * @version $Revision: 1326626 $
 * @since 2.3
 */
class LoggerPatternConverterDate extends LoggerPatternConverter {

	const DATE_FORMAT_ISO8601 = 'c';

	const DATE_FORMAT_ABSOLUTE = 'H:i:s';

	const DATE_FORMAT_DATE = 'd M Y H:i:s.u';

	private $format = self::DATE_FORMAT_ISO8601;

	private $specials = array(
		'ISO8601' => self::DATE_FORMAT_ISO8601,
		'ABSOLUTE' => self::DATE_FORMAT_ABSOLUTE,
		'DATE' => self::DATE_FORMAT_DATE,
	);

	private $useLocalDate = false;

	public function activateOptions() {

		// Parse the option (date format)
		if (!empty($this->option)) {
			if(isset($this->specials[$this->option])) {
				$this->format = $this->specials[$this->option];
			} else {
				$this->format = $this->option;
			}
		}

		// Check whether the pattern contains milliseconds (u)
		if (preg_match('/(?<!\\\\)u/', $this->format)) {
			$this->useLocalDate = true;
		}
	}

	public function convert(LoggerLoggingEvent $event) {
		if ($this->useLocalDate) {
			return $this->date($this->format, $event->getTimeStamp());
		}
		return date($this->format, $event->getTimeStamp());
	}

	/**
	 * Currently, PHP date() function always returns zeros for milliseconds (u)
	 * on Windows. This is a replacement function for date() which correctly
	 * displays milliseconds on all platforms.
	 *
	 * It is slower than PHP date() so it should only be used if necessary.
	 */
	private function date($format, $utimestamp) {
		$timestamp = floor($utimestamp);
		$ms = floor(($utimestamp - $timestamp) * 1000);
		$ms = str_pad($ms, 3, '0', STR_PAD_LEFT);

		return date(preg_replace('`(?<!\\\\)u`', $ms, $format), $timestamp);
	}
}


<?php
namespace FACTFinder\Data;

/**
 * A collection of all Campaign objects. Provides some convenience methods to
 * query all campaigns at once.
 */
class CampaignIterator extends \ArrayIterator
{
    /**
     * @param Campaign[] $campaigns
     */
    public function __construct(array $campaigns)
    {
        parent::__construct($campaigns);
    }

    /**
     * @see Campaign::hasRedirect()
     * @return bool
     */
    public function hasRedirect()
    {
        foreach ($this as $campaign)
            if ($campaign->hasRedirect())
                return true;

        return false;
    }

    /**
     * Gets the first redirect URL if any campaign has a redirect. Returns null
     * otherwise.
     *
     * @see Campaign::getRedirectUrl()
     * @return string
     */
    public function getRedirectUrl()
    {
        foreach ($this as $campaign)
            if ($campaign->hasRedirect())
                return $campaign->getRedirectUrl();

        return null;
    }

    /**
     * @see Campaign::hasFeedback()
     * @return bool
     */
    public function hasFeedback()
    {
        foreach ($this as $campaign)
            if ($campaign->hasFeedback())
                return true;

        return false;
    }

    /**
     * Returns all feedback texts for the given label, concatenated with
     * PHP_EOL.
     *
     * @see Campaign::getFeedback()
     * @param string $label
     * @return string
     */
    public function getFeedback($label)
    {
        $feedback = array();
        foreach ($this as $campaign)
            if ($campaign->hasFeedback($label))
                $feedback[] = $campaign->getFeedback($label);

        return implode(PHP_EOL, $feedback);
    }

    /**
     * @see Campaign::hasPushedProducts()
     * @return bool
     */
    public function hasPushedProducts()
    {
        foreach ($this as $campaign)
            if ($campaign->hasPushedProducts())
                return true;

        return false;
    }

    /**
     * Returns an array with the pushed products from all campaigns. Note that
     * if a product shows up in multiple campaigns, it will be duplicated in the
     * result of this function.
     *
     * @see Campaign::getPushedProducts()
     * @return Record[]
     */
    public function getPushedProducts()
    {
        $pushedProducts = array();
        foreach ($this as $campaign)
            if ($campaign->hasPushedProducts())
                foreach ($campaign->getPushedProducts() as $product)
                    $pushedProducts[] = $product;

        return $pushedProducts;
    }

    /**
     * @see Campaign::hasActiveQuestions()
     * @return bool
     */
    public function hasActiveQuestions()
    {
        foreach ($this as $campaign)
            if ($campaign->hasActiveQuestions())
                return true;

        return false;
    }

    /**
     * Returns an array with the active questions from all campaigns.
     *
     * @see Campaign::getActiveQuestions()
     * @return Record[]
     */
    public function getActiveQuestions()
    {
        $activeQuestions = array();
        foreach ($this as $campaign)
            if ($campaign->hasActiveQuestions())
                foreach ($campaign->getActiveQuestions() as $question)
                    $activeQuestions[] = $question;

        return $activeQuestions;
    }

    /**
     * @see Campaign::hasAdvisorTree()
     * @return bool
     */
    public function hasAdvisorTree()
    {
        foreach ($this as $campaign)
            if ($campaign->hasAdvisorTree())
                return true;

        return false;
    }

    /**
     * Returns an array with the root questions of the advisor trees from all
     * campaigns.
     *
     * @see Campaign::getAdvisorTree()
     * @return Record[]
     */
    public function getAdvisorTree()
    {
        $advisorTree = array();
        foreach ($this as $campaign)
            if ($campaign->hasAdvisorTree())
                foreach ($campaign->getAdvisorTree() as $question)
                    $advisorTree[] = $question;

        return $advisorTree;
    }
}


<?php
/**
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements. See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 *
 *	   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * An Appender that automatically creates a new logfile each day.
 *
 * The file is rolled over once a day. That means, for each day a new file
 * is created. A formatted version of the date pattern is used as to create
 * the file name using the {@link PHP_MANUAL#sprintf} function.
 *
 * This appender uses a layout.
 *
 * ##Configurable parameters:##
 *
 * - **datePattern** - Format for the date in the file path, follows formatting
 *     rules used by the PHP date() function. Default value: "Ymd".
 * - **file** - Path to the target file. Should contain a %s which gets
 *     substituted by the date.
 * - **append** - If set to true, the appender will append to the file,
 *     otherwise the file contents will be overwritten. Defaults to true.
 *
 * @version $Revision: 1382274 $
 * @package log4php
 * @subpackage appenders
 * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
 * @link http://logging.apache.org/log4php/docs/appenders/daily-file.html Appender documentation
 */
class LoggerAppenderDailyFile extends LoggerAppenderFile {

	/**
	 * The 'datePattern' parameter.
	 * Determines how date will be formatted in file name.
	 * @var string
	 */
	protected $datePattern = "Ymd";

	/**
	 * Current date which was used when opening a file.
	 * Used to determine if a rollover is needed when the date changes.
	 * @var string
	 */
	protected $currentDate;

	/** Additional validation for the date pattern. */
	public function activateOptions() {
		parent::activateOptions();

		if (empty($this->datePattern)) {
			$this->warn("Required parameter 'datePattern' not set. Closing appender.");
			$this->closed = true;
			return;
		}
	}

	/**
	 * Appends a logging event.
	 *
	 * If the target file changes because of passage of time (e.g. at midnight)
	 * the current file is closed. A new file, with the new date, will be
	 * opened by the write() method.
	 */
	public function append(LoggerLoggingEvent $event) {
		$eventDate = $this->getDate($event->getTimestamp());

		// Initial setting of current date
		if (!isset($this->currentDate)) {
			$this->currentDate = $eventDate;
		}

		// Check if rollover is needed
		else if ($this->currentDate !== $eventDate) {
			$this->currentDate = $eventDate;

			// Close the file if it's open.
			// Note: $this->close() is not called here because it would set
			//       $this->closed to true and the appender would not recieve
			//       any more logging requests
			if (is_resource($this->fp)) {
				$this->write($this->layout->getFooter());
				fclose($this->fp);
			}
			$this->fp = null;
		}

		parent::append($event);
	}

	/** Renders the date using the configured <var>datePattern<var>. */
	protected function getDate($timestamp = null) {
		return date($this->datePattern, $timestamp);
	}

	/**
	 * Determines target file. Replaces %s in file path with a date.
	 */
	protected function getTargetFile() {
		return str_replace('%s', $this->currentDate, $this->file);
	}

	/**
	 * Sets the 'datePattern' parameter.
	 * @param string $datePattern
	 */
	public function setDatePattern($datePattern) {
		$this->setString('datePattern', $datePattern);
	}

	/**
	 * Returns the 'datePattern' parameter.
	 * @return string
	 */
	public function getDatePattern() {
		return $this->datePattern;
	}
}


<?php namespace MrJuliuss\Syntara\Models\Permissions;

use Illuminate\Support\Facades\Facade;
use MrJuliuss\Syntara\Models\Permissions\PermissionNotFoundException;
use MrJuliuss\Syntara\Models\Permissions\PermissionExistsException;

class PermissionProvider
{
    protected $_model = 'MrJuliuss\Syntara\Models\Permissions\Permission';

    /**
     * Create permission
     * @param  array $attributes
     * @return Permission permission object
     */
    public function createPermission($attributes)
    {
        $permission = $this->createModel();
        $permission->fill($attributes);
        $permission->save();

        return $permission;
    }

    /**
     * Create a new instance of the model.
     *
     * @return \Illuminate\Database\Eloquent\Model
     */
    public function createModel()
    {
        $class = '\\'.ltrim($this->_model, '\\');

        return new $class;
    }

    /**
     * Returns an all permissions.
     *
     * @return array
     */
    public function findAll()
    {
        return $this->createModel()->newQuery()->get()->all();
    }

    /**
     * Find a permission by the given permission id
     * @param  int $id
     * @return Permission
     */
    public function findById($id)
    {
        if(!$permission = $this->createModel()->newQuery()->find($id)) {
            throw new PermissionNotFoundException("A permission could not be found with ID [$id].");
        }

        return $permission;
    }

    /**
     * Find a permission by the given permission value
     * @param  string $value
     * @return Permission
     */
    public function findByValue($value)
    {
        if(!$permission = $this->createModel()->newQuery()->where('value', '=', $value)->get()->first()) {
            throw new PermissionNotFoundException("A permission could not be found with Value [$value].");
        }

        return $permission;
    }
}


<?php

use Illuminate\Database\Migrations\Migration;
use MrJuliuss\Syntara\Facades\PermissionProvider;

class CreatePermissionTable extends Migration
{
    /**
	 * Run the migrations.
	 *
	 * @return void
	 */
    public function up()
    {
        Schema::create('permissions', function ($table) {
            $table->increments('id');
            $table->string('name', 100);
            $table->string('value', 100)->unique();
            $table->string('description');
            $table->timestamps();
        });

        PermissionProvider::createPermission(array('name' => 'Super User', 'value' => 'superuser', 'description' => 'All permissions'));
        PermissionProvider::createPermission(array('name' => 'List Users', 'value' => 'view-users-list', 'description' => 'View the list of users'));
        PermissionProvider::createPermission(array('name' => 'Create user', 'value' => 'create-user', 'description' => 'Create new user'));
        PermissionProvider::createPermission(array('name' => 'Delete user', 'value' => 'delete-user', 'description' => 'Delete a user'));
        PermissionProvider::createPermission(array('name' => 'Update user', 'value' => 'update-user-info', 'description' => 'Update a user profile'));
        PermissionProvider::createPermission(array('name' => 'Update user group', 'value' => 'user-group-management', 'description' => 'Add/Remove a user in a group'));
        PermissionProvider::createPermission(array('name' => 'Groups management', 'value' => 'groups-management', 'description' => 'Manage group (CRUD)'));
        PermissionProvider::createPermission(array('name' => 'Permissions management', 'value' => 'permissions-management', 'description' => 'Manage permissions (CRUD)'));
    }

    /**
	 * Reverse the migrations.
	 *
	 * @return void
	 */
    public function down()
    {
        Schema::drop('permissions');
    }

}


<?php

namespace MrJuliuss\Syntara\Helpers;

use Illuminate\Support\Facades\URL;

/**
* Breadcrumb class
*/
class Breadcrumbs
{
    /**
    * Create breadcrumb
    * @param array $items breadcrumb items
    * @return string
    */
    public static function create($items)
    {
        if(empty($items)) {
            return;
        }

        $crumbs = array();
        foreach($items as $key => $item) {
            $active = false;
            if((count($items) - 1) === $key) {
                $active = true;
            }
            $crumbs[] = static::renderItem($item, $active);
        }

        $html = '
';
        $html .= implode('', $crumbs);;
        $html .= '
';

        return $html;
    }

    /**
    * Render the current item
    * @param array $item part of the breadcrumb
    * @param boolean $active current breadcrumb is active
    * @return string
    */
    public static function renderItem($item, $active)
    {
        $class = "";
        if($active === true) {
            $class = "current";
        }

        $html = '';
        if($item["icon"] !== '') {
            $html .= '';
        }

        $html .= $item["title"].'';

        return $html;
    }
}


<?php

namespace MrJuliuss\Syntara\Controllers;

use Illuminate\Support\Facades\View;
use Illuminate\Routing\Controller;
use Config;

class BaseController extends Controller
{
    /**
    * Setup the layout used by the controller.
    *
    * @return void
    */
    protected function setupLayout()
    {
        $this->layout = View::make(Config::get('syntara::views.master'));
        $this->layout->title = 'Syntara - Dashboard';
        $this->layout->breadcrumb = array();
    }
}


<?php

namespace MrJuliuss\Syntara\Commands;

use Illuminate\Console\Command;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputArgument;
use Sentry;

class UpdateCommand extends Command
{

    /**
     * The console command name.
     *
     * @var string
     */
    protected $name = 'syntara:update';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Syntara update command';

    /**
     * Create a new command instance.
     *
     * @return void
     */
    public function __construct()
    {
        parent::__construct();
    }

    /**
     * Execute the console command.
     *
     * @return void
     */
    public function fire()
    {
        $this->info('## Syntara Update ##');

        // publish syntara assets
        $this->call('asset:publish', array('package' => 'mrjuliuss/syntara' ) );

        // run migrations
        $this->call('migrate', array('--env' => $this->option('env'), '--package' => 'cartalyst/sentry' ) );
        $this->call('migrate', array('--env' => $this->option('env'), '--package' => 'mrjuliuss/syntara' ) );
    }
}


<?php

namespace MrJuliuss\Syntara\Controllers;

use MrJuliuss\Syntara\Controllers\BaseController;
use MrJuliuss\Syntara\Services\Validators\User as UserValidator;
use View;
use Input;
use Response;
use Request;
use Sentry;
use Config;
use URL;
use PermissionProvider;
use DB;
use Mail;

class UserController extends BaseController
{

    /**
    * Display a list of all users
    *
    * @return Response
    */
    public function getIndex()
    {
        // get alls users
        $emptyUsers =  Sentry::getUserProvider()->getEmptyUser();

        // users search
        $userId = Input::get('userIdSearch');
        if(!empty($userId)) {
            $emptyUsers = $emptyUsers->where('users.id', $userId);
        }

        $username = Input::get('usernameSearch');
        if(!empty($username)) {
            $emptyUsers = $emptyUsers->where('username', 'LIKE', '%'.$username.'%');
        }

        $firstName = Input::get('firstNameSearch');
        if(!empty($firstName)) {
            $emptyUsers = $emptyUsers->where('first_name', 'LIKE', '%'.$firstName.'%');
        }

        $lastName = Input::get('lastNameSearch');
        if(!empty($lastName)) {
            $emptyUsers = $emptyUsers->where('last_name', 'LIKE', '%'.$lastName.'%');
        }

        $email = Input::get('emailSearch');
        if(!empty($email)) {
            $emptyUsers = $emptyUsers->where('email', 'LIKE', '%'.$email.'%');
        }

        $bannedUsers = Input::get('bannedSearch');
        if(isset($bannedUsers) && $bannedUsers !== "") {
            $emptyUsers = $emptyUsers->join('throttle', 'throttle.user_id', '=', 'users.id')
                ->where('throttle.banned', '=', $bannedUsers)
                ->select('users.id', 'users.username', 'users.last_name', 'users.first_name', 'users.email', 'users.permissions', 'users.activated');
        }

        $emptyUsers->distinct();

        $users = $emptyUsers->paginate(Config::get('syntara::config.item-perge-page'));
        $datas['links'] = $users->links();
        $datas['users'] = $users;

        // ajax request : reload only content container
        if(Request::ajax()) {
            $html = View::make(Config::get('syntara::views.users-list'), array('datas' => $datas))->render();

            return Response::json(array('html' => $html));
        }

        $this->layout = View::make(Config::get('syntara::views.users-index'), array('datas' => $datas));
        $this->layout->title = trans('syntara::users.titles.list');
        $this->layout->breadcrumb = Config::get('syntara::breadcrumbs.users');
    }

    /**
    * Show new user form view
    */
    public function getCreate()
    {
        $groups = Sentry::getGroupProvider()->findAll();
        $permissions = PermissionProvider::findAll();

        $this->layout = View::make(Config::get('syntara::views.user-create'), array('groups' => $groups, 'permissions' => $permissions));
        $this->layout->title = trans('syntara::users.titles.new');
        $this->layout->breadcrumb = Config::get('syntara::breadcrumbs.create_user');
    }

    /**
    * Create new user
    */
    public function postCreate()
    {
        try {
            $validator = new UserValidator(Input::all(), 'create');

            $permissionsValues = Input::get('permission');
            $permissions = $this->_formatPermissions($permissionsValues);

            if(!$validator->passes()) {
                return Response::json(array('userCreated' => false, 'errorMessages' => $validator->getErrors()));
            }

            // create user
            $user = Sentry::getUserProvider()->create(array(
                'email'    => Input::get('email'),
                'password' => Input::get('pass'),
                'username' => Input::get('username'),
                'last_name' => (string)Input::get('last_name'),
                'first_name' => (string)Input::get('first_name'),
                'permissions' => $permissions
            ));

            // activate user
            $activationCode = $user->getActivationCode();
            if(Config::get('syntara::config.user-activation') === 'auto') {
                $user->attemptActivation($activationCode);
            } elseif(Config::get('syntara::config.user-activation') === 'email') {
                $datas = array(
                    'code' => $activationCode,
                    'username' => $user->username
                );

                // send email
                Mail::queue(Config::get('syntara::mails.user-activation-view'), $datas, function ($message) use ($user) {
                    $message->from(Config::get('syntara::mails.email'), Config::get('syntara::mails.contact'))
                            ->subject(Config::get('syntara::mails.user-activation-object'));
                    $message->to($user->getLogin());
                });
            }

            $groups = Input::get('groups');
            if(isset($groups) && is_array($groups)) {
                foreach($groups as $groupId) {
                    $group = Sentry::getGroupProvider()->findById($groupId);
                    $user->addGroup($group);
                }
            }
        } catch (\Cartalyst\Sentry\Users\LoginRequiredException $e){} // already catch by validators
        catch (\Cartalyst\Sentry\Users\PasswordRequiredException $e){} // already catch by validators
        catch (\Cartalyst\Sentry\Groups\GroupNotFoundException $e){} catch (\Cartalyst\Sentry\Users\UserExistsException $e) {
            return json_encode(array('userCreated' => false, 'message' => trans('syntara::users.messages.user-email-exists'), 'messageType' => 'danger'));
        } catch(\Exception $e) {
            return Response::json(array('userCreated' => false, 'message' => trans('syntara::users.messages.user-name-exists'), 'messageType' => 'danger'));
        }

        return json_encode(array('userCreated' => true, 'redirectUrl' => URL::route('listUsers')));
    }

    /**
     * Delete user
     * @param  int $userId
     * @return  Response
     */
    public function delete($userId)
    {
        try {
            if($userId !== Sentry::getUser()->getId()) {
                $user = Sentry::getUserProvider()->findById($userId);
                $user->delete();
            } else {
                return Response::json(array('deletedUser' => false, 'message' => trans('syntara::users.messages.remove-own-user'), 'messageType' => 'danger'));
            }
        } catch (\Cartalyst\Sentry\Users\UserNotFoundException $e) {
            return Response::json(array('deletedUser' => false, 'message' => trans('syntara::users.messages.not-found'), 'messageType' => 'danger'));
        }

        return Response::json(array('deletedUser' => true, 'message' => trans('syntara::users.messages.remove-success'), 'messageType' => 'success'));
    }

    /**
     * Activate a user since the dashboard
     * @param  int $userId
     * @return Response
     */
    public function putActivate($userId)
    {
        try {
            $user = Sentry::getUserProvider()->findById($userId);
            $activationCode = $user->getActivationCode();
            $user->attemptActivation($activationCode);
        } catch (\Cartalyst\Sentry\Users\UserNotFoundException $e) {
            return Response::json(array('deletedUser' => false, 'message' => trans('syntara::users.messages.not-found'), 'messageType' => 'danger'));
        } catch (\Cartalyst\Sentry\Users\UserAlreadyActivatedException $e) {
            return Response::json(array('deletedUser' => false, 'message' => trans('syntara::users.messages.activate-already'), 'messageType' => 'danger'));
        }

        return Response::json(array('deletedUser' => true, 'message' => trans('syntara::users.messages.activate-success'), 'messageType' => 'success'));
    }

    /**
     * Activate a user (from an email)
     * @param  string $activationCode
     */
    public function getActivate($activationCode)
    {
        $activated = false;
        try {
            // Find the user using the activation code
            $user = Sentry::getUserProvider()->findByActivationCode($activationCode);

            // Attempt to activate the user
            if($user->attemptActivation($activationCode)) {
                $message = trans("Your account is successfully activated.");
                $activated = true;
            } else {
                // User activation failed
                $message = trans("Your account could not be activated.");
            }
        } catch(\Exception $e) {
            // User not found, activation found or other errors
            $message = trans("Your account could not be activated.");
        }

        $this->layout = View::make(Config::get('syntara::views.user-activation'), array('activated' => $activated, 'message' => $message));
    }

    /**
    * View user account
    * @param int $userId
    */
    public function getShow($userId)
    {
        try {
            $user = Sentry::getUserProvider()->findById($userId);
            $throttle = Sentry::getThrottleProvider()->findByUserId($userId);
            $groups = Sentry::getGroupProvider()->findAll();

            // get user permissions
            $permissions = PermissionProvider::findAll();
            $userPermissions = array();
            foreach($user->getPermissions() as $permissionValue => $key) {
                try {
                    $p = PermissionProvider::findByValue($permissionValue);
                    foreach($permissions as $key => $permission) {
                        if($p->getId() === $permission->getId()) {
                            $userPermissions[] = $permission;
                            unset($permissions[$key]);
                        }
                    }
                } catch(\MrJuliuss\Syntara\Models\Permissions\PermissionNotFoundException $e){}
            }

            // ajax request : reload only content container
            if(Request::ajax()) {
                $html = View::make(Config::get('syntara::views.user-informations'), array('user' => $user, 'throttle' => $throttle))->render();

                return Response::json(array('html' => $html));
            }

            $this->layout = View::make(Config::get('syntara::views.user-profile'), array(
                'user' => $user,
                'throttle' => $throttle,
                'groups' => $groups,
                'ownPermissions' => $userPermissions,
                'permissions' => $permissions
            ));

            $this->layout->title = $user->username;
            $this->layout->breadcrumb = array(
                    array(
                        'title' => trans('syntara::breadcrumbs.users'),
                        'link' => URL::route('listUsers'),
                        'icon' => 'glyphicon-user'
                    ),
                    array(
                     'title' => $user->username,
                     'link' => URL::current(),
                     'icon' => ''
                    )
            );
        } catch (\Cartalyst\Sentry\Users\UserNotFoundException $e) {
            $this->layout = View::make(Config::get('syntara::views.error'), array('message' => trans('syntara::users.messages.not-found')));
        }
    }

    /**
    * Update user account
    * @param int $userId
    * @return Response
    */
    public function putShow($userId)
    {
        try {
            $validator = new UserValidator(Input::all(), 'update');

            if(!$validator->passes()) {
                return Response::json(array('userUpdated' => false, 'errorMessages' => $validator->getErrors()));
            }

            $permissionsValues = Input::get('permission');
            $permissions = $this->_formatPermissions($permissionsValues);

            // Find the user using the user id
            $user = Sentry::getUserProvider()->findById($userId);
            $user->username = Input::get('username');
            $user->email = Input::get('email');
            $user->last_name = Input::get('last_name');
            $user->first_name = Input::get('first_name');
            $user->permissions = $permissions;

            $currentUser = Sentry::getUser();
            $permissions = (empty($permissions)) ? '' : json_encode($permissions);
            $hasPermissionManagement = $currentUser->hasAccess(Config::get('syntara::permissions.addUserPermission')) || $currentUser->hasAccess('superuser');
            if($hasPermissionManagement === true) {
                DB::table('users')
                    ->where('id', $userId)
                    ->update(array('permissions' => $permissions));
            }

            $pass = Input::get('pass');
            if(!empty($pass)) {
                $user->password = $pass;
            }

            // Update the user
            if($user->save()) {
                // if the user has permission to update
                $banned = Input::get('banned');
                if(isset($banned) && Sentry::getUser()->getId() !== $user->getId()) {
                    $this->_banUser($userId, $banned);
                }

                if($currentUser->hasAccess(Config::get('syntara::permissions.addUserGroup'))) {
                    $groups = (Input::get('groups') === null) ? array() : Input::get('groups');
                    $userGroups = $user->getGroups()->toArray();

                    foreach($userGroups as $group) {
                        if(!in_array($group['id'], $groups)) {
                            $group = Sentry::getGroupProvider()->findById($group['id']);
                            $user->removeGroup($group);
                        }
                    }
                    if(isset($groups) && is_array($groups)) {
                        foreach($groups as $groupId) {
                            $group = Sentry::getGroupProvider()->findById($groupId);
                            $user->addGroup($group);
                        }
                    }
                }

                return Response::json(array('userUpdated' => true, 'message' => trans('syntara::users.messages.update-success'), 'messageType' => 'success'));
            } else {
                return Response::json(array('userUpdated' => false, 'message' => trans('syntara::users.messages.update-fail'), 'messageType' => 'danger'));
            }
        } catch(\Cartalyst\Sentry\Users\UserExistsException $e) {
            return Response::json(array('userUpdated' => false, 'message' => trans('syntara::users.messages.user-email-exists'), 'messageType' => 'danger'));
        } catch(\Exception $e) {
            return Response::json(array('userUpdated' => false, 'message' => trans('syntara::users.messages.user-name-exists'), 'messageType' => 'danger'));
        }
    }

    protected function _formatPermissions($permissionsValues)
    {
        $permissions = array();
        if(!empty($permissionsValues)) {
            foreach($permissionsValues as $key => $permission) {
               $permissions[$key] = 1;
            }
        }

        return $permissions;
    }

    protected function _banUser($userId, $value)
    {
        $throttle = Sentry::findThrottlerByUserId($userId);
        if($value === 'no' && $throttle->isBanned() === true) {
            $throttle->unBan();
        } elseif($value === 'yes' && $throttle->isBanned() === false) {
            $throttle->ban();
        }
    }
}