From 0111427a092b7e8bbaf18bb51d96c9acc3501304 Mon Sep 17 00:00:00 2001 From: Babak Alizadeh Date: Sun, 6 Apr 2025 14:20:29 +0000 Subject: [PATCH] progress in site admin dashboard --- package-lock.json | 24 ++++------ public/css/login.css | 38 +++++++++++++++ src/Controller/Admin/DashboardController.php | 31 ++---------- src/Controller/Admin/PostCrudController.php | 24 +++++----- src/Controller/PageController.php | 50 +++++++++++++++----- src/Controller/SecurityController.php | 40 +--------------- src/Entity/Post.php | 27 +++-------- templates/admin/login.html.twig | 7 +++ 8 files changed, 115 insertions(+), 126 deletions(-) create mode 100644 public/css/login.css create mode 100644 templates/admin/login.html.twig diff --git a/package-lock.json b/package-lock.json index 2f192a1..da610c0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2123,8 +2123,14 @@ } }, "node_modules/@symfony/ux-turbo": { - "resolved": "vendor/symfony/ux-turbo/assets", - "link": true + "version": "0.1.0", + "resolved": "file:vendor/symfony/ux-turbo/assets", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@hotwired/stimulus": "^3.0.0", + "@hotwired/turbo": "^7.1.1 || ^8.0" + } }, "node_modules/@symfony/webpack-encore": { "version": "5.0.1", @@ -6875,20 +6881,6 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } - }, - "vendor/symfony/ux-turbo/assets": { - "name": "@symfony/ux-turbo", - "version": "0.1.0", - "dev": true, - "license": "MIT", - "devDependencies": { - "@hotwired/stimulus": "^3.0.0", - "@hotwired/turbo": "^7.1.0 || ^8.0" - }, - "peerDependencies": { - "@hotwired/stimulus": "^3.0.0", - "@hotwired/turbo": "^7.1.1 || ^8.0" - } } } } diff --git a/public/css/login.css b/public/css/login.css new file mode 100644 index 0000000..afba457 --- /dev/null +++ b/public/css/login.css @@ -0,0 +1,38 @@ +/* public/css/login.css */ +body { + direction: rtl; + text-align: right; + font-family: 'Vazir', 'Tahoma', sans-serif; /* فونت فارسی دلخواه */ +} + +.login-wrapper { + direction: rtl; +} + +.form-group label { + text-align: right; +} + +.form-control { + direction: rtl; + text-align: right; +} + +.btn { + direction: rtl; +} + +.checkbox label { + padding-right: 25px; /* فاصله برای چک‌باکس */ + padding-left: 0; +} + +.login-box { + margin-right: auto; + margin-left: auto; +} + +/* تنظیمات اضافی برای المان‌های خاص */ +.login-box-header h1 { + text-align: right; +} \ No newline at end of file diff --git a/src/Controller/Admin/DashboardController.php b/src/Controller/Admin/DashboardController.php index f7ccf72..8194192 100644 --- a/src/Controller/Admin/DashboardController.php +++ b/src/Controller/Admin/DashboardController.php @@ -39,39 +39,16 @@ class DashboardController extends AbstractDashboardController public function configureDashboard(): Dashboard { return Dashboard::new() - // you can include HTML contents too (e.g. to link to an image) ->setTitle('پیشخوان') - - // by default EasyAdmin displays a black square as its default favicon; - // use this method to display a custom favicon: the given path is passed - // "as is" to the Twig asset() function: - // ->setFaviconPath('favicon/favicon.ico') - - // the domain used by default is 'messages' ->setTranslationDomain('admin') - - // set this option if you prefer the page content to span the entire - // browser width, instead of the default design which sets a max width ->renderContentMaximized() - - // by default, the UI color scheme is 'auto', which means that the backend - // will use the same mode (light/dark) as the operating system and will - // change in sync when the OS mode changes. - // Use this option to set which mode ('light', 'dark' or 'auto') will users see - // by default in the backend (users can change it via the color scheme selector) ->setDefaultColorScheme('dark') - // instead of magic strings, you can use constants as the value of - // this option: EasyCorp\Bundle\EasyAdminBundle\Config\Option\ColorScheme::DARK - - // by default, all backend URLs are generated as absolute URLs. If you - // need to generate relative URLs instead, call this method ->generateRelativeUrls() - - ->setLocales(['en','fa']) - // to further customize the locale option, pass an instance of - // EasyCorp\Bundle\EasyAdminBundle\Config\Locale - ; + ->setLocales([ + 'fa' => Locale::new('fa', 'فارسی', 'fa_IR'), // زبان پیش‌فرض + 'en' => Locale::new('en', 'English', 'en_US'), + ]); } public function configureMenuItems(): iterable diff --git a/src/Controller/Admin/PostCrudController.php b/src/Controller/Admin/PostCrudController.php index 7f451dc..03aeb16 100644 --- a/src/Controller/Admin/PostCrudController.php +++ b/src/Controller/Admin/PostCrudController.php @@ -5,15 +5,14 @@ namespace App\Controller\Admin; use App\Entity\Cat; use App\Entity\Post; use EasyCorp\Bundle\EasyAdminBundle\Config\Crud; +use EasyCorp\Bundle\EasyAdminBundle\Config\Filters; use EasyCorp\Bundle\EasyAdminBundle\Controller\AbstractCrudController; use EasyCorp\Bundle\EasyAdminBundle\Field\AssociationField; -use EasyCorp\Bundle\EasyAdminBundle\Field\BooleanField; -use EasyCorp\Bundle\EasyAdminBundle\Field\CodeEditorField; -use EasyCorp\Bundle\EasyAdminBundle\Field\IdField; use EasyCorp\Bundle\EasyAdminBundle\Field\ImageField; use EasyCorp\Bundle\EasyAdminBundle\Field\TextareaField; use EasyCorp\Bundle\EasyAdminBundle\Field\TextEditorField; use EasyCorp\Bundle\EasyAdminBundle\Field\TextField; +use EasyCorp\Bundle\EasyAdminBundle\Filter\EntityFilter; class PostCrudController extends AbstractCrudController { @@ -32,21 +31,25 @@ class PostCrudController extends AbstractCrudController TextField::new('title', 'عنوان'), TextareaField::new('intro', 'خلاصه مطلب')->hideOnIndex(), TextEditorField::new('body', 'متن')->hideOnIndex(), - CodeEditorField::new('plain', 'ساختار')->hideOnIndex(), TextField::new('keywords', 'کلیدواژه‌ها'), - ImageField::new('mainPic','تصویر شاخص') - ->setUploadDir('/public/uploaded/') - ->setBasePath('/uploaded/') + ImageField::new('mainPic', 'تصویر شاخص') + ->setUploadDir('/public/uploaded/') + ->setBasePath('/uploaded/'), ]; } public function configureCrud(Crud $crud): Crud { return $crud - // the labels used to refer to this entity in titles, buttons, etc. ->setEntityLabelInSingular('محتوا') ->setEntityLabelInPlural('محتواها') - ; + ->setDefaultSort(['dateSubmit' => 'DESC']); // مرتب‌سازی پیش‌فرض بر اساس تاریخ ارسال (جدیدترین) + } + + public function configureFilters(Filters $filters): Filters + { + return $filters + ->add(EntityFilter::new('cat', 'نوع محتوا')); // فیلتر برای نوع محتوا } public function createEntity(string $entityFqcn) @@ -57,5 +60,4 @@ class PostCrudController extends AbstractCrudController $item->setViews(0); return $item; } - -} +} \ No newline at end of file diff --git a/src/Controller/PageController.php b/src/Controller/PageController.php index 0b245fc..ba857d2 100644 --- a/src/Controller/PageController.php +++ b/src/Controller/PageController.php @@ -9,6 +9,8 @@ use Doctrine\ORM\EntityManagerInterface; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Attribute\Route; +use Symfony\Component\HttpFoundation\Request; + class PageController extends AbstractController { @@ -23,26 +25,48 @@ class PageController extends AbstractController ]); } - #[Route('/blog/{page}', name: 'app_blog_home')] - public function app_blog_home(EntityManagerInterface $entityManagerInterface, $page = 1): Response + #[Route('/blog/{page}', name: 'app_blog_home', defaults: ['page' => 1])] + public function app_blog_home(EntityManagerInterface $entityManagerInterface, Request $request, $page = 1): Response { $perpage = 9; - $posts = $entityManagerInterface->getRepository(Post::class)->findByCat('blog',$perpage,$page); - $cat = $entityManagerInterface->getRepository(Cat::class)->findOneBy(['code'=>'blog']); - $count = $entityManagerInterface->getRepository(Post::class)->count(['cat'=>$cat]); - if(fmod($count,$perpage) == 0){ - $maxpages = $count/$perpage; + $search = $request->query->get('search', ''); // پارامتر جستجو از URL + + $postRepository = $entityManagerInterface->getRepository(Post::class); + $catRepository = $entityManagerInterface->getRepository(Cat::class); + + // پیدا کردن دسته‌بندی "blog" + $cat = $catRepository->findOneBy(['code' => 'blog']); + + // گرفتن پست‌ها با فیلتر جستجو + $queryBuilder = $postRepository->createQueryBuilder('p') + ->where('p.cat = :cat') + ->setParameter('cat', $cat) + ->orderBy('p.dateSubmit', 'DESC'); // مرتب‌سازی بر اساس جدیدترین + + if ($search) { + $queryBuilder->andWhere('p.title LIKE :search OR p.intro LIKE :search') + ->setParameter('search', "%$search%"); } - else{ - $maxpages = ($count/$perpage) + 1; - } - $maxpages = $count / $perpage; + + $count = count($queryBuilder->getQuery()->getResult()); + $maxpages = ceil($count / $perpage); // محاسبه حداکثر صفحات + + $posts = $queryBuilder->setMaxResults($perpage) + ->setFirstResult(($page - 1) * $perpage) + ->getQuery() + ->getResult(); + + // گرفتن همه دسته‌بندی‌ها برای سایدبار + $categories = $catRepository->findAll(); + return $this->render('post/blog_home.html.twig', [ 'posts' => $posts, 'page' => $page, - 'perpage'=> $perpage, + 'perpage' => $perpage, 'count' => $count, - 'maxpages' => $maxpages + 'maxpages' => $maxpages, + 'categories' => $categories, + 'search' => $search, ]); } diff --git a/src/Controller/SecurityController.php b/src/Controller/SecurityController.php index f4c346e..3ff6b11 100644 --- a/src/Controller/SecurityController.php +++ b/src/Controller/SecurityController.php @@ -14,55 +14,19 @@ class SecurityController extends AbstractController $error = $authenticationUtils->getLastAuthenticationError(); $lastUsername = $authenticationUtils->getLastUsername(); - return $this->render('@EasyAdmin/page/login.html.twig', [ - // parameters usually defined in Symfony login forms + return $this->render('/admin/login.html.twig', [ 'error' => $error, 'last_username' => $lastUsername, - - // OPTIONAL parameters to customize the login form: - - // the translation_domain to use (define this option only if you are -// rendering the login template in a regular Symfony controller; when -// rendering it from an EasyAdmin Dashboard this is automatically set to -// the same domain as the rest of the Dashboard) 'translation_domain' => 'admin', - - // by default EasyAdmin displays a black square as its default favicon; -// use this method to display a custom favicon: the given path is passed -// "as is" to the Twig asset() function: -// - - // the title visible above the login form (define this option only if you are -// rendering the login template in a regular Symfony controller; when rendering -// it from an EasyAdmin Dashboard this is automatically set as the Dashboard title) 'page_title' => 'ورود', - - // the string used to generate the CSRF token. If you don't define -// this parameter, the login form won't include a CSRF token 'csrf_token_intention' => 'authenticate', - - // the URL users are redirected to after the login (default: '/admin') - 'target_path' => $this->generateUrl('admin'), - - // the label displayed for the username form field (the |trans filter is applied to it) + 'target_path' => $this->generateUrl('admin', ['_locale' => 'fa']), 'username_label' => 'پست الکترونیکی', - - // the label displayed for the password form field (the |trans filter is applied to it) 'password_label' => 'کلمه عبور', - - // the label displayed for the Sign In form button (the |trans filter is applied to it) 'sign_in_label' => 'ورود', - - // whether to enable or not the "forgot password?" link (default: false) 'forgot_password_enabled' => false, - - // whether to enable or not the "remember me" checkbox (default: false) 'remember_me_enabled' => true, - - // whether to check by default the "remember me" checkbox (default: false) 'remember_me_checked' => true, - - // the label displayed for the remember me checkbox (the |trans filter is applied to it) 'remember_me_label' => 'مرا به یاد داشته باش', ]); } diff --git a/src/Entity/Post.php b/src/Entity/Post.php index 872c81b..633aa8d 100644 --- a/src/Entity/Post.php +++ b/src/Entity/Post.php @@ -8,9 +8,12 @@ use Doctrine\Common\Collections\Collection; use Doctrine\DBAL\Types\Types; use Doctrine\ORM\Mapping as ORM; use Symfony\UX\Turbo\Attribute\Broadcast; +use Symfony\Component\Validator\Constraints as Assert; +use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; #[ORM\Entity(repositoryClass: PostRepository::class)] #[Broadcast] +#[UniqueEntity(fields: ['url'], message: 'این URL قبلاً استفاده شده است.')] class Post { #[ORM\Id] @@ -34,7 +37,8 @@ class Post #[ORM\Column(nullable: true)] private ?bool $publish = null; - #[ORM\Column(length: 255, nullable: true)] + #[ORM\Column(length: 255, nullable: true, unique: true)] + #[Assert\NotBlank(message: 'URL نمی‌تواند خالی باشد', allowNull: false)] private ?string $url = null; #[ORM\Column(length: 255, nullable: true)] @@ -93,7 +97,6 @@ class Post public function setTitle(?string $title): static { $this->title = $title; - return $this; } @@ -105,7 +108,6 @@ class Post public function setSubmitter(?User $submitter): static { $this->submitter = $submitter; - return $this; } @@ -117,7 +119,6 @@ class Post public function setBody(?string $body): static { $this->body = $body; - return $this; } @@ -129,7 +130,6 @@ class Post public function setDateSubmit(string $dateSubmit): static { $this->dateSubmit = $dateSubmit; - return $this; } @@ -141,7 +141,6 @@ class Post public function setPublish(?bool $publish): static { $this->publish = $publish; - return $this; } @@ -153,7 +152,6 @@ class Post public function setUrl(?string $url): static { $this->url = $url; - return $this; } @@ -165,7 +163,6 @@ class Post public function setMainPic(?string $mainPic): static { $this->mainPic = $mainPic; - return $this; } @@ -177,7 +174,6 @@ class Post public function setPlain(?string $plain): static { $this->plain = $plain; - return $this; } @@ -189,7 +185,6 @@ class Post public function setVersion(?string $version): static { $this->version = $version; - return $this; } @@ -201,7 +196,6 @@ class Post public function setKeywords(?string $keywords): static { $this->keywords = $keywords; - return $this; } @@ -213,7 +207,6 @@ class Post public function setSort(?string $sort): static { $this->sort = $sort; - return $this; } @@ -225,7 +218,6 @@ class Post public function setCat(?Cat $cat): static { $this->cat = $cat; - return $this; } @@ -242,14 +234,12 @@ class Post if (!$this->tree->contains($tree)) { $this->tree->add($tree); } - return $this; } public function removeTree(Tree $tree): static { $this->tree->removeElement($tree); - return $this; } @@ -267,19 +257,16 @@ class Post $this->comments->add($comment); $comment->setPost($this); } - return $this; } public function removeComment(Comment $comment): static { if ($this->comments->removeElement($comment)) { - // set the owning side to null (unless already changed) if ($comment->getPost() === $this) { $comment->setPost(null); } } - return $this; } @@ -291,7 +278,6 @@ class Post public function setIntro(?string $intro): static { $this->intro = $intro; - return $this; } @@ -303,7 +289,6 @@ class Post public function setViews(?string $views): static { $this->views = $views; - return $this; } -} +} \ No newline at end of file diff --git a/templates/admin/login.html.twig b/templates/admin/login.html.twig new file mode 100644 index 0000000..b3bbdf1 --- /dev/null +++ b/templates/admin/login.html.twig @@ -0,0 +1,7 @@ +{# templates/easy_admin/page/login.html.twig #} +{% extends '@!EasyAdmin/page/login.html.twig' %} + +{% block head %} + {{ parent() }} + +{% endblock %} \ No newline at end of file