at the end of the day, it was inevitable

This commit is contained in:
Mo Elzubeir
2022-12-09 08:36:26 -06:00
commit 1218570914
1768 changed files with 887087 additions and 0 deletions
@@ -0,0 +1,212 @@
<?php
namespace AppBundle\Configuration;
/**
* Class AbstractConfiguration
* @package AppBundle\Configuration
*/
abstract class AbstractConfiguration implements ConfigurationInterface
{
/**
* @var ConfigurationParameterInterface[]
*/
private $map = [];
/**
* Array of changed parameters name.
*
* @var string[]
*/
private $changed = [];
/**
* Array of removed parameters name.
*
* @var string[]
*/
private $removed = [];
/**
* @var ConfigurationDefinitionMap
*/
protected $definitions;
/**
* AbstractConfiguration constructor.
*
* @param ConfigurationDefinitionMap $definitions A ConfigurationDefinitionMap
* instance.
*/
public function __construct(ConfigurationDefinitionMap $definitions)
{
$this->syncParameters();
$this->definitions = $definitions;
}
/**
* Get parameter value by name.
*
* @param string $name Parameter name.
* @param mixed $default Default value if parameter not found.
*
* @return mixed
*/
public function getParameter($name, $default = null)
{
if (! isset($this->map[$name]) || isset($this->removed[$name])) {
return $default;
}
$param = $this->map[$name];
if ($param === null) {
return $default;
}
$value = $param->getValue();
settype($value, $this->definitions->getDefinition($name)['type']);
return $value;
}
/**
* Sync current parameters with database.
*
* @return void
*/
public function syncParameters()
{
$params = $this->loadData();
foreach ($params as $param) {
$this->map[$param->getName()] = $param;
}
}
/**
* Get all available parameters.
*
* @return ConfigurationParameterInterface[]
*/
public function getParameters()
{
return $this->map;
}
/**
* Get parameter value by name.
*
* @param string $name Parameter name.
* @param mixed $value New parameter value.
*
* @return void
*/
public function setParameter($name, $value)
{
$this->map[$name]->setValue($this->definitions->normalize($name, $value));
$this->changed[$name] = true;
}
/**
* Set parameters.
*
* @param array $params Array where key is parameter name and value is new
* value.
*
* @return void
*/
public function setParameters(array $params)
{
foreach ($params as $name => $newValue) {
$this->setParameter($name, $newValue);
}
}
/**
* Sync configuration with storage.
*
* @return void
*/
public function sync()
{
$changed = \nspl\a\filter(function (ConfigurationParameterInterface $parameter) {
return isset($this->changed[$parameter->getName()]);
}, $this->map);
$removed = \nspl\a\filter(function (ConfigurationParameterInterface $parameter) {
return isset($this->removed[$parameter->getName()]);
}, $this->map);
if ((count($changed) === 0) && (count($removed) === 0)) {
return;
}
$this->doSync($changed, $removed);
$this->map = \nspl\a\filter(function (ConfigurationParameterInterface $parameter) {
return ! isset($this->removed[$parameter->getName()]);
}, $this->map);
$this->changed = [];
$this->removed = [];
}
/**
* Sync parameters with list of available.
*
* @return void
*/
public function syncWithDefinitions()
{
$notExists = array_flip(ParametersName::getAvailables());
/** @var ConfigurationParameterMutableInterface $parameter */
foreach ($this->map as $name => $parameter) {
if (! ParametersName::isExists($name)) {
$this->removed[$name] = $parameter;
} else {
$definition = $this->definitions->getDefinition($name);
$parameter
->setTitle($definition['title'])
->setSection($definition['section']);
$this->changed[$name] = $parameter;
unset($notExists[$name]);
}
}
$notExists = array_keys($notExists);
foreach ($notExists as $name) {
$this->map[$name] = $this->createParameter($name);
$this->changed[$name] = true;
}
$this->sync();
}
/**
* Create default parameter from config.
*
* @param string $name Parameter name.
*
* @return ConfigurationParameterInterface
*/
abstract protected function createParameter($name);
/**
* Load configuration from storage.
*
* @return ConfigurationParameterInterface[]
*/
abstract protected function loadData();
/**
* @param ConfigurationParameterInterface[]|array $changed Array of changed
* instances.
* @param ConfigurationParameterInterface[]|array $removed Array of removed
* parameter names.
*
* @return void
*/
abstract protected function doSync(array $changed, array $removed);
}
@@ -0,0 +1,324 @@
<?php
namespace AppBundle\Configuration;
use Ivory\CKEditorBundle\Form\Type\CKEditorType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Validator\Constraints\Callback;
use Symfony\Component\Validator\Constraints\Type;
use Symfony\Component\Validator\Context\ExecutionContextInterface;
use Traversable;
/**
* Class ConfigurationDefinitionMap
* @package AppBundle\Configuration
*/
class ConfigurationDefinitionMap implements \IteratorAggregate
{
/**
* @var string[]
*/
private static $availablePeriods = [
'day',
'days',
'week',
'weeks',
'month',
'months',
'year',
'years',
'hour',
'hours',
'minute',
'minutes',
'second',
'seconds',
];
/**
* @var array[]
*/
private $definitions;
/**
* ConfigurationDefinitionMap constructor.
*/
public function __construct()
{
$this->definitions = [
ParametersName::MAILER_ADDRESS => [
'section' => 'Mailer',
'title' => 'support@socialhose.io',
'type' => 'string',
'formType' => null,
'default' => 'support@socialhose.io',
'normalizer' => null,
'denormalizer' => null,
'constrains' => new Type([ 'type' => 'string' ]),
],
ParametersName::MAILER_SENDER_NAME => [
'section' => 'Mailer',
'title' => 'Socialhose',
'type' => 'string',
'formType' => null,
'default' => 'Socialhose',
'normalizer' => null,
'denormalizer' => null,
'constrains' => new Type([ 'type' => 'string' ]),
],
ParametersName::NOTIFICATION_COMMENTS_PER_DOCUMENT => [
'section' => 'Notification',
'title' => 'Max comments per document',
'type' => 'integer',
'formType' => null,
'default' => 5,
'normalizer' => null,
'denormalizer' => null,
'constrains' => new Type([ 'type' => 'numeric' ]),
],
ParametersName::NOTIFICATION_DOCUMENT_PER_FEED => [
'section' => 'Notification',
'title' => 'Max documents per feed in notification',
'type' => 'integer',
'formType' => null,
'default' => 10,
'normalizer' => null,
'denormalizer' => null,
'constrains' => new Type([ 'type' => 'numeric' ]),
],
ParametersName::NOTIFICATION_START_EXTRACT_LENGTH => [
'section' => 'Search',
'title' => 'Number of character for \'Start of text extract\'',
'type' => 'integer',
'formType' => null,
'default' => 400,
'normalizer' => null,
'denormalizer' => null,
'constrains' => new Type([ 'type' => 'numeric' ]),
],
ParametersName::NOTIFICATION_CONTEXT_EXTRACT_LENGTH => [
'section' => 'Search',
'title' => 'Numbers of character before and after first search keyword',
'type' => 'integer',
'formType' => null,
'default' => 150,
'normalizer' => null,
'denormalizer' => null,
'constrains' => new Type([ 'type' => 'numeric' ]),
],
ParametersName::SEARCH_DOCUMENTS_FROM_FUTURE => [
'section' => 'Search',
'title' => 'What we should do if documents published date in future',
'type' => 'string',
'formType' => ChoiceType::class,
'choices' => [
'Exclude' => 'exclude',
'Fix date' => 'fix_date',
],
'default' => 'exclude',
'normalizer' => null,
'denormalizer' => null,
'constrains' => new Type([ 'type' => 'numeric' ]),
],
ParametersName::NOTIFICATION_EMPTY_MESSAGE => [
'section' => 'Notification',
'title' => 'Empty notification message',
'type' => 'string',
'formType' => CKEditorType::class,
'default' => '<p>We have not found any mentions for your search criteria today.</p>',
'normalizer' => null,
'denormalizer' => null,
'constrains' => new Type([ 'type' => 'string' ]),
],
ParametersName::NOTIFICATION_SEND_HISTORY_MODIFY => [
'section' => 'Notification',
'title' => 'How long we story notification history',
'type' => 'string',
'formType' => null,
'default' => '-3 months',
'normalizer' => function ($value) {
return preg_replace('/(\d+)/', '-$1', str_replace('-', '', $value));
},
'denormalizer' => function ($value) {
return str_replace('-', '', $value);
},
'constrains' => [
new Type([ 'type' => 'string' ]),
new Callback([ $this, 'validateHistoryLifetime' ]),
],
],
ParametersName::REGISTRATION_PAYMENT_AWAITING => [
'section' => 'Registration',
'title' => 'Message after user provide billing information',
'type' => 'string',
'formType' => CKEditorType::class,
'default' => '<p>Thanks for submitting the form. Your payment is processing. When it done, you will receive email with passwird.</p>',
'normalizer' => null,
'denormalizer' => null,
'constrains' => new Type([ 'type' => 'string' ]),
],
ParametersName::MAIL_PASSWORD => [
'section' => 'Email',
'title' => 'Password email content',
'type' => 'string',
'formType' => CKEditorType::class,
'default' => '<h4>Hello {{ user.firstName }} {{ user.lastName }}!</h4><p>You new password is {{ password }}</p><p>Regards, the Team.</p>',
'normalizer' => null,
'denormalizer' => null,
'constrains' => new Type([ 'type' => 'string' ]),
],
ParametersName::MAIL_VERIFICATION_SUCCESS => [
'section' => 'Email',
'title' => 'Verification success email content',
'type' => 'string',
'formType' => CKEditorType::class,
'default' => '<h4>Hello {{ user.firstName }} {{ user.lastName }}!</h4><p>You registration is verified and you may proceed login with you credentials </p><p> Email: {{ user.email }} Password: {{ password }}</p><p>Regards, the Team.</p>',
'normalizer' => null,
'denormalizer' => null,
'constrains' => new Type([ 'type' => 'string' ]),
],
ParametersName::MAIL_VERIFICATION_REJECT => [
'section' => 'Email',
'title' => 'Verification success email content',
'type' => 'string',
'formType' => CKEditorType::class,
'default' => '<h4>Hello {{ user.firstName }} {{ user.lastName }}!</h4><p>Unfortunately you registration is rejected. Payments will be refund. </p><p>Regards, the Team.</p>',
'normalizer' => null,
'denormalizer' => null,
'constrains' => new Type([ 'type' => 'string' ]),
],
ParametersName::MAIL_RESETTING_CONFIRMATION => [
'section' => 'Email',
'title' => 'Password resetting email content',
'type' => 'string',
'formType' => CKEditorType::class,
'default' => '<h4>Hello {{ user.firstName }} {{ user.lastName }}!</h4> <p> To reset your password - please visit {{ confirmationUrl }} </p> <p> Regards, the Team. </p>',
'normalizer' => null,
'denormalizer' => null,
'constrains' => new Type([ 'type' => 'string' ]),
],
ParametersName::MAIL_UNSUBSCRIBE => [
'section' => 'Email',
'title' => 'Unsubscribe email content',
'type' => 'string',
'formType' => CKEditorType::class,
'default' => '<p>{{ user.firstName}} {{ user.lastName }} has unsubscribed from your notification</p>',
'normalizer' => null,
'denormalizer' => null,
'constrains' => new Type([ 'type' => 'string' ]),
],
];
}
/**
* Get definition for specified parameter.
*
* @param string $name Parameter name.
*
* @return array
*/
public function getDefinition($name)
{
if (! isset($this->definitions[$name])) {
throw new \InvalidArgumentException('Unknown '. $name);
}
return $this->definitions[$name];
}
/**
* Normalize value.
*
* @param string $name Parameter name.
* @param mixed $value Raw value.
*
* @return mixed
*/
public function normalize($name, $value)
{
$definition = $this->getDefinition($name);
if (isset($definition['normalizer'])) {
$value = $definition['normalizer']($value);
}
return $value;
}
/**
* Denormalize value.
*
* @param string $name Parameter name.
* @param mixed $value Normalized value.
*
* @return mixed
*/
public function denormalize($name, $value)
{
$definition = $this->getDefinition($name);
if (isset($definition['denormalizer'])) {
$value = $definition['denormalizer']($value);
}
return $value;
}
/**
* Retrieve an external iterator.
*
* @return Traversable An instance of an object implementing \Iterator or
* \Traversable
*/
public function getIterator()
{
return new \ArrayIterator($this->definitions);
}
/**
* @param string $value Raw value from user.
* @param ExecutionContextInterface $context A ExecutionContextInterface instance.
*
* @return void
*/
public function validateHistoryLifetime($value, ExecutionContextInterface $context)
{
//
// Split expiration time on groups
//
$valid = true;
$matches = [];
$result = preg_match_all('/(\d+\s?[A-Za-z]+)/', $value, $matches);
if (($result === 0) || ($result === false)) {
$valid = false;
} else {
$matches = $matches[0];
foreach ($matches as $match) {
$match = trim($match);
$parts = explode(' ', $match);
$count = 1;
$period = $parts[0];
if (count($parts) === 2) {
list($count, $period) = $parts;
}
if (! is_numeric($count) || !in_array($period, self::$availablePeriods, true)) {
$valid = false;
break;
}
}
}
if (! $valid) {
$context->buildViolation('Invalid expiration time, should be space separated string where each element id match to patter "number year(s)|month(s)|week(s)|day(s)|hour(s)|minute(s)|second(s)"')
->atPath('expirationTime')
->addViolation();
}
}
}
@@ -0,0 +1,28 @@
<?php
namespace AppBundle\Configuration;
/**
* Interface ConfigurationImmutableInterface
* @package AppBundle\Configuration
*/
interface ConfigurationImmutableInterface
{
/**
* Get parameter value by name.
*
* @param string $name Parameter name.
* @param mixed $default Default value if parameter not found.
*
* @return mixed
*/
public function getParameter($name, $default = null);
/**
* Sync current parameters with database.
*
* @return void
*/
public function syncParameters();
}
@@ -0,0 +1,13 @@
<?php
namespace AppBundle\Configuration;
/**
* Interface ConfigurationMutableInterface
* @package AppBundle\Configuration
*/
interface ConfigurationInterface extends
ConfigurationMutableInterface,
ConfigurationImmutableInterface
{
}
@@ -0,0 +1,52 @@
<?php
namespace AppBundle\Configuration;
/**
* Interface ConfigurationMutableInterface
* @package AppBundle\Configuration
*/
interface ConfigurationMutableInterface
{
/**
* Get all available parameters.
*
* @return ConfigurationParameterInterface[]
*/
public function getParameters();
/**
* Sync parameters with list of available.
*
* @return void
*/
public function syncWithDefinitions();
/**
* Set parameter value by name.
*
* @param string $name Parameter name.
* @param mixed $value New parameter value.
*
* @return void
*/
public function setParameter($name, $value);
/**
* Set parameters.
*
* @param array $params Array where key is parameter name and value is new
* value.
*
* @return void
*/
public function setParameters(array $params);
/**
* Sync configuration with storage.
*
* @return void
*/
public function sync();
}
@@ -0,0 +1,48 @@
<?php
namespace AppBundle\Configuration;
/**
* Interface ConfigurationParameterInterface
* @package AppBundle\Configuration
*/
interface ConfigurationParameterInterface
{
/**
* Get parameter section.
*
* @return string
*/
public function getSection();
/**
* Get parameter name.
*
* @return string
*/
public function getName();
/**
* Get parameter title.
*
* @return string
*/
public function getTitle();
/**
* Get parameter value.
*
* @param mixed $value Parameter value.
*
* @return ConfigurationParameterInterface
*/
public function setValue($value);
/**
* Get parameter value.
*
* @return mixed
*/
public function getValue();
}
@@ -0,0 +1,38 @@
<?php
namespace AppBundle\Configuration;
/**
* Interface ConfigurationParameterMutableInterface
* @package AppBundle\Configuration
*/
interface ConfigurationParameterMutableInterface
{
/**
* Set section
*
* @param string $section Section name.
*
* @return ConfigurationParameterMutableInterface
*/
public function setSection($section);
/**
* Set value
*
* @param mixed $value Parameter value.
*
* @return ConfigurationParameterMutableInterface
*/
public function setValue($value);
/**
* Set title
*
* @param string $title Human readable parameter title.
*
* @return ConfigurationParameterMutableInterface
*/
public function setTitle($title);
}
@@ -0,0 +1,85 @@
<?php
namespace AppBundle\Configuration;
use AdminBundle\Entity\SiteSettings;
use Doctrine\ORM\EntityManagerInterface;
/**
* Class ORMConfiguration
* @package AppBundle\Configuration
*/
class ORMConfiguration extends AbstractConfiguration
{
/**
* @var EntityManagerInterface
*/
private $em;
/**
* Configuration constructor.
*
* @param ConfigurationDefinitionMap $definitions A ConfigurationDefinitionMap
* instance.
* @param EntityManagerInterface $em A EntityManagerInterface
* instance.
*/
public function __construct(
ConfigurationDefinitionMap $definitions,
EntityManagerInterface $em
) {
$this->em = $em;
parent::__construct($definitions);
}
/**
* Create default parameter from config.
*
* @param string $name Parameter name.
*
* @return ConfigurationParameterInterface
*/
protected function createParameter($name)
{
$config = $this->definitions->getDefinition($name);
return SiteSettings::create()
->setSection($config['section'])
->setName($name)
->setTitle($config['title'])
->setValue($config['default']);
}
/**
* Load configuration from storage.
*
* @return ConfigurationParameterInterface[]
*/
protected function loadData()
{
return $this->em->getRepository(SiteSettings::class)->findAll();
}
/**
* @param ConfigurationParameterInterface[]|array $changed Array of changed
* instances.
* @param ConfigurationParameterInterface[]|array $removed Array of removed
* parameter names.
*
* @return void
*/
protected function doSync(array $changed, array $removed)
{
foreach ($changed as $parameter) {
$this->em->persist($parameter);
}
foreach ($removed as $parameter) {
$this->em->remove($parameter);
}
$this->em->flush();
}
}
@@ -0,0 +1,85 @@
<?php
namespace AppBundle\Configuration;
/**
* Class ParametersName
* @package AppBundle\Configuration
*/
final class ParametersName
{
const MAILER_ADDRESS = 'mailer.address';
const MAILER_SENDER_NAME = 'mailer.sender_name';
const NOTIFICATION_COMMENTS_PER_DOCUMENT = 'notification.comments_per_document_limit';
const NOTIFICATION_DOCUMENT_PER_FEED = 'notification.documents_per_feed_limit';
const NOTIFICATION_START_EXTRACT_LENGTH = 'notification.start_extract_length';
const NOTIFICATION_CONTEXT_EXTRACT_LENGTH = 'notification.context_extract_length';
const NOTIFICATION_SEND_HISTORY_MODIFY = 'notification.notification_send_history_modify';
const NOTIFICATION_EMPTY_MESSAGE = 'notification.empty_massage';
const REGISTRATION_PAYMENT_AWAITING = 'registration.payment.awaiting';
const SEARCH_DOCUMENTS_FROM_FUTURE = 'search.documents_from_future';
const MAIL_PASSWORD = 'mail.password';
const MAIL_VERIFICATION_SUCCESS = 'mail.verification.success';
const MAIL_VERIFICATION_REJECT = 'mail.verification.reject';
const MAIL_RESETTING_CONFIRMATION = 'mail.resetting_confirmation';
const MAIL_UNSUBSCRIBE = 'mail.unsubscribe';
/**
* Get available parameters name.
*
* @return array
*/
public static function getAvailables()
{
return [
self::MAILER_ADDRESS,
self::MAILER_SENDER_NAME,
self::NOTIFICATION_COMMENTS_PER_DOCUMENT,
self::NOTIFICATION_DOCUMENT_PER_FEED,
self::NOTIFICATION_START_EXTRACT_LENGTH,
self::NOTIFICATION_CONTEXT_EXTRACT_LENGTH,
self::NOTIFICATION_SEND_HISTORY_MODIFY,
self::NOTIFICATION_EMPTY_MESSAGE,
self::REGISTRATION_PAYMENT_AWAITING,
self::SEARCH_DOCUMENTS_FROM_FUTURE,
self::MAIL_PASSWORD,
self::MAIL_VERIFICATION_SUCCESS,
self::MAIL_VERIFICATION_REJECT,
self::MAIL_RESETTING_CONFIRMATION,
self::MAIL_UNSUBSCRIBE,
];
}
/**
* @param string $name Checks that specified parameter is exists.
*
* @return boolean
*/
public static function isExists($name)
{
return in_array($name, self::getAvailables(), true);
}
/**
* Parameters constructor.
*/
private function __construct()
{
}
/**
* @return void
*/
private function __clone()
{
}
}