diff --git a/app/modules/view/ViewModule.php b/app/modules/view/ViewModule.php
new file mode 100644
index 0000000..e37befb
--- /dev/null
+++ b/app/modules/view/ViewModule.php
@@ -0,0 +1,8 @@
+registerCSS(slug: "select2", resource: "https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/css/select2.min.css", addResourceURI: false);
+ $this->registerCSS(
+ slug: "select2",
+ resource: "https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/css/select2.min.css",
+ addResourceURI: false,
+ after: 'bootstrap'
+ );
}
protected function createJS(): void
{
- $this->registerJS(slug: "select2", resource: "https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js", addResourceURI: false);
+ $this->registerJS(
+ slug: "select2",
+ resource: "https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js",
+ addResourceURI: false,
+ after: 'jquery',
+ );
+ $this->registerJS(
+ slug: "slider",
+ resource: "/slider.js",
+ after: 'select2',
+ );
}
}
\ No newline at end of file
diff --git a/app/themes/svo/assets/SvoLpThemeAssets.php b/app/themes/svo/assets/SvoLpThemeAssets.php
index dad7c1c..2b90eef 100644
--- a/app/themes/svo/assets/SvoLpThemeAssets.php
+++ b/app/themes/svo/assets/SvoLpThemeAssets.php
@@ -11,6 +11,7 @@ class SvoLpThemeAssets extends Assets
{
$this->registerCSS(slug: "bootstrap", resource: "/css/netic/bootstrap.css");
$this->registerCSS(slug: "style", resource: "/css/netic/style.css");
+ $this->registerCSS(slug: "posts", resource: "/css/netic/posts.css");
$this->registerCSS(slug: "responsive", resource: "/css/netic/responsive.css");
$this->registerCSS(slug: "mCustomScrollbar", resource: "/css/netic/jquery.mCustomScrollbar.min.css");
}
diff --git a/app/themes/svo/controllers/LpController.php b/app/themes/svo/controllers/LpController.php
index 5c304b2..984bdc7 100644
--- a/app/themes/svo/controllers/LpController.php
+++ b/app/themes/svo/controllers/LpController.php
@@ -2,13 +2,18 @@
namespace app\themes\svo\controllers;
+use app\themes\svo\services\MainPageSliderService;
use kernel\Controller;
+use kernel\helpers\Debug;
+use kernel\modules\post\models\Post;
+use kernel\modules\post\service\PostService;
use kernel\modules\user\service\UserService;
class LpController extends Controller
{
protected \kernel\modules\user\models\User $user;
+ protected MainPageSliderService $mainPageSliderService;
protected function init(): void
{
@@ -19,6 +24,7 @@ class LpController extends Controller
$this->cgView->addVarToLayout("resources", "/resources/themes/svo/assets");
$user = UserService::getAuthUser();
+ $this->mainPageSliderService = new MainPageSliderService();
if ($user){
$this->cgView->addVarToLayout("currentUser", $user);
$this->user = $user;
@@ -27,7 +33,14 @@ class LpController extends Controller
public function actionIndex(): void
{
- $this->cgView->render('index.php');
+ $this->cgView->render('index.php', []);
+ }
+
+ public function actionPost(int $postId): void
+ {
+ $post = Post::find($postId);
+
+ $this->cgView->render('post.php', ['post' => $post]);
}
}
\ No newline at end of file
diff --git a/app/themes/svo/controllers/SvoAdminController.php b/app/themes/svo/controllers/SvoAdminController.php
index b87e69f..44b1474 100644
--- a/app/themes/svo/controllers/SvoAdminController.php
+++ b/app/themes/svo/controllers/SvoAdminController.php
@@ -2,7 +2,13 @@
namespace app\themes\svo\controllers;
+use app\themes\svo\services\MainPageSliderService;
+use app\themes\svo\services\SvoThemeService;
+use JetBrains\PhpStorm\NoReturn;
use kernel\AdminController;
+use kernel\Flash;
+use kernel\helpers\Debug;
+use kernel\Request;
class SvoAdminController extends AdminController
{
@@ -11,6 +17,7 @@ class SvoAdminController extends AdminController
{
parent::init();
$this->cgView->viewPath = APP_DIR . "/themes/svo/views/admin/";
+ $this->cgView->addVarToLayout("svo_theme_resources", "/resources/themes/svo");
}
public function actionThemeSettings(): void
@@ -18,4 +25,24 @@ class SvoAdminController extends AdminController
$this->cgView->render('theme_settings.php');
}
+ #[NoReturn] public function actionAddSlide(): void
+ {
+ $request = new Request();
+ $slideId = $request->post('slide');
+
+ SvoThemeService::addPostSlide($slideId);
+
+ Flash::setMessage('success', 'Слайд добавлен.');
+ $this->redirect('/admin/svo-theme/settings?tab=slider', 302);
+ }
+
+ #[NoReturn] public function actionDeleteSlide(int $post_id): void
+ {
+ $mainPageSlideService = new MainPageSliderService();
+ $mainPageSlideService->removeSlide($post_id);
+
+ Flash::setMessage('success', 'Слайд удален.');
+ $this->redirect('/admin/svo-theme/settings?tab=slider', 302);
+ }
+
}
\ No newline at end of file
diff --git a/app/themes/svo/routs/svo.php b/app/themes/svo/routs/svo.php
index fc5e427..c7968cf 100644
--- a/app/themes/svo/routs/svo.php
+++ b/app/themes/svo/routs/svo.php
@@ -6,11 +6,14 @@ use kernel\CgRouteCollector;
App::$collector->filter("auth", [\app\themes\svo\middlewares\LkAuthMiddleware::class, "handler"]);
App::$collector->get('/', [\app\themes\svo\controllers\LpController::class, 'actionIndex']);
+App::$collector->get('/post/{postId}', [\app\themes\svo\controllers\LpController::class, 'actionPost']);
App::$collector->group(["prefix" => "admin"], function (CgRouteCollector $router) {
App::$collector->group(["before" => "auth"], function (CGRouteCollector $router) {
App::$collector->group(["prefix" => "svo-theme"], function (CGRouteCollector $router) {
App::$collector->get('/settings', [\app\themes\svo\controllers\SvoAdminController::class, 'actionThemeSettings']);
+ App::$collector->get('/delete_slide/{post_id}', [\app\themes\svo\controllers\SvoAdminController::class, 'actionDeleteSlide']);
+ App::$collector->post('/add_slide', [\app\themes\svo\controllers\SvoAdminController::class, 'actionAddSlide']);
});
});
});
diff --git a/app/themes/svo/services/MainPageSliderService.php b/app/themes/svo/services/MainPageSliderService.php
new file mode 100644
index 0000000..a510f62
--- /dev/null
+++ b/app/themes/svo/services/MainPageSliderService.php
@@ -0,0 +1,90 @@
+optionService = new OptionService();
+ }
+
+ /**
+ * Получить список слайдов
+ *
+ * @return array
+ */
+ public function getSlides(): array
+ {
+ $option = Option::where('key', $this->sliderKey)->first();
+
+ if (!$option || empty($option->value)) {
+ return [];
+ }
+
+ $data = json_decode($option->value, true);
+
+ return $data['ids'] ?? [];
+ }
+
+ /**
+ * Добавить слайд
+ *
+ * @param int $slideId
+ * @return bool
+ */
+ public function addSlide(int $slideId): bool
+ {
+ return App::$db->capsule::connection()->transaction(function () use ($slideId) {
+ $option = Option::firstOrNew(['key' => $this->sliderKey]);
+
+ $currentValue = $option->value ? json_decode($option->value, true) : ['ids' => []];
+
+ if (in_array($slideId, $currentValue['ids'])) {
+ return true; // слайд уже есть, ничего не делаем
+ }
+
+ $currentValue['ids'][] = $slideId;
+ $option->value = json_encode($currentValue);
+
+ return $option->save();
+ });
+ }
+
+ /**
+ * Удалить слайд
+ *
+ * @param int $slideId
+ * @return bool
+ */
+ public function removeSlide(int $slideId): bool
+ {
+ return App::$db->capsule::connection()->transaction(function () use ($slideId) {
+ /** @var Option $option */
+ $option = Option::where('key', $this->sliderKey)->first();
+
+ if (!$option || empty($option->value)) {
+ return false;
+ }
+
+ $currentValue = json_decode($option->value, true);
+
+ if (!in_array($slideId, $currentValue['ids'] ?? [])) {
+ return false; // слайда нет в списке
+ }
+
+ $currentValue['ids'] = array_diff($currentValue['ids'], [$slideId]);
+ $option->value = json_encode($currentValue);
+
+ return $option->save();
+ });
+ }
+}
\ No newline at end of file
diff --git a/app/themes/svo/services/SvoThemeService.php b/app/themes/svo/services/SvoThemeService.php
new file mode 100644
index 0000000..16da503
--- /dev/null
+++ b/app/themes/svo/services/SvoThemeService.php
@@ -0,0 +1,34 @@
+get();
+ }
+
+ public static function addPostSlide(int $postId)
+ {
+ $slides = json_decode(OptionService::getItem('main_news_slider'), true);
+
+ if (!in_array($postId, $slides['ids'])) {
+ $slides['ids'][] = $postId;
+ OptionService::createOrUpdate('main_news_slider', json_encode($slides));
+ }
+
+ return $slides;
+ }
+
+
+
+}
\ No newline at end of file
diff --git a/app/themes/svo/views/admin/_slider_post_list.php b/app/themes/svo/views/admin/_slider_post_list.php
new file mode 100644
index 0000000..808bcce
--- /dev/null
+++ b/app/themes/svo/views/admin/_slider_post_list.php
@@ -0,0 +1,3 @@
+
+Посты
diff --git a/app/themes/svo/views/admin/theme_settings.php b/app/themes/svo/views/admin/theme_settings.php
index dcc71d1..9de2dd9 100644
--- a/app/themes/svo/views/admin/theme_settings.php
+++ b/app/themes/svo/views/admin/theme_settings.php
@@ -1,24 +1,28 @@
registerAsset(new \app\themes\svo\assets\AdminSliderAssets($svo_theme_resources));
?>
-
+
Основные
-
+
Слайдер
-
+
Contact
@@ -29,16 +33,24 @@ new \app\themes\svo\assets\AdminSliderAssets($resources);
- Заголовок
+ Новости в сладере
-
- Option 1
- Option 2
- Option 3
- Option 4
-
+
-
+
+ = \kernel\helpers\Html::link('Добавить', '#', [
+ 'id' => 'saveSliderPost',
+ 'class' => 'btn btn-success',
+ 'onclick' => 'document.getElementById("add_slide_form").submit(); return false;'
+ ]) ?>
+
+ run(); ?>
Contact
@@ -46,8 +58,15 @@ new \app\themes\svo\assets\AdminSliderAssets($resources);
\ No newline at end of file
+
diff --git a/app/themes/svo/views/lp/post.php b/app/themes/svo/views/lp/post.php
new file mode 100644
index 0000000..558c745
--- /dev/null
+++ b/app/themes/svo/views/lp/post.php
@@ -0,0 +1,223 @@
+
+
+
+
+
+
+ Главная
+ Новости
+ = $post->title ?>
+
+
+
+
+
+
+
+
+
+
+ Иллюстрация: создание 3D-модели с помощью ИИ
+
+
+
+
+
Компания OpenAI представила новую версию своего алгоритма, способного генерировать трехмерные объекты на основе текстовых описаний.
+
+
Новая технология, получившая название 3D-GPT, позволяет создавать высокодетализированные модели всего за несколько секунд. Пользователю достаточно ввести описание объекта, например, "красный спортивный автомобиль с черными полосами", и система сгенерирует соответствующую 3D-модель.
+
+
Как это работает
+
+
Алгоритм основан на комбинации нескольких нейросетевых архитектур:
+
+
+ Текстовая модель анализирует описание и выделяет ключевые характеристики
+ Генеративная сеть создает базовую геометрию объекта
+ Дополнительные модули добавляют текстуры, материалы и освещение
+
+
+
По словам разработчиков, система особенно хорошо справляется с органическими формами и архитектурными объектами.
+
+
+
Экспертное мнение
+
"Эта технология может революционизировать индустрию 3D-моделирования, значительно сократив время и стоимость создания цифровых активов", - отмечает Джон Смит, профессор компьютерной графики в MIT.
+
+
+
Практическое применение
+
+
Технология уже тестируется в нескольких областях:
+
+
+
+
+
+
Игровая индустрия
+
Быстрое прототипирование персонажей и объектов для видеоигр.
+
+
+
+
+
+
+
Архитектура
+
Создание 3D-моделей зданий по словесным описаниям заказчиков.
+
+
+
+
+
+
+
Образование
+
Визуализация исторических объектов и научных концепций.
+
+
+
+
+
+
Ожидается, что коммерческая версия продукта будет выпущена в начале следующего года. Бета-тестирование начнется уже в сентябре для ограниченного круга разработчиков.
+
+
+
+
+
+ #искусственный_интеллект
+ #3d_моделирование
+ #технологии
+ #openai
+
+
+ Поделиться
+ Твитнуть
+ Отправить
+
+
+
+
+
+
+
+
Об авторе
+
+
+
+
Александра Петрова
+
Технологический обозреватель с 10-летним опытом
+
+
+
+
+
Источник
+
По материалам пресс-релиза OpenAI и эксклюзивного интервью с разработчиками
+
+
+
+
+
+
+ Похожие новости
+
+
+
+
+
+
Нейросети научились анимировать 2D-рисунки
+
12 июля 2023
+
Читать
+
+
+
+
+
+
+
+
ИИ в дизайне: как алгоритмы меняют творческие профессии
+
8 июля 2023
+
Читать
+
+
+
+
+
+
+
+
Обзор новых инструментов для 3D-художников в 2023 году
+
3 июля 2023
+
Читать
+
+
+
+
+
+
+
+
+ Комментарии (24)
+
+
+
+
+
+
+
diff --git a/app/themes/svo/views/widget/admin_slider_posts_list.php b/app/themes/svo/views/widget/admin_slider_posts_list.php
new file mode 100644
index 0000000..cebf509
--- /dev/null
+++ b/app/themes/svo/views/widget/admin_slider_posts_list.php
@@ -0,0 +1,25 @@
+setColumns([
+ 'id' => 'ID',
+ 'title' => 'Название',
+]);
+
+$table->setTableAttributes([
+ 'style' => 'margin-top:20px;'
+]);
+
+$table->addCustomColumn('action', 'Действия', function ($post) {
+ $btn = 'Удалить ';
+
+ return $btn;
+});
+
+
+$table->render();
\ No newline at end of file
diff --git a/app/themes/svo/views/widget/main_slider.php b/app/themes/svo/views/widget/main_slider.php
index c2cbfe2..ab1a7c8 100644
--- a/app/themes/svo/views/widget/main_slider.php
+++ b/app/themes/svo/views/widget/main_slider.php
@@ -1,50 +1,31 @@
-
-
-
diff --git a/app/themes/svo/widgets/AdminSliderPostsList.php b/app/themes/svo/widgets/AdminSliderPostsList.php
new file mode 100644
index 0000000..5c6e45b
--- /dev/null
+++ b/app/themes/svo/widgets/AdminSliderPostsList.php
@@ -0,0 +1,19 @@
+cgView->viewPath = APP_DIR . "/themes/svo/views/widget/";
+ }
+
+ public function run(): void
+ {
+ $this->cgView->render("admin_slider_posts_list.php", ['posts' => \app\themes\svo\services\SvoThemeService::getSliderPosts()]);
+ }
+
+}
\ No newline at end of file
diff --git a/app/themes/svo/widgets/MainSliderWidget.php b/app/themes/svo/widgets/MainSliderWidget.php
index a1bc17e..7051fd9 100644
--- a/app/themes/svo/widgets/MainSliderWidget.php
+++ b/app/themes/svo/widgets/MainSliderWidget.php
@@ -2,19 +2,29 @@
namespace app\themes\svo\widgets;
+use app\themes\svo\services\MainPageSliderService;
+use kernel\modules\post\models\Post;
use kernel\Widget;
class MainSliderWidget extends Widget
{
+ protected MainPageSliderService $mainPageSliderService;
protected function init(): void
{
$this->cgView->viewPath = APP_DIR . "/themes/svo/views/widget/";
+ $this->mainPageSliderService = new MainPageSliderService();
}
public function run(): void
{
- $this->cgView->render("main_slider.php", ['resources' => $this->data['resources']]);
+ $slides = $this->mainPageSliderService->getSlides();
+ $posts = Post::whereIn('id', $slides)->get();
+
+ $this->cgView->render("main_slider.php", [
+ 'resources' => $this->data['resources'],
+ 'posts' => $posts,
+ ]);
}
}
\ No newline at end of file
diff --git a/bootstrap.php b/bootstrap.php
index c6cba95..5552476 100644
--- a/bootstrap.php
+++ b/bootstrap.php
@@ -19,7 +19,7 @@ const KERNEL_APP_MODULES_DIR = KERNEL_DIR . "/app_modules";
const APP_DIR = ROOT_DIR . "/app";
-
+\kernel\Theme::$assetsCollector = new \kernel\AssetsCollector();
function getConst($text): array|false|string
{
diff --git a/kernel/Assets.php b/kernel/Assets.php
index 30c2c8b..b0af05a 100644
--- a/kernel/Assets.php
+++ b/kernel/Assets.php
@@ -7,8 +7,12 @@ class Assets
protected array $jsHeader = [];
protected array $jsBody = [];
+ protected array $collectorJs = [];
+
protected array $css = [];
+ protected array $collectorCss = [];
+
protected string $resourceURI = "/resource";
public function __construct(string $resourceURI)
@@ -26,7 +30,7 @@ class Assets
$this->resourceURI = $resourceURI;
}
- public function registerJS(string $slug, string $resource, bool $body = true, bool $addResourceURI = true): void
+ public function registerJS(string $slug, string $resource, bool $body = true, bool $addResourceURI = true, string $after = null): void
{
$resource = $addResourceURI ? $this->resourceURI . $resource : $resource;
if ($body) {
@@ -34,12 +38,14 @@ class Assets
} else {
$this->jsHeader[$slug] = $resource;
}
+ $this->collectorJs[$slug] = ['resource' => $resource, 'after' => $after, 'body' => $body];
}
- public function registerCSS(string $slug, string $resource, bool $addResourceURI = true): void
+ public function registerCSS(string $slug, string $resource, bool $addResourceURI = true, string $after = null): void
{
$resource = $addResourceURI ? $this->resourceURI . $resource : $resource;
$this->css[$slug] = $resource;
+ $this->collectorCss[$slug] = ['resource' => $resource, 'after' => $after];
}
public function getJSAsStr(bool $body = true): void
@@ -63,4 +69,14 @@ class Assets
}
}
+ public function getCollectorCss(): array
+ {
+ return $this->collectorCss;
+ }
+
+ public function getCollectorJs(): array
+ {
+ return $this->collectorJs;
+ }
+
}
\ No newline at end of file
diff --git a/kernel/AssetsCollector.php b/kernel/AssetsCollector.php
new file mode 100644
index 0000000..a6d2527
--- /dev/null
+++ b/kernel/AssetsCollector.php
@@ -0,0 +1,128 @@
+assetsPool[] = $assets;
+ }
+
+ public function renderCss(): void
+ {
+ $css = [];
+ foreach ($this->assetsPool as $item) {
+ /** @var Assets $item */
+ $css = array_merge($css, $item->getCollectorCss());
+ }
+
+ try {
+ $sortedStyles = $this->sortStyles($css);
+
+ // Выводим отсортированные стили
+ foreach ($sortedStyles as $style) {
+ echo ' ' . "\n";
+ }
+ } catch (RuntimeException $e) {
+ echo 'Ошибка: ' . $e->getMessage();
+ }
+ }
+
+ public function renderJs(bool $body = true): void
+ {
+ $scripts = [];
+ foreach ($this->assetsPool as $item) {
+ /** @var Assets $item */
+ $scripts = array_merge($scripts, $item->getCollectorJs());
+ }
+
+ try {
+ $sortedScripts = $this->sortScripts($scripts);
+
+ // Разделяем скрипты для head и body
+ $headScripts = [];
+ $bodyScripts = [];
+
+ foreach ($sortedScripts as $script) {
+ if ($script['body']) {
+ $bodyScripts[] = $script['resource'];
+ } else {
+ $headScripts[] = $script['resource'];
+ }
+ }
+
+ // Выводим скрипты для head
+ if ($body){
+ $scriptsToRender = $bodyScripts;
+ }
+ else {
+ $scriptsToRender = $headScripts;
+ }
+ foreach ($scriptsToRender as $script) {
+ echo '' . "\n";
+ }
+ }
+ catch (RuntimeException $e) {
+ echo 'Ошибка: ' . $e->getMessage();
+ }
+ }
+
+ protected function sortStyles(array $styles): array
+ {
+ $sorted = [];
+ $added = [];
+
+ // Пока не добавим все стили
+ while (count($sorted) < count($styles)) {
+ $found = false;
+
+ foreach ($styles as $name => $style) {
+ // Если стиль еще не добавлен и его зависимости выполнены
+ if (!isset($added[$name]) &&
+ (empty($style['after']) || isset($added[$style['after']]))) {
+ $sorted[] = $style['resource'];
+ $added[$name] = true;
+ $found = true;
+ }
+ }
+
+ if (!$found) {
+ // Если есть циклическая зависимость
+ throw new RuntimeException('Обнаружена циклическая зависимость в стилях');
+ }
+ }
+
+ return $sorted;
+ }
+
+ protected function sortScripts(array $scripts): array
+ {
+ $sorted = [];
+ $added = [];
+
+ while (count($sorted) < count($scripts)) {
+ $found = false;
+
+ foreach ($scripts as $name => $script) {
+ if (!isset($added[$name]) &&
+ (empty($script['after']) || isset($added[$script['after']]))) {
+ $sorted[] = $script;
+ $added[$name] = true;
+ $found = true;
+ }
+ }
+
+ if (!$found) {
+ throw new RuntimeException('Обнаружена циклическая зависимость в скриптах');
+ }
+ }
+
+ return $sorted;
+ }
+
+}
\ No newline at end of file
diff --git a/kernel/Theme.php b/kernel/Theme.php
new file mode 100644
index 0000000..17ca504
--- /dev/null
+++ b/kernel/Theme.php
@@ -0,0 +1,15 @@
+registerAsset(new \kernel\admin_themes\default\DefaultAdminThemeAssets($resources));
?>
@@ -18,8 +22,8 @@ $assets = new \kernel\admin_themes\default\DefaultAdminThemeAssets($resources)
- getCSSAsSTR(); ?>
- getJSAsStr(body: false); ?>
+ renderCss(); ?>
+ renderJs(body: false); ?>
@@ -92,6 +96,6 @@ $assets = new \kernel\admin_themes\default\DefaultAdminThemeAssets($resources)
-getJSAsStr(); ?>
+renderJs(); ?>
Иван Иванов
+ 2 часа назад +Очень интересная технология! Интересно, насколько она будет доступна для индивидуальных разработчиков по цене.
+Мария Смирнова
+ 1 час назад +@Иван Иванов, по предварительной информации, будет несколько тарифных планов, включая бесплатный с ограничениями.
+Алексей Технарев
+ 30 минут назад +Жду не дождусь, когда можно будет попробовать! Уже есть несколько идей, как это можно применить в нашем проекте.
+