Revert "Move AzuraCore back into the main app, and move library code to src/ because it's what all the cool kids are doing."

This reverts commit 95e393d60d.
This commit is contained in:
Buster Silver 2016-12-30 23:27:06 -06:00
parent 95e393d60d
commit e9c62a3ea5
79 changed files with 227 additions and 6275 deletions

View File

@ -22,6 +22,7 @@ define("APP_INCLUDE_MODULES", APP_INCLUDE_BASE.'/modules');
define("APP_INCLUDE_TEMP", APP_INCLUDE_ROOT.'/../www_tmp');
define("APP_INCLUDE_CACHE", APP_INCLUDE_TEMP.'/cache');
define("APP_INCLUDE_LIB", APP_INCLUDE_BASE.'/library');
define("APP_INCLUDE_VENDOR", APP_INCLUDE_ROOT.'/vendor');
define("APP_UPLOAD_FOLDER", APP_INCLUDE_STATIC);
@ -41,6 +42,7 @@ if (isset($_SERVER['HTTP_X_FORWARDED_PROTO']))
// Composer autoload.
$autoloader = require(APP_INCLUDE_VENDOR . '/autoload.php');
$autoloader->add('AzuraCast', APP_INCLUDE_LIB);
// Set up DI container.
$app_settings = [

View File

@ -18,13 +18,12 @@ coverage:
enabled: true
include:
- app/*
- src/*
exclude:
# Not used in application
- src/AzuraCast/Radio/Frontend/ShoutCast2.php
- app/library/AzuraCast/Radio/Frontend/ShoutCast2.php
# Used in application, but not used in tests
- src/AzuraCast/Console/Command/*.php
- app/library/AzuraCast/Console/Command/*.php
- app/**/*.conf.sample.php
- app/models/Migration/*
- app/locale/**/*

View File

@ -4,21 +4,7 @@
"license": "Apache-2.0",
"require": {
"slim/slim": "^3.0",
"league/plates": "^3.1",
"filp/whoops": "1.*",
"tedivm/stash": "0.14.*",
"nibble/nibble-forms": "dev-master",
"zendframework/zend-paginator": "^2.7",
"zendframework/zend-config": "^2.6",
"doctrine/orm": "~2.5",
"doctrine/migrations": "^1.4",
"jdorn/sql-formatter": "^1.2",
"nette/mail": "2.3.0",
"packaged/helpers": "^1.5",
"slvreagle23/azuracore": "dev-master",
"guzzlehttp/guzzle": ">6.0",
"electrolinux/phpquery": "0.9.6",
@ -37,12 +23,6 @@
"codeclimate/php-test-reporter": "^0.3.2",
"flow/jsonpath": "^0.3.4"
},
"autoload": {
"psr-4": {
"App\\": "src/App",
"AzuraCast\\": "src/AzuraCast"
}
},
"authors": [
{
"name": "Buster Neece",

396
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -1,185 +0,0 @@
<?php
/**
* Access Control List (ACL) manager
*/
namespace App;
use Entity\RolePermission;
use Entity\User;
use Entity\Role;
class Acl
{
/** @var \Doctrine\ORM\EntityManager */
protected $_em;
/** @var Auth */
protected $_auth;
/** @var array|null An array of actions enabled by each role. */
protected $_actions = null;
/** @var array|null The roles of the currently logged in user. */
protected $_roles = null;
/** @var array|null A cache of permissions and return statuses. */
protected $_cache = null;
public function __construct(\Doctrine\ORM\EntityManager $em, Auth $auth)
{
$this->_em = $em;
$this->_auth = $auth;
}
/**
* Initialize role/actions cache upon the first permission check.
*/
protected function init()
{
if (null === $this->_actions)
$this->_actions = $this->_em->getRepository(RolePermission::class)->getActionsForAllRoles();
}
/**
* Force a reload of the internal ACL cache (used in the event of a user status change.
*/
public function reload()
{
$this->_actions = null;
$this->_roles = null;
$this->_cache = null;
$this->init();
}
/**
* Check if a specified User entity is allowed to perform an action (or array of actions).
*
* @param string|array $action
* @param User|null $user
* @return mixed
*/
public function userAllowed($action, User $user = null)
{
// Make all actions lower-case and sort alphabetically (so memoization returns the same result).
$action = array_map('strtolower', (array)$action);
asort($action);
$memoize = md5(serialize($action));
$user_id = ($user instanceof User) ? $user->id : 'anonymous';
if( !isset($this->_cache[$user_id][$memoize]) )
{
if($user instanceof User)
{
if(!isset($this->_roles[$user_id]))
{
$this->_roles[$user_id] = array();
if (count($user->roles) > 0)
{
foreach($user->roles as $role)
$this->_roles[$user_id][] = $role->id;
}
}
$this->_cache[$user_id][$memoize] = $this->roleAllowed($this->_roles[$user_id], $action);
}
else
{
$this->_cache[$user_id][$memoize] = $this->roleAllowed(array('Unauthenticated'), $action);
}
}
return $this->_cache[$user_id][$memoize];
}
/**
* Check if the currently logged-in user can perform a specified action.
*
* @param string $action
* @return bool|mixed
*/
public function isAllowed($action)
{
static $is_logged_in, $user;
if ($is_logged_in === NULL)
{
$user = $this->_auth->getLoggedInUser();
$is_logged_in = ($user instanceof User);
}
if ($action == "is logged in")
return ($is_logged_in);
elseif ($action == "is not logged in")
return (!$is_logged_in);
elseif ($is_logged_in)
return $this->userAllowed($action, $user);
else
return false;
}
/**
* Check if a role (or array of roles) is allowed to perform an action (or array of actions).
*
* @param int|array $role_id
* @param string|array $action
* @return bool
*/
public function roleAllowed($role_id, $action)
{
$this->init();
if(is_array($role_id))
{
foreach($role_id as $r)
{
if($this->roleAllowed($r, $action))
return true;
}
return false;
}
else if(is_array($action))
{
foreach($action as $a)
{
if($this->roleAllowed($role_id, $a))
return true;
}
return false;
}
else
{
if($role_id == 1) // Default super-administrator role.
return true;
if (in_array('administer all', (array)$this->_actions[$role_id]))
return true;
if (isset($this->_actions[$role_id]) && in_array($action, $this->_actions[$role_id]))
return true;
return false;
}
}
/**
* Pretty wrapper around the 'isAllowed' function that throws a UI-friendly exception upon failure.
*
* @param $action
* @throws \App\Exception\NotLoggedIn
* @throws \App\Exception\PermissionDenied
*/
public function checkPermission($action)
{
if (!$this->isAllowed($action))
{
if (!$this->_auth->isLoggedIn())
throw new \App\Exception\NotLoggedIn();
else
throw new \App\Exception\PermissionDenied();
}
}
}

View File

@ -1,228 +0,0 @@
<?php
namespace App;
use Entity\User;
use Entity\UserRepository;
class Auth
{
/** @var Session */
protected $_session;
/** @var UserRepository */
protected $_user_repo;
/** @var User|null */
protected $_user = null;
/** @var User|null */
protected $_masqueraded_user = null;
public function __construct(Session $session, UserRepository $user_repo)
{
$this->_user_repo = $user_repo;
$class_name = strtolower(str_replace(array('\\', '_'), array('', ''), get_called_class()));
$this->_session = $session->get('auth_' . $class_name . '_user');
}
/**
* Authenticate a given username and password combination against the User repository.
*
* @param $username
* @param $password
* @return bool
*/
public function authenticate($username, $password)
{
$user_auth = $this->_user_repo->authenticate($username, $password);
if ($user_auth instanceof User)
{
$this->setUser($user_auth);
return true;
}
else
{
return false;
}
}
/**
* Log out of the currently active session.
*
* @param null $destination
* @param bool $unset_session
*/
public function logout()
{
unset($this->_session->user_id);
unset($this->_session->masquerade_user_id);
$this->_user = null;
@session_unset();
}
/**
* Check if a user account is currently authenticated.
*
* @return bool
*/
public function isLoggedIn()
{
if (APP_IS_COMMAND_LINE && !APP_TESTING_MODE)
return false;
$user = $this->getUser();
return ($user instanceof User);
}
/**
* Get the currently logged in user.
*
* @param bool $real_user_only
* @return bool|User|null|object
*/
public function getLoggedInUser($real_user_only = FALSE)
{
if ($this->isMasqueraded() && !$real_user_only)
return $this->getMasquerade();
else
return $this->getUser();
}
/**
* Get the authenticated user entity.
*
* @return bool|User|null|object
* @throws Exception
*/
public function getUser()
{
if ($this->_user === NULL)
{
$user_id = (int)$this->_session->user_id;
if ($user_id == 0)
{
$this->_user = FALSE;
return false;
}
$user = $this->_user_repo->find($user_id);
if ($user instanceof User)
{
$this->_user = $user;
}
else
{
unset($this->_session->user_id);
$this->_user = FALSE;
$this->logout();
throw new Exception('Invalid user!');
}
}
return $this->_user;
}
/**
* Set the currently authenticated user.
*
* @param User $user
* @return bool
*/
public function setUser(User $user)
{
// Prevent any previous identity from being used.
// @session_regenerate_id(true);
$this->_session->user_id = $user->id;
$this->_user = $user;
return true;
}
/**
* Masquerading
*/
/**
* Become a different user across the application.
*
* @param $user_info
*/
public function masqueradeAsUser($user_info)
{
if (!($user_info instanceof User))
$user_info = $this->_user_repo->findOneBy($user_info);
$this->_session->masquerade_user_id = $user_info->id;
$this->_masqueraded_user = $user_info;
}
/**
* Return to the regular authenticated account.
*/
public function endMasquerade()
{
unset($this->_session->masquerade_user_id);
$this->_masqueraded_user = null;
}
/**
* Return the currently masqueraded user, if one is set.
*
* @return User|null
*/
public function getMasquerade()
{
return $this->_masqueraded_user;
}
/**
* Check if the current user is masquerading as another account.
*
* @return bool
*/
public function isMasqueraded()
{
if (!$this->isLoggedIn())
{
$this->_masqueraded_user = FALSE;
return false;
}
if ($this->_masqueraded_user === NULL)
{
if (!$this->_session->masquerade_user_id)
{
$this->_masqueraded_user = FALSE;
}
else
{
$mask_user_id = (int)$this->_session->masquerade_user_id;
if ($mask_user_id != 0)
$user = $this->_user_repo->find($mask_user_id);
else
$user = NULL;
if ($user instanceof User)
{
$this->_masqueraded_user = $user;
}
else
{
unset($this->_session->user_id);
unset($this->_session->masquerade_user_id);
$this->_masqueraded_user = FALSE;
}
}
}
return ($this->_masqueraded_user instanceof User);
}
}

View File

@ -1,185 +0,0 @@
<?php
namespace App;
class Cache
{
/**
* @var \Stash\Pool
*/
protected $_cache;
/**
* @var int Default length of time to keep cached items.
*/
protected $_cache_lifetime = 3600;
public function __construct(\Stash\Interfaces\DriverInterface $cache_driver, $cache_level = 'user')
{
$pool = new \Stash\Pool($cache_driver);
$pool->setNamespace(self::getSitePrefix($cache_level));
$this->_cache = $pool;
}
/**
* Return the raw cache itself for manipulation.
*
* @return \Stash\Pool
*/
public function getRawCache()
{
return $this->_cache;
}
/**
* Attempt to load an item from cache, or return default value if not found.
*
* @param $id
* @param null $default
* @return mixed|null
*/
public function load($id, $default = NULL)
{
$item = $this->_cache->getItem($id);
if ($item->isHit())
return $item->get();
elseif (is_callable($default))
return $default();
else
return $default;
}
/**
* Alias of the "load" function.
*
* @param $id
* @param null $default
* @return mixed|null
*/
public function get($id, $default = NULL)
{
return $this->load($id, $default);
}
/**
* Test whether an ID is present in the cache.
*
* @param $id
* @return bool
*/
public function test($id)
{
$item = $this->_cache->getItem($id);
return $item->isHit();
}
/**
* Save an item to the cache.
*
* @param $data
* @param $id
* @param bool|false $specificLifetime
*/
public function save($data, $id, $specificLifetime = false)
{
if ($specificLifetime === false)
$specificLifetime = $this->_cache_lifetime;
$item = $this->_cache->getItem($id);
$item->set($data);
$item->expiresAfter($specificLifetime);
}
/**
* Alias for the "save" function.
*
* @param $data
* @param $id
* @param bool|false $specificLifetime
*/
public function set($data, $id, $specificLifetime = false)
{
$this->save($data, $id, $specificLifetime);
}
/**
* Combination of the "get" and "set" functions to return an existing cache
* item or set it if one doesn't exist.
*
* @param $id
* @param null $default
* @param bool|false $specificLifetime
* @return mixed|null
*/
public function getOrSet($id, $default = NULL, $specificLifetime = false)
{
if ($specificLifetime === false)
$specificLifetime = $this->_cache_lifetime;
$item = $this->_cache->getItem($id);
if (!$item->isMiss())
{
return $item->get();
}
else
{
$item->lock();
$result = (is_callable($default)) ? $default() : $default;
if ($result !== null)
$item->set($result, $specificLifetime);
return $result;
}
}
/**
* Delete an item from the cache.
*
* @param $id
* @return bool
*/
public function remove($id)
{
$item = $this->_cache->getItem($id);
return $item->clear();
}
/**
* Clean the cache of all items.
*
* @return bool
*/
public function clean()
{
return $this->_cache->clear();
}
/**
* @param string $cache_level
* @param string $cache_separator
* @return string Compiled site prefix for cache use.
*/
public static function getSitePrefix($cache_level = 'user', $cache_separator = '')
{
static $cache_base;
if (!$cache_base)
{
$dir_hash = md5(APP_INCLUDE_ROOT);
$cache_base = substr($dir_hash, 0, 3);
}
// Shortening of cache level names.
if ($cache_level == 'user')
$cache_level = 'u';
elseif ($cache_level == 'doctrine')
$cache_level = 'db';
elseif ($cache_level == 'session')
$cache_level = 's';
return $cache_base.$cache_separator.$cache_level.$cache_separator;
}
}

View File

@ -1,70 +0,0 @@
<?php
namespace App;
use Interop\Container\ContainerInterface;
class Config
{
protected $_baseFolder;
protected $_loaded_configs;
/** @var ContainerInterface */
protected $di;
public function __construct($baseFolder, ContainerInterface $di)
{
$this->di = $di;
$this->_loaded_configs = array();
if(is_dir($baseFolder))
$this->_baseFolder = $baseFolder;
else
throw new \Exception("Invalid base folder for configurations.");
}
public function preload($configs)
{
$config_array = (is_array($configs)) ? $configs : array($configs);
foreach($config_array as $config_item)
{
$this->__get($config_item);
}
}
public function __set($name, $value)
{
throw new \Exception("Configuration is read-only.");
}
public function __get($name)
{
if (!isset($this->_loaded_configs[$name]))
{
$config_name = str_replace(array('.','..'), array('', ''), $name);
$config_base = $this->_baseFolder.DIRECTORY_SEPARATOR.$config_name;
if (is_dir($config_base))
return new self($config_base, $this->di); // Return entire directories.
else
$this_config = $this->getFile($config_base); // Return single files.
$this->_loaded_configs[$name] = $this_config;
}
return $this->_loaded_configs[$name];
}
public function getFile($config_base)
{
$di = $this->di;
if (file_exists($config_base))
$config = require $config_base;
elseif (file_exists($config_base.'.conf.php'))
$config = require $config_base.'.conf.php';
else
$config = [];
return new \Zend\Config\Config($config);
}
}

View File

@ -1,19 +0,0 @@
<?php
namespace App\Console\Command;
use Interop\Container\ContainerInterface;
use Symfony\Component\Console\Command\Command;
abstract class CommandAbstract extends Command
{
/**
* @var ContainerInterface
*/
protected $di;
public function __construct(ContainerInterface $di, $name = null)
{
$this->di = $di;
parent::__construct($name);
}
}

View File

@ -1,43 +0,0 @@
<?php
namespace App;
use \Defuse\Crypto\Crypto as DefuseCrypto;
use \Defuse\Crypto\Key;
/**
* General cryptography helpers for message handling.
*
* @package App
*/
class Crypto
{
/**
* @var \Defuse\Crypto\Key
*/
protected $_key;
public function __construct(Config $config)
{
$crypto_key = $config->apis->crypto_key;
if (empty($crypto_key))
{
$random_key = DefuseCrypto::createNewRandomKey();
$random_key_str = $random_key->saveToAsciiSafeString();
throw new Exception('No crypto key exists! Specify one in "apis.conf.php". Here\'s a random one for development: '.$random_key_str);
}
$this->_key = Key::LoadFromAsciiSafeString($crypto_key);
}
public function encrypt($message)
{
return DefuseCrypto::encrypt($message, $this->_key);
}
public function decrypt($message)
{
return DefuseCrypto::decrypt($message, $this->_key);
}
}

View File

@ -1,136 +0,0 @@
<?php
namespace App;
class Csrf
{
/**
* @var \App\Session\Instance
*/
protected $_session;
/**
* @var int The length of the string to generate.
*/
protected $_csrf_code_length = 10;
/**
* @var int The lifetime (in seconds) of an un-renewed CSRF token.
*/
protected $_csrf_lifetime = 3600;
/**
* @var string The default namespace used for token generation.
*/
protected $_csrf_default_namespace = 'general';
public function __construct(Session $session)
{
$this->_session = $session->get('csrf');
}
/**
* Generate a Cross-Site Request Forgery (CSRF) protection token.
* The "namespace" allows distinct CSRF tokens for different site functions,
* while not crowding the session namespace with one token for each action.
*
* If not renewed (with another "generate" call to the same namespace),
* a CSRF token will last the time specified in $this->_csrf_lifetime.
*
* @param string $namespace
* @return null|String
*/
public function generate($namespace = null)
{
if ($namespace === null)
$namespace = $this->_csrf_default_namespace;
$key = NULL;
if (isset($this->_session[$namespace]))
{
$key = $this->_session[$namespace]['key'];
if (strlen($key) !== $this->_csrf_code_length)
$key = NULL;
}
if (!$key)
$key = $this->randomString($this->_csrf_code_length);
$this->_session[$namespace] = array(
'key' => $key,
'timestamp' => time(),
);
return $key;
}
/**
* Verify a supplied CSRF token against the tokens stored in the session.
*
* @param $key
* @param string $namespace
* @return array
*/
public function verify($key, $namespace = null)
{
if ($namespace === null)
$namespace = $this->_csrf_default_namespace;
if (empty($key))
return array('is_valid' => false, 'message' => 'A CSRF token is required for this request.');
if (strlen($key) !== $this->_csrf_code_length)
return array('is_valid' => false, 'message' => 'Malformed CSRF token supplied.');
if (!isset($this->_session[$namespace]))
return array('is_valid' => false, 'message' => 'No CSRF token supplied for this namespace.');
$namespace_info = $this->_session[$namespace];
if (strcmp($key, $namespace_info['key']) !== 0)
return array('is_valid' => false, 'message' => 'Invalid CSRF token supplied.');
// Compare against time threshold (CSRF keys last 60 minutes).
$threshold = $namespace_info['timestamp']+$this->_csrf_lifetime;
if (time() >= $threshold)
return array('is_valid' => false, 'message' => 'This CSRF token has expired!');
return array('is_valid' => true);
}
/**
* Wrapper to the verify() function that triggers an exception for invalid tokens.
*
* @param $key
* @param null $namespace
* @return bool
* @throws Exception
*/
public function requireValid($key, $namespace = null)
{
$verify_result = $this->verify($key, $namespace);
if ($verify_result['is_valid'])
return true;
else
throw new Exception('Cannot validate CSRF token: '.$verify_result['message']);
}
/**
* Generates a random string of given $length.
*
* @param Integer $length The string length.
* @return String The randomly generated string.
*/
public function randomString($length)
{
$seed = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijqlmnopqrtsuvwxyz0123456789';
$max = strlen( $seed ) - 1;
$string = '';
for ( $i = 0; $i < $length; ++$i )
$string .= $seed{intval( mt_rand( 0.0, $max ) )};
return $string;
}
}

View File

@ -1,180 +0,0 @@
<?php
namespace App;
class Debug
{
static $echo_debug = false;
static $debug_log = array();
static $timers = array();
static function setEchoMode($new_value = true)
{
self::$echo_debug = $new_value;
}
static function showErrors($include_notices = FALSE)
{
if ($include_notices)
error_reporting(E_ALL & ~E_STRICT);
else
error_reporting(E_ALL & ~E_STRICT & ~E_NOTICE);
ini_set('display_errors', 1);
}
// Logging
static function log($entry)
{
$row = array('type' => 'log', 'message' => $entry);
if (self::$echo_debug)
self::display($row);
self::$debug_log[] = $row;
}
static function print_r($item)
{
$row = array('type' => 'array', 'message' => $item);
if (self::$echo_debug)
self::display($row);
self::$debug_log[] = $row;
}
static function divider()
{
$row = array('type' => 'divider');
if (self::$echo_debug)
self::display($row);
self::$debug_log[] = $row;
}
static function display($info)
{
switch($info['type'])
{
case 'divider':
if (APP_IS_COMMAND_LINE)
{
echo '---------------------------------------------'."\n";
}
else
{
echo '<div style="
padding: 3px;
background: #DDD;
border-left: 4px solid #DDD;
border-bottom: 1px solid #DDD;
margin: 0;"></div>';
}
break;
case 'array':
if (APP_IS_COMMAND_LINE)
{
echo print_r($info['message'], TRUE);
echo "\n";
}
else
{
echo '<pre style="
padding: 3px;
font-family: Consolas, Courier New, Courier, monospace;
font-size: 12px;
background: #EEE;
color: #111;
border-left: 4px solid #FFD24D;
border-bottom: 1px solid #DDD;
margin: 0;">';
$message = print_r($info['message'], TRUE);
if ($message)
echo $message;
else
echo '&nbsp;';
echo '</pre>';
}
break;
case 'log':
default:
if (APP_IS_COMMAND_LINE)
{
echo $info['message']."\n";
}
else
{
echo '<div style="
padding: 3px;
font-family: Consolas, Courier New, Courier, monospace;
font-size: 12px;
background: #EEE;
color: #111;
border-left: 4px solid #4DA6FF;
border-bottom: 1px solid #DDD;
margin: 0;">';
echo $info['message'];
echo '</div>';
}
break;
}
}
// Retrieval
static function getLog()
{
return self::$debug_log;
}
static function printLog()
{
foreach(self::$debug_log as $log_row)
self::display($log_row);
}
/**
* Start a timer with the specified name.
*
* @param $timer_name
*/
static function startTimer($timer_name)
{
self::$timers[$timer_name] = microtime(true);
}
/**
* End a timer with the specified name, logging total time consumed.
*
* @param $timer_name
*/
static function endTimer($timer_name)
{
$start_time = (isset(self::$timers[$timer_name])) ? self::$timers[$timer_name] : microtime(true);
$end_time = microtime(true);
$time_diff = $end_time - $start_time;
self::log('Timer "'.$timer_name.'" completed in '.round($time_diff, 3).' second(s).');
unset(self::$timers[$timer_name]);
}
/**
* Surrounds the specified callable function with a timer for observation.
*
* @param $timer_name
* @param callable $timed_function
*/
static function runTimer($timer_name, callable $timed_function)
{
self::startTimer($timer_name);
$timed_function();
self::endTimer($timer_name);
}
}

View File

@ -1,101 +0,0 @@
<?php
/**
* Doctrine/App Cache Connector
*/
namespace App\Doctrine;
class Cache extends \Doctrine\Common\Cache\CacheProvider
{
/**
* @var \Stash\Pool
*/
protected $_cache;
public function __construct($cache_driver)
{
$pool = new \Stash\Pool($cache_driver);
$pool->setNamespace(\App\Cache::getSitePrefix('doctrine'));
$this->_cache = $pool;
}
protected function doFetch($id, $testCacheValidity = true)
{
$id = $this->_filterCacheId($id);
$item = $this->_cache->getItem($id);
if (!$testCacheValidity || !$item->isMiss())
return $item->get();
else
return FALSE;
}
protected function doContains($id)
{
$id = $this->_filterCacheId($id);
$item = $this->_cache->getItem($id);
return !$item->isMiss();
}
protected function doSave($id, $data, $lifeTime = NULL)
{
if ($lifeTime == 0 || $lifeTime == NULL)
$lifeTime = 3600;
$id = $this->_filterCacheId($id);
$item = $this->_cache->getItem($id);
return $item->set($data, $lifeTime);
}
protected function doDelete($id)
{
$id = $this->_filterCacheId($id);
$item = $this->_cache->getItem($id);
return $item->clear();
}
protected function doGetStats()
{
return null;
}
protected function doFlush()
{
$this->_cache->flush();
}
public function getIds()
{
return null;
/*
$all_keys = $this->_cache->queryKeys();
if (!$this->getNamespace())
{
return $all_keys;
}
else
{
$relevant_keys = array();
foreach((array)$all_keys as $key_name => $key_value)
{
if (strpos($key_name, $this->getNamespace()) === 0)
{
$filtered_name = str_replace($this->getNamespace().'_', '', $key_name);
$relevant_keys[$filtered_name] = $key_value;
}
}
return $relevant_keys;
}
*/
}
protected function _filterCacheId($id)
{
return preg_replace("/[^a-zA-Z0-9_]/", "", $id);
}
}

View File

@ -1,431 +0,0 @@
<?php
namespace App\Doctrine;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\EntityManagerInterface;
class Entity implements \ArrayAccess
{
/**
* Magic methods.
*/
public function __get($key)
{
$method_name = $this->_getMethodName($key, 'get');
if (method_exists($this, $method_name))
return $this->$method_name();
else
return $this->_getVar($key);
}
public function __set($key, $value)
{
$method_name = $this->_getMethodName($key, 'set');
if (method_exists($this, $method_name))
return $this->$method_name($value);
else
return $this->_setVar($key, $value);
}
public function __isset($key)
{
$method_name = $this->_getMethodName($key, 'get');
if (method_exists($this, $method_name))
return $this->$method_name();
else
return property_exists($this, $key);
}
public function __call($method, $arguments)
{
if (substr($method, 0, 3) == "get") {
$var = $this->_getVarName(substr($method, 3));
return $this->_getVar($var);
}
else if (substr($method, 0, 3) == "set") {
$var = $this->_getVarName(substr($method, 3));
$this->_setVar($var, $arguments[0]);
return $this;
}
return null;
}
protected function _getVar($var)
{
if (property_exists($this, $var))
return $this->$var;
else
return NULL;
}
protected function _setVar($var, $value)
{
if (property_exists($this, $var))
$this->$var = $value;
return $this;
}
// Converts "varNameBlah" to "var_name_blah".
protected function _getVarName($var)
{
return strtolower(preg_replace('~(?<=\\w)([A-Z])~', '_$1', $var));
}
// Converts "getvar_name_blah" to "getVarNameBlah".
protected function _getMethodName($var, $prefix = '')
{
return $prefix.str_replace(" ", "", ucwords(strtr($var, "_-", " ")));
}
/**
* ArrayAccess implementation
*/
public function offsetExists($offset)
{
return property_exists($this, $offset);
}
public function offsetSet($key, $value)
{
$method_name = $this->_getMethodName($key, 'set');
if (method_exists($this, $method_name))
return $this->$method_name($value);
else
return $this->_setVar($key, $value);
}
public function offsetGet($key)
{
$method_name = $this->_getMethodName($key, 'get');
if (method_exists($this, $method_name))
return $this->$method_name();
else
return $this->_getVar($key);
}
public function offsetUnset($offset)
{
if (property_exists($this, $offset))
unset($this->$offset);
}
/**
* FromArray (A Doctrine 1 Classic)
*
* @param EntityManagerInterface $em
* @param $source
* @return $this
*/
public function fromArray(EntityManager $em, $source)
{
$metadata = self::getMetadata($em);
$meta = $metadata['meta'];
$mappings = $metadata['mappings'];
foreach((array)$source as $field => $value)
{
if (isset($mappings[$field]))
{
$mapping = $mappings[$field];
switch($mapping['type'])
{
case "one_id":
$entity_field = $mapping['name'];
$entity_id = $mapping['ids'][0];
if (empty($value))
{
$this->$field = NULL;
$this->$entity_field = NULL;
}
else if ($value != $this->$field)
{
$obj_class = $mapping['entity'];
$obj = $em->getRepository($obj_class)->find($value);
if ($obj instanceof $obj_class)
{
$this->$field = $obj->$entity_id;
$this->$entity_field = $obj;
}
}
break;
case "one_entity":
$entity_id = $mapping['ids'][0];
$id_field = $mapping['name'];
if (empty($value))
{
$this->$field = NULL;
$this->$id_field = NULL;
}
else if ($value->$entity_id != $this->$field)
{
$this->$field = $value;
$this->$id_field = $value->$entity_id;
}
break;
case "many":
$obj_class = $mapping['entity'];
if ($mapping['is_owning_side'])
{
$this->$field->clear();
if ($value)
{
foreach((array)$value as $field_id)
{
if(($field_item = $em->getRepository($obj_class)->find((int)$field_id)) instanceof $obj_class)
{
$this->$field->add($field_item);
}
}
}
}
else
{
$foreign_field = $mapping['mappedBy'];
if (count($this->$field) > 0)
{
foreach($this->$field as $record)
{
$record->$foreign_field->removeElement($this);
$em->persist($record);
}
}
foreach((array)$value as $field_id)
{
$record = $em->getRepository($obj_class)->find((int)$field_id);
if($record instanceof $obj_class)
{
$record->$foreign_field->add($this);
$em->persist($record);
}
}
$em->flush();
}
break;
}
}
else
{
if (!isset($meta->fieldMappings[$field]))
$field_info = array();
else
$field_info = $meta->fieldMappings[$field];
switch($field_info['type'])
{
case "datetime":
case "date":
if (!($value instanceof \DateTime))
{
if ($value)
{
if (!is_numeric($value))
$value = strtotime($value.' UTC');
$value = \DateTime::createFromFormat(\DateTime::ISO8601, gmdate(\DateTime::ISO8601, (int)$value));
}
else
{
$value = NULL;
}
}
break;
case "string":
if ($field_info['length'] && strlen($value) > $field_info['length'])
$value = substr($value, 0, $field_info['length']);
break;
case "decimal":
case "float":
if ($value !== NULL && !is_float($value))
$value = (float)$value;
break;
case "integer":
case "smallint":
case "bigint":
if ($value !== NULL)
$value = (int)$value;
break;
case "boolean":
if ($value !== NULL)
$value = (bool)$value;
break;
}
$this->__set($field, $value);
}
}
return $this;
}
/**
* ToArray (A Doctrine 1 Classic)
*
* @param EntityManagerInterface $em
* @param bool $deep Iterate through collections associated with this item.
* @param bool $form_mode Return values in a format suitable for ZendForm setDefault function.
* @return array
*/
public function toArray(EntityManager $em, $deep = FALSE, $form_mode = FALSE)
{
$return_arr = array();
$class_meta = $em->getClassMetadata(get_called_class());
$reflect = new \ReflectionClass($this);
$props = $reflect->getProperties(\ReflectionProperty::IS_PUBLIC | \ReflectionProperty::IS_PROTECTED);
if ($props)
{
foreach($props as $property)
{
$property->setAccessible(true);
$prop_name = $property->getName();
$prop_val = $property->getValue($this);
if (isset($class_meta->fieldMappings[$prop_name]))
$prop_info = $class_meta->fieldMappings[$prop_name];
else
$prop_info = array();
if (is_array($prop_val))
{
$return_arr[$prop_name] = $prop_val;
}
else if (!is_object($prop_val))
{
if ($prop_info['type'] == "array")
$return_arr[$prop_name] = (array)$prop_val;
else
$return_arr[$prop_name] = (string)$prop_val;
}
else if ($prop_val instanceof \DateTime)
{
$return_arr[$prop_name] = $prop_val->getTimestamp();
}
else if ($deep)
{
if ($prop_val instanceof \Doctrine\Common\Collections\Collection)
{
$return_val = array();
if (count($prop_val) > 0)
{
foreach($prop_val as $val_obj)
{
if ($form_mode)
{
$obj_meta = $em->getClassMetadata(get_class($val_obj));
$id_field = $obj_meta->identifier;
if ($id_field && count($id_field) == 1)
$return_val[] = $val_obj->{$id_field[0]};
}
else
{
$return_val[] = $val_obj->toArray($em, FALSE);
}
}
}
$return_arr[$prop_name] = $return_val;
}
else
{
$return_arr[$prop_name] = $prop_val->toArray($em, FALSE);
}
}
}
}
return $return_arr;
}
/**
* Internal function for pulling metadata, used in toArray and fromArray
*
* @param null $class
* @return array
*/
public static function getMetadata(EntityManager $em, $class = null)
{
if ($class === null)
$class = get_called_class();
$meta_result = array();
$meta_result['em'] = $em;
$meta_result['factory'] = $em->getMetadataFactory();
$meta_result['meta'] = $meta_result['factory']->getMetadataFor($class);
$meta_result['mappings'] = array();
if ($meta_result['meta']->associationMappings)
{
foreach ($meta_result['meta']->associationMappings as $mapping_name => $mapping_info)
{
$entity = $mapping_info['targetEntity'];
if (isset($mapping_info['joinTable']))
{
$meta_result['mappings'][$mapping_info['fieldName']] = array(
'type' => 'many',
'entity' => $entity,
'is_owning_side' => ($mapping_info['isOwningSide'] == 1),
'mappedBy' => $mapping_info['mappedBy'],
);
}
else
{
if (isset($mapping_info['joinColumns']))
{
foreach ($mapping_info['joinColumns'] as $col)
{
$join_meta = $meta_result['factory']->getMetadataFor($entity);
$join_ids = $join_meta->getIdentifierFieldNames();
$col_name = $col['name'];
$col_name = (isset($meta_result['meta']->fieldNames[$col_name])) ? $meta_result['meta']->fieldNames[$col_name] : $col_name;
$meta_result['mappings'][$col_name] = array(
'name' => $mapping_name,
'type' => 'one_id',
'entity' => $entity,
'ids' => $join_ids,
);
$meta_result['mappings'][$mapping_name] = array(
'name' => $col_name,
'type' => 'one_entity',
'entity' => $entity,
'ids' => $join_ids,
);
}
}
}
}
}
return $meta_result;
}
}

View File

@ -1,82 +0,0 @@
<?php
namespace App\Doctrine;
use Doctrine\DBAL\Types\Type;
use Interop\Container\ContainerInterface;
class EntityManagerFactory
{
public static function create(ContainerInterface $di, $options)
{
if(empty($options))
return false;
// Register custom data types.
if (!Type::hasType('json'))
Type::addType('json', 'App\Doctrine\Type\Json');
if (!Type::hasType('unixdatetime'))
Type::addType('unixdatetime', 'App\Doctrine\Type\UnixDateTime');
Type::overrideType('array', 'App\Doctrine\Type\SoftArray');
Type::overrideType('datetime', 'App\Doctrine\Type\UTCDateTime');
// Fetch and store entity manager.
$config = new \Doctrine\ORM\Configuration;
// Handling for class names specified as platform types.
if (!empty($options['conn']['platform']))
{
$class_obj = new \ReflectionClass($options['conn']['platform']);
$options['conn']['platform'] = $class_obj->newInstance();
}
// Special handling for the utf8mb4 type.
if ($options['conn']['driver'] == 'pdo_mysql' && $options['conn']['charset'] == 'utf8mb4')
{
$options['conn']['platform'] = new \App\Doctrine\Platform\MysqlUnicode;
}
$metadata_driver = $config->newDefaultAnnotationDriver($options['modelPath']);
$config->setMetadataDriverImpl($metadata_driver);
$cache = new \App\Doctrine\Cache($di['cache_driver']);
$config->setMetadataCacheImpl($cache);
$config->setQueryCacheImpl($cache);
$config->setResultCacheImpl($cache);
$config->setProxyDir($options['proxyPath']);
$config->setProxyNamespace($options['proxyNamespace']);
$config->setDefaultRepositoryClassName('\App\Doctrine\Repository');
if (isset($options['conn']['debug']) && $options['conn']['debug'])
$config->setSQLLogger(new \App\Doctrine\Logger\EchoSQL);
$config->addFilter('softdelete', '\App\Doctrine\Filter\SoftDelete');
$config->addCustomNumericFunction('RAND', '\App\Doctrine\Functions\Rand');
$config->addCustomStringFunction('FIELD', 'DoctrineExtensions\Query\Mysql\Field');
$config->addCustomStringFunction('IF', 'DoctrineExtensions\Query\Mysql\IfElse');
$evm = new \Doctrine\Common\EventManager();
$em = \Doctrine\ORM\EntityManager::create($options['conn'], $config, $evm);
$em->getFilters()->enable("softdelete");
// Workaround to allow ENUM types to exist as strings in Doctrine.
$conn = $em->getConnection();
$platform = $conn->getDatabasePlatform();
$platform->registerDoctrineTypeMapping('enum', 'string');
$platform->markDoctrineTypeCommented('json');
$platform->markDoctrineTypeCommented('unixdatetime');
$platform->markDoctrineTypeCommented('binary_uuid');
$platform->markDoctrineTypeCommented('ip_integer');
$conn->connect();
return $em;
}
}

View File

@ -1,27 +0,0 @@
<?php
namespace App\Doctrine\Filter;
use Doctrine\ORM\Mapping\ClassMetadata,
Doctrine\ORM\Query\Filter\SQLFilter;
class SoftDelete extends SQLFilter
{
public function addFilterConstraint(ClassMetadata $targetEntity, $targetTableAlias)
{
if (!isset($targetEntity->fieldMappings['deleted_at']))
return '';
// Check for whether filter is being called from within a proxy and exempt from filter if so.
$has_proxy = FALSE;
$backtrace = debug_backtrace();
foreach($backtrace as $log)
{
if (stristr($log['class'], 'Proxy') !== FALSE)
$has_proxy = TRUE;
}
if ($has_proxy)
return '';
else
return $targetTableAlias.'.deleted_at IS NULL';
}
}

View File

@ -1,26 +0,0 @@
<?php
namespace App\Doctrine\Functions;
use \Doctrine\ORM\Query\AST\Functions\FunctionNode;
use \Doctrine\ORM\Query\SqlWalker;
use \Doctrine\ORM\Query\Parser;
use \Doctrine\ORM\Query\Lexer;
/**
* RandFunction ::= "RAND" "(" ")"
*/
class Rand extends FunctionNode
{
public function parse(Parser $parser)
{
$parser->match(Lexer::T_IDENTIFIER);
$parser->match(Lexer::T_OPEN_PARENTHESIS);
$parser->match(Lexer::T_CLOSE_PARENTHESIS);
}
public function getSql(SqlWalker $sqlWalker)
{
return 'RAND()';
}
}

View File

@ -1,34 +0,0 @@
<?php
namespace App\Doctrine\Logger;
use App\Debug;
class EchoSQL extends \Doctrine\DBAL\Logging\EchoSQLLogger
{
public function startQuery($sql, array $params = null, array $types = null)
{
static $is_started;
if (!$is_started)
{
Debug::setEchoMode();
$is_started = true;
}
Debug::log($sql);
if ($params)
Debug::print_r($params);
if ($types)
Debug::print_r($types);
$memory = memory_get_usage();
$mb = round($memory / (1024 * 1024), 4).'M';
Debug::log('Memory: '.$mb.'/'.ini_get('memory_limit'));
}
public function stopQuery()
{}
}

View File

@ -1,71 +0,0 @@
<?php
/**
* DoctrineExtensions Paginate
*
* LICENSE
*
* This source file is subject to the new BSD license that is bundled
* with this package in the file LICENSE.txt.
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to kontakt@beberlei.de so I can send you a copy immediately.
*/
namespace App\Doctrine\Paginate;
use Doctrine\ORM\Query\TreeWalkerAdapter,
Doctrine\ORM\Query\AST\SelectStatement,
Doctrine\ORM\Query\AST\SelectExpression,
Doctrine\ORM\Query\AST\PathExpression,
Doctrine\ORM\Query\AST\AggregateExpression;
class CountWalker extends TreeWalkerAdapter
{
/**
* Walks down a SelectStatement AST node, modifying it to retrieve a COUNT
*
* @param SelectStatement $AST
* @return void
*/
public function walkSelectStatement(SelectStatement $AST)
{
$parent = null;
$parentName = null;
foreach ($this->_getQueryComponents() AS $dqlAlias => $qComp)
{
// skip mixed data in query
if (isset($qComp['resultVariable']))
{
continue;
}
if ($qComp['parent'] === null && $qComp['nestingLevel'] == 0)
{
$parent = $qComp;
$parentName = $dqlAlias;
break;
}
}
$pathExpression = new PathExpression(
PathExpression::TYPE_STATE_FIELD | PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION, $parentName,
$parent['metadata']->getSingleIdentifierFieldName()
);
$pathExpression->type = PathExpression::TYPE_STATE_FIELD;
$AST->selectClause->selectExpressions = array(
new SelectExpression(
new AggregateExpression('count', $pathExpression, true), null
)
);
// ORDER BY is not needed, only increases query execution through unnecessary sorting.
$AST->orderByClause = null;
// GROUP BY will break things, we are trying to get a count of all
$AST->groupByClause = null;
}
}

View File

@ -1,20 +0,0 @@
<?php
/**
* Custom MySQL platform that supports utf8mb4 charset and encoding by default for tables.
*/
namespace App\Doctrine\Platform;
class MysqlUnicode extends \Doctrine\DBAL\Platforms\MySqlPlatform
{
protected function _getCreateTableSQL($tableName, array $columns, array $options = array())
{
if (!isset($options['charset']))
$options['charset'] = 'utf8mb4';
if (!isset($options['collate']))
$options['collate'] = 'utf8mb4_unicode_ci';
return parent::_getCreateTableSQL($tableName, $columns, $options);
}
}

View File

@ -1,65 +0,0 @@
<?php
namespace App\Doctrine;
use Doctrine\ORM\EntityRepository;
class Repository extends EntityRepository
{
/**
* Generate an array result of all records.
*
* @param bool $cached
* @param null $order_by
* @param string $order_dir
* @return array
*/
public function fetchArray($cached = true, $order_by = NULL, $order_dir = 'ASC')
{
$qb = $this->_em->createQueryBuilder()
->select('e')
->from($this->_entityName, 'e');
if ($order_by)
$qb->orderBy('e.'.str_replace('e.', '', $order_by), $order_dir);
return $qb->getQuery()->getArrayResult();
}
/**
* Generic dropdown builder function (can be overridden for specialized use cases).
*
* @param bool $add_blank
* @param \Closure|NULL $display
* @param string $pk
* @param string $order_by
* @return array
*/
public function fetchSelect($add_blank = FALSE, \Closure $display = NULL, $pk = 'id', $order_by = 'name')
{
$select = array();
// Specify custom text in the $add_blank parameter to override.
if ($add_blank !== FALSE)
$select[''] = ($add_blank === TRUE) ? 'Select...' : $add_blank;
// Build query for records.
$qb = $this->_em->createQueryBuilder()->from($this->_entityName, 'e');
if ($display === NULL)
$qb->select('e.'.$pk)->addSelect('e.name')->orderBy('e.'.$order_by, 'ASC');
else
$qb->select('e')->orderBy('e.'.$order_by, 'ASC');
$results = $qb->getQuery()->getArrayResult();
// Assemble select values and, if necessary, call $display callback.
foreach((array)$results as $result)
{
$key = $result[$pk];
$value = ($display === NULL) ? $result['name'] : $display($result);
$select[$key] = $value;
}
return $select;
}
}

View File

@ -1,33 +0,0 @@
<?php
namespace App\Doctrine\Type;
use Doctrine\DBAL\Types\ArrayType;
use Doctrine\DBAL\Platforms\AbstractPlatform;
/**
* My custom datatype.
*/
class Json extends ArrayType
{
const TYPENAME = 'json';
public function convertToDatabaseValue($value, AbstractPlatform $platform)
{
return json_encode($value);
}
public function convertToPHPValue($value, AbstractPlatform $platform)
{
if ($value === null)
return null;
$value = (is_resource($value)) ? stream_get_contents($value, -1) : $value;
return json_decode((string)$value, 1);
}
public function getName()
{
return self::TYPENAME;
}
}

View File

@ -1,29 +0,0 @@
<?php
namespace App\Doctrine\Type;
use Doctrine\DBAL\Types\ArrayType;
use Doctrine\DBAL\Platforms\AbstractPlatform;
/**
* "Soft Array" datatype - same as Array, but with silent failure.
*/
class SoftArray extends ArrayType
{
const TYPENAME = 'array';
public function convertToPHPValue($value, \Doctrine\DBAL\Platforms\AbstractPlatform $platform)
{
if ($value === null)
return null;
$value = (is_resource($value)) ? stream_get_contents($value) : $value;
$val = @unserialize($value);
return $val;
}
public function getName()
{
return self::TYPENAME;
}
}

View File

@ -1,41 +0,0 @@
<?php
namespace App\Doctrine\Type;
use \Doctrine\DBAL\Types\DateTimeType;
use \Doctrine\DBAL\Platforms\AbstractPlatform;
use \Doctrine\DBAL\Types\ConversionException;
class UTCDateTime extends DateTimeType
{
static private $utc = null;
public function convertToDatabaseValue($value, AbstractPlatform $platform)
{
if ($value === null) {
return null;
}
$utc = (self::$utc) ? self::$utc : (self::$utc = new \DateTimeZone('UTC'));
$value->setTimezone($utc);
return $value->format($platform->getDateTimeFormatString());
}
public function convertToPHPValue($value, AbstractPlatform $platform)
{
if ($value === null) {
return null;
}
$val = \DateTime::createFromFormat(
$platform->getDateTimeFormatString(),
$value,
(self::$utc) ? self::$utc : (self::$utc = new \DateTimeZone('UTC'))
);
if (!$val) {
throw ConversionException::conversionFailed($value, $this->getName());
}
return $val;
}
}

View File

@ -1,39 +0,0 @@
<?php
namespace App\Doctrine\Type;
use Doctrine\DBAL\Types\IntegerType;
use Doctrine\DBAL\Types\Type;
use Doctrine\DBAL\Platforms\AbstractPlatform;
/**
* "UNIX Timestamp Date/Time" datatype - same as DateTime, but stored as an integer (for BC)
*/
class UnixDateTime extends IntegerType
{
const UNIX_DATETIME = 'unixdatetime';
public function getName()
{
return self::UNIX_DATETIME;
}
public function convertToDatabaseValue($value, AbstractPlatform $platform)
{
if ($value !== NULL)
{
if ($value instanceof \DateTime)
return $value->getTimestamp();
else
return (int)$value;
}
return NULL;
}
public function convertToPHPValue($value, AbstractPlatform $platform)
{
if ((int)$value)
return \DateTime::createFromFormat(\DateTime::ISO8601, date(\DateTime::ISO8601, (int)$value));
else
return NULL;
}
}

View File

@ -1,3 +0,0 @@
<?php
namespace App;
class Exception extends \Exception {}

View File

@ -1,7 +0,0 @@
<?php
namespace App\Exception;
class Bootstrap extends \App\Exception
{
}

View File

@ -1,3 +0,0 @@
<?php
namespace App\Exception;
class NotLoggedIn extends \Exception {}

View File

@ -1,7 +0,0 @@
<?php
/**
* Permission Denied exception
*/
namespace App\Exception;
class PermissionDenied extends \Exception {}

View File

@ -1,136 +0,0 @@
<?php
/**
* Class with static methods for exporting data into various formats.
*/
namespace App;
class Export
{
/**
* Generate a CSV-compatible file body given an array.
*
* @param $table_data
* @param bool $headers_first_row
* @return string
*/
public static function csv($table_data, $headers_first_row = true)
{
$final_display = [];
$row_count = 0;
foreach ($table_data as $table_row)
{
$row_count++;
$col_count = 0;
$header_row = [];
$body_row = [];
foreach ($table_row as $table_col => $table_val)
{
$col_count++;
if (!$headers_first_row && $row_count == 1)
$header_row[] = '"' . str_replace('"', '""', $table_col) . '"';
$body_row[] = '"' . str_replace('"', '""', $table_val) . '"';
}
if ($header_row)
$final_display[] = implode(',', $header_row);
if ($body_row)
$final_display[] = implode(',', $body_row);
}
return implode("\n", $final_display);
}
/**
* Convert from an XML string into a PHP array.
*
* @param $xml
* @return array
*/
public static function xml_to_array($xml)
{
$values = $index = $array = array();
$parser = xml_parser_create();
xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 1);
xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0);
xml_parse_into_struct($parser, $xml, $values, $index);
xml_parser_free($parser);
$i = 0;
$name = $values[$i]['tag'];
$array[$name] = isset($values[$i]['attributes']) ? $values[$i]['attributes'] : '';
$array[$name] = self::_struct_to_array($values, $i);
return $array;
}
protected static function _struct_to_array($values, &$i)
{
$child = array();
if (isset($values[$i]['value'])) array_push($child, $values[$i]['value']);
while ($i++ < count($values)) {
switch ($values[$i]['type']) {
case 'cdata':
array_push($child, $values[$i]['value']);
break;
case 'complete':
$name = $values[$i]['tag'];
if(!empty($name)){
$child[$name]= ($values[$i]['value'])?($values[$i]['value']):'';
if(isset($values[$i]['attributes'])) {
$child[$name] = $values[$i]['attributes'];
}
}
break;
case 'open':
$name = $values[$i]['tag'];
$size = isset($child[$name]) ? sizeof($child[$name]) : 0;
$child[$name][$size] = self::_struct_to_array($values, $i);
break;
case 'close':
return $child;
break;
}
}
return $child;
}
/**
* Convert a PHP array into an XML string.
*
* @param $array
* @return mixed
*/
public static function array_to_xml($array)
{
$xml_info = new \SimpleXMLElement('<?xml version="1.0"?><return></return>');
self::_arr_to_xml($array, $xml_info);
return $xml_info->asXML();
}
protected static function _arr_to_xml($array, &$xml)
{
foreach((array)$array as $key => $value)
{
if(is_array($value))
{
$key = is_numeric($key) ? "item$key" : $key;
$subnode = $xml->addChild("$key");
self::_arr_to_xml($value, $subnode);
}
else
{
$key = is_numeric($key) ? "item$key" : $key;
$xml->addChild("$key", htmlspecialchars($value));
}
}
}
}

View File

@ -1,281 +0,0 @@
<?php
/**
* Static class that facilitates the uploading, reading and deletion of files in a controlled directory.
*/
namespace App;
use Psr\Http\Message\UploadedFileInterface;
class File
{
/**
* @var string The file's name (the portion after the base directory).
*/
protected $name;
/**
* @var string The base directory in which the file is uploaded.
*/
protected $base_dir;
public function __construct($file_name, $base_dir = null)
{
$this->name = $file_name;
$this->base_dir = $base_dir ?: APP_UPLOAD_FOLDER;
}
/**
* @param $file_name
*/
public function setName($file_name)
{
$this->name = $file_name;
}
/**
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* Add a suffix to a file *before* its extension.
*
* @param $suffix
* @return $this
*/
public function addSuffix($suffix)
{
$file_parts = pathinfo($this->name);
$new_file_name = $file_parts['filename'].$suffix.'.'.$file_parts['extension'];
if ($file_parts['dirname'] != '.')
$this->name = $file_parts['dirname'].DIRECTORY_SEPARATOR.$new_file_name;
else
$this->name = $new_file_name;
return $this;
}
/**
* Get the file's extension.
*
* @return string
*/
public function getExtension()
{
// Significantly more performant than using pathinfo function.
return substr($this->name, strrpos($this->name, '.')+1);
}
/**
* Return the full path of the file.
*
* @return string
*/
public function getPath()
{
$file_name = trim($this->name);
$file_name = ltrim($file_name, '/');
$file_name = str_replace('/', DIRECTORY_SEPARATOR, $file_name);
return $this->base_dir.DIRECTORY_SEPARATOR.$file_name;
}
/**
* Check if an uploaded file (from the $_FILES array) is valid.
*
* @param $uploaded_file
* @return bool
*/
public function isValid($uploaded_file)
{
return (!empty($uploaded_file) && $uploaded_file['error'] == UPLOAD_ERR_OK);
}
/**
* Attempt to move an uploaded file to the file name specified by the object.
*
* @param $uploaded_file
* @return bool
* @throws Exception
*/
public function upload($uploaded_file)
{
if ($uploaded_file instanceof UploadedFileInterface)
return $uploaded_file->moveTo($this->getPath());
if (!$this->isValid($uploaded_file))
{
switch($uploaded_file['error'])
{
case UPLOAD_ERR_INI_SIZE:
throw new Exception('File Upload Error: The file you are attempting to upload is larger than allowed (upload_max_filesize).');
break;
case UPLOAD_ERR_FORM_SIZE:
throw new Exception('File Upload Error: The file you are attempting to upload is larger than allowed (MAX_FILE_SIZE).');
break;
case UPLOAD_ERR_PARTIAL:
throw new Exception('File Upload Error: The file you are attempting to upload was only partially uploaded.');
break;
case UPLOAD_ERR_NO_FILE:
throw new Exception('File Upload Error: No file was uploaded.');
break;
case UPLOAD_ERR_NO_TMP_DIR:
throw new Exception('File Upload Error: Missing a temporary folder.');
break;
case UPLOAD_ERR_CANT_WRITE:
throw new Exception('File Upload Error: Failed to write file to disk.');
break;
case UPLOAD_ERR_EXTENSION:
throw new Exception('File Upload Error: Upload stopped by extension.');
break;
default:
throw new Exception('File Upload Error: No file was specified.');
break;
}
}
if (move_uploaded_file($uploaded_file['tmp_name'], $this->getPath()))
return true;
else
throw new Exception('File Upload Error: Could not upload the file requested.');
}
/**
* Create the specified file containing a string passed to the function.
* Passes flags on to file_put_contents.
*
* @param $file_data
* @param null $flags
* @return $this
*/
public function putContents($file_data, $flags = null)
{
file_put_contents($this->getPath(), $file_data, $flags);
return $this;
}
/**
* Return the file's contents as a string.
*
* @param $file_name
* @return string
*/
public function getContents()
{
return file_get_contents($this->getPath());
}
/**
* Get a fopen resource pointer to the file.
*
* @param string $mode
* @return resource
*/
public function getPointer($mode = 'r')
{
return fopen($this->getPath(), $mode);
}
/**
* Get raw CSV data from a file.
*
* @return array
*/
public function getCsv()
{
@ini_set('auto_detect_line_endings', 1);
$csv_data = array();
$handle = $this->getPointer();
while (($data = fgetcsv($handle)) !== FALSE)
{
$csv_data[] = $data;
}
fclose($handle);
return $csv_data;
}
/**
* Returns a "clean" array with the first row's text as the column names.
*
* @return array
*/
public function getCleanCsv()
{
$csv_data = $this->getCsv();
$clean_data = array();
if ($csv_data)
{
$headers = array();
$row_num = 0;
$col_num = 0;
$header_row = array_shift($csv_data);
foreach($header_row as $csv_col)
{
$field_name = strtolower(preg_replace("/[^a-zA-Z0-9_]/", "", $csv_col));
if (!empty($field_name))
$headers[$col_num] = $field_name;
$col_num++;
}
foreach($csv_data as $csv_row)
{
$col_num = 0;
$clean_row = array();
foreach($csv_row as $csv_col)
{
$col_name = (isset($headers[$col_num])) ? $headers[$col_num] : $col_num;
$clean_row[$col_name] = $csv_col;
$col_num++;
}
$clean_data[] = $clean_row;
$row_num++;
}
}
return $clean_data;
}
/**
* Rename the file to the new name specified, preserving the base directory.
*
* @param $new_name
* @return $this
*/
public function rename($new_name)
{
$old_path = $this->getPath();
$this->setName($new_name);
$new_path = $this->getPath();
if (file_exists($old_path))
rename($old_path, $new_path);
return $this;
}
/**
* Delete the file.
*/
public function delete()
{
unlink($this->getPath());
}
}

View File

@ -1,93 +0,0 @@
<?php
/**
* Quick message queue service.
*/
namespace App;
class Flash
{
const SUCCESS = 'success';
const WARNING = 'warning';
const ERROR = 'error';
const INFO = 'info';
protected $messages = array();
/**
* @var Session\Instance
*/
protected $_session;
public function __construct(Session $session)
{
$this->_session = $session->get('alerts');
// Load any previously saved messages.
$this->messages = (array)$this->_session->messages;
unset($this->_session->messages);
}
/**
* Add a message to the flash message queue.
*
* @param $message
* @param string $level
* @param bool|false $save_in_session
*/
public function addMessage($message, $level = self::INFO, $save_in_session = true)
{
$color_chart = array(
'green' => 'success',
'success' => 'success',
'yellow' => 'warning',
'warning' => 'warning',
'red' => 'danger',
'error' => 'danger',
'info' => 'info',
'blue' => 'info',
'default' => '',
);
$message_row = array(
'text' => $message,
'color' => (isset($color_chart[$level])) ? $color_chart[$level] : $color_chart['default'],
);
$message_hash = md5(json_encode($message_row));
$this->messages[$message_hash] = $message_row;
if ($save_in_session)
{
$messages = $this->_session->messages;
$messages[$message_hash] = $message_row;
$this->_session->messages = $messages;
}
}
/**
* Indicate whether messages are currently pending display.
*
* @return bool
*/
public function hasMessages()
{
return (count($this->messages) > 0);
}
/**
* Return all messages, removing them from the internal storage in the process.
*
* @return array|bool
*/
public function getMessages()
{
$messages = $this->messages;
$this->messages = array();
unset($this->_session->messages);
return (count($messages) > 0) ? $messages : false;
}
}

View File

@ -1,216 +0,0 @@
<?php
namespace App;
use App\Forms\NibbleForm;
/**
* A helper class that extends allows flatfile configuration form management.
*
* Class Form
* @package App
*/
class Form
{
/**
* @var Forms\NibbleForm
*/
protected $form;
/**
* @var array
*/
protected $options;
/**
* @var array An array of "belongsTo" lookup groups (for processing data).
*/
protected $groups;
/**
* Form constructor.
* @param $options
*/
public function __construct($options = [])
{
if ($options instanceof \Zend\Config\Config)
$options = $options->toArray();
// Clean up options.
$this->groups = [];
$this->options = $this->_cleanUpConfig($options);
$form_name = $options['name'] ?: 'app_form';
$form_action = $options['action'] ?: '';
$this->form = new NibbleForm($form_action);
$this->form->setName($form_name);
$this->_setUpForm();
}
public function getOptions()
{
return $this->options;
}
public function getForm()
{
return $this->form;
}
public function setDefaults($data)
{
$this->populate($data);
}
public function populate($data)
{
$set_data = [];
foreach((array)$data as $row_key => $row_value)
{
if (is_array($row_value) && isset($this->groups[$row_key]))
{
foreach($row_value as $row_subkey => $row_subvalue)
$set_data[$row_key.'_'.$row_subkey] = $row_subvalue;
}
else
{
$set_data[$row_key] = $row_value;
}
}
foreach($set_data as $field_name => $field_value)
{
if ($this->form->checkField($field_name))
{
$field = $this->form->getField($field_name);
if ($field instanceof \Nibble\NibbleForms\Field\Radio ||
$field instanceof \Nibble\NibbleForms\Field\Checkbox)
{
if ($field_value === "")
$field_value = '0';
}
$set_data[$field_name] = $field_value;
}
}
$this->form->addData($set_data);
}
public function isValid()
{
return $this->form->validate();
}
public function getValues()
{
$values = array();
foreach($this->options['groups'] as $fieldset)
{
foreach($fieldset['elements'] as $element_id => $element_info)
{
if (!empty($element_info[1]['belongsTo']))
{
$group = $element_info[1]['belongsTo'];
$values[$group][$element_id] = $this->form->getData($group.'_'.$element_id);
}
else
{
$values[$element_id] = $this->form->getData($element_id);
}
}
}
return $values;
}
public function getValue($key)
{
return $this->form->getData($key);
}
protected function _cleanUpConfig($options)
{
if (empty($options['groups']))
$options['groups'] = array();
if (!empty($options['elements']))
{
$options['groups'][] = ['elements' => $options['elements']];
unset($options['elements']);
}
// Standardize some field input.
$field_type_lookup = [
'checkboxes' => 'checkbox',
'multicheckbox' => 'checkbox',
'multiselect' => 'multipleSelect',
'textarea' => 'textArea',
];
foreach($options['groups'] as &$group)
{
foreach($group['elements'] as &$element)
{
if (!empty($element[1]['label']) && substr($element[1]['label'], -1) !== ':')
$element[1]['label'] = $element[1]['label'].':';
$element[0] = strtolower($element[0]);
if (isset($field_type_lookup[$element[0]]))
$element[0] = $field_type_lookup[$element[0]];
if (!empty($element[1]['multiOptions']))
$element[1]['choices'] = $element[1]['multiOptions'];
unset($element[1]['multiOptions']);
if (!empty($element[1]['options']))
$element[1]['choices'] = $element[1]['options'];
unset($element[1]['options']);
}
}
return $options;
}
protected function _setUpForm()
{
foreach($this->options['groups'] as $group_id => $group_info)
{
foreach($group_info['elements'] as $element_name => $element_info)
$this->_setUpElement($element_name, $element_info);
}
}
protected function _setUpElement($element_name, $element_info)
{
$field_type = $element_info[0];
$field_options = $element_info[1];
if (!empty($field_options['belongsTo']))
{
$group = $field_options['belongsTo'];
$this->groups[$group][] = $element_name;
$element_name = $group.'_'.$element_name;
}
$defaults = [
'required' => false,
];
$field_options = array_merge($defaults, $field_options);
if ($field_type == 'submit')
return null;
if (isset($field_options['default']))
$this->form->addData([$element_name => (string)$field_options['default']]);
unset($field_options['default']);
unset($field_options['description']);
$this->form->addField($element_name, $field_type, $field_options);
}
}

View File

@ -1,33 +0,0 @@
<?php
namespace App\Forms\Element;
use Nibble\NibbleForms\Field;
class Markup extends Field
{
public $error = array();
protected $label;
protected $markup;
public function __construct($label = 'CAPTCHA', $attributes = array())
{
$this->label = $label;
$this->markup = $attributes['markup'];
}
public function returnField($form_name, $name, $value = '')
{
return array(
'messages' => !empty($this->custom_error) && !empty($this->error) ? $this->custom_error : $this->error,
'label' => $this->label == false ? false : sprintf('<label for="%s">%s</label>', $name, $this->label),
'field' => $this->markup,
'html' => $this->html
);
}
public function validate($val)
{
return true;
}
}

View File

@ -1,49 +0,0 @@
<?php
namespace App\Forms\Element;
use Nibble\NibbleForms\Field;
class Captcha extends Field
{
public $error = array();
protected $label;
protected $attributes;
public function __construct($label = 'CAPTCHA', $attributes = array())
{
$this->label = $label;
$this->attributes = $attributes;
}
public function returnField($form_name, $name, $value = '')
{
$field = <<<FIELD
<script src="https://www.google.com/recaptcha/api.js" async defer></script>';
<div class="g-recaptcha" data-sitekey="%S" data-theme="dark"></div>
FIELD;
$class = !empty($this->error) ? ' class="error"' : '';
return array(
'messages' => !empty($this->custom_error) && !empty($this->error) ? $this->custom_error : $this->error,
'label' => $this->label == false ? false : sprintf('<label for="%s"%s>%s</label>', $name, $class, $this->label),
'field' => sprintf($field, $this->attributes['public_key']),
'html' => $this->html
);
}
public function validate($val)
{
$params = array(
'secret' => $this->attributes['private_key'],
'response' => $val,
'remoteip' => $_SERVER['REMOTE_ADDR']
);
$url = 'https://www.google.com/recaptcha/api/siteverify?' .http_build_query($params);
$response = json_decode(file_get_contents($url));
return $response->success;
}
}

View File

@ -1,70 +0,0 @@
<?php
namespace App\Forms;
class NibbleForm extends \Nibble\NibbleForms\NibbleForm
{
public function __construct(
$action = '',
$submit_value = 'Submit',
$html5 = true,
$method = 'post',
$sticky = true,
$message_type = 'list',
$format = 'list',
$multiple_errors = false
) {
return parent::__construct($action, $submit_value, $html5, $method, $sticky, $message_type, $format, $multiple_errors);
}
/**
* @inheritdoc
*/
public function addField($field_name, $type = 'text', array $attributes = array(), $overwrite = false)
{
$namespace_options = [
"\\App\\Forms\\Element\\" . ucfirst($type),
"\\Nibble\\NibbleForms\\Field\\" . ucfirst($type),
];
foreach($namespace_options as $namespace_option)
{
if (class_exists($namespace_option))
{
$namespace = $namespace_option;
break;
}
}
if (!isset($namespace))
return false;
if (isset($attributes['label']))
$label = $attributes['label'];
else
$label = ucfirst(str_replace('_', ' ', $field_name));
$field_name = \Nibble\NibbleForms\Useful::slugify($field_name, '_');
if (isset($this->fields->$field_name) && !$overwrite)
return false;
$this->fields->$field_name = new $namespace($label, $attributes);
$this->fields->$field_name->setForm($this);
return $this->fields->$field_name;
}
public function getField($key)
{
return $this->fields->$key;
}
public function validate()
{
$request = strtoupper($this->method) == 'POST' ? $_POST : $_GET;
if (isset($request[$this->name]))
$this->data = $request[$this->name];
return parent::validate();
}
}

View File

@ -1,143 +0,0 @@
<?php
/**
* Messenger Class (E-mail Delivery)
*/
namespace App;
use Interop\Container\ContainerInterface;
use Nette\Mail\Message;
use Nette\Mail\SendmailMailer;
use Nette\Mail\SmtpMailer;
class Messenger
{
/**
* @var ContainerInterface
*/
protected $di;
public function __construct(ContainerInterface $di)
{
$this->di = $di;
}
/**
* New messenger method:
* Uses an expandable array of options and supports direct template rendering and subject prepending.
*
* @param array $message_options An array of message options.
* @return bool|void
*/
public function send($message_options)
{
$config = $this->di['config'];
$default_options = array(
'reply_to' => NULL,
'delivery_date' => NULL,
'options' => NULL,
);
$options = array_merge($default_options, $message_options);
// Render the template as the message if a template is specified.
if (isset($options['template']))
{
$vars = (array)$options['vars'];
$vars['subject'] = $options['subject'];
$view = $this->di['view'];
$options->message = $view->fetch($options['template'], $vars);
}
else if (isset($options['body']) && !isset($options['message']))
{
$options['message'] = $options['body'];
unset($options['body']);
}
// Append the system name as a prefix to the message.
if (!isset($options['no_prefix']) || $options['no_prefix'] == FALSE)
{
$app_name = $config->application->name;
$options['subject'] = $app_name.': '.$options['subject'];
}
$mail_config = $config->application->mail->toArray();
// Do not deliver mail on development environments.
if (APP_APPLICATION_ENV == "development" && !defined('APP_FORCE_EMAIL'))
{
$email_to = $mail_config['from_addr'];
if (!empty($email_to))
$options['to'] = $email_to;
else
return false;
}
if (isset($mail_config['use_smtp']) && $mail_config['use_smtp'])
{
$smtp_config = $config->apis->smtp->toArray();
$smtp_config['host'] = $smtp_config['server'];
unset($smtp_config['server']);
$transport = new SmtpMailer($smtp_config);
}
else
{
$transport = new SendmailMailer();
}
if (!is_array($options['to']))
$options['to'] = array($options['to']);
else
$options['to'] = array_unique($options['to']);
foreach((array)$options['to'] as $mail_to_addr)
{
if (empty($mail_to_addr))
continue;
$mail_to_addr = str_replace('mailto:', '', $mail_to_addr);
$mail = new Message;
$mail->setSubject($options['subject']);
$from_addr = (isset($options['from'])) ? $options['from'] : $mail_config['from_addr'];
$from_name = (isset($options['from_name'])) ? $options['from_name'] : $mail_config['from_name'];
$mail->setFrom($from_addr, $from_name);
if (isset($mail_config['bounce_addr']))
$mail->setReturnPath($mail_config['bounce_addr']);
// Change the type of the e-mail's body if specified in the options.
if (isset($options['text_only']) && $options['text_only'])
$mail->setBody(strip_tags($options['message']));
else
$mail->setHtmlBody($options['message'], false);
// Add attachment if specified in options.
if (isset($options['attachments']))
{
foreach((array)$options['attachments'] as $attachment)
{
$mail->addAttachment($attachment);
}
}
// Catch invalid e-mails.
try
{
$mail->addTo($mail_to_addr);
}
catch(\Nette\Utils\AssertionException $e)
{
continue;
}
$transport->send($mail);
}
return true;
}
}

View File

@ -1,502 +0,0 @@
<?php
namespace App\Mvc;
use App\Acl;
use App\Config;
use App\Url;
use Doctrine\ORM\EntityManager;
use Interop\Container\ContainerInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Http\Message\ResponseInterface as Response;
use Entity\Settings;
class Controller
{
/** @var ContainerInterface */
protected $di;
/** @var View */
protected $view;
/** @var Url */
protected $url;
/** @var EntityManager */
protected $em;
/** @var Acl */
protected $acl;
/** @var Config */
protected $current_module_config;
protected $module;
protected $controller;
protected $action;
public function __construct(ContainerInterface $di, $module, $controller, $action)
{
$this->di = $di;
$this->current_module_config = $di['current_module_config'] = $di['module_config'][$module];
$this->module = $module;
$this->controller = $controller;
$this->action = $action;
$this->view = $di['view'];
$this->url = $di['url'];
$this->em = $di['em'];
$this->acl = $di['acl'];
$common_views_dir = APP_INCLUDE_MODULES.'/'.$module.'/views/scripts';
if (is_dir($common_views_dir))
{
$this->view->setFolder('common', $common_views_dir);
$controller_views_dir = $common_views_dir.'/'.$controller;
if (is_dir($controller_views_dir))
$this->view->setFolder('controller', $controller_views_dir);
}
}
/** @var Request */
protected $request;
/** @var Response */
protected $response;
/** @var array */
protected $params;
/**
* Handle the MVC-style dispatching of a controller action.
*
* @param Request $request
* @param Response $response
* @param $args
* @return Response
*/
public function dispatch(Request $request, Response $response, $args)
{
$this->request = $request;
$this->response = $response;
$this->params = $args;
$this->url->setCurrentRoute([
'module' => $this->module,
'controller' => $this->controller,
'action' => $this->action,
'params' => $args,
]);
$init_result = $this->init();
if ($init_result instanceof Response)
return $init_result;
$predispatch_result = $this->preDispatch();
if ($predispatch_result instanceof Response)
return $predispatch_result;
$action_name = $this->action.'Action';
$action_result = $this->$action_name();
if ($action_result instanceof Response)
return $action_result;
if (!$this->view->isDisabled())
return $this->render();
return $this->response;
}
public function __get($key)
{
if ($this->di->has($key))
return $this->di->get($key);
else
return null;
}
public function init()
{
$isAllowed = $this->permissions();
if (!$isAllowed)
{
if (!$this->auth->isLoggedIn())
throw new \App\Exception\NotLoggedIn;
else
throw new \App\Exception\PermissionDenied;
}
return null;
}
protected function preDispatch()
{
return true;
}
/**
* Overridable permissions check. Return false to generate "access denied" message.
* @return bool
*/
protected function permissions()
{
return true;
}
/* HTTP Cache Handling */
protected $_cache_privacy = null;
protected $_cache_lifetime = 0;
/**
* Set new HTTP cache "privacy" level, used by intermediate caches.
*
* @param $new_privacy "private" or "public"
*/
public function setCachePrivacy($new_privacy)
{
$this->_cache_privacy = strtolower($new_privacy);
}
/**
* Set new HTTP cache "lifetime", expressed as seconds after current time.
*
* @param $new_lifetime
*/
public function setCacheLifetime($new_lifetime)
{
$this->_cache_lifetime = (int)$new_lifetime;
}
/**
* Internal cache handling after page handling is complete.
*/
protected function handleCache()
{
// Set default caching parameters for pages that do not customize it.
if ($this->_cache_privacy === null)
{
$auth = $this->di->get('auth');
if ($auth->isLoggedIn())
{
$this->_cache_privacy = 'private';
$this->_cache_lifetime = 0;
}
else
{
$this->_cache_privacy = 'public';
$this->_cache_lifetime = 30;
}
}
if ($this->_cache_privacy == 'private')
{
// $this->response->setHeader('Cache-Control', 'must-revalidate, private, max-age=' . $this->_cache_lifetime);
$this->response = $this->response->withHeader('X-Accel-Expires', 'off');
}
else
{
// $this->response->setHeader('Cache-Control', 'public, max-age=' . $this->_cache_lifetime);
$this->response = $this->response->withHeader('X-Accel-Expires', $this->_cache_lifetime);
}
}
/* URL Parameter Handling */
/**
* Retrieve parameter from request.
*
* @param $param_name
* @param null $default_value
* @return mixed|null
*/
public function getParam($param_name, $default_value = NULL)
{
$query_params = $this->request->getQueryParams();
if (isset($this->params[$param_name]))
return $this->params[$param_name];
elseif (isset($query_params[$param_name]))
return $query_params[$param_name];
else
return $default_value;
}
/**
* Detect if parameter is present in request.
*
* @param $param_name
* @return bool
*/
public function hasParam($param_name)
{
return ($this->getParam($param_name) !== null);
}
/**
* Trigger rendering of template.
*
* @param null $template_name
* @return Response
*/
public function render($template_name = NULL, $template_args = array())
{
if ($template_name === null)
$template_name = 'controller::'.$this->action;
$this->response = $this->response->withHeader('Content-type', 'text/html; charset=utf-8');
$template = $this->view->render($template_name, $template_args);
$body = $this->response->getBody();
$body->write($template);
return $this->response->withBody($body);
}
/**
* Render a form using the system's form template.
*
* @param \App\Form $form
* @param string $mode
* @param null $form_title
* @return Response
*/
protected function renderForm(\App\Form $form, $mode = 'edit', $form_title = NULL)
{
if ($form_title)
$this->view->title = $form_title;
$this->view->form = $form;
$this->view->render_mode = $mode;
return $this->render('system/form_page');
}
/**
* Disable rendering of template for this page view.
*/
public function doNotRender()
{
$this->view->disable();
}
/**
* Render the page output as the supplied JSON.
*
* @param $json_data
* @return Response
*/
public function renderJson($json_data)
{
$this->doNotRender();
$body = $this->response->getBody();
$body->write(json_encode($json_data));
return $this->response
->withHeader('Content-type', 'application/json; charset=utf-8')
->withBody($body);
}
/**
* @param string $file_path
* @param string|null $file_name
* @return Response
*/
public function renderFile($file_path, $file_name = null)
{
$this->doNotRender();
set_time_limit(600);
if ($file_name == null)
$file_name = basename($file_path);
$this->response = $this->response
->withHeader('Pragma', 'public')
->withHeader('Expires', '0')
->withHeader('Cache-Control', 'must-revalidate, post-check=0, pre-check=0')
->withHeader('Content-Type', mime_content_type($file_path))
->withHeader('Content-Length', filesize($file_path))
->withHeader('Content-Disposition', 'attachment; filename='.$file_name);
$fh = fopen($file_path, 'rb');
$stream = new \Slim\Http\Stream($fh);
return $this->response->withBody($stream);
}
/**
* @param string $file_data The body of the file contents.
* @param string $content_type The HTTP header content-type (i.e. text/csv)
* @param string|null $file_name
* @return Response
*/
public function renderStringAsFile($file_data, $content_type, $file_name = null)
{
$this->doNotRender();
$this->response = $this->response
->withHeader('Pragma', 'public')
->withHeader('Expires', '0')
->withHeader('Cache-Control', 'must-revalidate, post-check=0, pre-check=0')
->withHeader('Content-Type', $content_type);
if ($file_name !== null)
$this->response = $this->response->withHeader('Content-Disposition', 'attachment; filename='.$file_name);
$body = $this->response->getBody();
$body->write($file_data);
return $this->response->withBody($body);
}
/* URL Redirection */
/**
* Redirect to the URL specified.
*
* @param $new_url
* @param int $code
* @return Response
*/
public function redirect($new_url, $code = 302)
{
$this->doNotRender();
return $this->response->withStatus($code)->withHeader('Location', $new_url);
}
/**
* Redirect to the route specified.
*
* @param $route
* @param int $code
* @return Response
*/
public function redirectToRoute($route, $code = 302)
{
return $this->redirect($this->di['url']->route($route), $code);
}
/**
* Redirect with parameters from the current URL.
*
* @param string $route
* @param int $code
* @return Response
*/
public function redirectFromHere($route, $code = 302)
{
return $this->redirect($this->di['url']->routeFromHere($route), $code);
}
/**
* Redirect to the current page.
*
* @param int $code
* @return Response
*/
public function redirectHere($code = 302)
{
return $this->redirect($_SERVER['REQUEST_URI'], $code);
}
/**
* Redirect to the homepage.
*
* @param int $code
* @return Response
*/
public function redirectHome($code = 302)
{
return $this->redirect($this->di['url']->named('home'), $code);
}
/**
* Redirect with parameters to named route.
*
* @param string $route
* @param int $code
* @return Response
*/
public function redirectToName($name, $route_params = array(), $code = 302)
{
return $this->redirect($this->di['url']->named($name, $route_params), $code);
}
/**
* Force redirection to a HTTPS secure URL.
*/
protected function forceSecure()
{
if (APP_APPLICATION_ENV == 'production' && !APP_IS_SECURE)
{
$this->doNotRender();
$url = 'https://'.$this->request->getHttpHost().$this->request->getUri();
return $this->redirect($url, 301);
}
}
/* Referrer storage */
protected function storeReferrer($namespace = 'default', $loose = true)
{
$session = $this->di['session']->get('referrer_'.$namespace);
if( !isset($session->url) || ($loose && isset($session->url) && $this->di['url']->current() != $this->di['url']->referrer()) )
$session->url = $this->di['url']->referrer();
}
protected function getStoredReferrer($namespace = 'default')
{
$session = $this->di['session']->get('referrer_'.$namespace);
return $session->url;
}
protected function clearStoredReferrer($namespace = 'default')
{
$session = $this->di['session']->get('referrer_'.$namespace);
unset($session->url);
}
protected function redirectToStoredReferrer($namespace = 'default', $default_url = false)
{
$referrer = $this->getStoredReferrer($namespace);
$this->clearStoredReferrer($namespace);
$home_url = $this->di['url']->named('home');
if (strcmp($referrer, $this->request->getUri()->getPath()) == 0)
$referrer = $home_url;
if( trim($referrer) == '' )
$referrer = ($default_url) ? $default_url : $home_url;
return $this->redirect($referrer);
}
protected function redirectToReferrer($default = false)
{
if( !$default )
$default = $this->di['url']->baseUrl();
return $this->redirect($this->di['url']->referrer($default));
}
/* Notifications */
public function flash($message, $level = \App\Flash::INFO)
{
$this->alert($message, $level);
}
public function alert($message, $level = \App\Flash::INFO)
{
$this->di['flash']->addMessage($message, $level, TRUE);
}
}

View File

@ -1,96 +0,0 @@
<?php
namespace App\Mvc;
use Exception;
use Interop\Container\ContainerInterface;
use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Http\Message\ResponseInterface as Response;
class ErrorHandler
{
public static function handle(ContainerInterface $di, Request $req, Response $res, Exception $e)
{
if ($e instanceof \App\Exception\NotLoggedIn)
{
// Redirect to login page for not-logged-in users.
$flash = $di['flash'];
$flash->addMessage('<b>Error:</b> You must be logged in to access this page!', 'red');
// Set referrer for login redirection.
$session = $di['session'];
$session = $session->get('referrer_login');
$session->url = $di['url']->current();
// Redirect to login page.
$login_url = $di['url']->named('account:login');
return $res->withStatus(302)->withHeader('Location', $login_url);
}
elseif ($e instanceof \App\Exception\PermissionDenied)
{
// Bounce back to homepage for permission-denied users.
$di['flash']->addMessage('You do not have permission to access this portion of the site.', \App\Flash::ERROR);
$home_url = $di['url']->named('home');
return $res->withStatus(302)->withHeader('Location', $home_url);
}
elseif (APP_IS_COMMAND_LINE)
{
$body = $res->getBody();
$body->write(json_encode([
'code' => $e->getCode(),
'message' => $e->getMessage(),
'stack_trace' => $e->getTraceAsString(),
]));
return $res->withStatus(500)
->withBody($body);
}
else
{
$show_debug = false;
if ($di->has('acl'))
{
$acl = $di->get('acl');
if ($acl->isAllowed('administer all'))
$show_debug = true;
}
if (APP_APPLICATION_ENV != 'production')
$show_debug = true;
if ($show_debug)
{
$view = $di->get('view');
$view->disable();
// Register error-handler.
$handler = new \Whoops\Handler\PrettyPageHandler;
$handler->setPageTitle('An error occurred!');
$run = new \Whoops\Run;
$run->pushHandler($handler);
$body = $res->getBody();
$body->write($run->handleException($e));
return $res->withStatus(500)
->withBody($body);
}
else
{
$view = $di->get('view');
$view->exception = $e;
$template = $view->render('system/error_general');
$body = $res->getBody();
$body->write($template);
return $res->withStatus(500)
->withBody($body);
}
}
}
}

View File

@ -1,54 +0,0 @@
<?php
namespace App\Mvc;
use RuntimeException;
use Interop\Container\ContainerInterface;
use Slim\Interfaces\CallableResolverInterface;
/**
* This class resolves a string of the format 'class:method' into a closure
* that can be dispatched.
*/
class Resolver implements CallableResolverInterface
{
/**
* @var ContainerInterface
*/
protected $di;
/**
* @param ContainerInterface $container
*/
public function __construct(ContainerInterface $container)
{
$this->di = $container;
}
public function resolve($toResolve)
{
$resolved = $toResolve;
if (!is_callable($toResolve) && is_string($toResolve))
{
list($module, $controller, $action) = explode(':', $toResolve);
$class = '\\Modules\\'.ucfirst($module).'\\Controllers\\'.ucfirst($controller).'Controller';
if (!class_exists($class))
throw new RuntimeException(sprintf('Callable %s does not exist', $class));
$resolved = function($request, $response, $args) use ($class, $module, $controller, $action) {
$controller = new $class($this->di, $module, $controller, $action);
return $controller->dispatch($request, $response, $args);
};
}
if (!is_callable($resolved)) {
throw new RuntimeException(sprintf(
'%s is not resolvable',
is_array($toResolve) || is_object($toResolve) ? json_encode($toResolve) : $toResolve
));
}
return $resolved;
}
}

View File

@ -1,93 +0,0 @@
<?php
namespace App\Mvc;
use Interop\Container\ContainerInterface;
use League\Plates\Template\Data;
class View extends \League\Plates\Engine
{
/**
* Add "View Helpers" for common functions.
*/
public function addAppCommands(ContainerInterface $di)
{
$this->loadExtension(new View\Paginator($di['url']));
$this->registerFunction('mailto', function ($address, $link_text = NULL) {
$address = substr(chunk_split(bin2hex(" $address"), 2, ";&#x"), 3,-3);
$link_text = (is_null($link_text)) ? $address : $link_text;
return '<a href="mailto:'.$address.'">'.$link_text.'</a>';
});
$this->registerFunction('pluralize', function($word, $num = 0) {
if ((int)$num == 1)
return $word;
else
return \Doctrine\Common\Inflector\Inflector::pluralize($word);
});
$this->registerFunction('truncate', function($text, $length=80) {
return \App\Utilities::truncate_text($text, $length);
});
}
protected $rendered = false;
protected $disabled = false;
public function reset()
{
$this->rendered = false;
$this->disabled = false;
$this->data = new Data();
}
public function disable()
{
$this->disabled = true;
}
public function isDisabled()
{
return $this->disabled;
}
public function isRendered()
{
return $this->rendered;
}
public function __set($key, $value)
{
$this->addData([$key => $value]);
}
public function __get($key)
{
return $this->getData($key);
}
public function render($name, array $data = array())
{
if (!$this->isDisabled())
{
$this->rendered = true;
return parent::render($name, $data);
}
return null;
}
public function fetch($name, array $data = array())
{
return parent::render($name, $data);
}
public function setFolder($name, $directory, $fallback = false)
{
if ($this->folders->exists($name))
$this->folders->remove($name);
$this->folders->add($name, $directory, $fallback);
return $this;
}
}

View File

@ -1,82 +0,0 @@
<?php
namespace App\Mvc\View;
use League\Plates\Engine;
use League\Plates\Extension\ExtensionInterface;
use App\Url;
class Paginator implements ExtensionInterface
{
/**
* @var Url
*/
protected $url;
public function __construct(Url $url)
{
$this->url = $url;
}
public function register(Engine $engine)
{
$engine->registerFunction('paginate', [$this, 'paginate']);
}
/**
* @param $pager \App\Paginator\Doctrine|\Zend\Paginator\Paginator
* @param bool $show_if_zero_pages
* @return string
*/
public function paginate($pager, $show_if_zero_pages = false)
{
$pages = (array)$pager->getPages();
$query_string = '';
if (!empty($_GET))
$query_string = '?'.http_build_query($_GET);
$return_string = '';
if ($pages['pageCount'] > 1 || $show_if_zero_pages)
{
$return_string .= '<nav><ul class="pagination">';
// First page link
if ($pages['first'] != $pages['current'])
$return_string .= '<li class="prev"><a href="'.$this->url->routeFromHere(array('page' => $pages['first'])).$query_string.'" rel="'.$pages['first'].'">&laquo;</a></li>';
else
$return_string .= '<li class="prev disabled"><a href="#">&laquo;</a></li>';
// Previous page link
if ($pages['previous'])
$return_string .= '<li><a href="'.$this->url->routeFromHere(array('page' => $pages['previous'])).$query_string.'" rel="'.$pages['previous'].'">&lt;</a></li>';
else
$return_string .= '<li class="disabled"><a href="#">&lt;</a></li>';
// Produce full page range
foreach($pages['pagesInRange'] as $page)
{
if ($page != $pages['current'])
$return_string .= '<li><a href="'.$this->url->routeFromHere(array('page' => $page)).$query_string.'" rel="'.$page.'">'.$page.'</a></li>';
else
$return_string .= '<li class="active"><a href="#">'.$page.'</a></li>';
}
// Next page link
if ($pages['next'])
$return_string .= '<li><a href="'.$this->url->routeFromHere(array('page' => $pages['next'])).$query_string.'" rel="'.$pages['next'].'">&gt;</a></li>';
else
$return_string .= '<li class="disabled"><a href="#">&gt;</a></li>';
// Last page link
if ($pages['last'] != $pages['current'])
$return_string .= '<li class="next"><a href="'.$this->url->routeFromHere(array('page' => $pages['last'])).$query_string.'" rel="'.$pages['last'].'">&raquo;</a></li>';
else
$return_string .= '<li class="next disabled"><a href="#">&raquo;</a></li>';
$return_string .= '</ul></nav>';
}
return $return_string;
}
}

View File

@ -1,74 +0,0 @@
<?php
namespace App\Paginator;
class Doctrine implements \Countable, \IteratorAggregate
{
protected $_page_number;
protected $_num_per_page;
protected $_query;
protected $_paginator;
public function __construct($query, $page = 1, $limit = 10)
{
$this->_page_number = $page;
$this->_num_per_page = $limit;
if ($query instanceof \Doctrine\ORM\QueryBuilder)
$query = $query->getQuery();
$query->setFirstResult(($page - 1) * $limit);
$query->setMaxResults($limit);
$this->_query = $query;
$this->_paginator = new \Doctrine\ORM\Tools\Pagination\Paginator($this->_query);
}
public function count()
{
return $this->_paginator->count();
}
public function getIterator()
{
return $this->_paginator->getIterator();
}
public function getPageCount()
{
return ceil($this->_paginator->count() / $this->_num_per_page);
}
public function getPages()
{
$pageCount = $this->getPageCount();
$currentPageNumber = $this->_page_number;
$pages = new \stdClass();
$pages->pageCount = $pageCount;
$pages->itemCountPerPage = $this->_num_per_page;
$pages->first = 1;
$pages->current = $currentPageNumber;
$pages->last = $pageCount;
// Previous and next
if ($currentPageNumber - 1 > 0) {
$pages->previous = $currentPageNumber - 1;
}
if ($currentPageNumber + 1 <= $pageCount) {
$pages->next = $currentPageNumber + 1;
}
// Pages in range
$pages_in_range = array();
for($i = 1; $i <= $pageCount; $i++)
$pages_in_range[] = $i;
$pages->pagesInRange = $pages_in_range;
$pages->firstPageInRange = 1;
$pages->lastPageInRange = $pageCount;
return $pages;
}
}

View File

@ -1,141 +0,0 @@
<?php
namespace App\Service;
use App\Debug;
use App\Utilities;
class Curl
{
/**
* Submit a URL request with a specified cache lifetime.
*
* @param null $c_opts
* @param int $cache_time
* @return string
*/
public static function request($c_opts = null)
{
// Compose cURL configuration array.
if (is_null($c_opts))
$c_opts = array();
elseif (!is_array($c_opts))
$c_opts = array('url' => $c_opts);
$c_defaults = array(
'method' => 'GET',
'useragent' => 'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.2) Gecko/20070219 Firefox/2.0.0.2',
'timeout' => 10,
);
$c_opts = array_merge($c_defaults, $c_opts);
Debug::log('cURL Outgoing Request: '.$c_opts['url']);
Debug::startTimer('Make cURL Request');
$postfields = false;
if (!empty($c_opts['params']))
{
if (strtoupper($c_opts['method']) == 'POST')
$postfields = $c_opts['params'];
else
$c_opts['url'] = $c_opts['url'].'?'.http_build_query($c_opts['params']);
}
// Start cURL request.
$curl = curl_init($c_opts['url']);
// Handle POST support.
if (strtoupper($c_opts['method']) == 'POST')
curl_setopt($curl, CURLOPT_POST, true);
if (!empty($c_opts['referer']))
curl_setopt($curl, CURLOPT_REFERER, $c_opts['referer']);
if ($postfields)
curl_setopt($curl, CURLOPT_POSTFIELDS, $postfields);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, $c_opts['timeout']);
curl_setopt($curl, CURLOPT_TIMEOUT, $c_opts['timeout']);
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($curl, CURLOPT_USERAGENT, $c_opts['useragent']);
curl_setopt($curl, CURLOPT_FOLLOWLOCATION, TRUE);
curl_setopt($curl, CURLOPT_MAXREDIRS, 3);
// Custom DNS management.
curl_setopt($curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
curl_setopt($curl, CURLOPT_DNS_CACHE_TIMEOUT, 600);
// Set custom HTTP headers.
if (!empty($c_opts['headers']))
curl_setopt($curl, CURLOPT_HTTPHEADER, $c_opts['headers']);
$return_raw = self::curl_exec_utf8($curl);
// End cURL request.
Debug::endTimer('Make cURL Request');
// Log more detailed information to screen about resolution times.
$conn_info = curl_getinfo($curl);
$important_conn_info = array('url', 'http_code', 'total_time', 'namelookup_time', 'connect_time', 'pretransfer_time', 'starttransfer_time', 'redirect_time');
$debug_conn_info = array();
foreach($important_conn_info as $conn_param)
$debug_conn_info[$conn_param] = $conn_info[$conn_param];
Debug::print_r($debug_conn_info);
$error = curl_error($curl);
if ($error)
Debug::log("Curl error: ".$error);
return trim($return_raw);
}
public static function curl_exec_utf8($ch)
{
$data = curl_exec($ch);
if (!is_string($data)) return $data;
unset($charset);
$content_type = curl_getinfo($ch, CURLINFO_CONTENT_TYPE);
/* 1: HTTP Content-Type: header */
preg_match( '@([\w/+]+)(;\s*charset=(\S+))?@i', $content_type, $matches );
if ( isset( $matches[3] ) )
$charset = $matches[3];
/* 2: <meta> element in the page */
if (!isset($charset)) {
preg_match( '@<meta\s+http-equiv="Content-Type"\s+content="([\w/]+)(;\s*charset=([^\s"]+))?@i', $data, $matches );
if ( isset( $matches[3] ) )
$charset = $matches[3];
}
/* 3: <xml> element in the page */
if (!isset($charset)) {
preg_match( '@<\?xml.+encoding="([^\s"]+)@si', $data, $matches );
if ( isset( $matches[1] ) )
$charset = $matches[1];
}
/* 4: PHP's heuristic detection */
if (!isset($charset)) {
$encoding = mb_detect_encoding($data);
if ($encoding)
$charset = $encoding;
}
/* 5: Default for HTML */
if (!isset($charset)) {
if (strstr($content_type, "text/html") === 0)
$charset = "ISO 8859-1";
}
/* Convert it if it is anything but UTF-8 */
if (isset($charset) && strtoupper($charset) != "UTF-8")
$data = iconv($charset, 'UTF-8//IGNORE', $data);
return $data;
}
}

View File

@ -1,21 +0,0 @@
<?php
/**
* Gravatar - Globally Recognized Avatars Connector
*/
namespace App\Service;
class Gravatar
{
public static function get($email, $size=50, $default='mm')
{
$grav_prefix = (APP_IS_SECURE) ? 'https://secure.gravatar.com' : 'http://www.gravatar.com';
$url_params = array(
'd' => $default,
'r' => 'g',
'size' => $size,
);
$grav_url = $grav_prefix.'/avatar/'.md5(strtolower($email)).'?'.http_build_query($url_params);
return htmlspecialchars($grav_url);
}
}

View File

@ -1,167 +0,0 @@
<?php
namespace App;
class Session
{
protected $_prevent_sessions = false;
protected $_is_started = false;
protected $_sessions = array();
/**
* Start the session handler if allowed and not already started.
*
* @return bool
*/
public function start()
{
if ($this->_is_started)
return true;
if (!$this->isActive())
return false;
$this->_is_started = @session_start();
return $this->_is_started;
}
/**
* Alias for self::getNamespace()
*
* @param string $namespace
* @return mixed
*/
public function get($namespace = 'default')
{
return $this->getNamespace($namespace);
}
/**
* Get a session management namespace.
*
* @param string $namespace
* @return mixed
*/
public function getNamespace($namespace = 'default')
{
$session_name = self::getNamespaceName($namespace);
if (!isset($this->_sessions[$session_name]))
{
if (self::isActive())
$this->_sessions[$session_name] = new \App\Session\Instance($this, $session_name);
else
$this->_sessions[$session_name] = new \App\Session\Temporary($this, $session_name);
}
return $this->_sessions[$session_name];
}
/**
* Clean up the name of a session namespace for storage.
*
* @param string $suffix
* @return string
*/
public function getNamespaceName($suffix = 'default')
{
$app_hash = strtoupper(substr(md5(APP_INCLUDE_BASE), 0, 5));
return 'APP_'.$app_hash.'_'.$suffix;
}
/**
* Temporarily suspend the session in mid-page.
*/
public function suspend()
{
@session_write_close();
}
/**
* Resume a temporarily suspended session.
*/
public function resume()
{
@session_start();
}
/**
* Prevent sessions from being created.
*/
public function disable()
{
$this->_prevent_sessions = true;
}
/**
* Reallow sessions to be created after previously prevented.
*/
public function enable()
{
$this->_prevent_sessions = false;
}
/**
* Indicate if a session exists on the user's computer already.
*
* @return bool
*/
public function exists()
{
return isset($_COOKIE[session_name()]);
}
/**
* Indicates if sessions are currently active (and permitted).
*
* @return bool
*/
public function isActive()
{
if (APP_IS_COMMAND_LINE && !APP_TESTING_MODE)
return false;
if ($this->_prevent_sessions)
return false;
return true;
}
/**
* Destroy a session.
*/
public function destroy()
{
$this->start();
// Unset all of the session variables.
$_SESSION = array();
// Destroy session cookie.
if (ini_get("session.use_cookies"))
{
$params = session_get_cookie_params();
setcookie(session_name(), '', time() - 42000,
$params["path"], $params["domain"],
$params["secure"], $params["httponly"]
);
}
// Destroy session formally.
session_destroy();
}
/**
* Indicates if a session has already been started in this page load.
*
* @return bool
*/
public function isStarted()
{
if (defined('PHP_SESSION_ACTIVE'))
return (session_status() !== PHP_SESSION_ACTIVE);
else
return (!session_id());
}
}

View File

@ -1,157 +0,0 @@
<?php
namespace App\Session;
class Instance implements \ArrayAccess
{
/**
* @var \App\Session
*/
protected $_session;
/**
* @var string The current namespace name.
*/
protected $_namespace;
/**
* @var array
*/
protected $_data;
public function __construct(\App\Session $session, $namespace = 'default')
{
$this->_session = $session;
$this->_namespace = $namespace;
// Lazy load session.
if ($this->_session->exists())
{
$this->_session->start();
$this->_data = $_SESSION[$this->_namespace];
}
else
{
$this->_data = array();
}
}
/**
* Magic Method __set
*
* @param $name
* @param $value
*/
public function __set($name, $value)
{
$this->_data[$name] = $value;
if ($this->_session->isActive()) {
$this->_session->start();
if (!isset($_SESSION[$this->_namespace]))
$_SESSION[$this->_namespace] = array();
$_SESSION[$this->_namespace][$name] = $value;
}
}
/**
* ArrayAccess form of __set
*
* @param mixed $name
* @param mixed $value
*/
public function offsetSet($name, $value)
{
$this->_data[$name] = $value;
if ($this->_session->isActive()) {
$this->_session->start();
if (!isset($_SESSION[$this->_namespace]))
$_SESSION[$this->_namespace] = array();
$_SESSION[$this->_namespace][$name] = $value;
}
}
/**
* Magic Method __get
*
* @param $name
* @return mixed
*/
public function __get($name)
{
if (isset($this->_data[$name]))
return $this->_data[$name];
return null;
}
/**
* ArrayAccess form of __get
*
* @param mixed $name
* @return mixed|void
*/
public function offsetGet($name)
{
if (isset($this->_data[$name]))
return $this->_data[$name];
return null;
}
/**
* Magic Method __isset
*
* @param $name
* @return bool
*/
public function __isset($name)
{
return isset($this->_data[$name]);
}
/**
* ArrayAccess form of __isset
*
* @param mixed $name
* @return bool
*/
public function offsetExists($name)
{
return isset($this->_data[$name]);
}
/**
* Magic Method __unset
*
* @param $name
*/
public function __unset($name)
{
unset($this->_data[$name]);
if ($this->_session->isActive()) {
$this->_session->start();
unset($_SESSION[$this->_namespace][$name]);
}
}
/**
* ArrayAccess form of __unset
*
* @param mixed $name
*/
public function offsetUnset($name)
{
unset($this->_data[$name]);
if ($this->_session->isActive()) {
$this->_session->start();
unset($_SESSION[$this->_namespace][$name]);
}
}
}

View File

@ -1,108 +0,0 @@
<?php
namespace App\Session;
class Temporary implements \ArrayAccess
{
protected $_session;
protected $_namespace;
protected $_data;
public function __construct(\App\Session $session, $namespace = 'default')
{
$this->_session = $session;
$this->_namespace = $namespace;
$this->_data = array();
}
/**
* Magic Method __set
*
* @param $name
* @param $value
*/
public function __set($name, $value)
{
$this->_data[$name] = $value;
}
/**
* ArrayAccess form of __set
*
* @param mixed $name
* @param mixed $value
*/
public function offsetSet($name, $value)
{
$this->_data[$name] = $value;
}
/**
* Magic Method __get
*
* @param $name
* @return mixed
*/
public function __get($name)
{
if (isset($this->_data[$name]))
return $this->_data[$name];
return null;
}
/**
* ArrayAccess form of __get
*
* @param mixed $name
* @return mixed|void
*/
public function offsetGet($name)
{
if (isset($this->_data[$name]))
return $this->_data[$name];
return null;
}
/**
* Magic Method __isset
*
* @param $name
* @return bool
*/
public function __isset($name)
{
return isset($this->_data[$name]);
}
/**
* ArrayAccess form of __isset
*
* @param mixed $name
* @return bool
*/
public function offsetExists($name)
{
return isset($this->_data[$name]);
}
/**
* Magic Method __unset
*
* @param $name
*/
public function __unset($name)
{
unset($this->_data[$name]);
}
/**
* ArrayAccess form of __unset
*
* @param mixed $name
*/
public function offsetUnset($name)
{
unset($this->_data[$name]);
}
}

View File

@ -1,168 +0,0 @@
<?php
/**
* Based on Herloct's Slim 3.0 Connector
* https://github.com/herloct/codeception-slim-module
*/
namespace App\Tests;
use Codeception\Lib\Connector\Shared\PhpSuperGlobalsConverter;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Message\UploadedFileInterface;
use Slim\App;
use Slim\Http\Environment;
use Slim\Http\Headers;
use Slim\Http\Cookies;
use Slim\Http\RequestBody;
use Slim\Http\Stream;
use Slim\Http\UploadedFile;
use Slim\Http\Uri;
use Symfony\Component\BrowserKit\Client;
use Symfony\Component\BrowserKit\Request as BrowserKitRequest;
use Symfony\Component\BrowserKit\Response as BrowserKitResponse;
class Connector extends Client
{
use PhpSuperGlobalsConverter;
/**
* @var App
*/
protected $app;
/**
* @param App $app
*/
public function setApp(App $app)
{
$this->app = $app;
}
/**
* Makes a request.
*
* @param BrowserKitRequest $request An origin request instance
*
* @return BrowserKitResponse An origin response instance
*/
public function doRequest($request)
{
$_COOKIE = $request->getCookies();
$_SERVER = $request->getServer();
$_FILES = $this->remapFiles($request->getFiles());
$uri = str_replace('http://localhost', '', $request->getUri());
$_REQUEST = $this->remapRequestParameters($request->getParameters());
if (strtoupper($request->getMethod()) == 'GET')
{
$_GET = $_REQUEST;
$_POST = [];
}
else
{
$_GET = [];
$_POST = $_REQUEST;
}
$_SERVER['REMOTE_ADDR'] = '127.0.0.1';
$_SERVER['REQUEST_METHOD'] = strtoupper($request->getMethod());
$_SERVER['REQUEST_URI'] = $uri;
$slimRequest = $this->convertRequest($request);
$container = $this->app->getContainer();
/* @var $slimResponse ResponseInterface */
$slimResponse = $container->get('response');
// reset body stream
$slimResponse = $slimResponse->withBody(new Stream(fopen('php://temp', 'w+')));
$slimResponse = $this->app->process($slimRequest, $slimResponse);
return new BrowserKitResponse(
(string) $slimResponse->getBody(),
$slimResponse->getStatusCode(),
$slimResponse->getHeaders()
);
}
/**
* Convert to PSR-7's ServerRequestInterface.
*
* @param BrowserKitRequest $request
* @return ServerRequestInterface
*/
protected function convertRequest(BrowserKitRequest $request)
{
$environment = Environment::mock($request->getServer());
$uri = Uri::createFromString($request->getUri());
$headers = Headers::createFromEnvironment($environment);
$cookies = Cookies::parseHeader($headers->get('Cookie', []));
$container = $this->app->getContainer();
/* @var $slimRequest ServerRequestInterface */
$slimRequest = $container->get('request');
$slimRequest = $slimRequest->withMethod($request->getMethod())
->withUri($uri)
->withUploadedFiles($this->convertFiles($request->getFiles()))
->withCookieParams($cookies);
foreach ($headers->keys() as $key) {
$slimRequest = $slimRequest->withHeader($key, $headers->get($key));
}
if ($request->getContent() !== null) {
$body = new RequestBody();
$body->write($request->getContent());
$slimRequest = $slimRequest
->withBody($body);
}
$parsed = [];
if ($request->getMethod() !== 'GET') {
$parsed = $request->getParameters();
}
// make sure we do not overwrite a request with a parsed body
if (!$slimRequest->getParsedBody()) {
$slimRequest = $slimRequest
->withParsedBody($parsed);
}
return $slimRequest;
}
/**
* Convert to PSR-7's UploadedFileInterface.
*
* @param array $files
* @return array
*/
protected function convertFiles(array $files)
{
$fileObjects = [];
foreach ($files as $fieldName => $file) {
if ($file instanceof UploadedFileInterface) {
$fileObjects[$fieldName] = $file;
} elseif (!isset($file['tmp_name']) && !isset($file['name'])) {
$fileObjects[$fieldName] = $this->convertFiles($file);
} else {
$fileObjects[$fieldName] = new UploadedFile(
$file['tmp_name'],
$file['name'],
$file['type'],
$file['size'],
$file['error']
);
}
}
return $fileObjects;
}
}

View File

@ -1,80 +0,0 @@
<?php
/**
* Based on Herloct's Slim 3.0 Connector
* https://github.com/herloct/codeception-slim-module
*/
namespace App\Tests;
use Doctrine\ORM\EntityManagerInterface;
use Interop\Container\ContainerInterface;
use Codeception\Configuration;
use Codeception\TestInterface;
use Codeception\Lib\Framework;
use Codeception\Lib\Interfaces\DoctrineProvider;
use Slim\App;
class Module extends Framework implements DoctrineProvider
{
protected $requiredFields = ['container'];
/**
* @var ContainerInterface
*/
public $container;
/**
* @var App
*/
public $app;
/**
* @var EntityManagerInterface
*/
public $em;
public function _initialize()
{
if (!defined('APP_TESTING_MODE'))
define('APP_TESTING_MODE', true);
$cwd = getcwd();
chdir(Configuration::projectDir());
$this->container = include Configuration::projectDir() . $this->config['container'];
chdir($cwd);
$this->app = $this->container->get('app');
$this->em = $this->container->get('em');
parent::_initialize();
}
public function _before(TestInterface $test)
{
$this->client = new Connector();
$this->client->setApp($this->app);
parent::_before($test);
}
public function _after(TestInterface $test)
{
if (session_status() === PHP_SESSION_ACTIVE) {
session_write_close();
}
$_GET = [];
$_POST = [];
$_COOKIE = [];
parent::_after($test);
}
/**
* @return EntityManagerInterface
*/
public function _getEntityManager()
{
return $this->em;
}
}

View File

@ -1,234 +0,0 @@
<?php
namespace App;
class Timezone
{
public static function l(\DateTime $date_time)
{
return self::localize($date_time);
}
public static function localize(\DateTime $date_time)
{
$tz_name = date_default_timezone_get();
$tz = new \DateTimeZone($tz_name);
return $date_time->setTimezone($tz);
}
public static function getInfo()
{
static $tz_info;
if (!$tz_info)
{
$tz = date_default_timezone_get();
$utc = new \DateTimeZone('UTC');
$dt = new \DateTime('now', $utc);
$current_tz = new \DateTimeZone($tz);
$offset = $current_tz->getOffset($dt);
$transition = $current_tz->getTransitions($dt->getTimestamp(), $dt->getTimestamp());
$dt_in_tz = new \DateTime('now', $current_tz);
$tz_info = array(
'code' => $tz,
'gmt_offset_seconds' => (float)$offset,
'gmt_offset_hours' => (float)($offset / 3600),
'name' => $transition[0]['name'],
'abbr' => $transition[0]['abbr'],
'tz_object' => $current_tz,
'utc_object' => $utc,
'now_utc' => $dt,
'now' => $dt_in_tz,
);
}
return $tz_info;
}
public static function getOffsetMinutes($tz = null)
{
if ($tz === null)
$tz = date_default_timezone_get();
$utc = new \DateTimeZone('UTC');
$dt = new \DateTime('now', $utc);
$current_tz = new \DateTimeZone($tz);
$offset = $current_tz->getOffset($dt);
return (int)($offset / 60);
}
public static function formatOffset($offset)
{
$hours = $offset / 3600;
$remainder = $offset % 3600;
$sign = $hours > 0 ? '+' : '-';
$hour = (int) abs($hours);
$minutes = (int) abs($remainder / 60);
if ($hour == 0 && $minutes == 0)
return 'GMT';
else
return 'GMT '. $sign . str_pad($hour, 2, '0', STR_PAD_LEFT) .':'. str_pad($minutes,2, '0');
}
public static function fetchSelect()
{
// Time zone configuration
$utc = new \DateTimeZone('UTC');
$dt = new \DateTime('now', $utc);
$tz_options = array(
'UTC' => 'UTC',
'US/Pacific' => "Pacific Time (US & Canada)",
'US/Mountain' => "Mountain Time (US & Canada)",
'US/Central' => "Central Time (US & Canada)",
'US/Eastern' => "Eastern Time (US & Canada)",
'Canada/Atlantic' => "Atlantic Time (Canada)",
'Pacific/Midway' => "Midway Island",
'US/Samoa' => "Samoa",
'US/Hawaii' => "Hawaii",
'US/Alaska' => "Alaska",
'America/Tijuana' => "Tijuana",
'US/Arizona' => "Arizona",
'America/Chihuahua' => "Chihuahua",
'America/Mazatlan' => "Mazatlan",
'America/Mexico_City' => "Mexico City",
'America/Monterrey' => "Monterrey",
'Canada/Saskatchewan' => "Saskatchewan",
'US/East-Indiana' => "Indiana (East)",
'America/Bogota' => "Bogota",
'America/Lima' => "Lima",
'America/Caracas' => "Caracas",
'America/La_Paz' => "La Paz",
'America/Santiago' => "Santiago",
'Canada/Newfoundland' => "Newfoundland",
'America/Buenos_Aires' => "Buenos Aires",
'Atlantic/Stanley' => "Stanley",
'Atlantic/Azores' => "Azores",
'Atlantic/Cape_Verde' => "Cape Verde Is.",
'Africa/Casablanca' => "Casablanca",
'Europe/Dublin' => "Dublin",
'Europe/Lisbon' => "Lisbon",
'Europe/London' => "London",
'Africa/Monrovia' => "Monrovia",
'Europe/Amsterdam' => "Amsterdam",
'Europe/Belgrade' => "Belgrade",
'Europe/Berlin' => "Berlin",
'Europe/Bratislava' => "Bratislava",
'Europe/Brussels' => "Brussels",
'Europe/Budapest' => "Budapest",
'Europe/Copenhagen' => "Copenhagen",
'Europe/Ljubljana' => "Ljubljana",
'Europe/Madrid' => "Madrid",
'Europe/Paris' => "Paris",
'Europe/Prague' => "Prague",
'Europe/Rome' => "Rome",
'Europe/Sarajevo' => "Sarajevo",
'Europe/Skopje' => "Skopje",
'Europe/Stockholm' => "Stockholm",
'Europe/Vienna' => "Vienna",
'Europe/Warsaw' => "Warsaw",
'Europe/Zagreb' => "Zagreb",
'Europe/Athens' => "Athens",
'Europe/Bucharest' => "Bucharest",
'Africa/Cairo' => "Cairo",
'Africa/Harare' => "Harare",
'Europe/Helsinki' => "Helsinki",
'Europe/Istanbul' => "Istanbul",
'Asia/Jerusalem' => "Jerusalem",
'Europe/Kiev' => "Kyiv",
'Europe/Minsk' => "Minsk",
'Europe/Riga' => "Riga",
'Europe/Sofia' => "Sofia",
'Europe/Tallinn' => "Tallinn",
'Europe/Vilnius' => "Vilnius",
'Asia/Baghdad' => "Baghdad",
'Asia/Kuwait' => "Kuwait",
'Africa/Nairobi' => "Nairobi",
'Asia/Riyadh' => "Riyadh",
'Asia/Tehran' => "Tehran",
'Europe/Moscow' => "Moscow",
'Asia/Baku' => "Baku",
'Europe/Volgograd' => "Volgograd",
'Asia/Muscat' => "Muscat",
'Asia/Tbilisi' => "Tbilisi",
'Asia/Yerevan' => "Yerevan",
'Asia/Kabul' => "Kabul",
'Asia/Karachi' => "Karachi",
'Asia/Tashkent' => "Tashkent",
'Asia/Kolkata' => "Kolkata",
'Asia/Kathmandu' => "Kathmandu",
'Asia/Yekaterinburg' => "Ekaterinburg",
'Asia/Almaty' => "Almaty",
'Asia/Dhaka' => "Dhaka",
'Asia/Novosibirsk' => "Novosibirsk",
'Asia/Bangkok' => "Bangkok",
'Asia/Jakarta' => "Jakarta",
'Asia/Krasnoyarsk' => "Krasnoyarsk",
'Asia/Chongqing' => "Chongqing",
'Asia/Hong_Kong' => "Hong Kong",
'Asia/Kuala_Lumpur' => "Kuala Lumpur",
'Australia/Perth' => "Perth",
'Asia/Singapore' => "Singapore",
'Asia/Taipei' => "Taipei",
'Asia/Ulaanbaatar' => "Ulaan Bataar",
'Asia/Urumqi' => "Urumqi",
'Asia/Irkutsk' => "Irkutsk",
'Asia/Seoul' => "Seoul",
'Asia/Tokyo' => "Tokyo",
'Australia/Adelaide' => "Adelaide",
'Australia/Darwin' => "Darwin",
'Asia/Yakutsk' => "Yakutsk",
'Australia/Brisbane' => "Brisbane",
'Australia/Canberra' => "Canberra",
'Pacific/Guam' => "Guam",
'Australia/Hobart' => "Hobart",
'Australia/Melbourne' => "Melbourne",
'Pacific/Port_Moresby' => "Port Moresby",
'Australia/Sydney' => "Sydney",
'Asia/Vladivostok' => "Vladivostok",
'Asia/Magadan' => "Magadan",
'Pacific/Auckland' => "Auckland",
'Pacific/Fiji' => "Fiji",
);
$tz_select_raw = array();
foreach($tz_options as $tz => $tz_display)
{
$current_tz = new \DateTimeZone($tz);
$offset = $current_tz->getOffset($dt);
$tz_select_raw[$offset][$tz] = $tz_display;
}
ksort($tz_select_raw);
$tz_select = array();
foreach($tz_select_raw as $offset => $cities)
{
$offset_name = self::formatOffset($offset);
$offset_key = key($cities);
if (count($cities) > 5)
{
$cities = array_slice($cities, 0, 5);
$offset_cities = implode(', ', $cities).'...';
}
else
{
$offset_cities = implode(', ', $cities);
}
$tz_select[$offset_key] = $offset_name.': '.$offset_cities;
}
return $tz_select;
}
}

View File

@ -1,276 +0,0 @@
<?php
namespace App;
use Interop\Container\ContainerInterface;
class Url
{
/** @var ContainerInterface */
protected $di;
/** @var \App\Config */
protected $config;
/** @var bool Whether to include the domain in the URLs generated. */
protected $include_domain = false;
public function __construct(ContainerInterface $di)
{
$this->di = $di;
$this->config = $di['config'];
/*
$this->setBaseUri($this->config->application->base_uri);
*/
}
/**
* Get the URI for the current page.
*
* @return mixed
*/
public function current($absolute = false)
{
if (!empty($_SERVER['REQUEST_URI']))
return $this->getUrl($_SERVER['REQUEST_URI'], $absolute);
else
return '';
}
/**
* Generate a callback-friendly URL.
*/
public function callback()
{
return $this->getUrl($this->routeFromHere(array()), true);
}
/**
* Get the HTTP_REFERER value for the current page.
*
* @param null $default_url
* @return mixed
*/
public function referrer($default_url = null)
{
if (isset($_SERVER['HTTP_REFERER']))
return $this->getUrl($_SERVER['HTTP_REFERER']);
return null;
}
/**
* Return the base URL of the site.
*
* @return mixed
*/
public function baseUrl($include_host = false)
{
$router = $this->di['router'];
$uri = $router->pathFor('home');
if ($include_host)
return $this->addSchemePrefix($uri);
else
return $this->getUrl($uri);
}
/**
* Return the static URL for a given path segment.
*
* @param null $file_name
* @return string The routed URL.
*/
public function content($file_name = NULL)
{
return $this->config->application->static_uri.$file_name;
}
/**
* Generate a route using the ZendFramework 1 MVC route standard.
*
* @param $path_info
* @return string The routed URL.
*/
public function route($path_info = array(), $absolute = null)
{
$router = $this->di['router'];
$default_module = 'frontend';
$components = array(
'module' => $default_module,
'controller' => 'index',
'action' => 'index',
);
if (isset($path_info['module']))
{
$components['module'] = $path_info['module'];
unset($path_info['module']);
}
if (isset($path_info['controller']))
{
$components['controller'] = $path_info['controller'];
unset($path_info['controller']);
}
if (isset($path_info['action']))
{
$components['action'] = $path_info['action'];
unset($path_info['action']);
}
if (isset($path_info['params']))
{
$path_info = array_merge($path_info, $path_info['params']);
unset($path_info['params']);
}
// Handle the legacy "default" module being so-named.
if ($components['module'] == 'default')
$components['module'] = $default_module;
// Special exception for homepage.
if ($components['module'] == $default_module &&
$components['controller'] == 'index' &&
$components['action'] == 'index' &&
empty($path_info))
{
return $router->pathFor('home');
}
// Otherwise compile URL using a uniform format.
$url_parts = array();
if ($components['module'] != $default_module)
$url_parts[] = $components['module'];
$url_parts[] = $components['controller'];
$url_parts[] = $components['action'];
$router_path = implode(':', $url_parts);
return $this->getUrl($router->pathFor($router_path, $path_info), $absolute);
}
protected $current_route;
public function setCurrentRoute($route_info)
{
$this->current_route = $route_info;
}
/**
* Generate a route based on the current URL.
*
* @param $path_info
* @return string The routed URL.
*/
public function routeFromHere($path_info)
{
$new_path = (array)$this->current_route;
if (isset($path_info['module']))
{
$new_path['module'] = $path_info['module'];
unset($path_info['module']);
}
if (isset($path_info['controller']))
{
$new_path['controller'] = $path_info['controller'];
unset($path_info['controller']);
}
if (isset($path_info['action']))
{
$new_path['action'] = $path_info['action'];
unset($path_info['action']);
}
if (count($path_info) > 0)
{
foreach ((array)$path_info as $param_key => $param_value)
{
$new_path['params'][$param_key] = $param_value;
}
}
if (isset($new_path['params']['name']))
{
// Allow support for named routes.
$route_name = $new_path['params']['name'];
unset($new_path['params']['name']);
return $this->named($route_name, $new_path['params']);
}
else
{
return $this->route($new_path);
}
}
public function getSchemePrefixSetting()
{
return $this->include_domain;
}
public function forceSchemePrefix($new_value = true)
{
$this->include_domain = $new_value;
}
public function addSchemePrefix($url_raw)
{
return $this->getUrl($url_raw, true);
}
public function getUrl($url_raw, $absolute = false)
{
// Ignore preformed URLs.
if (stristr($url_raw, '://'))
return $url_raw;
// Retrieve domain from either MVC controller or config file.
if ($this->include_domain || $absolute) {
$url_domain = $this->di['em']->getRepository('Entity\Settings')->getSetting('base_url', '');
if (empty($url_domain))
$url_domain = $this->config->application->base_url;
else
$url_domain = ((APP_IS_SECURE) ? 'https://' : 'http://') . $url_domain;
if (empty($url_domain))
{
$http_host = trim($_SERVER['HTTP_HOST'], ':');
if (!empty($http_host))
$url_domain = ((APP_IS_SECURE) ? 'https://' : 'http://') . $http_host;
}
$url_raw = $url_domain . $url_raw;
}
return $url_raw;
}
/**
* Simpler format for calling "named" routes with parameters.
*
* @param $route_name
* @param array $route_params
* @return string
*/
public function named($route_name, $route_params = array())
{
$router = $this->di['router'];
return $router->pathFor($route_name, $route_params);
}
/**
* Return URL for user-uploaded content.
*
* @param null $path
* @return string
*/
public function upload($path = NULL)
{
return $this->content($path);
}
}

View File

@ -1,591 +0,0 @@
<?php
/**
* Miscellaneous Utilities Class
**/
namespace App;
class Utilities
{
/**
* Pretty print_r
*
* @param $var
* @param bool $return
* @return string
*/
public static function print_r($var, $return = FALSE)
{
$return_value = '<pre style="font-size: 13px; font-family: Consolas, Courier New, Courier, monospace; color: #000; background: #EFEFEF; border: 1px solid #CCC; padding: 5px;">';
$return_value .= print_r($var, TRUE);
$return_value .= '</pre>';
if ($return)
{
return $return_value;
}
else
{
echo $return_value;
}
}
/**
* Replacement for money_format that places the negative sign ahead of the dollar sign.
*
* @param $number
* @return string
*/
public static function money_format($number)
{
return (($number < 0) ? '-' : '') .'$'.number_format(abs($number), 2);
}
/**
* Generate a randomized password of specified length.
*
* @param $char_length
* @return string
*/
public static function generatePassword($char_length = 8)
{
// String of all possible characters. Avoids using certain letters and numbers that closely resemble others.
$numeric_chars = str_split('234679');
$uppercase_chars = str_split('ACDEFGHJKLMNPQRTWXYZ');
$lowercase_chars = str_split('acdefghjkmnpqrtwxyz');
$chars = array($numeric_chars, $uppercase_chars, $lowercase_chars);
$password = '';
for($i = 1; $i <= $char_length; $i++)
{
$char_array = $chars[$i % 3];
$password .= $char_array[mt_rand(0, count($char_array)-1)];
}
return str_shuffle($password);
}
/**
* Convert a specified number of seconds into a date range.
*
* @param $timestamp
* @return string
*/
public static function timeToText($timestamp)
{
return self::timeDifferenceText(0, $timestamp);
}
/**
* Get the textual difference between two strings.
*
* @param $timestamp1
* @param $timestamp2
* @param int $precision
* @return string
*/
public static function timeDifferenceText($timestamp1, $timestamp2, $precision = 1)
{
$time_diff = abs($timestamp1 - $timestamp2);
if ($time_diff < 60)
{
$time_num = intval($time_diff);
return sprintf(ngettext("%d second", "%d seconds", $time_num), $time_num);
}
else if ($time_diff >= 60 && $time_diff < 3600)
{
$time_num = round($time_diff / 60, $precision);
return sprintf(ngettext("%d minute", "%d minutes", $time_num), $time_num);
}
else if ($time_diff >= 3600 && $time_diff < 216000)
{
$time_num = round($time_diff / 3600, $precision);
return sprintf(ngettext("%d hour", "%d hours", $time_num), $time_num);
}
else if ($time_diff >= 216000 && $time_diff < 10368000)
{
$time_num = round($time_diff / 86400);
return sprintf(ngettext("%d day", "%d days", $time_num), $time_num);
}
else
{
$time_num = round($time_diff / 2592000);
return sprintf(ngettext("%d month", "%d months", $time_num), $time_num);
}
}
/**
* Forced-GMT strtotime alternative.
*
* @param $time
* @param null $now
* @return int
*/
public static function gstrtotime($time, $now = NULL)
{
$prev_timezone = @date_default_timezone_get();
@date_default_timezone_set('UTC');
$timestamp = strtotime($time, $now);
@date_default_timezone_set($prev_timezone);
return $timestamp;
}
/**
* Truncate text (adding "..." if needed)
*
* @param $text
* @param int $limit
* @param string $pad
* @return string
*/
public static function truncate_text($text, $limit = 80, $pad = '...')
{
mb_internal_encoding('UTF-8');
if (mb_strlen($text) <= $limit)
{
return $text;
}
else
{
$wrapped_text = self::mb_wordwrap($text, $limit, "{N}", TRUE);
$shortened_text = mb_substr($wrapped_text, 0, strpos($wrapped_text, "{N}"));
// Prevent the padding string from bumping up against punctuation.
$punctuation = array('.',',',';','?','!');
if (in_array(mb_substr($shortened_text, -1), $punctuation))
{
$shortened_text = mb_substr($shortened_text, 0, -1);
}
return $shortened_text.$pad;
}
}
/**
* UTF-8 capable replacement for wordwrap function.
*
* @param $str
* @param int $width
* @param string $break
* @param bool $cut
* @return string
*/
public static function mb_wordwrap($str, $width = 75, $break = "\n", $cut = false)
{
$lines = explode($break, $str);
foreach ($lines as &$line) {
$line = rtrim($line);
if (mb_strlen($line) <= $width)
continue;
$words = explode(' ', $line);
$line = '';
$actual = '';
foreach ($words as $word) {
if (mb_strlen($actual.$word) <= $width)
$actual .= $word.' ';
else {
if ($actual != '')
$line .= rtrim($actual).$break;
$actual = $word;
if ($cut) {
while (mb_strlen($actual) > $width) {
$line .= mb_substr($actual, 0, $width).$break;
$actual = mb_substr($actual, $width);
}
}
$actual .= ' ';
}
}
$line .= trim($actual);
}
return implode($break, $lines);
}
/**
* Truncate URL in text-presentable format (i.e. "http://www.example.com" becomes "example.com")
*
* @param $url
* @param int $length
* @return string
*/
public static function truncate_url($url, $length=40)
{
$url = str_replace(array('http://', 'https://', 'www.'), array('', '', ''), $url);
return self::truncate_text(rtrim($url, '/'), $length);
}
/**
* Join one or more items into an array.
*
* @param array $items
* @return string
*/
public static function join_compound(array $items)
{
$count = count($items);
if ($count == 0)
return '';
if ($count == 1)
return $items[0];
return implode(', ', array_slice($items, 0, -1)) . ' and ' . end($items);
}
/**
* Create an array where the keys and values match each other.
*
* @param $array
* @return array
*/
public static function pairs($array)
{
return array_combine($array, $array);
}
/**
* Split an array into "columns", typically for display purposes.
*
* @param $array
* @param int $num_cols
* @param bool $preserve_keys
* @return array
*/
public static function columns($array, $num_cols = 2, $preserve_keys = true)
{
$items_total = (int)count($array);
$items_per_col = ceil($items_total / $num_cols);
return array_chunk($array, $items_per_col, $preserve_keys);
}
/**
* Split an array into "rows", typically for display purposes.
*
* @param $array
* @param int $num_per_row
* @param bool $preserve_keys
* @return array
*/
public static function rows($array, $num_per_row = 3, $preserve_keys = true)
{
return array_chunk($array, $num_per_row, $preserve_keys);
}
/**
* array_merge_recursive does indeed merge arrays, but it converts values with duplicate
* keys to arrays rather than overwriting the value in the first array with the duplicate
* value in the second array, as array_merge does. I.e., with array_merge_recursive,
* this happens (documented behavior):
*
* array_merge_recursive(array('key' => 'org value'), array('key' => 'new value'));
* => array('key' => array('org value', 'new value'));
*
* array_merge_recursive_distinct does not change the datatypes of the values in the arrays.
* Matching keys' values in the second array overwrite those in the first array, as is the
* case with array_merge, i.e.:
*
* array_merge_recursive_distinct(array('key' => 'org value'), array('key' => 'new value'));
* => array('key' => array('new value'));
*
* Parameters are passed by reference, though only for performance reasons. They're not
* altered by this function.
*
* @param array $array1
* @param array $array2
* @return array
* @author Daniel <daniel (at) danielsmedegaardbuus (dot) dk>
* @author Gabriel Sobrinho <gabriel (dot) sobrinho (at) gmail (dot) com>
*/
public static function array_merge_recursive_distinct(array &$array1, array &$array2)
{
$merged = $array1;
foreach ($array2 as $key => &$value)
{
if (is_array($value) && isset($merged[$key]) && is_array($merged[$key]))
$merged[$key] = self::array_merge_recursive_distinct($merged[$key], $value);
else
$merged[$key] = $value;
}
return $merged;
}
/**
* Return all keys in a multi-dimensional array.
* Useful for getting all possible values in an optgroup-stacked select dropdown.
*
* @param $array
* @return array The keys found.
*/
public static function array_keys_recursive($array)
{
$keys = array();
foreach((array)$array as $key => $value)
{
if (is_array($value))
$keys = array_merge($keys, self::array_keys_recursive($value));
else
$keys[] = $key;
}
return $keys;
}
/**
* Sort a supplied array (the first argument) by one or more indices, specified in this format:
* arrayOrderBy($data, [ 'index_name', SORT_ASC, 'index2_name', SORT_DESC ])
*
* Internally uses array_multisort().
*
* @param $data
* @param array $args
* @return mixed
*/
public static function array_order_by($data, array $args = array())
{
if (empty($args))
return $data;
foreach ($args as $n => $field)
{
if (is_string($field))
{
$tmp = array();
foreach ($data as $key => $row)
$tmp[$key] = $row[$field];
$args[$n] = $tmp;
}
}
$args[] = &$data;
call_user_func_array('array_multisort', $args);
return array_pop($args);
}
/**
* Split a URL into an array (similar to parse_url() itself) but with cleaner parameter handling.
*
* @param $url
* @return mixed
*/
public static function parse_url($url)
{
$url_parts = @parse_url($url);
$url_parts['path_clean'] = trim($url_parts['path'], '/');
$url_parts['query_arr'] = self::convert_url_query($url_parts['query']);
return $url_parts;
}
/**
* Convert the query string of a URL into an array of keys and values.
*
* @param $query
* @return array
*/
public static function convert_url_query($query)
{
$queryParts = explode('&', $query);
$params = array();
foreach ($queryParts as $param)
{
$item = explode('=', $param);
$params[$item[0]] = $item[1];
}
return $params;
}
/**
* Construct a URL based on an array returned from parseUrl().
*
* @param $url
* @return string
*/
public static function build_url($url)
{
is_array($url) || $url = parse_url($url);
if (is_array($url['query']))
$url['query'] = http_build_query($url['query']);
if (isset($url['path']) && substr($url['path'], 0, 1) !== '/')
$url['path'] = '/' . $url['path'];
$parsed_string = '';
if (isset($url['scheme']))
$parsed_string .= $url['scheme'] . '://';
if (isset($url['user']))
{
$parsed_string .= $url['user'];
if (isset($url['pass']))
$parsed_string .= ':' . $url['pass'];
$parsed_string .= '@';
}
if (isset($url['host']))
$parsed_string .= $url['host'];
if (isset($url['port']))
$parsed_string .= ':' . $url['port'];
if (!empty($url['path']))
$parsed_string .= $url['path'];
else
$parsed_string .= '/';
if (isset($url['query']))
$parsed_string .= '?' . $url['query'];
if (isset($url['fragment']))
$parsed_string .= '#' . $url['fragment'];
return $parsed_string;
}
/**
* Construct a URL based on an array returned from parseUrl().
*
* @param $needle The value we're looking for
* @param $haystack The array we're looking through
* @param $strict If true, checks type as well
* @return string
*/
public static function recursive_array_search($needle, $haystack, $strict = false)
{
foreach($haystack as $key => $value) {
if (is_array($value)) {
// Value is an array, check that instead!
$nextKey = self::recursive_array_search($needle, $value, $strict);
if ($nextKey)
return $nextKey;
}
else if($strict ? $value === $needle : $value == $needle)
return $key;
}
return false;
}
/**
* Detect if the User-Agent matches common crawler UAs.
* Not expected to be 100% accurate or trustworthy, just used to prevent
* common crawlers from accessing features like API endpoints.
*
* @return bool
*/
public static function is_crawler()
{
$ua = strtolower($_SERVER['HTTP_USER_AGENT']);
$crawlers_agents = strtolower('Bloglines subscriber|Dumbot|Sosoimagespider|QihooBot|FAST-WebCrawler|Superdownloads Spiderman|LinkWalker|msnbot|ASPSeek|WebAlta Crawler|Lycos|FeedFetcher-Google|Yahoo|YoudaoBot|AdsBot-Google|Googlebot|Scooter|Gigabot|Charlotte|eStyle|AcioRobot|GeonaBot|msnbot-media|Baidu|CocoCrawler|Google|Charlotte t|Yahoo! Slurp China|Sogou web spider|YodaoBot|MSRBOT|AbachoBOT|Sogou head spider|AltaVista|IDBot|Sosospider|Yahoo! Slurp|Java VM|DotBot|LiteFinder|Yeti|Rambler|Scrubby|Baiduspider|accoona');
$crawlers = explode("|", $crawlers_agents);
foreach($crawlers as $crawler)
{
if (strpos($ua, trim($crawler)) !== false)
return true;
}
return false;
}
/**
* Get the system time zone.
* @return string
*/
public static function get_system_time_zone()
{
if (file_exists('/etc/timezone'))
{
// Ubuntu / Debian.
$data = file_get_contents('/etc/timezone');
if ($data)
return trim($data);
}
elseif (is_link('/etc/localtime'))
{
// Mac OS X (and older Linuxes)
// /etc/localtime is a symlink to the
// timezone in /usr/share/zoneinfo.
$filename = readlink('/etc/localtime');
if (strpos($filename, '/usr/share/zoneinfo/') === 0)
return substr($filename, 20);
}
elseif (file_exists('/etc/sysconfig/clock'))
{
// RHEL / CentOS
$data = parse_ini_file('/etc/sysconfig/clock');
if (!empty($data['ZONE']))
return trim($data['ZONE']);
}
return 'UTC';
}
/**
* Execute a command using the proc_open function and pipe stderr and stdout back to the caller.
*
* @param $command
* @param string $base_dir
* @return array
*/
public static function run_command($command)
{
ob_start();
exec($command, $stdout, $return_code);
$stderr = ob_get_clean();
return [
'output'=> trim(implode("\n", $stdout)),
'error' => (string)$stderr,
'code' => $return_code,
];
}
/**
* Recursively remove a directory and its contents.
*
* @param $dir
*/
public static function rmdir_recursive($dir)
{
if(is_dir($dir))
{
$files = array_diff(scandir($dir), array('.','..'));
foreach ($files as $file)
self::rmdir_recursive($dir.'/'.$file);
@rmdir($dir);
}
else
{
@unlink($dir);
}
}
/**
* Convert from a bytes number to text (i.e.
*
* @param $bytes
* @return string
*/
public static function bytes_to_text($bytes)
{
$si_prefix = array( 'B', 'KB', 'MB', 'GB', 'TB', 'EB', 'ZB', 'YB' );
$base = 1024;
$class = min((int)log($bytes , $base) , count($si_prefix) - 1);
return sprintf('%1.2f' , $bytes / pow($base,$class)) . ' ' . $si_prefix[$class];
}
}

View File

@ -1,35 +0,0 @@
<?php
/**
* Extends the Zend Config XML library to allow attribute handling.
*/
namespace App\Xml;
use XMLReader;
use Zend\Config\Exception;
/**
* XML config reader.
*/
class Reader extends \Zend\Config\Reader\Xml
{
/**
* Get all attributes on the current node.
*
* @return array
*/
protected function getAttributes()
{
$attributes = [];
if ($this->reader->hasAttributes) {
while ($this->reader->moveToNextAttribute()) {
$attributes['@'.$this->reader->localName] = $this->reader->value;
}
$this->reader->moveToElement();
}
return $attributes;
}
}

View File

@ -1,112 +0,0 @@
<?php
/**
* Extends the Zend Config XML library to allow attribute handling.
*/
namespace App\Xml;
use Traversable;
use Zend\Config\Exception;
use Zend\Stdlib\ArrayUtils;
use XMLWriter;
class Writer extends \Zend\Config\Writer\Xml
{
/**
* toString(): defined by Writer interface.
*
* @see WriterInterface::toString()
* @param mixed $config
* @param string $base_element
* @return string
*/
public function toString($config, $base_element = 'zend-config')
{
if ($config instanceof Traversable) {
$config = ArrayUtils::iteratorToArray($config);
} elseif (!is_array($config)) {
throw new Exception\InvalidArgumentException(__METHOD__ . ' expects an array or Traversable config');
}
return $this->processConfig($config, $base_element);
}
/**
* processConfig(): defined by AbstractWriter.
*
* @param array $config
* @param string $base_element
* @return string
*/
public function processConfig(array $config, $base_element = 'zend-config')
{
$writer = new XMLWriter();
$writer->openMemory();
$writer->setIndent(true);
$writer->setIndentString(str_repeat(' ', 4));
$writer->startDocument('1.0', 'UTF-8');
$writer->startElement($base_element);
foreach ($config as $sectionName => $data) {
if (!is_array($data)) {
$writer->writeElement($sectionName, (string) $data);
} else {
$this->addBranch($sectionName, $data, $writer);
}
}
$writer->endElement();
$writer->endDocument();
return $writer->outputMemory();
}
/**
* Add a branch to an XML object recursively.
*
* @param string $branchName
* @param array $config
* @param XMLWriter $writer
* @return void
* @throws Exception\RuntimeException
*/
protected function addBranch($branchName, array $config, XMLWriter $writer)
{
$branchType = null;
foreach ($config as $key => $value) {
if ($branchType === null) {
if (is_numeric($key)) {
$branchType = 'numeric';
} else {
$writer->startElement($branchName);
$branchType = 'string';
}
} elseif ($branchType !== (is_numeric($key) ? 'numeric' : 'string')) {
throw new Exception\RuntimeException('Mixing of string and numeric keys is not allowed');
}
if ($branchType === 'numeric') {
if (is_array($value)) {
$this->addBranch($branchName, $value, $writer);
} else {
$writer->writeElement($branchName, (string) $value);
}
} else {
if (is_array($value)) {
$this->addBranch($key, $value, $writer);
} else {
if (substr($key, 0, 1) == '@')
$writer->writeAttribute(substr($key, 1), (string)$value);
else
$writer->writeElement($key, (string) $value);
}
}
}
if ($branchType === 'string') {
$writer->endElement();
}
}
}