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