This commit is contained in:
emmymayo
2025-02-05 23:15:46 +01:00
commit 7269c99357
16995 changed files with 3389680 additions and 0 deletions
@@ -0,0 +1,72 @@
<?php // phpcs:ignore SlevomatCodingStandard.TypeHints.DeclareStrictTypes.DeclareStrictTypesMissing
namespace MailPoet\Listing;
if (!defined('ABSPATH')) exit;
class Handler {
public function getListingDefinition(array $data): ListingDefinition {
$data = $this->processData($data);
return new ListingDefinition(
$data['group'],
$data['filter'] ?? [],
$data['search'],
$data['params'] ?? [],
$data['sort_by'],
$data['sort_order'],
$data['offset'],
$data['limit'],
$data['selection'] ?? []
);
}
/**
* Polyfill for deprecated FILTER_SANITIZE_STRING which was used to sanitize
* $data['sort_by'].
*/
private function filterString(string $string): string {
$str = (string)preg_replace('/\x00|<[^>]*>?/', '', $string);
return str_replace(["'", '"'], ['&#39;', '&#34;'], $str);
}
private function processData(array $data) {
// check if sort order was specified or default to "asc"
$sortOrder = (!empty($data['sort_order'])) ? $data['sort_order'] : 'asc';
// constrain sort order value to either be "asc" or "desc"
$sortOrder = ($sortOrder === 'asc') ? 'asc' : 'desc';
// sanitize sort by
$sortBy = (!empty($data['sort_by']))
? $this->filterString($data['sort_by'])
: '';
if (empty($sortBy)) {
$sortBy = 'id';
}
$data = [
// extra parameters
'params' => (isset($data['params']) ? $data['params'] : []),
// pagination
'offset' => (isset($data['offset']) ? (int)$data['offset'] : 0),
'limit' => (isset($data['limit'])
? (int)$data['limit']
: PageLimit::DEFAULT_LIMIT_PER_PAGE
),
// searching
'search' => (isset($data['search']) ? $data['search'] : null),
// sorting
'sort_by' => $sortBy,
'sort_order' => $sortOrder,
// grouping
'group' => (isset($data['group']) ? $data['group'] : null),
// filters
'filter' => (isset($data['filter']) ? $data['filter'] : null),
// selection
'selection' => (isset($data['selection']) ? $data['selection'] : null),
];
return $data;
}
}
@@ -0,0 +1,95 @@
<?php declare(strict_types = 1);
namespace MailPoet\Listing;
if (!defined('ABSPATH')) exit;
class ListingDefinition {
/** @var string|null */
private $group;
/** @var array */
private $filters;
/** @var string|null */
private $search;
/** @var array */
private $parameters;
/** @var string */
private $sortBy;
/** @var string */
private $sortOrder;
/** @var int */
private $offset;
/** @var int */
private $limit;
/** @var int[] */
private $selection;
public function __construct(
string $group = null,
array $filters = [],
string $search = null,
array $parameters = [],
string $sortBy = 'created_at',
string $sortOrder = 'desc',
int $offset = 0,
int $limit = 20,
array $selection = []
) {
$this->group = $group;
$this->filters = $filters;
$this->search = $search;
$this->parameters = $parameters;
$this->sortBy = $sortBy;
$this->sortOrder = $sortOrder;
$this->offset = $offset;
$this->limit = $limit;
$this->selection = array_map('intval', $selection);
}
/** @return string|null */
public function getGroup() {
return $this->group;
}
public function getFilters(): array {
return $this->filters;
}
/** @return string|null */
public function getSearch() {
return $this->search;
}
public function getParameters(): array {
return $this->parameters;
}
public function getSortBy(): string {
return $this->sortBy;
}
public function getSortOrder(): string {
return $this->sortOrder;
}
public function getOffset(): int {
return $this->offset;
}
public function getLimit(): int {
return $this->limit;
}
public function getSelection(): array {
return $this->selection;
}
}
@@ -0,0 +1,108 @@
<?php declare(strict_types = 1);
namespace MailPoet\Listing;
if (!defined('ABSPATH')) exit;
use MailPoet\Util\Helpers;
use MailPoetVendor\Doctrine\ORM\EntityManager;
use MailPoetVendor\Doctrine\ORM\QueryBuilder;
abstract class ListingRepository {
/** @var QueryBuilder */
protected $queryBuilder;
public function __construct(
EntityManager $entityManager
) {
$this->queryBuilder = $entityManager->createQueryBuilder();
}
public function getData(ListingDefinition $definition): array {
$queryBuilder = clone $this->queryBuilder;
$sortBy = Helpers::underscoreToCamelCase($definition->getSortBy());
$this->applySelectClause($queryBuilder);
$this->applyFromClause($queryBuilder);
$this->applyConstraints($queryBuilder, $definition);
$this->applySorting($queryBuilder, $sortBy, $definition->getSortOrder());
$this->applyPaging($queryBuilder, $definition->getOffset(), $definition->getLimit());
return $queryBuilder->getQuery()->getResult();
}
public function getCount(ListingDefinition $definition): int {
$queryBuilder = clone $this->queryBuilder;
$this->applyFromClause($queryBuilder);
$this->applyConstraints($queryBuilder, $definition);
$alias = $queryBuilder->getRootAliases()[0];
$queryBuilder->select("COUNT(DISTINCT $alias)");
return (int)$queryBuilder->getQuery()->getSingleScalarResult();
}
public function getActionableIds(ListingDefinition $definition): array {
$ids = $definition->getSelection();
if (!empty($ids)) {
return $ids;
}
$queryBuilder = clone $this->queryBuilder;
$this->applyFromClause($queryBuilder);
$this->applyConstraints($queryBuilder, $definition);
$alias = $queryBuilder->getRootAliases()[0];
$queryBuilder->select("$alias.id");
$ids = $queryBuilder->getQuery()->getScalarResult();
return array_column($ids, 'id');
}
public function getGroups(ListingDefinition $definition): array {
return [];
}
public function getFilters(ListingDefinition $definition): array {
return [];
}
abstract protected function applySelectClause(QueryBuilder $queryBuilder);
abstract protected function applyFromClause(QueryBuilder $queryBuilder);
protected function applyConstraints(QueryBuilder $queryBuilder, ListingDefinition $definition) {
$group = $definition->getGroup();
if ($group) {
$this->applyGroup($queryBuilder, $group);
}
$search = $definition->getSearch();
$parameters = $definition->getParameters();
if ($search && strlen(trim($search)) > 0) {
$this->applySearch($queryBuilder, $search, $parameters ?: []);
}
$filters = $definition->getFilters();
if ($filters) {
$this->applyFilters($queryBuilder, $filters);
}
if ($parameters) {
$this->applyParameters($queryBuilder, $parameters);
}
}
abstract protected function applyGroup(QueryBuilder $queryBuilder, string $group);
abstract protected function applySearch(QueryBuilder $queryBuilder, string $search, array $parameters);
abstract protected function applyFilters(QueryBuilder $queryBuilder, array $filters);
abstract protected function applyParameters(QueryBuilder $queryBuilder, array $parameters);
protected function applySorting(QueryBuilder $queryBuilder, string $sortBy, string $sortOrder) {
$alias = $this->queryBuilder->getRootAliases()[0];
$queryBuilder->addOrderBy("$alias.$sortBy", $sortOrder);
}
protected function applyPaging(QueryBuilder $queryBuilder, int $offset, int $limit) {
$queryBuilder->setFirstResult($offset);
$queryBuilder->setMaxResults($limit);
}
}
@@ -0,0 +1,36 @@
<?php // phpcs:ignore SlevomatCodingStandard.TypeHints.DeclareStrictTypes.DeclareStrictTypesMissing
namespace MailPoet\Listing;
if (!defined('ABSPATH')) exit;
use MailPoet\WP\Functions as WPFunctions;
class PageLimit {
const DEFAULT_LIMIT_PER_PAGE = 20;
/** @var WPFunctions */
private $wp;
public function __construct(
WPFunctions $wp
) {
$this->wp = $wp;
}
public function getLimitPerPage($model = null) {
if ($model === null) {
return self::DEFAULT_LIMIT_PER_PAGE;
}
$listingPerPage = $this->wp->getUserMeta(
$this->wp->getCurrentUserId(),
'mailpoet_' . $model . '_per_page',
true
);
return (!empty($listingPerPage))
? (int)$listingPerPage
: self::DEFAULT_LIMIT_PER_PAGE;
}
}
@@ -0,0 +1 @@
<?php