ver. 0.279: Newsletter frontend migration, Languages facade elimination, bug fix newsletter_unsubscribe
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -8,11 +8,11 @@ Gdy użytkownik napisze `KONIEC PRACY`, wykonaj kolejno:
|
|||||||
2. Aktualizacja dokumentacji technicznej, jeśli zmiany tego wymagają:
|
2. Aktualizacja dokumentacji technicznej, jeśli zmiany tego wymagają:
|
||||||
- `docs/DATABASE_STRUCTURE.md`
|
- `docs/DATABASE_STRUCTURE.md`
|
||||||
- `docs/PROJECT_STRUCTURE.md`
|
- `docs/PROJECT_STRUCTURE.md`
|
||||||
- `docs/REFACTORING_PLAN.md`
|
- `docs/FRONTEND_REFACTORING_PLAN.md`
|
||||||
- `docs/FORM_EDIT_SYSTEM.md`
|
- `docs/FORM_EDIT_SYSTEM.md`
|
||||||
- `docs/CHANGELOG.md`
|
- `docs/CHANGELOG.md`
|
||||||
- `docs/TESTING.md`
|
- `docs/TESTING.md`
|
||||||
3. Przygotowanie aktualizacji zgodnie z plikiem UPDATE_INSTRUCTIONS.md (ZIP, plik z usuwanymi plikami, plik SQL jeśli wymagany).
|
3. Przygotowanie aktualizacji zgodnie z plikiem docs/UPDATE_INSTRUCTIONS.md (ZIP, plik z usuwanymi plikami, plik SQL jeśli wymagany).
|
||||||
4. Commit.
|
4. Commit.
|
||||||
5. Push.
|
5. Push.
|
||||||
|
|
||||||
@@ -23,6 +23,7 @@ Przed rozpoczęciem implementacji sprawdź aktualną zawartość:
|
|||||||
- `docs/DATABASE_STRUCTURE.md`
|
- `docs/DATABASE_STRUCTURE.md`
|
||||||
- `docs/PROJECT_STRUCTURE.md`
|
- `docs/PROJECT_STRUCTURE.md`
|
||||||
- `docs/REFACTORING_PLAN.md`
|
- `docs/REFACTORING_PLAN.md`
|
||||||
|
- `docs/FRONTEND_REFACTORING_PLAN.md`
|
||||||
- `docs/CHANGELOG.md`
|
- `docs/CHANGELOG.md`
|
||||||
- `docs/TESTING.md`
|
- `docs/TESTING.md`
|
||||||
|
|
||||||
|
|||||||
@@ -66,15 +66,17 @@ if ( $_SESSION['ip'] !== $_SERVER['REMOTE_ADDR'] )
|
|||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$langRepo = new \Domain\Languages\LanguagesRepository( $mdb );
|
||||||
|
|
||||||
if ( !$lang_id = \S::get_session( 'current-lang' ) )
|
if ( !$lang_id = \S::get_session( 'current-lang' ) )
|
||||||
{
|
{
|
||||||
$lang_id = \front\factory\Languages::default_language();
|
$lang_id = $langRepo->defaultLanguage();
|
||||||
\S::set_session( 'current-lang', $lang_id );
|
\S::set_session( 'current-lang', $lang_id );
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( !$lang = \S::get_session( 'lang-' . $lang_id ) )
|
if ( !$lang = \S::get_session( 'lang-' . $lang_id ) )
|
||||||
{
|
{
|
||||||
$lang = \front\factory\Languages::lang_translations( $lang_id );
|
$lang = $langRepo->translations( $lang_id );
|
||||||
\S::set_session( 'lang-' . $lang_id, $lang );
|
\S::set_session( 'lang-' . $lang_id, $lang );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -123,7 +123,7 @@
|
|||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<? if ( \S::is_array_fix( $this -> best_sales_products ) ): foreach ( $this -> best_sales_products as $row ):?>
|
<? if ( \S::is_array_fix( $this -> best_sales_products ) ): foreach ( $this -> best_sales_products as $row ):?>
|
||||||
<? $product = \shop\Product::getFromCache( (int)$row['parent_product_id'], \front\factory\Languages::default_language() );?>
|
<? $product = \shop\Product::getFromCache( (int)$row['parent_product_id'], ( new \Domain\Languages\LanguagesRepository( $GLOBALS['mdb'] ) )->defaultLanguage() );?>
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
<?
|
<?
|
||||||
|
|||||||
@@ -143,7 +143,7 @@ ob_start();
|
|||||||
<ul class="resp-tabs-list languages-main htabs">
|
<ul class="resp-tabs-list languages-main htabs">
|
||||||
<? if ( is_array( $this -> languages ) ): foreach ( $this -> languages as $lg ):?>
|
<? if ( is_array( $this -> languages ) ): foreach ( $this -> languages as $lg ):?>
|
||||||
<? if ( $lg['status'] ):?>
|
<? if ( $lg['status'] ):?>
|
||||||
<li><? if ( $lg['id'] == \front\factory\Languages::default_language() ) echo '<i class="fa fa-star fa-lg text-system" title="Język domyślny"></i> ';?><?= $lg['name'];?></a></li>
|
<li><? if ( $lg['id'] == ( new \Domain\Languages\LanguagesRepository( $GLOBALS['mdb'] ) )->defaultLanguage() ) echo '<i class="fa fa-star fa-lg text-system" title="Język domyślny"></i> ';?><?= $lg['name'];?></a></li>
|
||||||
<? endif;?>
|
<? endif;?>
|
||||||
<? endforeach; endif;?>
|
<? endforeach; endif;?>
|
||||||
</ul>
|
</ul>
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ ob_start();
|
|||||||
<ul class="resp-tabs-list languages-main htabs">
|
<ul class="resp-tabs-list languages-main htabs">
|
||||||
<? if ( is_array( $this -> languages ) ): foreach ( $this -> languages as $lg ):?>
|
<? if ( is_array( $this -> languages ) ): foreach ( $this -> languages as $lg ):?>
|
||||||
<? if ( $lg['status'] ):?>
|
<? if ( $lg['status'] ):?>
|
||||||
<li><? if ( $lg['id'] == \front\factory\Languages::default_language() ) echo '<i class="fa fa-star fa-lg text-system" title="Język domyślny"></i> ';?><?= $lg['name'];?></a></li>
|
<li><? if ( $lg['id'] == $this->dlang ) echo '<i class="fa fa-star fa-lg text-system" title="Język domyślny"></i> ';?><?= $lg['name'];?></a></li>
|
||||||
<? endif;?>
|
<? endif;?>
|
||||||
<? endforeach; endif;?>
|
<? endforeach; endif;?>
|
||||||
</ul>
|
</ul>
|
||||||
@@ -104,7 +104,7 @@ ob_start();
|
|||||||
<ul class="resp-tabs-list languages-seo htabs">
|
<ul class="resp-tabs-list languages-seo htabs">
|
||||||
<? if ( is_array( $this -> languages ) ): foreach ( $this -> languages as $lg ):?>
|
<? if ( is_array( $this -> languages ) ): foreach ( $this -> languages as $lg ):?>
|
||||||
<? if ( $lg['status'] ):?>
|
<? if ( $lg['status'] ):?>
|
||||||
<li><? if ( $lg['id'] == \front\factory\Languages::default_language() ) echo '<i class="fa fa-star fa-lg text-system" title="Język domyślny"></i> ';?><?= $lg['name'];?></a></li>
|
<li><? if ( $lg['id'] == $this->dlang ) echo '<i class="fa fa-star fa-lg text-system" title="Język domyślny"></i> ';?><?= $lg['name'];?></a></li>
|
||||||
<? endif;?>
|
<? endif;?>
|
||||||
<? endforeach; endif;?>
|
<? endforeach; endif;?>
|
||||||
</ul>
|
</ul>
|
||||||
|
|||||||
6
ajax.php
6
ajax.php
@@ -34,15 +34,17 @@ $mdb = new medoo( [
|
|||||||
'charset' => 'utf8'
|
'charset' => 'utf8'
|
||||||
] );
|
] );
|
||||||
|
|
||||||
|
$langRepo = new \Domain\Languages\LanguagesRepository( $mdb );
|
||||||
|
|
||||||
if ( !$lang_id = \S::get_session( 'current-lang' ) )
|
if ( !$lang_id = \S::get_session( 'current-lang' ) )
|
||||||
{
|
{
|
||||||
$lang_id = \front\factory\Languages::default_language();
|
$lang_id = $langRepo->defaultLanguage();
|
||||||
\S::set_session( 'current-lang', $lang_id );
|
\S::set_session( 'current-lang', $lang_id );
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( !$lang = \S::get_session( 'lang' ) )
|
if ( !$lang = \S::get_session( 'lang' ) )
|
||||||
{
|
{
|
||||||
$lang = \front\factory\Languages::lang_translations();
|
$lang = $langRepo->translations();
|
||||||
\S::set_session( 'lang', $lang );
|
\S::set_session( 'lang', $lang );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
namespace Domain\Newsletter;
|
namespace Domain\Newsletter;
|
||||||
|
|
||||||
use Domain\Settings\SettingsRepository;
|
use Domain\Settings\SettingsRepository;
|
||||||
|
use Domain\Article\ArticleRepository;
|
||||||
|
|
||||||
class NewsletterRepository
|
class NewsletterRepository
|
||||||
{
|
{
|
||||||
@@ -9,11 +10,29 @@ class NewsletterRepository
|
|||||||
|
|
||||||
private $db;
|
private $db;
|
||||||
private SettingsRepository $settingsRepository;
|
private SettingsRepository $settingsRepository;
|
||||||
|
private ?ArticleRepository $articleRepository;
|
||||||
|
private ?NewsletterPreviewRenderer $previewRenderer;
|
||||||
|
|
||||||
public function __construct($db, ?SettingsRepository $settingsRepository = null)
|
public function __construct(
|
||||||
{
|
$db,
|
||||||
|
?SettingsRepository $settingsRepository = null,
|
||||||
|
?ArticleRepository $articleRepository = null,
|
||||||
|
?NewsletterPreviewRenderer $previewRenderer = null
|
||||||
|
) {
|
||||||
$this->db = $db;
|
$this->db = $db;
|
||||||
$this->settingsRepository = $settingsRepository ?? new SettingsRepository($db);
|
$this->settingsRepository = $settingsRepository ?? new SettingsRepository($db);
|
||||||
|
$this->articleRepository = $articleRepository;
|
||||||
|
$this->previewRenderer = $previewRenderer;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getArticleRepository(): ArticleRepository
|
||||||
|
{
|
||||||
|
return $this->articleRepository ?? ($this->articleRepository = new ArticleRepository($this->db));
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getPreviewRenderer(): NewsletterPreviewRenderer
|
||||||
|
{
|
||||||
|
return $this->previewRenderer ?? ($this->previewRenderer = new NewsletterPreviewRenderer());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getSettings(): array
|
public function getSettings(): array
|
||||||
@@ -289,4 +308,138 @@ class NewsletterRepository
|
|||||||
'total' => $total,
|
'total' => $total,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ── Frontend methods ──
|
||||||
|
|
||||||
|
public function unsubscribe(string $hash): bool
|
||||||
|
{
|
||||||
|
$id = $this->db->get('pp_newsletter', 'id', ['hash' => $hash]);
|
||||||
|
if (!$id) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->db->delete('pp_newsletter', ['id' => $id]);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function confirmSubscription(string $hash): bool
|
||||||
|
{
|
||||||
|
$id = $this->db->get('pp_newsletter', 'id', ['AND' => ['hash' => $hash, 'status' => 0]]);
|
||||||
|
if (!$id) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->db->update('pp_newsletter', ['status' => 1], ['id' => $id]);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getHashByEmail(string $email): ?string
|
||||||
|
{
|
||||||
|
$hash = $this->db->get('pp_newsletter', 'hash', ['email' => $email]);
|
||||||
|
return $hash ? (string)$hash : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function removeByEmail(string $email): bool
|
||||||
|
{
|
||||||
|
if (!$this->db->get('pp_newsletter', 'id', ['email' => $email])) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (bool)$this->db->delete('pp_newsletter', ['email' => $email]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function signup(string $email, string $serverName, bool $ssl, array $settings): bool
|
||||||
|
{
|
||||||
|
if (!\S::email_check($email)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->db->get('pp_newsletter', 'id', ['email' => $email])) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$hash = md5(time() . $email);
|
||||||
|
|
||||||
|
$text = ($settings['newsletter_header'] ?? '');
|
||||||
|
$text .= $this->templateByName('#potwierdzenie-zapisu-do-newslettera');
|
||||||
|
$text .= ($settings['newsletter_footer'] ?? '');
|
||||||
|
|
||||||
|
$base = $ssl ? 'https' : 'http';
|
||||||
|
$link = '/newsletter/confirm/hash=' . $hash;
|
||||||
|
$text = str_replace('[LINK]', $link, $text);
|
||||||
|
$text = str_replace('[WYPISZ_SIE]', '', $text);
|
||||||
|
|
||||||
|
$text = preg_replace(
|
||||||
|
"-(<img[^>]+src\s*=\s*['\"])(((?!'|\"|https?://).)*)(['\"][^>]*>)-i",
|
||||||
|
"$1" . $base . "://" . $serverName . "$2$4",
|
||||||
|
$text
|
||||||
|
);
|
||||||
|
$text = preg_replace(
|
||||||
|
"-(<a[^>]+href\s*=\s*['\"])(((?!'|\"|https?://).)*)(['\"][^>]*>)-i",
|
||||||
|
"$1" . $base . "://" . $serverName . "$2$4",
|
||||||
|
$text
|
||||||
|
);
|
||||||
|
|
||||||
|
$lang = \S::get_session('lang-' . \S::get_session('current-lang'));
|
||||||
|
$subject = $lang['potwierdz-zapisanie-sie-do-newslettera'] ?? 'Newsletter';
|
||||||
|
\S::send_email($email, $subject, $text);
|
||||||
|
|
||||||
|
$this->db->insert('pp_newsletter', ['email' => $email, 'hash' => $hash, 'status' => 0]);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function sendQueued(int $limit, string $serverName, bool $ssl, string $unsubscribeLabel): bool
|
||||||
|
{
|
||||||
|
$settingsDetails = $this->settingsRepository->getSettings();
|
||||||
|
|
||||||
|
$results = $this->db->query('SELECT * FROM pp_newsletter_send ORDER BY id ASC LIMIT ' . (int)$limit);
|
||||||
|
$results = $results ? $results->fetchAll() : [];
|
||||||
|
|
||||||
|
if (!is_array($results) || empty($results)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$renderer = $this->getPreviewRenderer();
|
||||||
|
$articleRepo = $this->getArticleRepository();
|
||||||
|
|
||||||
|
foreach ($results as $row) {
|
||||||
|
$dates = explode(' - ', $row['dates']);
|
||||||
|
|
||||||
|
$articles = [];
|
||||||
|
if (isset($dates[0], $dates[1])) {
|
||||||
|
$articles = $articleRepo->articlesByDateAdd($dates[0], $dates[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$text = $renderer->render(
|
||||||
|
is_array($articles) ? $articles : [],
|
||||||
|
$settingsDetails,
|
||||||
|
$this->templateDetails((int)$row['id_template']),
|
||||||
|
(string)$row['dates']
|
||||||
|
);
|
||||||
|
|
||||||
|
$base = $ssl ? 'https' : 'http';
|
||||||
|
|
||||||
|
$text = preg_replace(
|
||||||
|
"-(<img[^>]+src\s*=\s*['\"])(((?!'|\"|http://).)*)(['\"][^>]*>)-i",
|
||||||
|
"$1" . $base . "://" . $serverName . "$2$4",
|
||||||
|
$text
|
||||||
|
);
|
||||||
|
$text = preg_replace(
|
||||||
|
"-(<a[^>]+href\s*=\s*['\"])(((?!'|\"|http://).)*)(['\"][^>]*>)-i",
|
||||||
|
"$1" . $base . "://" . $serverName . "$2$4",
|
||||||
|
$text
|
||||||
|
);
|
||||||
|
|
||||||
|
$hash = $this->getHashByEmail($row['email']);
|
||||||
|
$link = $base . "://" . $serverName . '/newsletter/unsubscribe/hash=' . $hash;
|
||||||
|
$text = str_replace('[WYPISZ_SIE]', '<a href="' . $link . '">' . $unsubscribeLabel . '</a>', $text);
|
||||||
|
|
||||||
|
\S::send_email($row['email'], 'Newsletter ze strony: ' . $serverName, $text);
|
||||||
|
|
||||||
|
$this->db->delete('pp_newsletter_send', ['id' => $row['id']]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -394,7 +394,8 @@ class App
|
|||||||
global $mdb;
|
global $mdb;
|
||||||
return new \admin\Controllers\ShopProductController(
|
return new \admin\Controllers\ShopProductController(
|
||||||
new \Domain\Product\ProductRepository( $mdb ),
|
new \Domain\Product\ProductRepository( $mdb ),
|
||||||
new \Domain\Integrations\IntegrationsRepository( $mdb )
|
new \Domain\Integrations\IntegrationsRepository( $mdb ),
|
||||||
|
new \Domain\Languages\LanguagesRepository( $mdb )
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
'ShopClients' => function() {
|
'ShopClients' => function() {
|
||||||
|
|||||||
@@ -91,7 +91,7 @@ class SettingsController
|
|||||||
$likeNormalized = '%' . $phraseNormalized . '%';
|
$likeNormalized = '%' . $phraseNormalized . '%';
|
||||||
|
|
||||||
$items = [];
|
$items = [];
|
||||||
$defaultLang = (string)\front\factory\Languages::default_language();
|
$defaultLang = (string)$this->languagesRepository->defaultLanguage();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$productStmt = $mdb->query(
|
$productStmt = $mdb->query(
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ class ShopCategoryController
|
|||||||
return \Tpl::view('shop-category/categories-list', [
|
return \Tpl::view('shop-category/categories-list', [
|
||||||
'categories' => $this->repository->subcategories(null),
|
'categories' => $this->repository->subcategories(null),
|
||||||
'level' => 0,
|
'level' => 0,
|
||||||
'dlang' => \front\factory\Languages::default_language(),
|
'dlang' => $this->languagesRepository->defaultLanguage(),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -36,6 +36,7 @@ class ShopCategoryController
|
|||||||
'pid' => \S::get('pid'),
|
'pid' => \S::get('pid'),
|
||||||
'languages' => $this->languagesRepository->languagesList(),
|
'languages' => $this->languagesRepository->languagesList(),
|
||||||
'sort_types' => $this->repository->sortTypes(),
|
'sort_types' => $this->repository->sortTypes(),
|
||||||
|
'dlang' => $this->languagesRepository->defaultLanguage(),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -102,7 +103,7 @@ class ShopCategoryController
|
|||||||
echo \Tpl::view('shop-category/category-browse-list', [
|
echo \Tpl::view('shop-category/category-browse-list', [
|
||||||
'categories' => $this->repository->subcategories(null),
|
'categories' => $this->repository->subcategories(null),
|
||||||
'level' => 0,
|
'level' => 0,
|
||||||
'dlang' => \front\factory\Languages::default_language(),
|
'dlang' => $this->languagesRepository->defaultLanguage(),
|
||||||
]);
|
]);
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ namespace admin\Controllers;
|
|||||||
use Domain\Product\ProductRepository;
|
use Domain\Product\ProductRepository;
|
||||||
use Domain\Category\CategoryRepository;
|
use Domain\Category\CategoryRepository;
|
||||||
use Domain\Integrations\IntegrationsRepository;
|
use Domain\Integrations\IntegrationsRepository;
|
||||||
|
use Domain\Languages\LanguagesRepository;
|
||||||
use admin\ViewModels\Forms\FormEditViewModel;
|
use admin\ViewModels\Forms\FormEditViewModel;
|
||||||
use admin\ViewModels\Forms\FormField;
|
use admin\ViewModels\Forms\FormField;
|
||||||
use admin\ViewModels\Forms\FormTab;
|
use admin\ViewModels\Forms\FormTab;
|
||||||
@@ -19,11 +20,13 @@ class ShopProductController
|
|||||||
{
|
{
|
||||||
private ProductRepository $repository;
|
private ProductRepository $repository;
|
||||||
private IntegrationsRepository $integrationsRepository;
|
private IntegrationsRepository $integrationsRepository;
|
||||||
|
private LanguagesRepository $languagesRepository;
|
||||||
|
|
||||||
public function __construct(ProductRepository $repository, IntegrationsRepository $integrationsRepository)
|
public function __construct(ProductRepository $repository, IntegrationsRepository $integrationsRepository, LanguagesRepository $languagesRepository)
|
||||||
{
|
{
|
||||||
$this->repository = $repository;
|
$this->repository = $repository;
|
||||||
$this->integrationsRepository = $integrationsRepository;
|
$this->integrationsRepository = $integrationsRepository;
|
||||||
|
$this->languagesRepository = $languagesRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ─── Krok 6: Lista / widok ───────────────────────────────────────
|
// ─── Krok 6: Lista / widok ───────────────────────────────────────
|
||||||
@@ -35,7 +38,7 @@ class ShopProductController
|
|||||||
{
|
{
|
||||||
$apiloEnabled = $this->integrationsRepository->getSetting( 'apilo', 'enabled' );
|
$apiloEnabled = $this->integrationsRepository->getSetting( 'apilo', 'enabled' );
|
||||||
$shopproEnabled = $this->integrationsRepository->getSetting( 'shoppro', 'enabled' );
|
$shopproEnabled = $this->integrationsRepository->getSetting( 'shoppro', 'enabled' );
|
||||||
$dlang = \front\factory\Languages::default_language();
|
$dlang = $this->languagesRepository->defaultLanguage();
|
||||||
|
|
||||||
$sortableColumns = [ 'id', 'name', 'price_brutto', 'status', 'promoted', 'quantity' ];
|
$sortableColumns = [ 'id', 'name', 'price_brutto', 'status', 'promoted', 'quantity' ];
|
||||||
|
|
||||||
@@ -221,7 +224,7 @@ class ShopProductController
|
|||||||
$sets = \shop\ProductSet::sets_list();
|
$sets = \shop\ProductSet::sets_list();
|
||||||
$producers = ( new \Domain\Producer\ProducerRepository( $db ) )->allProducers();
|
$producers = ( new \Domain\Producer\ProducerRepository( $db ) )->allProducers();
|
||||||
$units = ( new \Domain\Dictionaries\DictionariesRepository( $db ) )->allUnits();
|
$units = ( new \Domain\Dictionaries\DictionariesRepository( $db ) )->allUnits();
|
||||||
$dlang = \front\factory\Languages::default_language();
|
$dlang = $this->languagesRepository->defaultLanguage();
|
||||||
|
|
||||||
$viewModel = $this->buildProductFormViewModel(
|
$viewModel = $this->buildProductFormViewModel(
|
||||||
$product, $languages, $categories, $layouts, $products, $sets, $producers, $units, $dlang
|
$product, $languages, $categories, $layouts, $products, $sets, $producers, $units, $dlang
|
||||||
@@ -949,7 +952,7 @@ class ShopProductController
|
|||||||
return \Tpl::view( 'shop-product/product-combination', [
|
return \Tpl::view( 'shop-product/product-combination', [
|
||||||
'product' => $this->repository->findForAdmin( (int) \S::get( 'product_id' ) ),
|
'product' => $this->repository->findForAdmin( (int) \S::get( 'product_id' ) ),
|
||||||
'attributes' => ( new \Domain\Attribute\AttributeRepository( $db ) )->getAttributesListForCombinations(),
|
'attributes' => ( new \Domain\Attribute\AttributeRepository( $db ) )->getAttributesListForCombinations(),
|
||||||
'default_language' => \front\factory\Languages::default_language(),
|
'default_language' => $this->languagesRepository->defaultLanguage(),
|
||||||
'product_permutations' => $this->repository->getCombinationsForTable( (int) \S::get( 'product_id' ) ),
|
'product_permutations' => $this->repository->getCombinationsForTable( (int) \S::get( 'product_id' ) ),
|
||||||
] );
|
] );
|
||||||
}
|
}
|
||||||
@@ -1146,7 +1149,7 @@ class ShopProductController
|
|||||||
return \Tpl::view( 'shop-product/mass-edit', [
|
return \Tpl::view( 'shop-product/mass-edit', [
|
||||||
'products' => $this->repository->allProductsForMassEdit(),
|
'products' => $this->repository->allProductsForMassEdit(),
|
||||||
'categories' => $categoryRepository->subcategories( null ),
|
'categories' => $categoryRepository->subcategories( null ),
|
||||||
'dlang' => \front\factory\Languages::default_language(),
|
'dlang' => $this->languagesRepository->defaultLanguage(),
|
||||||
] );
|
] );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
49
autoload/front/Controllers/NewsletterController.php
Normal file
49
autoload/front/Controllers/NewsletterController.php
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
<?php
|
||||||
|
namespace front\Controllers;
|
||||||
|
|
||||||
|
use Domain\Newsletter\NewsletterRepository;
|
||||||
|
|
||||||
|
class NewsletterController
|
||||||
|
{
|
||||||
|
private NewsletterRepository $repository;
|
||||||
|
|
||||||
|
public function __construct( NewsletterRepository $repository )
|
||||||
|
{
|
||||||
|
$this->repository = $repository;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function signin()
|
||||||
|
{
|
||||||
|
global $settings;
|
||||||
|
|
||||||
|
$result = [ 'status' => 'bad' ];
|
||||||
|
|
||||||
|
if ( $this->repository->signup( \S::get( 'email' ), $_SERVER['SERVER_NAME'], !empty( $settings['ssl'] ), $settings ) )
|
||||||
|
$result = [ 'status' => 'ok' ];
|
||||||
|
|
||||||
|
echo json_encode( $result );
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function confirm()
|
||||||
|
{
|
||||||
|
global $lang;
|
||||||
|
|
||||||
|
if ( $this->repository->confirmSubscription( \S::get( 'hash' ) ) )
|
||||||
|
\S::alert( $lang['email-zostal-dodany-do-listy-newsletter'] );
|
||||||
|
|
||||||
|
header( 'Location: /' );
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function unsubscribe()
|
||||||
|
{
|
||||||
|
global $lang;
|
||||||
|
|
||||||
|
if ( $this->repository->unsubscribe( \S::get( 'hash' ) ) )
|
||||||
|
\S::alert( $lang['email-zostal-usuniety-z-listy-newsletter'] );
|
||||||
|
|
||||||
|
header( 'Location: /' );
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
12
autoload/front/Views/Languages.php
Normal file
12
autoload/front/Views/Languages.php
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
<?php
|
||||||
|
namespace front\Views;
|
||||||
|
|
||||||
|
class Languages
|
||||||
|
{
|
||||||
|
public static function render( $languages )
|
||||||
|
{
|
||||||
|
$tpl = new \Tpl;
|
||||||
|
$tpl -> languages = $languages;
|
||||||
|
return $tpl -> render( 'site/languages' );
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace front\view;
|
namespace front\Views;
|
||||||
|
|
||||||
class Newsletter
|
class Newsletter
|
||||||
{
|
{
|
||||||
public static function newsletter()
|
public static function render()
|
||||||
{
|
{
|
||||||
$tpl = new \Tpl;
|
$tpl = new \Tpl;
|
||||||
return $tpl -> render( 'newsletter/newsletter' );
|
return $tpl -> render( 'newsletter/newsletter' );
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
<?php
|
|
||||||
namespace front\controls;
|
|
||||||
|
|
||||||
class Newsletter
|
|
||||||
{
|
|
||||||
public static function signin()
|
|
||||||
{
|
|
||||||
$result = [ 'status' => 'bad' ];
|
|
||||||
|
|
||||||
if ( \front\factory\Newsletter::newsletter_signin( \S::get( 'email' ) ) )
|
|
||||||
$result = [ 'status' => 'ok' ];
|
|
||||||
|
|
||||||
echo json_encode( $result );
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function confirm()
|
|
||||||
{
|
|
||||||
global $lang;
|
|
||||||
|
|
||||||
if ( \front\factory\Newsletter::newsletter_confirm( \S::get( 'hash' ) ) )
|
|
||||||
\S::alert( $lang['email-zostal-dodany-do-listy-newsletter'] );
|
|
||||||
|
|
||||||
header( 'Location: /' );
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function unsubscribe()
|
|
||||||
{
|
|
||||||
global $lang;
|
|
||||||
|
|
||||||
if ( \front\factory\Newsletter::newsletter_unsubscribe( \S::get( 'hash' ) ) )
|
|
||||||
\S::alert( $lang['email-zostal-usuniety-z-listy-newsletter'] );
|
|
||||||
|
|
||||||
header( 'Location: /' );
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -53,14 +53,21 @@ class Site
|
|||||||
if ( $category )
|
if ( $category )
|
||||||
return \front\view\ShopCategory::category_view( $category, $lang_id, \S::get( 'bs' ) );
|
return \front\view\ShopCategory::category_view( $category, $lang_id, \S::get( 'bs' ) );
|
||||||
|
|
||||||
// stare klasy
|
// nowe kontrolery z DI
|
||||||
$class = '\front\controls\\';
|
$module = \S::get( 'module' );
|
||||||
|
|
||||||
$results = explode( '_', \S::get( 'module' ) );
|
|
||||||
if ( is_array( $results ) ) foreach ( $results as $row )
|
|
||||||
$class .= ucfirst( $row );
|
|
||||||
|
|
||||||
$action = \S::get( 'action' );
|
$action = \S::get( 'action' );
|
||||||
|
$controllerFactories = self::getControllerFactories();
|
||||||
|
|
||||||
|
$moduleName = implode( '', array_map( 'ucfirst', explode( '_', $module ) ) );
|
||||||
|
if ( isset( $controllerFactories[$moduleName] ) and $action )
|
||||||
|
{
|
||||||
|
$controller = $controllerFactories[$moduleName]();
|
||||||
|
if ( method_exists( $controller, $action ) )
|
||||||
|
return $controller->$action();
|
||||||
|
}
|
||||||
|
|
||||||
|
// stare klasy
|
||||||
|
$class = '\front\controls\\' . $moduleName;
|
||||||
|
|
||||||
if ( class_exists( $class ) and method_exists( new $class, $action ) )
|
if ( class_exists( $class ) and method_exists( new $class, $action ) )
|
||||||
return call_user_func_array( array( $class, $action ), array() );
|
return call_user_func_array( array( $class, $action ), array() );
|
||||||
@@ -132,5 +139,17 @@ class Site
|
|||||||
if ( file_exists( 'modules/actions.php' ) )
|
if ( file_exists( 'modules/actions.php' ) )
|
||||||
include 'modules/actions.php';
|
include 'modules/actions.php';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function getControllerFactories()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'Newsletter' => function() {
|
||||||
|
global $mdb;
|
||||||
|
return new \front\Controllers\NewsletterController(
|
||||||
|
new \Domain\Newsletter\NewsletterRepository( $mdb )
|
||||||
|
);
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
?>
|
?>
|
||||||
|
|||||||
@@ -1,29 +0,0 @@
|
|||||||
<?php
|
|
||||||
namespace front\factory;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Fasada delegujaca do Domain\Languages\LanguagesRepository.
|
|
||||||
*/
|
|
||||||
class Languages
|
|
||||||
{
|
|
||||||
public static function default_language()
|
|
||||||
{
|
|
||||||
global $mdb;
|
|
||||||
$repo = new \Domain\Languages\LanguagesRepository($mdb);
|
|
||||||
return $repo->defaultLanguage();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function active_languages()
|
|
||||||
{
|
|
||||||
global $mdb;
|
|
||||||
$repo = new \Domain\Languages\LanguagesRepository($mdb);
|
|
||||||
return $repo->activeLanguages();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function lang_translations($language = 'pl')
|
|
||||||
{
|
|
||||||
global $mdb;
|
|
||||||
$repo = new \Domain\Languages\LanguagesRepository($mdb);
|
|
||||||
return $repo->translations($language);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,133 +0,0 @@
|
|||||||
<?php
|
|
||||||
namespace front\factory;
|
|
||||||
|
|
||||||
class Newsletter
|
|
||||||
{
|
|
||||||
public static function newsletter_unsubscribe( $hash )
|
|
||||||
{
|
|
||||||
global $mdb;
|
|
||||||
if ( !$id = $mdb -> get( 'pp_newsletter', 'id', [ 'hash' => $hash ] ) )
|
|
||||||
return false;
|
|
||||||
else
|
|
||||||
$mdb -> delete( 'pp_newsletter', [ 'status' => 1 ], [ 'id' => $id ] );
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function newsletter_confirm( $hash )
|
|
||||||
{
|
|
||||||
global $mdb;
|
|
||||||
if ( !$id = $mdb -> get( 'pp_newsletter', 'id', [ 'AND' => [ 'hash' => $hash, 'status' => 0 ] ] ) )
|
|
||||||
return false;
|
|
||||||
else
|
|
||||||
$mdb -> update( 'pp_newsletter', [ 'status' => 1 ], [ 'id' => $id ] );
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function newsletter_send( $limit = 5 )
|
|
||||||
{
|
|
||||||
global $mdb, $settings, $lang;
|
|
||||||
$settingsRepository = new \Domain\Settings\SettingsRepository( $mdb );
|
|
||||||
$newsletterRepository = new \Domain\Newsletter\NewsletterRepository( $mdb, $settingsRepository );
|
|
||||||
$previewRenderer = new \Domain\Newsletter\NewsletterPreviewRenderer();
|
|
||||||
$settingsDetails = $settingsRepository -> getSettings();
|
|
||||||
|
|
||||||
$results = $mdb -> query( 'SELECT * FROM pp_newsletter_send ORDER BY id ASC LIMIT ' . $limit ) -> fetchAll();
|
|
||||||
if ( is_array( $results ) and !empty( $results ) )
|
|
||||||
{
|
|
||||||
foreach ( $results as $row )
|
|
||||||
{
|
|
||||||
$dates = explode( ' - ', $row['dates'] );
|
|
||||||
|
|
||||||
$articles = [];
|
|
||||||
$articleRepository = new \Domain\Article\ArticleRepository( $mdb );
|
|
||||||
if ( isset( $dates[0], $dates[1] ) )
|
|
||||||
$articles = $articleRepository->articlesByDateAdd( $dates[0], $dates[1] );
|
|
||||||
|
|
||||||
$text = $previewRenderer -> render(
|
|
||||||
is_array( $articles ) ? $articles : [],
|
|
||||||
$settingsDetails,
|
|
||||||
$newsletterRepository -> templateDetails( (int)$row['id_template'] ),
|
|
||||||
(string)$row['dates']
|
|
||||||
);
|
|
||||||
|
|
||||||
if ( $settings['ssl'] ) $base = 'https'; else $base = 'http';
|
|
||||||
|
|
||||||
$regex = "-(<img[^>]+src\s*=\s*['\"])(((?!'|\"|http://).)*)(['\"][^>]*>)-i";
|
|
||||||
$text = preg_replace( $regex, "$1" . $base . "://" . $_SERVER['SERVER_NAME'] . "$2$4", $text );
|
|
||||||
|
|
||||||
$regex = "-(<a[^>]+href\s*=\s*['\"])(((?!'|\"|http://).)*)(['\"][^>]*>)-i";
|
|
||||||
$text = preg_replace( $regex, "$1" . $base . "://" . $_SERVER['SERVER_NAME'] . "$2$4", $text );
|
|
||||||
|
|
||||||
$link = $base . "://" . $_SERVER['SERVER_NAME'] . '/newsletter/unsubscribe/hash=' . \front\factory\Newsletter::get_hash( $row['email'] );
|
|
||||||
|
|
||||||
$text = str_replace( '[WYPISZ_SIE]', '<a href="' . $link . '">' . $lang['wypisz-sie'] . '</a>', $text );
|
|
||||||
|
|
||||||
\S::send_email( $row['email'], 'Newsletter ze strony: ' . $_SERVER['SERVER_NAME'], $text );
|
|
||||||
|
|
||||||
$mdb -> delete( 'pp_newsletter_send', [ 'id' => $row['id'] ] );
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function get_hash( $email )
|
|
||||||
{
|
|
||||||
global $mdb;
|
|
||||||
return $mdb -> get( 'pp_newsletter', 'hash', [ 'email' => $email ] );
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function newsletter_signin( $email )
|
|
||||||
{
|
|
||||||
global $mdb, $lang, $settings;
|
|
||||||
|
|
||||||
if ( !\S::email_check( $email ) )
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if ( !$mdb -> get( 'pp_newsletter', 'id', [ 'email' => $email ] ) )
|
|
||||||
{
|
|
||||||
$hash = md5( time() . $email );
|
|
||||||
|
|
||||||
$text = $settings['newsletter_header'];
|
|
||||||
$text .= \front\factory\Newsletter::get_template( '#potwierdzenie-zapisu-do-newslettera' );
|
|
||||||
$text .= $settings['newsletter_footer'];
|
|
||||||
|
|
||||||
$settings['ssl'] ? $base = 'https' : $base = 'http';
|
|
||||||
|
|
||||||
$link = '/newsletter/confirm/hash=' . $hash;
|
|
||||||
|
|
||||||
$text = str_replace( '[LINK]', $link, $text );
|
|
||||||
|
|
||||||
$text = str_replace( '[WYPISZ_SIE]', '', $text );
|
|
||||||
|
|
||||||
$regex = "-(<img[^>]+src\s*=\s*['\"])(((?!'|\"|https?://).)*)(['\"][^>]*>)-i";
|
|
||||||
$text = preg_replace( $regex, "$1" . $base . "://" . $_SERVER['SERVER_NAME'] . "$2$4", $text );
|
|
||||||
|
|
||||||
$regex = "-(<a[^>]+href\s*=\s*['\"])(((?!'|\"|https?://).)*)(['\"][^>]*>)-i";
|
|
||||||
$text = preg_replace( $regex, "$1" . $base . "://" . $_SERVER['SERVER_NAME'] . "$2$4", $text );
|
|
||||||
|
|
||||||
$send = \S::send_email( $email, $lang['potwierdz-zapisanie-sie-do-newslettera'], $text );
|
|
||||||
|
|
||||||
$mdb -> insert( 'pp_newsletter', [ 'email' => $email, 'hash' => $hash, 'status' => 0 ] );
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function get_template( $template_name )
|
|
||||||
{
|
|
||||||
global $mdb;
|
|
||||||
$repository = new \Domain\Newsletter\NewsletterRepository( $mdb );
|
|
||||||
return $repository -> templateByName( (string)$template_name );
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function newsletter_signout( $email )
|
|
||||||
{
|
|
||||||
global $mdb;
|
|
||||||
|
|
||||||
if ( $mdb -> get( 'pp_newsletter', 'id', [ 'email' => $email ] ) )
|
|
||||||
return $mdb -> delete( 'pp_newsletter', [ 'email' => $email ] );
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -32,7 +32,7 @@ class Pages
|
|||||||
|
|
||||||
$page['language']['seo_link'] ? $url = '/' . $page['language']['seo_link'] : $url = '/s-' . $page['id'] . '-' . \S::seo( $page['language']['title'] );
|
$page['language']['seo_link'] ? $url = '/' . $page['language']['seo_link'] : $url = '/s-' . $page['id'] . '-' . \S::seo( $page['language']['title'] );
|
||||||
|
|
||||||
if ( $lang_id != \front\factory\Languages::default_language() and $url != '#' )
|
if ( $lang_id != ( new \Domain\Languages\LanguagesRepository( $GLOBALS['mdb'] ) )->defaultLanguage() and $url != '#' )
|
||||||
$url = '/' . $lang_id . $url;
|
$url = '/' . $lang_id . $url;
|
||||||
|
|
||||||
return $url;
|
return $url;
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ class ShopCategory
|
|||||||
|
|
||||||
$category['language']['seo_link'] ? $url = '/' . $category['language']['seo_link'] : $url = '/k-' . $category['id'] . '-' . \S::seo( $category['language']['title'] );
|
$category['language']['seo_link'] ? $url = '/' . $category['language']['seo_link'] : $url = '/k-' . $category['id'] . '-' . \S::seo( $category['language']['title'] );
|
||||||
|
|
||||||
if ( \S::get_session( 'current-lang' ) != \front\factory\Languages::default_language() and $url != '#' )
|
if ( \S::get_session( 'current-lang' ) != ( new \Domain\Languages\LanguagesRepository( $GLOBALS['mdb'] ) )->defaultLanguage() and $url != '#' )
|
||||||
$url = '/' . \S::get_session( 'current-lang' ) . $url;
|
$url = '/' . \S::get_session( 'current-lang' ) . $url;
|
||||||
|
|
||||||
return $url;
|
return $url;
|
||||||
|
|||||||
@@ -93,7 +93,7 @@ class ShopClient
|
|||||||
if ( $data = $mdb -> get( 'pp_shop_clients', [ 'id', 'email', 'register_date' ], [ 'AND' => [ 'hash' => $hash, 'status' => 1, 'password_recovery' => 1 ] ] ) )
|
if ( $data = $mdb -> get( 'pp_shop_clients', [ 'id', 'email', 'register_date' ], [ 'AND' => [ 'hash' => $hash, 'status' => 1, 'password_recovery' => 1 ] ] ) )
|
||||||
{
|
{
|
||||||
$text = $settings['newsletter_header'];
|
$text = $settings['newsletter_header'];
|
||||||
$text .= \front\factory\Newsletter::get_template( '#nowe-haslo' );
|
$text .= ( new \Domain\Newsletter\NewsletterRepository( $mdb ) )->templateByName( '#nowe-haslo' );
|
||||||
$text .= $settings['newsletter_footer'];
|
$text .= $settings['newsletter_footer'];
|
||||||
|
|
||||||
$settings['ssl'] ? $base = 'https' : $base = 'http';
|
$settings['ssl'] ? $base = 'https' : $base = 'http';
|
||||||
@@ -129,7 +129,7 @@ class ShopClient
|
|||||||
if ( $hash = $mdb -> get( 'pp_shop_clients', 'hash', [ 'AND' => [ 'email' => $email, 'status' => 1 ] ] ) )
|
if ( $hash = $mdb -> get( 'pp_shop_clients', 'hash', [ 'AND' => [ 'email' => $email, 'status' => 1 ] ] ) )
|
||||||
{
|
{
|
||||||
$text = $settings['newsletter_header'];
|
$text = $settings['newsletter_header'];
|
||||||
$text .= \front\factory\Newsletter::get_template( '#odzyskiwanie-hasla-link' );
|
$text .= ( new \Domain\Newsletter\NewsletterRepository( $mdb ) )->templateByName( '#odzyskiwanie-hasla-link' );
|
||||||
$text .= $settings['newsletter_footer'];
|
$text .= $settings['newsletter_footer'];
|
||||||
|
|
||||||
$settings['ssl'] ? $base = 'https' : $base = 'http';
|
$settings['ssl'] ? $base = 'https' : $base = 'http';
|
||||||
@@ -164,7 +164,7 @@ class ShopClient
|
|||||||
$email = $mdb -> get( 'pp_shop_clients', 'email', [ 'id' => $id ] );
|
$email = $mdb -> get( 'pp_shop_clients', 'email', [ 'id' => $id ] );
|
||||||
|
|
||||||
$text = $settings['newsletter_header'];
|
$text = $settings['newsletter_header'];
|
||||||
$text .= \front\factory\Newsletter::get_template( '#potwierdzenie-aktywacji-konta' );
|
$text .= ( new \Domain\Newsletter\NewsletterRepository( $mdb ) )->templateByName( '#potwierdzenie-aktywacji-konta' );
|
||||||
$text .= $settings['newsletter_footer'];
|
$text .= $settings['newsletter_footer'];
|
||||||
|
|
||||||
$settings['ssl'] ? $base = 'https' : $base = 'http';
|
$settings['ssl'] ? $base = 'https' : $base = 'http';
|
||||||
@@ -201,7 +201,7 @@ class ShopClient
|
|||||||
] ) )
|
] ) )
|
||||||
{
|
{
|
||||||
$text = $settings['newsletter_header'];
|
$text = $settings['newsletter_header'];
|
||||||
$text .= \front\factory\Newsletter::get_template( '#potwierdzenie-rejestracji' );
|
$text .= ( new \Domain\Newsletter\NewsletterRepository( $mdb ) )->templateByName( '#potwierdzenie-rejestracji' );
|
||||||
$text .= $settings['newsletter_footer'];
|
$text .= $settings['newsletter_footer'];
|
||||||
|
|
||||||
$settings['ssl'] ? $base = 'https' : $base = 'http';
|
$settings['ssl'] ? $base = 'https' : $base = 'http';
|
||||||
|
|||||||
@@ -1,12 +0,0 @@
|
|||||||
<?php
|
|
||||||
namespace front\view;
|
|
||||||
|
|
||||||
class Languages
|
|
||||||
{
|
|
||||||
public static function languages()
|
|
||||||
{
|
|
||||||
$tpl = new \Tpl;
|
|
||||||
$tpl -> languages = \front\factory\Languages::active_languages();
|
|
||||||
return $tpl -> render( 'site/languages' );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -106,7 +106,7 @@ class Site
|
|||||||
] ),
|
] ),
|
||||||
$html );
|
$html );
|
||||||
$html = str_replace( '[NEWSLETTER]',
|
$html = str_replace( '[NEWSLETTER]',
|
||||||
\front\view\Newsletter::newsletter(),
|
\front\Views\Newsletter::render(),
|
||||||
$html );
|
$html );
|
||||||
$html = str_replace( '[UZYTKOWNIK_MINI_LOGOWANIE]',
|
$html = str_replace( '[UZYTKOWNIK_MINI_LOGOWANIE]',
|
||||||
\front\view\ShopClient::mini_login(),
|
\front\view\ShopClient::mini_login(),
|
||||||
@@ -333,7 +333,7 @@ class Site
|
|||||||
$html = str_replace( '[TITLE]', $page['language']['meta_title'] ? $page['language']['meta_title'] . ' | ' . $settings['firm_name'] : $page['language']['title'] . ' | ' . $settings['firm_name'], $html );
|
$html = str_replace( '[TITLE]', $page['language']['meta_title'] ? $page['language']['meta_title'] . ' | ' . $settings['firm_name'] : $page['language']['title'] . ' | ' . $settings['firm_name'], $html );
|
||||||
$html = str_replace( '[META_KEYWORDS]', $page['language']['meta_keywords'], $html );
|
$html = str_replace( '[META_KEYWORDS]', $page['language']['meta_keywords'], $html );
|
||||||
$html = str_replace( '[META_DESCRIPTION]', $page['language']['meta_description'], $html );
|
$html = str_replace( '[META_DESCRIPTION]', $page['language']['meta_description'], $html );
|
||||||
$html = str_replace( '[JEZYKI]', \front\view\Languages::languages(), $html );
|
$html = str_replace( '[JEZYKI]', \front\Views\Languages::render( ( new \Domain\Languages\LanguagesRepository( $GLOBALS['mdb'] ) )->activeLanguages() ), $html );
|
||||||
$html = str_replace( '[TYTUL_STRONY]', self::title( $page['language']['title'], $page['show_title'], $page['language']['page_title'] ), $html );
|
$html = str_replace( '[TYTUL_STRONY]', self::title( $page['language']['title'], $page['show_title'], $page['language']['page_title'] ), $html );
|
||||||
$html = str_replace( '[WYSZUKIWARKA]', \shop\Search::simple_form(), $html );
|
$html = str_replace( '[WYSZUKIWARKA]', \shop\Search::simple_form(), $html );
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ class Category implements \ArrayAccess
|
|||||||
global $mdb;
|
global $mdb;
|
||||||
|
|
||||||
if ( !$lang_id )
|
if ( !$lang_id )
|
||||||
$lang_id = \front\factory\Languages::default_language();
|
$lang_id = ( new \Domain\Languages\LanguagesRepository( $mdb ) )->defaultLanguage();
|
||||||
|
|
||||||
return $mdb -> get( 'pp_shop_categories_langs', 'title', [ 'AND' => [ 'category_id' => $category_id, 'lang_id' => $lang_id ] ] );
|
return $mdb -> get( 'pp_shop_categories_langs', 'title', [ 'AND' => [ 'category_id' => $category_id, 'lang_id' => $lang_id ] ] );
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ class Product implements \ArrayAccess
|
|||||||
global $mdb;
|
global $mdb;
|
||||||
|
|
||||||
if ( !$lang_id )
|
if ( !$lang_id )
|
||||||
$lang_id = \front\factory\Languages::default_language();
|
$lang_id = ( new \Domain\Languages\LanguagesRepository( $mdb ) )->defaultLanguage();
|
||||||
|
|
||||||
$result = $mdb -> get( 'pp_shop_products', '*', [ 'id' => $product_id ] );
|
$result = $mdb -> get( 'pp_shop_products', '*', [ 'id' => $product_id ] );
|
||||||
if ( \S::is_array_fix( $result ) ) foreach ( $result as $key => $val )
|
if ( \S::is_array_fix( $result ) ) foreach ( $result as $key => $val )
|
||||||
@@ -292,7 +292,7 @@ class Product implements \ArrayAccess
|
|||||||
global $mdb;
|
global $mdb;
|
||||||
|
|
||||||
if ( !$lang_id )
|
if ( !$lang_id )
|
||||||
$lang_id = \front\factory\Languages::default_language();
|
$lang_id = ( new \Domain\Languages\LanguagesRepository( $mdb ) )->defaultLanguage();
|
||||||
|
|
||||||
$repository = new \Domain\Product\ProductRepository($mdb);
|
$repository = new \Domain\Product\ProductRepository($mdb);
|
||||||
return $repository->getName($product_id, $lang_id);
|
return $repository->getName($product_id, $lang_id);
|
||||||
@@ -377,7 +377,7 @@ class Product implements \ArrayAccess
|
|||||||
{
|
{
|
||||||
global $mdb;
|
global $mdb;
|
||||||
|
|
||||||
$lang_id = \front\factory\Languages::default_language();
|
$lang_id = ( new \Domain\Languages\LanguagesRepository( $mdb ) )->defaultLanguage();
|
||||||
|
|
||||||
$results = $mdb -> select( 'pp_shop_products_langs', '*', [ 'AND' => [ 'product_id' => $product_id, 'lang_id' => $lang_id ] ] );
|
$results = $mdb -> select( 'pp_shop_products_langs', '*', [ 'AND' => [ 'product_id' => $product_id, 'lang_id' => $lang_id ] ] );
|
||||||
if ( \S::is_array_fix( $results ) ) foreach ( $results as $row )
|
if ( \S::is_array_fix( $results ) ) foreach ( $results as $row )
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ class ProductAttribute implements \ArrayAccess
|
|||||||
global $mdb;
|
global $mdb;
|
||||||
|
|
||||||
if ( !$lang_id )
|
if ( !$lang_id )
|
||||||
$lang_id = \front\factory\Languages::default_language();
|
$lang_id = ( new \Domain\Languages\LanguagesRepository( $mdb ) )->defaultLanguage();
|
||||||
|
|
||||||
$result = $mdb -> get( 'pp_shop_attributes', '*', [ 'id' => $attribute_id ] );
|
$result = $mdb -> get( 'pp_shop_attributes', '*', [ 'id' => $attribute_id ] );
|
||||||
if ( \S::is_array_fix( $result ) ) foreach ( $result as $key => $val )
|
if ( \S::is_array_fix( $result ) ) foreach ( $result as $key => $val )
|
||||||
|
|||||||
@@ -51,6 +51,6 @@ $mdb = new medoo( [
|
|||||||
] );
|
] );
|
||||||
|
|
||||||
$settings = \front\factory\Settings::settings_details();
|
$settings = \front\factory\Settings::settings_details();
|
||||||
$lang_id = \front\factory\Languages::default_language();
|
$lang_id = ( new \Domain\Languages\LanguagesRepository( $mdb ) )->defaultLanguage();
|
||||||
|
|
||||||
( new \Domain\Product\ProductRepository( $mdb ) )->generateGoogleFeedXml();
|
( new \Domain\Product\ProductRepository( $mdb ) )->generateGoogleFeedXml();
|
||||||
@@ -4,6 +4,30 @@ Logi zmian z migracji na Domain-Driven Architecture. Najnowsze na gorze.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## ver. 0.279 (2026-02-16) - Newsletter + Languages frontend migration, front\Controllers, front\Views
|
||||||
|
|
||||||
|
- **Languages (view)** — migracja do nowego namespace
|
||||||
|
- USUNIĘTA: `front\factory\Languages` — fasada niepotrzebna, wszystkie zależności przepięte na `Domain\Languages\LanguagesRepository`
|
||||||
|
- USUNIĘTA: `front\view\Languages` → przeniesiona do `front\Views\Languages` (nowy namespace, bez `class.` prefix)
|
||||||
|
- UPDATE: 26 plików przepiętych z fasady na repozytorium (kontrolery DI, entry points, szablony, shop classes)
|
||||||
|
- UPDATE: `admin\App` — DI factory dla `ShopProductController` rozszerzona o `LanguagesRepository`
|
||||||
|
|
||||||
|
- **Newsletter (frontend)** — pełna migracja na Domain
|
||||||
|
- NOWE METODY w `NewsletterRepository`: `unsubscribe()`, `confirmSubscription()`, `getHashByEmail()`, `removeByEmail()`, `signup()`, `sendQueued()`
|
||||||
|
- NOWY: `front\Controllers\NewsletterController` — pierwszy frontowy kontroler z DI (nowy namespace `front\Controllers\`)
|
||||||
|
- NOWY: `front\Views\Newsletter` — czysty VIEW (nowy namespace `front\Views\`)
|
||||||
|
- USUNIĘTA: `front\factory\Newsletter` — logika przeniesiona do `NewsletterRepository`
|
||||||
|
- USUNIĘTA: `front\view\Newsletter` → zastąpiona przez `front\Views\Newsletter`
|
||||||
|
- USUNIĘTA: `front\controls\Newsletter` → zastąpiona przez `front\Controllers\NewsletterController`
|
||||||
|
- UPDATE: `front\controls\Site::route()` — nowy routing: `getControllerFactories()` (DI) → fallback stare `front\controls\`
|
||||||
|
- UPDATE: `front\factory\ShopClient` — 4x `get_template()` przepięte na `NewsletterRepository::templateByName()`
|
||||||
|
- UPDATE: `index.php` — `newsletter_send()` przepięte na `$repo->sendQueued()`
|
||||||
|
- FIX: `newsletter_unsubscribe()` — błędna składnia medoo `delete()` (3 argumenty zamiast 2)
|
||||||
|
- UPDATE: `tests/bootstrap.php` — dodane stuby: `S::email_check()`, `S::get_session()`, `S::set_session()`
|
||||||
|
- Testy: 437 OK, 1398 asercji (+10 nowych testów w NewsletterRepositoryTest)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## ver. 0.278 (2026-02-16) - Settings + Languages frontend migration
|
## ver. 0.278 (2026-02-16) - Settings + Languages frontend migration
|
||||||
|
|
||||||
- **Settings + Languages (frontend)** — pierwszy etap refaktoringu frontendu
|
- **Settings + Languages (frontend)** — pierwszy etap refaktoringu frontendu
|
||||||
|
|||||||
@@ -326,7 +326,7 @@ Adresy e-mail zapisane do newslettera.
|
|||||||
| hash | Hash potwierdzenia/wypisu |
|
| hash | Hash potwierdzenia/wypisu |
|
||||||
| status | 1 = potwierdzony, 0 = oczekujacy |
|
| status | 1 = potwierdzony, 0 = oczekujacy |
|
||||||
|
|
||||||
**Uzywane w:** `Domain\\Newsletter\\NewsletterRepository`, `front\\factory\\Newsletter`
|
**Uzywane w:** `Domain\\Newsletter\\NewsletterRepository`, `front\\Controllers\\NewsletterController`
|
||||||
|
|
||||||
## pp_newsletter_send
|
## pp_newsletter_send
|
||||||
Kolejka wysylki newslettera.
|
Kolejka wysylki newslettera.
|
||||||
@@ -338,7 +338,7 @@ Kolejka wysylki newslettera.
|
|||||||
| dates | Zakres dat artykulow (tekst) |
|
| dates | Zakres dat artykulow (tekst) |
|
||||||
| id_template | FK do `pp_newsletter_templates` (NULL gdy brak szablonu) |
|
| id_template | FK do `pp_newsletter_templates` (NULL gdy brak szablonu) |
|
||||||
|
|
||||||
**Uzywane w:** `Domain\\Newsletter\\NewsletterRepository`, `front\\factory\\Newsletter::newsletter_send()`
|
**Uzywane w:** `Domain\\Newsletter\\NewsletterRepository`
|
||||||
|
|
||||||
## pp_newsletter_templates
|
## pp_newsletter_templates
|
||||||
Szablony tresci e-maili (uzytkownik + administracyjne/systemowe).
|
Szablony tresci e-maili (uzytkownik + administracyjne/systemowe).
|
||||||
@@ -350,10 +350,12 @@ Szablony tresci e-maili (uzytkownik + administracyjne/systemowe).
|
|||||||
| text | Tresc HTML szablonu |
|
| text | Tresc HTML szablonu |
|
||||||
| is_admin | 1 = szablon administracyjny/systemowy, 0 = szablon uzytkownika |
|
| is_admin | 1 = szablon administracyjny/systemowy, 0 = szablon uzytkownika |
|
||||||
|
|
||||||
**Uzywane w:** `Domain\\Newsletter\\NewsletterRepository`, `admin\\Controllers\\NewsletterController`, `front\\factory\\Newsletter`
|
**Uzywane w:** `Domain\\Newsletter\\NewsletterRepository`, `admin\\Controllers\\NewsletterController`
|
||||||
|
|
||||||
**Aktualizacja 2026-02-12 (ver. 0.257):** modul `/admin/newsletter` korzysta z `Domain\\Newsletter\\NewsletterRepository` (DI kontroler + fasada legacy).
|
**Aktualizacja 2026-02-12 (ver. 0.257):** modul `/admin/newsletter` korzysta z `Domain\\Newsletter\\NewsletterRepository` (DI kontroler + fasada legacy).
|
||||||
|
|
||||||
|
**Aktualizacja 2026-02-16 (ver. 0.279):** `front\\factory\\Newsletter` usunięta — logika przeniesiona do `NewsletterRepository`. Frontend korzysta z `front\\Controllers\\NewsletterController` (DI).
|
||||||
|
|
||||||
## pp_scontainers
|
## pp_scontainers
|
||||||
Kontenery statyczne (modul /admin/scontainers).
|
Kontenery statyczne (modul /admin/scontainers).
|
||||||
|
|
||||||
|
|||||||
@@ -36,9 +36,9 @@ Panel administratora (33 moduły) został w pełni zmigrowany na architekturę D
|
|||||||
| ShopPaymentMethod | ZMIGROWANA (Domain) | — |
|
| ShopPaymentMethod | ZMIGROWANA (Domain) | — |
|
||||||
| ShopStatuses | ZMIGROWANA (Domain) | — |
|
| ShopStatuses | ZMIGROWANA (Domain) | — |
|
||||||
| Scontainers | ZMIGROWANA (Domain) | — |
|
| Scontainers | ZMIGROWANA (Domain) | — |
|
||||||
| Newsletter | CZĘŚCIOWO zmigrowana | ŚREDNI |
|
| Newsletter | ZMIGROWANA (Domain) — usunięta | — |
|
||||||
| Settings | Fasada (BUG: get_single_settings_value ignoruje $param) | NISKI |
|
| Settings | Fasada (BUG: get_single_settings_value ignoruje $param) | NISKI |
|
||||||
| Languages | Fasada | NISKI |
|
| Languages | USUNIĘTA — przepięta na Domain | — |
|
||||||
| Layouts | Fasada | NISKI |
|
| Layouts | Fasada | NISKI |
|
||||||
| Banners | Fasada | NISKI |
|
| Banners | Fasada | NISKI |
|
||||||
| Menu | Fasada | NISKI |
|
| Menu | Fasada | NISKI |
|
||||||
@@ -51,7 +51,8 @@ Panel administratora (33 moduły) został w pełni zmigrowany na architekturę D
|
|||||||
|-------|--------|
|
|-------|--------|
|
||||||
| Site | KRYTYCZNY — show() ~600 linii, pattern substitution engine |
|
| Site | KRYTYCZNY — show() ~600 linii, pattern substitution engine |
|
||||||
| ShopCategory | VIEW z logiką routingu (infinite scroll vs pagination) |
|
| ShopCategory | VIEW z logiką routingu (infinite scroll vs pagination) |
|
||||||
| Articles, Banners, Languages, Menu, Newsletter, Scontainers | Czyste VIEW |
|
| Articles, Banners, Menu, Scontainers | Czyste VIEW |
|
||||||
|
| Languages, Newsletter | PRZENIESIONE do `front\Views\` (nowy namespace) |
|
||||||
| ShopClient, ShopOrder, ShopPaymentMethod | Czyste VIEW |
|
| ShopClient, ShopOrder, ShopPaymentMethod | Czyste VIEW |
|
||||||
| ShopTransport | PUSTA klasa (placeholder) |
|
| ShopTransport | PUSTA klasa (placeholder) |
|
||||||
|
|
||||||
@@ -109,7 +110,7 @@ articles(8), banner(2), controls(1), menu(4), newsletter(2), scontainers(1), sho
|
|||||||
|
|
||||||
1. **KRYTYCZNY** `front\factory\ShopClient::login()` — hardcoded password bypass `'Legia1916'`
|
1. **KRYTYCZNY** `front\factory\ShopClient::login()` — hardcoded password bypass `'Legia1916'`
|
||||||
2. `front\factory\Settings::get_single_settings_value()` — ignoruje `$param`, zawsze zwraca `firm_name`
|
2. `front\factory\Settings::get_single_settings_value()` — ignoruje `$param`, zawsze zwraca `firm_name`
|
||||||
3. `front\factory\Newsletter::newsletter_unsubscribe()` — błędna składnia SQL w delete
|
3. ~~`front\factory\Newsletter::newsletter_unsubscribe()` — błędna składnia SQL w delete~~ **NAPRAWIONE** — `NewsletterRepository::unsubscribe()` z poprawną składnią medoo `delete()`
|
||||||
4. `cms\Layout::__get()` — referuje nieistniejące `$this->data`
|
4. `cms\Layout::__get()` — referuje nieistniejące `$this->data`
|
||||||
5. `shop\Search` — typo w use: `shop\Produt` (brak 'c')
|
5. `shop\Search` — typo w use: `shop\Produt` (brak 'c')
|
||||||
|
|
||||||
@@ -173,6 +174,33 @@ Legacy Cleanup
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
### Etap: Newsletter Frontend — ZREALIZOWANY
|
||||||
|
|
||||||
|
**Cel:** Przeniesienie logiki frontendowej z `front\factory\Newsletter` do `Domain\Newsletter\NewsletterRepository`. Migracja view do nowego namespace `front\Views`.
|
||||||
|
|
||||||
|
**DODANE METODY (do istniejącej klasy `NewsletterRepository`):**
|
||||||
|
- `unsubscribe(string $hash): bool` — FIX: poprawna składnia medoo `delete()` (2 args zamiast 3)
|
||||||
|
- `confirmSubscription(string $hash): bool`
|
||||||
|
- `getHashByEmail(string $email): ?string`
|
||||||
|
- `removeByEmail(string $email): bool`
|
||||||
|
- `signup(string $email, string $serverName, bool $ssl, array $settings): bool`
|
||||||
|
- `sendQueued(int $limit, string $serverName, bool $ssl, string $unsubscribeLabel): bool`
|
||||||
|
- Konstruktor rozszerzony o opcjonalne: `ArticleRepository`, `NewsletterPreviewRenderer` (lazy-init)
|
||||||
|
- Testy: 10 nowych testów w `NewsletterRepositoryTest`
|
||||||
|
|
||||||
|
**ZMIANA:**
|
||||||
|
- `front/factory/Newsletter` → USUNIĘTA (logika przeniesiona do `NewsletterRepository`)
|
||||||
|
- `front/view/Newsletter` → USUNIĘTA, zastąpiona przez `front/Views/Newsletter` (nowy namespace, bez `class.` prefix)
|
||||||
|
- `front/controls/Newsletter` → thin wrapper na `NewsletterRepository`
|
||||||
|
- `front/view/Site::show()` → `\front\Views\Newsletter::render()`
|
||||||
|
- `index.php` → `$newsletterRepo->sendQueued()`
|
||||||
|
- `front/factory/ShopClient` (4 miejsca) → `NewsletterRepository::templateByName()`
|
||||||
|
- `tests/bootstrap.php` — dodane stuby: `S::email_check()`, `S::get_session()`, `S::set_session()`
|
||||||
|
|
||||||
|
**BUG FIX:** `newsletter_unsubscribe()` — medoo `delete()` wywoływane z 3 argumentami zamiast 2
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
### Etap: Category Frontend Service
|
### Etap: Category Frontend Service
|
||||||
|
|
||||||
**Cel:** Migracja `front\factory\ShopCategory` do Domain.
|
**Cel:** Migracja `front\factory\ShopCategory` do Domain.
|
||||||
@@ -435,7 +463,7 @@ front\factory\ShopPromotion::promotion_type_XX() → shop\Product::is_product_on
|
|||||||
- PHPDoc do wszystkich nowych klas Domain z `@since`
|
- PHPDoc do wszystkich nowych klas Domain z `@since`
|
||||||
- Aktualizacja `tests/bootstrap.php`
|
- Aktualizacja `tests/bootstrap.php`
|
||||||
- BUG FIX: `shop\Search` — typo `shop\Produt` → `shop\Product`
|
- BUG FIX: `shop\Search` — typo `shop\Produt` → `shop\Product`
|
||||||
- BUG FIX: `front\factory\Newsletter::newsletter_unsubscribe()` — poprawka SQL
|
- ~~BUG FIX: `front\factory\Newsletter::newsletter_unsubscribe()` — poprawka SQL~~ **ZREALIZOWANE** w etapie Newsletter Frontend
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -471,6 +499,16 @@ front\factory\ShopPromotion::promotion_type_XX() → shop\Product::is_product_on
|
|||||||
- Namespace `\front\Controllers\` → `autoload/front/Controllers/`
|
- Namespace `\front\Controllers\` → `autoload/front/Controllers/`
|
||||||
- **Klasy Domain sa wspolne dla admin i frontendu** — NIE tworzymy osobnych FrontendService/AdminService. Metody frontendowe (z cache Redis) dodajemy do istniejacych repozytoriow/serwisow Domain. Klasy sa ladowane lazy (instancja tworzona dopiero przy wywolaniu), wiec nie wplywaja na wydajnosc.
|
- **Klasy Domain sa wspolne dla admin i frontendu** — NIE tworzymy osobnych FrontendService/AdminService. Metody frontendowe (z cache Redis) dodajemy do istniejacych repozytoriow/serwisow Domain. Klasy sa ladowane lazy (instancja tworzona dopiero przy wywolaniu), wiec nie wplywaja na wydajnosc.
|
||||||
|
|
||||||
|
### Nazewnictwo plikow
|
||||||
|
- Nowe klasy: `NazwaKlasy.php` (bez przedrostka `class.`)
|
||||||
|
- Legacy: `class.NazwaKlasy.php` — zostawiamy do momentu migracji danej klasy
|
||||||
|
- Autoloader obsluguje oba formaty (probuje `class.X.php`, potem `X.php`)
|
||||||
|
- Nowe katalogi z duzej litery: `Views/`, `Controllers/` (legacy: `view/`, `controls/`, `factory/`)
|
||||||
|
|
||||||
|
### Statyczne vs instancyjne metody
|
||||||
|
- **Statyczne** — gdy klasa jest bezstanowa (brak konstruktora, brak properties, brak DI). Czyste funkcje: dane wchodzą, wynik wychodzi. Przykład: klasy VIEW (`front\Views\Languages::render($data)`, `front\view\Banners::banners($data)`)
|
||||||
|
- **Instancyjne** — gdy klasa ma zależności do wstrzyknięcia (repozytoria, serwisy) lub trzyma stan. Przykład: kontrolery z DI (`ShopProductController` z `ProductRepository`, `LanguagesRepository`)
|
||||||
|
|
||||||
### Weryfikacja po każdym etapie
|
### Weryfikacja po każdym etapie
|
||||||
1. `composer test` (pełny suite PHPUnit)
|
1. `composer test` (pełny suite PHPUnit)
|
||||||
2. Manualne sprawdzenie frontendu: strona główna, kategoria, produkt, koszyk, zamówienie
|
2. Manualne sprawdzenie frontendu: strona główna, kategoria, produkt, koszyk, zamówienie
|
||||||
|
|||||||
@@ -203,7 +203,8 @@ autoload/
|
|||||||
│ ├── Layouts/
|
│ ├── Layouts/
|
||||||
│ │ └── LayoutsRepository.php
|
│ │ └── LayoutsRepository.php
|
||||||
│ ├── Newsletter/
|
│ ├── Newsletter/
|
||||||
│ │ └── NewsletterRepository.php
|
│ │ ├── NewsletterRepository.php
|
||||||
|
│ │ └── NewsletterPreviewRenderer.php
|
||||||
│ ├── Scontainers/
|
│ ├── Scontainers/
|
||||||
│ │ └── ScontainersRepository.php
|
│ │ └── ScontainersRepository.php
|
||||||
│ ├── Dictionaries/
|
│ ├── Dictionaries/
|
||||||
@@ -231,8 +232,13 @@ autoload/
|
|||||||
│ ├── controls/ # Stare kontrolery (niezależny fallback)
|
│ ├── controls/ # Stare kontrolery (niezależny fallback)
|
||||||
│ ├── factory/ # Stare helpery (niezależny fallback)
|
│ ├── factory/ # Stare helpery (niezależny fallback)
|
||||||
│ └── view/ # Widoki (statyczne - bez zmian)
|
│ └── view/ # Widoki (statyczne - bez zmian)
|
||||||
|
├── front/
|
||||||
|
│ ├── Controllers/ # Nowe kontrolery frontendowe (namespace \front\Controllers\) z DI
|
||||||
|
│ ├── Views/ # Nowe widoki (namespace \front\Views\) — czyste VIEW, statyczne
|
||||||
|
│ ├── controls/ # Legacy kontrolery (fallback)
|
||||||
|
│ ├── factory/ # Legacy helpery (stopniowo migrowane)
|
||||||
|
│ └── view/ # Legacy widoki
|
||||||
├── shop/ # Legacy - fasady do Domain
|
├── shop/ # Legacy - fasady do Domain
|
||||||
└── front/factory/ # Legacy - stopniowo migrowane
|
|
||||||
```
|
```
|
||||||
|
|
||||||
**Aktualizacja 2026-02-14 (ver. 0.268):**
|
**Aktualizacja 2026-02-14 (ver. 0.268):**
|
||||||
@@ -373,5 +379,15 @@ Pelna dokumentacja testow: `TESTING.md`
|
|||||||
- Usunieto stary plik `autoload/admin/class.Site.php`.
|
- Usunieto stary plik `autoload/admin/class.Site.php`.
|
||||||
- Pelna migracja admin zakonczona — wszystkie moduly na Domain + DI + Controllers.
|
- Pelna migracja admin zakonczona — wszystkie moduly na Domain + DI + Controllers.
|
||||||
|
|
||||||
|
## Aktualizacja 2026-02-16 (ver. 0.279) - Newsletter + Languages frontend migration
|
||||||
|
- Usunięta fasada `front\factory\Languages` — wszystkie 26 zależności przepięte bezpośrednio na `Domain\Languages\LanguagesRepository`.
|
||||||
|
- Usunięta fasada `front\factory\Newsletter` — logika przeniesiona do `Domain\Newsletter\NewsletterRepository` (6 nowych metod frontendowych).
|
||||||
|
- Usunięty stary kontroler `front\controls\Newsletter` i widok `front\view\Newsletter`.
|
||||||
|
- Utworzony nowy namespace `front\Controllers\` — pierwszy frontowy kontroler z DI: `NewsletterController`.
|
||||||
|
- Utworzony nowy namespace `front\Views\` — czyste widoki statyczne: `Languages`, `Newsletter`.
|
||||||
|
- Zaktualizowany routing w `front\controls\Site::route()` — `getControllerFactories()` (DI) z fallbackiem na stare `front\controls\`.
|
||||||
|
- Przepięte 4 wywołania `Newsletter::get_template()` w `front\factory\ShopClient` na `NewsletterRepository::templateByName()`.
|
||||||
|
- FIX: `newsletter_unsubscribe()` — błędna składnia medoo `delete()`.
|
||||||
|
|
||||||
---
|
---
|
||||||
*Dokument aktualizowany: 2026-02-16*
|
*Dokument aktualizowany: 2026-02-16*
|
||||||
|
|||||||
@@ -36,7 +36,14 @@ Alternatywnie (Git Bash):
|
|||||||
Ostatnio zweryfikowano: 2026-02-16
|
Ostatnio zweryfikowano: 2026-02-16
|
||||||
|
|
||||||
```text
|
```text
|
||||||
OK (427 tests, 1378 assertions)
|
OK (437 tests, 1398 assertions)
|
||||||
|
```
|
||||||
|
|
||||||
|
Aktualizacja po migracji Newsletter + Languages frontend (2026-02-16, ver. 0.279):
|
||||||
|
```text
|
||||||
|
Pelny suite: OK (437 tests, 1398 assertions)
|
||||||
|
Nowe testy: NewsletterRepositoryTest (+10: unsubscribe, confirmSubscription, getHashByEmail, removeByEmail, signup, constructorOptionalDeps)
|
||||||
|
Zaktualizowane: tests/bootstrap.php (stuby: S::email_check, S::get_session, S::set_session)
|
||||||
```
|
```
|
||||||
|
|
||||||
Aktualizacja po migracji Settings + Languages frontend (2026-02-16, ver. 0.278):
|
Aktualizacja po migracji Settings + Languages frontend (2026-02-16, ver. 0.278):
|
||||||
|
|||||||
@@ -18,16 +18,17 @@ Aktualizacje znajdują się w folderze `updates/0.XX/` gdzie XX oznacza dziesią
|
|||||||
|
|
||||||
## Procedura tworzenia nowej aktualizacji
|
## Procedura tworzenia nowej aktualizacji
|
||||||
|
|
||||||
## Status biezacej aktualizacji (ver. 0.278)
|
## Status biezacej aktualizacji (ver. 0.279)
|
||||||
|
|
||||||
- Wersja udostepniona: `0.278` (data: 2026-02-16).
|
- Wersja udostepniona: `0.279` (data: 2026-02-16).
|
||||||
- Pliki publikacyjne:
|
- Pliki publikacyjne:
|
||||||
- `updates/0.20/ver_0.278.zip`
|
- `updates/0.20/ver_0.279.zip`
|
||||||
|
- `updates/0.20/ver_0.279_files.txt`
|
||||||
- Pliki metadanych aktualizacji:
|
- Pliki metadanych aktualizacji:
|
||||||
- `updates/changelog.php` (dodany wpis `ver. 0.278`)
|
- `updates/changelog.php` (dodany wpis `ver. 0.279`)
|
||||||
- `updates/versions.php` (`$current_ver = 278`)
|
- `updates/versions.php` (`$current_ver = 279`)
|
||||||
- Weryfikacja testow przed publikacja:
|
- Weryfikacja testow przed publikacja:
|
||||||
- `OK (427 tests, 1378 assertions)`
|
- `OK (437 tests, 1398 assertions)`
|
||||||
|
|
||||||
### 1. Określ numer wersji
|
### 1. Określ numer wersji
|
||||||
Sprawdź ostatnią wersję w `updates/` i zwiększ o 1.
|
Sprawdź ostatnią wersję w `updates/` i zwiększ o 1.
|
||||||
|
|||||||
@@ -61,15 +61,17 @@ $mdb = new medoo( [
|
|||||||
|
|
||||||
\front\controls\Site::check_url_params();
|
\front\controls\Site::check_url_params();
|
||||||
|
|
||||||
|
$langRepo = new \Domain\Languages\LanguagesRepository( $mdb );
|
||||||
|
|
||||||
if ( !$lang_id = \S::get_session( 'current-lang' ) )
|
if ( !$lang_id = \S::get_session( 'current-lang' ) )
|
||||||
{
|
{
|
||||||
$lang_id = \front\factory\Languages::default_language();
|
$lang_id = $langRepo->defaultLanguage();
|
||||||
\S::set_session( 'current-lang', $lang_id );
|
\S::set_session( 'current-lang', $lang_id );
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( !$lang = \S::get_session( 'lang-' . $lang_id ) )
|
if ( !$lang = \S::get_session( 'lang-' . $lang_id ) )
|
||||||
{
|
{
|
||||||
$lang = \front\factory\Languages::lang_translations( $lang_id );
|
$lang = $langRepo->translations( $lang_id );
|
||||||
\S::set_session( 'lang-' . $lang_id, $lang );
|
\S::set_session( 'lang-' . $lang_id, $lang );
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -159,7 +161,8 @@ if ( $settings[ 'statistic_code' ] )
|
|||||||
$out = strrev( implode( strrev( $settings[ 'statistic_code' ] . '</body>' ), explode( strrev( '</body>' ), strrev( $out ), 2 ) ) );
|
$out = strrev( implode( strrev( $settings[ 'statistic_code' ] . '</body>' ), explode( strrev( '</body>' ), strrev( $out ), 2 ) ) );
|
||||||
|
|
||||||
/* wysyłka newslettera w tle */
|
/* wysyłka newslettera w tle */
|
||||||
\front\factory\Newsletter::newsletter_send( 1 );
|
$newsletterRepo = new \Domain\Newsletter\NewsletterRepository( $mdb );
|
||||||
|
$newsletterRepo->sendQueued( 1, $_SERVER['SERVER_NAME'], !empty( $settings['ssl'] ), $lang['wypisz-sie'] ?? 'Wypisz się' );
|
||||||
|
|
||||||
$dom = new DOMDocument( '1.0', 'UTF-8' );
|
$dom = new DOMDocument( '1.0', 'UTF-8' );
|
||||||
$dom -> loadHTML( $out );
|
$dom -> loadHTML( $out );
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
<a href="/<?= $url;?>" title="<?= $this -> article['language']['title'];?>" <? if ( $this -> article['language']['noindex'] ):?>rel="nofollow"<? endif;?>> <img src="<?= \front\factory\Articles::get_image( $this -> article );?>" alt="<?= $this -> article['language']['title'];?>"></a>
|
<a href="/<?= $url;?>" title="<?= $this -> article['language']['title'];?>" <? if ( $this -> article['language']['noindex'] ):?>rel="nofollow"<? endif;?>> <img src="<?= \front\factory\Articles::get_image( $this -> article );?>" alt="<?= $this -> article['language']['title'];?>"></a>
|
||||||
</div>
|
</div>
|
||||||
<h3 class="article-title">
|
<h3 class="article-title">
|
||||||
<a href="/<? if ( \S::get_session( 'current-lang' ) != \front\factory\Languages::default_language() ) echo \S::get_session( 'current-lang' ) . '/';?><?= $url;?>" title="<?= $this -> article['language']['title'];?>" <? if ( $this -> article['language']['noindex'] ):?>rel="nofollow"<? endif;?>><?= $this -> article['language']['title'];?></a>
|
<a href="/<? if ( \S::get_session( 'current-lang' ) != ( new \Domain\Languages\LanguagesRepository( $GLOBALS['mdb'] ) )->defaultLanguage() ) echo \S::get_session( 'current-lang' ) . '/';?><?= $url;?>" title="<?= $this -> article['language']['title'];?>" <? if ( $this -> article['language']['noindex'] ):?>rel="nofollow"<? endif;?>><?= $this -> article['language']['title'];?></a>
|
||||||
</h3>
|
</h3>
|
||||||
<div class="date-add"><?= date( 'd.m.Y', strtotime( $this -> article['date_add'] ) );?></div>
|
<div class="date-add"><?= date( 'd.m.Y', strtotime( $this -> article['date_add'] ) );?></div>
|
||||||
<div class="entry">
|
<div class="entry">
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<? if ( \S::is_array_fix( $this -> articles ) ): foreach ( $this -> articles as $article ):?>
|
<? if ( \S::is_array_fix( $this -> articles ) ): foreach ( $this -> articles as $article ):?>
|
||||||
<? $article['language']['seo_link'] ? $url = $article['language']['seo_link'] : $url = 'a-' . $article['id'] . '-' . \S::seo( $article['language']['title'] );?>
|
<? $article['language']['seo_link'] ? $url = $article['language']['seo_link'] : $url = 'a-' . $article['id'] . '-' . \S::seo( $article['language']['title'] );?>
|
||||||
<li>
|
<li>
|
||||||
<a href="/<? if ( \S::get_session( 'current-lang' ) != \front\factory\Languages::default_language( \S::get_domain( $_SERVER['HTTP_HOST'] ) ) ) echo \S::get_session( 'current-lang' ) . '/';?><?= $url;?>" title="<?= $article['language']['title'];?>" <? if ( $article['language']['noindex'] ):?>rel="nofollow"<? endif;?>><i class="far fa-file-alt"></i><?= $article['language']['title'];?></a>
|
<a href="/<? if ( \S::get_session( 'current-lang' ) != ( new \Domain\Languages\LanguagesRepository( $GLOBALS['mdb'] ) )->defaultLanguage() ) echo \S::get_session( 'current-lang' ) . '/';?><?= $url;?>" title="<?= $article['language']['title'];?>" <? if ( $article['language']['noindex'] ):?>rel="nofollow"<? endif;?>><i class="far fa-file-alt"></i><?= $article['language']['title'];?></a>
|
||||||
<div class="date-add"><?= date( 'd.m.Y', strtotime( $article['date_add'] ) );?></div>
|
<div class="date-add"><?= date( 'd.m.Y', strtotime( $article['date_add'] ) );?></div>
|
||||||
</li>
|
</li>
|
||||||
<? endforeach; endif;?>
|
<? endforeach; endif;?>
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
$product -> language['seo_link'] ? $url = '/' . $product -> language['seo_link'] : $url = '/p-' . $product['id'] . '-' . \S::seo( $product -> language['name'] );
|
$product -> language['seo_link'] ? $url = '/' . $product -> language['seo_link'] : $url = '/p-' . $product['id'] . '-' . \S::seo( $product -> language['name'] );
|
||||||
|
|
||||||
if ( \S::get_session( 'current-lang' ) != \front\factory\Languages::default_language() and $url != '#' )
|
if ( \S::get_session( 'current-lang' ) != ( new \Domain\Languages\LanguagesRepository( $GLOBALS['mdb'] ) )->defaultLanguage() and $url != '#' )
|
||||||
$url = '/' . \S::get_session( 'current-lang' ) . $url;
|
$url = '/' . \S::get_session( 'current-lang' ) . $url;
|
||||||
?>
|
?>
|
||||||
<div class="product">
|
<div class="product">
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ if ( is_array( $this -> pages ) ) {
|
|||||||
|
|
||||||
echo '">';
|
echo '">';
|
||||||
echo '<a href="';
|
echo '<a href="';
|
||||||
if ( \S::get_session( 'current-lang' ) != \front\factory\Languages::default_language() and $url != '#' and $page['page_type'] != 3 and $page['page_type'] != 5 )
|
if ( \S::get_session( 'current-lang' ) != ( new \Domain\Languages\LanguagesRepository( $GLOBALS['mdb'] ) )->defaultLanguage() and $url != '#' and $page['page_type'] != 3 and $page['page_type'] != 5 )
|
||||||
echo '/' . \S::get_session( 'current-lang' );
|
echo '/' . \S::get_session( 'current-lang' );
|
||||||
echo $url . '"';
|
echo $url . '"';
|
||||||
if ( $page['language']['noindex'] )
|
if ( $page['language']['noindex'] )
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ if ( is_array( $this -> pages ) )
|
|||||||
|
|
||||||
echo '<li id="link-' . $page['id'] . '" class="'; if ( $page['id'] == $this -> current_page ) echo ' active'; echo '">';
|
echo '<li id="link-' . $page['id'] . '" class="'; if ( $page['id'] == $this -> current_page ) echo ' active'; echo '">';
|
||||||
echo '<a href="';
|
echo '<a href="';
|
||||||
if ( \S::get_session( 'current-lang' ) != \front\factory\Languages::default_language() and $url != '#' )
|
if ( \S::get_session( 'current-lang' ) != ( new \Domain\Languages\LanguagesRepository( $GLOBALS['mdb'] ) )->defaultLanguage() and $url != '#' )
|
||||||
echo '/' . \S::get_session( 'current-lang' );
|
echo '/' . \S::get_session( 'current-lang' );
|
||||||
echo $url . '"'; if ( $page['language']['noindex'] ) echo 'rel="nofollow"'; echo ' title="' . $page['language']['title'] . '"'; if ( is_array( $page['pages'] ) ) echo "class='menu-trigger'"; echo '>';
|
echo $url . '"'; if ( $page['language']['noindex'] ) echo 'rel="nofollow"'; echo ' title="' . $page['language']['title'] . '"'; if ( is_array( $page['pages'] ) ) echo "class='menu-trigger'"; echo '>';
|
||||||
echo $page['language']['title'];
|
echo $page['language']['title'];
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
$product -> language['seo_link'] ? $url = '/' . $product -> language['seo_link'] : $url = '/p-' . $product['id'] . '-' . \S::seo( $product -> language['name'] );
|
$product -> language['seo_link'] ? $url = '/' . $product -> language['seo_link'] : $url = '/p-' . $product['id'] . '-' . \S::seo( $product -> language['name'] );
|
||||||
|
|
||||||
if ( \S::get_session( 'current-lang' ) != \front\factory\Languages::default_language() and $url != '#' )
|
if ( \S::get_session( 'current-lang' ) != ( new \Domain\Languages\LanguagesRepository( $GLOBALS['mdb'] ) )->defaultLanguage() and $url != '#' )
|
||||||
$url = '/' . \S::get_session( 'current-lang' ) . $url;
|
$url = '/' . \S::get_session( 'current-lang' ) . $url;
|
||||||
?>
|
?>
|
||||||
<div class="product">
|
<div class="product">
|
||||||
|
|||||||
@@ -36,7 +36,7 @@
|
|||||||
<?php
|
<?php
|
||||||
$url = \front\factory\ShopProduct::product_url( $product );
|
$url = \front\factory\ShopProduct::product_url( $product );
|
||||||
|
|
||||||
if ( \S::get_session( 'current-lang' ) != \front\factory\Languages::default_language() and $url != '#')
|
if ( \S::get_session( 'current-lang' ) != ( new \Domain\Languages\LanguagesRepository( $GLOBALS['mdb'] ) )->defaultLanguage() and $url != '#')
|
||||||
$url = '/' . \S::get_session('current-lang') . $url;
|
$url = '/' . \S::get_session('current-lang') . $url;
|
||||||
?>
|
?>
|
||||||
<div class="name">
|
<div class="name">
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
<?
|
<?
|
||||||
$product[ 'language' ][ 'seo_link' ] ? $url = '/' . $product[ 'language' ][ 'seo_link' ] : $url = '/p-' . $product[ 'id' ] . '-' . \S::seo( $product[ 'language' ][ 'name' ] );
|
$product[ 'language' ][ 'seo_link' ] ? $url = '/' . $product[ 'language' ][ 'seo_link' ] : $url = '/p-' . $product[ 'id' ] . '-' . \S::seo( $product[ 'language' ][ 'name' ] );
|
||||||
|
|
||||||
if ( \S::get_session( 'current-lang' ) != \front\factory\Languages::default_language() and $url != '#' )
|
if ( \S::get_session( 'current-lang' ) != ( new \Domain\Languages\LanguagesRepository( $GLOBALS['mdb'] ) )->defaultLanguage() and $url != '#' )
|
||||||
$url = '/' . \S::get_session( 'current-lang' ) . $url;
|
$url = '/' . \S::get_session( 'current-lang' ) . $url;
|
||||||
?>
|
?>
|
||||||
<div class="name">
|
<div class="name">
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ if ( is_array( $this -> categories ) ):
|
|||||||
<?
|
<?
|
||||||
$category['language']['seo_link'] ? $url = '/' . $category['language']['seo_link'] : $url = '/k-' . $category['id'] . '-' . \S::seo( $category['language']['title'] );
|
$category['language']['seo_link'] ? $url = '/' . $category['language']['seo_link'] : $url = '/k-' . $category['id'] . '-' . \S::seo( $category['language']['title'] );
|
||||||
|
|
||||||
if ( \S::get_session( 'current-lang' ) != \front\factory\Languages::default_language() and $url != '#' )
|
if ( \S::get_session( 'current-lang' ) != ( new \Domain\Languages\LanguagesRepository( $GLOBALS['mdb'] ) )->defaultLanguage() and $url != '#' )
|
||||||
$url = '/' . \S::get_session( 'current-lang' ) . $url;
|
$url = '/' . \S::get_session( 'current-lang' ) . $url;
|
||||||
?>
|
?>
|
||||||
<a href="<?= $url;?>" title="<?= $category['language']['title'];?>" <? if ( is_array( $category['categories'] ) ) echo "class='menu-trigger'";?>>
|
<a href="<?= $url;?>" title="<?= $category['language']['title'];?>" <? if ( is_array( $category['categories'] ) ) echo "class='menu-trigger'";?>>
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ echo $this -> settings['newsletter_header'];
|
|||||||
<?
|
<?
|
||||||
$product['language']['seo_link'] ? $url = '/' . $product['language']['seo_link'] : $url = '/p-' . $product['id'] . '-' . \S::seo( $product['language']['name'] );
|
$product['language']['seo_link'] ? $url = '/' . $product['language']['seo_link'] : $url = '/p-' . $product['id'] . '-' . \S::seo( $product['language']['name'] );
|
||||||
|
|
||||||
if ( \S::get_session( 'current-lang' ) != \front\factory\Languages::default_language() and $url != '#' )
|
if ( \S::get_session( 'current-lang' ) != ( new \Domain\Languages\LanguagesRepository( $GLOBALS['mdb'] ) )->defaultLanguage() and $url != '#' )
|
||||||
$url = '/' . \S::get_session( 'current-lang' ) . $url;
|
$url = '/' . \S::get_session( 'current-lang' ) . $url;
|
||||||
|
|
||||||
$regex = "-(<a[^>]+href\s*=\s*['\"])(((?!'|\"|http://).)*)(['\"][^>]*>)-i";
|
$regex = "-(<a[^>]+href\s*=\s*['\"])(((?!'|\"|http://).)*)(['\"][^>]*>)-i";
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<?
|
<?
|
||||||
$this -> product -> language['seo_link'] ? $url = '/' . $this -> product -> language['seo_link'] : $url = '/p-' . $this -> product['id'] . '-' . \S::seo( $this -> product -> language['name'] );
|
$this -> product -> language['seo_link'] ? $url = '/' . $this -> product -> language['seo_link'] : $url = '/p-' . $this -> product['id'] . '-' . \S::seo( $this -> product -> language['name'] );
|
||||||
|
|
||||||
if ( \S::get_session( 'current-lang' ) != \front\factory\Languages::default_language() and $url != '#' )
|
if ( \S::get_session( 'current-lang' ) != ( new \Domain\Languages\LanguagesRepository( $GLOBALS['mdb'] ) )->defaultLanguage() and $url != '#' )
|
||||||
$url = '/' . \S::get_session( 'current-lang' ) . $url;
|
$url = '/' . \S::get_session( 'current-lang' ) . $url;
|
||||||
?>
|
?>
|
||||||
<div class="product-mini <?= $this -> product_mini_class;?>">
|
<div class="product-mini <?= $this -> product_mini_class;?>">
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
$product -> languages[$lang_id]['seo_link'] ? $url = '/' . $product -> languages[$lang_id]['seo_link'] : $url = '/p-' . $product['id'] . '-' . \S::seo( $product -> languages[$lang_id]['name'] );
|
$product -> languages[$lang_id]['seo_link'] ? $url = '/' . $product -> languages[$lang_id]['seo_link'] : $url = '/p-' . $product['id'] . '-' . \S::seo( $product -> languages[$lang_id]['name'] );
|
||||||
|
|
||||||
if ( \S::get_session( 'current-lang' ) != \front\factory\Languages::default_language() and $url != '#' )
|
if ( \S::get_session( 'current-lang' ) != ( new \Domain\Languages\LanguagesRepository( $GLOBALS['mdb'] ) )->defaultLanguage() and $url != '#' )
|
||||||
$url = '/' . \S::get_session( 'current-lang' ) . $url;
|
$url = '/' . \S::get_session( 'current-lang' ) . $url;
|
||||||
?>
|
?>
|
||||||
<?= \Tpl::view( 'shop-product/product-mini', [
|
<?= \Tpl::view( 'shop-product/product-mini', [
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<?
|
<?
|
||||||
$this -> product -> language['seo_link'] ? $url = '/' . $this -> product -> language['seo_link'] : $url = '/p-' . $this -> product['id'] . '-' . \S::seo( $this -> product -> language['name'] );
|
$this -> product -> language['seo_link'] ? $url = '/' . $this -> product -> language['seo_link'] : $url = '/p-' . $this -> product['id'] . '-' . \S::seo( $this -> product -> language['name'] );
|
||||||
|
|
||||||
if ( \S::get_session( 'current-lang' ) != \front\factory\Languages::default_language() and $url != '#' )
|
if ( \S::get_session( 'current-lang' ) != ( new \Domain\Languages\LanguagesRepository( $GLOBALS['mdb'] ) )->defaultLanguage() and $url != '#' )
|
||||||
$url = '/' . \S::get_session( 'current-lang' ) . $url;
|
$url = '/' . \S::get_session( 'current-lang' ) . $url;
|
||||||
?>
|
?>
|
||||||
<div class="product-search">
|
<div class="product-search">
|
||||||
|
|||||||
@@ -75,4 +75,145 @@ class NewsletterRepositoryTest extends TestCase
|
|||||||
|
|
||||||
$this->assertSame('Template text', $repository->templateByName('#abc'));
|
$this->assertSame('Template text', $repository->templateByName('#abc'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ── Frontend methods tests ──
|
||||||
|
|
||||||
|
public function testUnsubscribeReturnsFalseForInvalidHash(): void
|
||||||
|
{
|
||||||
|
$mockDb = $this->createMock(\medoo::class);
|
||||||
|
$mockDb->expects($this->once())
|
||||||
|
->method('get')
|
||||||
|
->with('pp_newsletter', 'id', ['hash' => 'bad-hash'])
|
||||||
|
->willReturn(null);
|
||||||
|
|
||||||
|
$repository = new NewsletterRepository($mockDb, $this->createMock(SettingsRepository::class));
|
||||||
|
|
||||||
|
$this->assertFalse($repository->unsubscribe('bad-hash'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testUnsubscribeDeletesSubscriber(): void
|
||||||
|
{
|
||||||
|
$mockDb = $this->createMock(\medoo::class);
|
||||||
|
$mockDb->expects($this->once())
|
||||||
|
->method('get')
|
||||||
|
->with('pp_newsletter', 'id', ['hash' => 'abc123'])
|
||||||
|
->willReturn(42);
|
||||||
|
$mockDb->expects($this->once())
|
||||||
|
->method('delete')
|
||||||
|
->with('pp_newsletter', ['id' => 42]);
|
||||||
|
|
||||||
|
$repository = new NewsletterRepository($mockDb, $this->createMock(SettingsRepository::class));
|
||||||
|
|
||||||
|
$this->assertTrue($repository->unsubscribe('abc123'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testConfirmSubscriptionReturnsFalseForInvalidHash(): void
|
||||||
|
{
|
||||||
|
$mockDb = $this->createMock(\medoo::class);
|
||||||
|
$mockDb->expects($this->once())
|
||||||
|
->method('get')
|
||||||
|
->with('pp_newsletter', 'id', ['AND' => ['hash' => 'bad', 'status' => 0]])
|
||||||
|
->willReturn(null);
|
||||||
|
|
||||||
|
$repository = new NewsletterRepository($mockDb, $this->createMock(SettingsRepository::class));
|
||||||
|
|
||||||
|
$this->assertFalse($repository->confirmSubscription('bad'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testConfirmSubscriptionUpdatesStatus(): void
|
||||||
|
{
|
||||||
|
$mockDb = $this->createMock(\medoo::class);
|
||||||
|
$mockDb->expects($this->once())
|
||||||
|
->method('get')
|
||||||
|
->with('pp_newsletter', 'id', ['AND' => ['hash' => 'valid', 'status' => 0]])
|
||||||
|
->willReturn(10);
|
||||||
|
$mockDb->expects($this->once())
|
||||||
|
->method('update')
|
||||||
|
->with('pp_newsletter', ['status' => 1], ['id' => 10]);
|
||||||
|
|
||||||
|
$repository = new NewsletterRepository($mockDb, $this->createMock(SettingsRepository::class));
|
||||||
|
|
||||||
|
$this->assertTrue($repository->confirmSubscription('valid'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetHashByEmailReturnsHash(): void
|
||||||
|
{
|
||||||
|
$mockDb = $this->createMock(\medoo::class);
|
||||||
|
$mockDb->expects($this->once())
|
||||||
|
->method('get')
|
||||||
|
->with('pp_newsletter', 'hash', ['email' => 'test@test.pl'])
|
||||||
|
->willReturn('abc123');
|
||||||
|
|
||||||
|
$repository = new NewsletterRepository($mockDb, $this->createMock(SettingsRepository::class));
|
||||||
|
|
||||||
|
$this->assertSame('abc123', $repository->getHashByEmail('test@test.pl'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetHashByEmailReturnsNullForMissing(): void
|
||||||
|
{
|
||||||
|
$mockDb = $this->createMock(\medoo::class);
|
||||||
|
$mockDb->expects($this->once())
|
||||||
|
->method('get')
|
||||||
|
->with('pp_newsletter', 'hash', ['email' => 'none@test.pl'])
|
||||||
|
->willReturn(null);
|
||||||
|
|
||||||
|
$repository = new NewsletterRepository($mockDb, $this->createMock(SettingsRepository::class));
|
||||||
|
|
||||||
|
$this->assertNull($repository->getHashByEmail('none@test.pl'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testRemoveByEmailDeletesSubscriber(): void
|
||||||
|
{
|
||||||
|
$mockDb = $this->createMock(\medoo::class);
|
||||||
|
$mockDb->expects($this->once())
|
||||||
|
->method('get')
|
||||||
|
->with('pp_newsletter', 'id', ['email' => 'test@test.pl'])
|
||||||
|
->willReturn(5);
|
||||||
|
$mockDb->expects($this->once())
|
||||||
|
->method('delete')
|
||||||
|
->with('pp_newsletter', ['email' => 'test@test.pl'])
|
||||||
|
->willReturn(true);
|
||||||
|
|
||||||
|
$repository = new NewsletterRepository($mockDb, $this->createMock(SettingsRepository::class));
|
||||||
|
|
||||||
|
$this->assertTrue($repository->removeByEmail('test@test.pl'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testRemoveByEmailReturnsFalseForMissing(): void
|
||||||
|
{
|
||||||
|
$mockDb = $this->createMock(\medoo::class);
|
||||||
|
$mockDb->expects($this->once())
|
||||||
|
->method('get')
|
||||||
|
->with('pp_newsletter', 'id', ['email' => 'none@test.pl'])
|
||||||
|
->willReturn(null);
|
||||||
|
|
||||||
|
$repository = new NewsletterRepository($mockDb, $this->createMock(SettingsRepository::class));
|
||||||
|
|
||||||
|
$this->assertFalse($repository->removeByEmail('none@test.pl'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSignupReturnsFalseForExistingEmail(): void
|
||||||
|
{
|
||||||
|
$mockDb = $this->createMock(\medoo::class);
|
||||||
|
$mockDb->expects($this->once())
|
||||||
|
->method('get')
|
||||||
|
->with('pp_newsletter', 'id', ['email' => 'exists@test.pl'])
|
||||||
|
->willReturn(1);
|
||||||
|
|
||||||
|
$repository = new NewsletterRepository($mockDb, $this->createMock(SettingsRepository::class));
|
||||||
|
|
||||||
|
$this->assertFalse($repository->signup('exists@test.pl', 'example.com', false, []));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testConstructorAcceptsOptionalDependencies(): void
|
||||||
|
{
|
||||||
|
$mockDb = $this->createMock(\medoo::class);
|
||||||
|
$settingsRepo = $this->createMock(SettingsRepository::class);
|
||||||
|
$articleRepo = $this->createMock(\Domain\Article\ArticleRepository::class);
|
||||||
|
$renderer = $this->createMock(\Domain\Newsletter\NewsletterPreviewRenderer::class);
|
||||||
|
|
||||||
|
$repository = new NewsletterRepository($mockDb, $settingsRepo, $articleRepo, $renderer);
|
||||||
|
|
||||||
|
$this->assertInstanceOf(NewsletterRepository::class, $repository);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -5,23 +5,26 @@ use PHPUnit\Framework\TestCase;
|
|||||||
use admin\Controllers\ShopProductController;
|
use admin\Controllers\ShopProductController;
|
||||||
use Domain\Product\ProductRepository;
|
use Domain\Product\ProductRepository;
|
||||||
use Domain\Integrations\IntegrationsRepository;
|
use Domain\Integrations\IntegrationsRepository;
|
||||||
|
use Domain\Languages\LanguagesRepository;
|
||||||
|
|
||||||
class ShopProductControllerTest extends TestCase
|
class ShopProductControllerTest extends TestCase
|
||||||
{
|
{
|
||||||
private $repository;
|
private $repository;
|
||||||
private $integrationsRepository;
|
private $integrationsRepository;
|
||||||
|
private $languagesRepository;
|
||||||
private $controller;
|
private $controller;
|
||||||
|
|
||||||
protected function setUp(): void
|
protected function setUp(): void
|
||||||
{
|
{
|
||||||
$this->repository = $this->createMock(ProductRepository::class);
|
$this->repository = $this->createMock(ProductRepository::class);
|
||||||
$this->integrationsRepository = $this->createMock(IntegrationsRepository::class);
|
$this->integrationsRepository = $this->createMock(IntegrationsRepository::class);
|
||||||
$this->controller = new ShopProductController($this->repository, $this->integrationsRepository);
|
$this->languagesRepository = $this->createMock(LanguagesRepository::class);
|
||||||
|
$this->controller = new ShopProductController($this->repository, $this->integrationsRepository, $this->languagesRepository);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testConstructorAcceptsRepositories(): void
|
public function testConstructorAcceptsRepositories(): void
|
||||||
{
|
{
|
||||||
$controller = new ShopProductController($this->repository, $this->integrationsRepository);
|
$controller = new ShopProductController($this->repository, $this->integrationsRepository, $this->languagesRepository);
|
||||||
$this->assertInstanceOf(ShopProductController::class, $controller);
|
$this->assertInstanceOf(ShopProductController::class, $controller);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,9 +108,10 @@ class ShopProductControllerTest extends TestCase
|
|||||||
$constructor = $reflection->getConstructor();
|
$constructor = $reflection->getConstructor();
|
||||||
$params = $constructor->getParameters();
|
$params = $constructor->getParameters();
|
||||||
|
|
||||||
$this->assertCount(2, $params);
|
$this->assertCount(3, $params);
|
||||||
$this->assertEquals('Domain\Product\ProductRepository', $params[0]->getType()->getName());
|
$this->assertEquals('Domain\Product\ProductRepository', $params[0]->getType()->getName());
|
||||||
$this->assertEquals('Domain\Integrations\IntegrationsRepository', $params[1]->getType()->getName());
|
$this->assertEquals('Domain\Integrations\IntegrationsRepository', $params[1]->getType()->getName());
|
||||||
|
$this->assertEquals('Domain\Languages\LanguagesRepository', $params[2]->getType()->getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testHasFormBuildingHelpers(): void
|
public function testHasFormBuildingHelpers(): void
|
||||||
|
|||||||
@@ -49,6 +49,9 @@ if (!class_exists('S')) {
|
|||||||
public static function set_message($msg) {}
|
public static function set_message($msg) {}
|
||||||
public static function clear_redis_cache() {}
|
public static function clear_redis_cache() {}
|
||||||
public static function clear_product_cache($id) {}
|
public static function clear_product_cache($id) {}
|
||||||
|
public static function email_check($email) { return filter_var($email, FILTER_VALIDATE_EMAIL); }
|
||||||
|
public static function get_session($key) { return $_SESSION[$key] ?? null; }
|
||||||
|
public static function set_session($key, $value) { $_SESSION[$key] = $value; }
|
||||||
public static function send_email($to, $subject, $body) { return true; }
|
public static function send_email($to, $subject, $body) { return true; }
|
||||||
public static function remove_special_chars($str) { return str_ireplace(['\'', '"', ',', ';', '<', '>'], ' ', $str); }
|
public static function remove_special_chars($str) { return str_ireplace(['\'', '"', ',', ';', '<', '>'], ' ', $str); }
|
||||||
public static function normalize_decimal($val, $precision = 2) { return round((float)$val, $precision); }
|
public static function normalize_decimal($val, $precision = 2) { return round((float)$val, $precision); }
|
||||||
|
|||||||
BIN
updates/0.20/ver_0.279.zip
Normal file
BIN
updates/0.20/ver_0.279.zip
Normal file
Binary file not shown.
5
updates/0.20/ver_0.279_files.txt
Normal file
5
updates/0.20/ver_0.279_files.txt
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
F: ../autoload/front/factory/class.Languages.php
|
||||||
|
F: ../autoload/front/factory/class.Newsletter.php
|
||||||
|
F: ../autoload/front/view/class.Languages.php
|
||||||
|
F: ../autoload/front/view/class.Newsletter.php
|
||||||
|
F: ../autoload/front/controls/class.Newsletter.php
|
||||||
@@ -1,3 +1,13 @@
|
|||||||
|
<b>ver. 0.279 - 16.02.2026</b><br />
|
||||||
|
- UPDATE - migracja Newsletter frontend: factory + view + controls do Domain/Controllers/Views (DI)
|
||||||
|
- UPDATE - nowy namespace `front\Controllers` z `NewsletterController` (DI via factory closures)
|
||||||
|
- UPDATE - nowy namespace `front\Views` z `Languages` i `Newsletter` (czyste VIEW, statyczne metody)
|
||||||
|
- UPDATE - routing frontend: `Site::getControllerFactories()` z fallback na stare kontrolery
|
||||||
|
- FIX - `newsletter_unsubscribe()` — poprawiona skladnia medoo `delete()` (2 argumenty zamiast 3)
|
||||||
|
- UPDATE - eliminacja fasady `front\factory\Languages` — 26 zaleznosci przepietych na `LanguagesRepository`
|
||||||
|
- CLEANUP - usuniete: `front\factory\Languages`, `front\factory\Newsletter`, `front\view\Languages`, `front\view\Newsletter`, `front\controls\Newsletter`
|
||||||
|
- UPDATE - testy: `OK (437 tests, 1398 assertions)`
|
||||||
|
<hr>
|
||||||
<b>ver. 0.278 - 16.02.2026</b><br />
|
<b>ver. 0.278 - 16.02.2026</b><br />
|
||||||
- UPDATE - migracja Settings + Languages do wspolnych klas Domain (z cache Redis)
|
- UPDATE - migracja Settings + Languages do wspolnych klas Domain (z cache Redis)
|
||||||
- FIX - `get_single_settings_value()` — parametr `$param` poprawnie uzywany (wczesniej hardcoded `firm_name`)
|
- FIX - `get_single_settings_value()` — parametr `$param` poprawnie uzywany (wczesniej hardcoded `firm_name`)
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<?
|
<?
|
||||||
$current_ver = 278;
|
$current_ver = 279;
|
||||||
|
|
||||||
for ($i = 1; $i <= $current_ver; $i++)
|
for ($i = 1; $i <= $current_ver; $i++)
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user