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:
parent
95e393d60d
commit
e9c62a3ea5
|
@ -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 = [
|
||||
|
|
|
@ -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/**/*
|
||||
|
|
|
@ -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",
|
||||
|
|
File diff suppressed because it is too large
Load Diff
185
src/App/Acl.php
185
src/App/Acl.php
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
228
src/App/Auth.php
228
src/App/Auth.php
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
136
src/App/Csrf.php
136
src/App/Csrf.php
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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 ' ';
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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';
|
||||
}
|
||||
}
|
|
@ -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()';
|
||||
}
|
||||
}
|
|
@ -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()
|
||||
{}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -1,3 +0,0 @@
|
|||
<?php
|
||||
namespace App;
|
||||
class Exception extends \Exception {}
|
|
@ -1,7 +0,0 @@
|
|||
<?php
|
||||
namespace App\Exception;
|
||||
|
||||
class Bootstrap extends \App\Exception
|
||||
{
|
||||
|
||||
}
|
|
@ -1,3 +0,0 @@
|
|||
<?php
|
||||
namespace App\Exception;
|
||||
class NotLoggedIn extends \Exception {}
|
|
@ -1,7 +0,0 @@
|
|||
<?php
|
||||
/**
|
||||
* Permission Denied exception
|
||||
*/
|
||||
|
||||
namespace App\Exception;
|
||||
class PermissionDenied extends \Exception {}
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
281
src/App/File.php
281
src/App/File.php
|
@ -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());
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
216
src/App/Form.php
216
src/App/Form.php
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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'].'">«</a></li>';
|
||||
else
|
||||
$return_string .= '<li class="prev disabled"><a href="#">«</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'].'"><</a></li>';
|
||||
else
|
||||
$return_string .= '<li class="disabled"><a href="#"><</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'].'">></a></li>';
|
||||
else
|
||||
$return_string .= '<li class="disabled"><a href="#">></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'].'">»</a></li>';
|
||||
else
|
||||
$return_string .= '<li class="next disabled"><a href="#">»</a></li>';
|
||||
|
||||
$return_string .= '</ul></nav>';
|
||||
}
|
||||
|
||||
return $return_string;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
|
@ -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]);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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]);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
276
src/App/Url.php
276
src/App/Url.php
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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];
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue