├── Resources ├── views │ ├── widgetBase.html.twig │ └── widgetAction.html.twig └── config │ ├── routing.yml │ └── services.yml ├── .gitignore ├── PdWidgetBundle.php ├── Entity ├── UserInterface.php └── WidgetUser.php ├── Render ├── RenderInterface.php └── TwigRender.php ├── composer.json ├── Repository └── WidgetUserRepository.php ├── Widget ├── WidgetBuilderInterface.php ├── WidgetInterface.php ├── Widget.php └── WidgetBuilder.php ├── Event └── WidgetEvent.php ├── DependencyInjection ├── Configuration.php └── PdWidgetExtension.php ├── LICENSE ├── .php_cs.dist ├── Twig └── WidgetExtension.php ├── Builder ├── ItemInterface.php └── Item.php ├── Controller └── WidgetController.php └── README.md /Resources/views/widgetBase.html.twig: -------------------------------------------------------------------------------- 1 | {{ widgets|raw }} 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /vendor 2 | /composer.lock 3 | .php_cs.cache 4 | Entity/*~ 5 | .DS_STORE 6 | -------------------------------------------------------------------------------- /PdWidgetBundle.php: -------------------------------------------------------------------------------- 1 | 9 | * @link https://github.com/appaydin/pd-widget 10 | */ 11 | 12 | namespace Pd\WidgetBundle; 13 | 14 | use Symfony\Component\HttpKernel\Bundle\Bundle; 15 | 16 | class PdWidgetBundle extends Bundle 17 | { 18 | } 19 | -------------------------------------------------------------------------------- /Entity/UserInterface.php: -------------------------------------------------------------------------------- 1 | 9 | * @link https://github.com/appaydin/pd-widget 10 | */ 11 | 12 | namespace Pd\WidgetBundle\Entity; 13 | 14 | /** 15 | * User Interface. 16 | * 17 | * @author Ramazan APAYDIN 18 | */ 19 | interface UserInterface 20 | { 21 | public function getId(): int; 22 | } 23 | -------------------------------------------------------------------------------- /Resources/config/routing.yml: -------------------------------------------------------------------------------- 1 | widget_status: 2 | path: /widget/status/{widgetId}/{status} 3 | controller: Pd\WidgetBundle\Controller\WidgetController::status 4 | requirements: 5 | status: \d+ 6 | 7 | widget_config: 8 | path: /widget/config/{widgetId} 9 | controller: Pd\WidgetBundle\Controller\WidgetController::configs 10 | 11 | widget_order: 12 | path: /widget/order/{widgetId}/{order} 13 | controller: Pd\WidgetBundle\Controller\WidgetController::order 14 | requirements: 15 | order: \d+ 16 | -------------------------------------------------------------------------------- /Render/RenderInterface.php: -------------------------------------------------------------------------------- 1 | 9 | * @link https://github.com/appaydin/pd-widget 10 | */ 11 | 12 | namespace Pd\WidgetBundle\Render; 13 | 14 | /** 15 | * WidgetRender Interface. 16 | * 17 | * @author Ramazan APAYDIN 18 | */ 19 | interface RenderInterface 20 | { 21 | public function render(bool|array $widgets): string; 22 | } 23 | -------------------------------------------------------------------------------- /Resources/config/services.yml: -------------------------------------------------------------------------------- 1 | services: 2 | _defaults: 3 | autowire: true # Automatically injects dependencies in your services. 4 | autoconfigure: true # Automatically registers your services as commands, event subscribers, etc. 5 | public: false # Allows optimizing the container by removing unused services; this also means 6 | 7 | # makes classes in src/ available to be used as services 8 | # this creates a service per class whose id is the fully-qualified class name 9 | Pd\WidgetBundle\: 10 | resource: '../../*' 11 | exclude: '../../{Entity,Migrations,Tests,Kernel.php}' 12 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "appaydin/pd-widget", 3 | "description": "Symfony Simple Widget Bundle", 4 | "homepage": "https://github.com/appaydin/pd-widget", 5 | "type": "symfony-bundle", 6 | "license": "MIT", 7 | "keywords": [ 8 | "pd-admin", 9 | "pd-widget", 10 | "widget", 11 | "symfony-widget", 12 | "admin-widget" 13 | ], 14 | "authors": [ 15 | { 16 | "name": "Ramazan APAYDIN", 17 | "homepage": "https://github.com/appaydin" 18 | } 19 | ], 20 | "require": { 21 | "php": ">=8.0.0", 22 | "twig/twig": "*", 23 | "symfony/event-dispatcher": "*" 24 | }, 25 | "autoload": { 26 | "psr-4": { 27 | "Pd\\WidgetBundle\\": "" 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Repository/WidgetUserRepository.php: -------------------------------------------------------------------------------- 1 | 9 | * @link https://github.com/appaydin/pd-widget 10 | */ 11 | 12 | namespace Pd\WidgetBundle\Repository; 13 | 14 | use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository; 15 | use Doctrine\Persistence\ManagerRegistry; 16 | use Pd\WidgetBundle\Entity\WidgetUser; 17 | 18 | class WidgetUserRepository extends ServiceEntityRepository 19 | { 20 | public function __construct(ManagerRegistry $registry) 21 | { 22 | parent::__construct($registry, WidgetUser::class); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Widget/WidgetBuilderInterface.php: -------------------------------------------------------------------------------- 1 | 9 | * @link https://github.com/appaydin/pd-widget 10 | */ 11 | 12 | namespace Pd\WidgetBundle\Widget; 13 | 14 | use Pd\WidgetBundle\Builder\ItemInterface; 15 | 16 | /** 17 | * Widget Builder Interface. 18 | * 19 | * @author Ramazan APAYDIN 20 | */ 21 | interface WidgetBuilderInterface 22 | { 23 | /** 24 | * Build Widgets. 25 | * 26 | * @return ItemInterface[]|null 27 | */ 28 | public function build(array $widgets, string $widgetGroup = '', array $widgetId = [], bool $render = false): ?array; 29 | } 30 | -------------------------------------------------------------------------------- /Event/WidgetEvent.php: -------------------------------------------------------------------------------- 1 | 9 | * @link https://github.com/appaydin/pd-widget 10 | */ 11 | 12 | namespace Pd\WidgetBundle\Event; 13 | 14 | use Pd\WidgetBundle\Widget\WidgetInterface; 15 | use Symfony\Contracts\EventDispatcher\Event; 16 | 17 | /** 18 | * Widget Events. 19 | * 20 | * @author Ramazan APAYDIN 21 | */ 22 | class WidgetEvent extends Event 23 | { 24 | public const WIDGET_START = 'widget.start'; 25 | 26 | public function __construct( 27 | private WidgetInterface $widget) 28 | { 29 | } 30 | 31 | public function getWidgetContainer(): WidgetInterface 32 | { 33 | return $this->widget; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /DependencyInjection/Configuration.php: -------------------------------------------------------------------------------- 1 | 9 | * @link https://github.com/appaydin/pd-widget 10 | */ 11 | 12 | namespace Pd\WidgetBundle\DependencyInjection; 13 | 14 | use Symfony\Component\Config\Definition\Builder\TreeBuilder; 15 | use Symfony\Component\Config\Definition\ConfigurationInterface; 16 | 17 | class Configuration implements ConfigurationInterface 18 | { 19 | public function getConfigTreeBuilder(): TreeBuilder 20 | { 21 | $treeBuilder = new TreeBuilder('pd_widget'); 22 | $rootNode = $treeBuilder->getRootNode(); 23 | 24 | $rootNode 25 | ->children() 26 | ->scalarNode('base_template')->defaultValue('@PdWidget/widgetBase.html.twig')->end() 27 | ->scalarNode('return_route')->defaultValue('admin_dashboard')->end() 28 | ->end(); 29 | 30 | return $treeBuilder; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Widget/WidgetInterface.php: -------------------------------------------------------------------------------- 1 | 9 | * @link https://github.com/appaydin/pd-widget 10 | */ 11 | 12 | namespace Pd\WidgetBundle\Widget; 13 | 14 | use Pd\WidgetBundle\Builder\ItemInterface; 15 | 16 | /** 17 | * Widget Interface. 18 | * 19 | * @author Ramazan APAYDIN 20 | */ 21 | interface WidgetInterface 22 | { 23 | /** 24 | * Get Items to Widget Storage. 25 | * 26 | * @return ItemInterface[]|null 27 | */ 28 | public function getWidgets($checkRole = true): ?array; 29 | 30 | /** 31 | * Add Item to Widget Storage. 32 | */ 33 | public function addWidget(ItemInterface $item): self; 34 | 35 | /** 36 | * Remove Item to Widget Storage. 37 | */ 38 | public function removeWidget(string $widgetId): self; 39 | 40 | /** 41 | * Clear Current User Widget Cache. 42 | */ 43 | public function clearWidgetCache(): void; 44 | } 45 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Ramazan APAYDIN 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Resources/views/widgetAction.html.twig: -------------------------------------------------------------------------------- 1 | 15 | -------------------------------------------------------------------------------- /.php_cs.dist: -------------------------------------------------------------------------------- 1 | 9 | @link https://github.com/appaydin/pd-widget 10 | COMMENT; 11 | 12 | $finder = PhpCsFixer\Finder::create() 13 | ->in(__DIR__) 14 | ; 15 | 16 | return PhpCsFixer\Config::create() 17 | ->setRiskyAllowed(true) 18 | ->setRules([ 19 | '@Symfony' => true, 20 | '@Symfony:risky' => true, 21 | 'array_syntax' => ['syntax' => 'short'], 22 | 'header_comment' => ['header' => $fileHeaderComment, 'separate' => 'both', 'commentType' => 'PHPDoc'], 23 | 'linebreak_after_opening_tag' => true, 24 | 'mb_str_functions' => true, 25 | 'no_php4_constructor' => true, 26 | 'no_useless_else' => true, 27 | 'no_useless_return' => true, 28 | 'ordered_imports' => true, 29 | 'php_unit_strict' => true, 30 | 'phpdoc_order' => true, 31 | 'semicolon_after_instruction' => true, 32 | 'strict_comparison' => true, 33 | 'strict_param' => true, 34 | 'blank_line_after_namespace' => true, 35 | 'class_definition' => true, 36 | 'multiline_comment_opening_closing' => true, 37 | 'single_line_comment_style' => true, 38 | 'single_blank_line_before_namespace' => true, 39 | 'phpdoc_trim' => true, 40 | 'no_extra_consecutive_blank_lines' => true 41 | ]) 42 | ->setFinder($finder) 43 | ->setCacheFile(__DIR__.'.php_cs.cache') 44 | ; 45 | -------------------------------------------------------------------------------- /DependencyInjection/PdWidgetExtension.php: -------------------------------------------------------------------------------- 1 | 9 | * @link https://github.com/appaydin/pd-widget 10 | */ 11 | 12 | namespace Pd\WidgetBundle\DependencyInjection; 13 | 14 | use Pd\WidgetBundle\Render\TwigRender; 15 | use Symfony\Component\Config\FileLocator; 16 | use Symfony\Component\DependencyInjection\ContainerBuilder; 17 | use Symfony\Component\DependencyInjection\Extension\Extension; 18 | use Symfony\Component\DependencyInjection\Loader; 19 | 20 | /** 21 | * This is the class that loads and manages your bundle configuration. 22 | * 23 | * @see http://symfony.com/doc/current/cookbook/bundles/extension.html 24 | */ 25 | class PdWidgetExtension extends Extension 26 | { 27 | public function load(array $configs, ContainerBuilder $container) 28 | { 29 | // Load Services 30 | $loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); 31 | $loader->load('services.yml'); 32 | 33 | // Load Configuration 34 | $configuration = new Configuration(); 35 | $config = $this->processConfiguration($configuration, $configs); 36 | 37 | // Set Parameter 38 | $container->setParameter('pd_widget.return_route', $config['return_route']); 39 | 40 | // Set Configuration 41 | $container 42 | ->getDefinition(TwigRender::class) 43 | ->setArgument('$baseTemplate', $config['base_template']); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Twig/WidgetExtension.php: -------------------------------------------------------------------------------- 1 | 9 | * @link https://github.com/appaydin/pd-widget 10 | */ 11 | 12 | namespace Pd\WidgetBundle\Twig; 13 | 14 | use Pd\WidgetBundle\Builder\ItemInterface; 15 | use Pd\WidgetBundle\Render\RenderInterface; 16 | use Pd\WidgetBundle\Widget\WidgetBuilderInterface; 17 | use Pd\WidgetBundle\Widget\WidgetInterface; 18 | use Twig\Extension\AbstractExtension; 19 | use Twig\TwigFunction; 20 | 21 | /** 22 | * Widget Twig Extension. 23 | * 24 | * @author Ramazan APAYDIN 25 | */ 26 | class WidgetExtension extends AbstractExtension 27 | { 28 | public function __construct( 29 | private RenderInterface $engine, 30 | private WidgetBuilderInterface $widgetBuilder, 31 | private WidgetInterface $widgets) 32 | { 33 | } 34 | 35 | /** 36 | * Twig Functions. 37 | */ 38 | public function getFunctions(): array 39 | { 40 | return [ 41 | new TwigFunction('pd_widget_render', [$this, 'renderWidget'], ['is_safe' => ['html']]), 42 | new TwigFunction('pd_widget_get', [$this, 'getWidget']), 43 | ]; 44 | } 45 | 46 | /** 47 | * Render Widget Group|Item. 48 | */ 49 | public function renderWidget(string $widgetGroup = '', array $widgetId = []): string 50 | { 51 | return $this->engine->render($this->widgetBuilder->build($this->widgets->getWidgets(), $widgetGroup, $widgetId, true)); 52 | } 53 | 54 | /** 55 | * Get Widgets. 56 | * 57 | * @return ItemInterface[] 58 | */ 59 | public function getWidget(string $widgetGroup = '', array $widgetId = []): array 60 | { 61 | return $this->widgetBuilder->build($this->widgets->getWidgets(), $widgetGroup, $widgetId, false); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /Entity/WidgetUser.php: -------------------------------------------------------------------------------- 1 | 9 | * @link https://github.com/appaydin/pd-widget 10 | */ 11 | 12 | namespace Pd\WidgetBundle\Entity; 13 | 14 | use Doctrine\ORM\Mapping as ORM; 15 | 16 | /** 17 | * Widget Private User Data. 18 | * 19 | * @ORM\Entity(repositoryClass="Pd\WidgetBundle\Repository\WidgetUserRepository") 20 | * @ORM\Table(name="widget_user") 21 | * @ORM\Cache(usage="NONSTRICT_READ_WRITE") 22 | * 23 | * @author Ramazan APAYDIN 24 | */ 25 | class WidgetUser 26 | { 27 | /** 28 | * @ORM\Id 29 | * @ORM\GeneratedValue 30 | * @ORM\Column(type="integer") 31 | */ 32 | private $id; 33 | 34 | /** 35 | * @ORM\Column(type="array") 36 | */ 37 | private $config; 38 | 39 | /** 40 | * @ORM\OneToOne(targetEntity="UserInterface") 41 | * @ORM\JoinColumn(referencedColumnName="id", unique=true, onDelete="CASCADE") 42 | */ 43 | private $owner; 44 | 45 | /** 46 | * Get id. 47 | */ 48 | public function getId(): int 49 | { 50 | return $this->id; 51 | } 52 | 53 | public function setConfig(array $config): self 54 | { 55 | $this->config = $config; 56 | 57 | return $this; 58 | } 59 | 60 | public function getConfig(): array 61 | { 62 | return $this->config; 63 | } 64 | 65 | public function addWidgetConfig(string $widgetId, array $config = []): self 66 | { 67 | $this->config[$widgetId] = array_merge($this->config[$widgetId] ?? [], $config); 68 | 69 | return $this; 70 | } 71 | 72 | public function removeWidgetConfig(string $widgetId, array $config = []): self 73 | { 74 | foreach ($config as $id => $content) { 75 | if (isset($this->config[$widgetId][$id])) { 76 | unset($this->config[$widgetId][$id]); 77 | } 78 | } 79 | 80 | return $this; 81 | } 82 | 83 | public function getOwner(): ?UserInterface 84 | { 85 | return $this->owner; 86 | } 87 | 88 | public function setOwner($owner = null): self 89 | { 90 | $this->owner = $owner; 91 | 92 | return $this; 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /Render/TwigRender.php: -------------------------------------------------------------------------------- 1 | 9 | * @link https://github.com/appaydin/pd-widget 10 | */ 11 | 12 | namespace Pd\WidgetBundle\Render; 13 | 14 | use Pd\WidgetBundle\Builder\ItemInterface; 15 | use Psr\Cache\CacheItemPoolInterface; 16 | use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; 17 | use Twig\Environment; 18 | 19 | /** 20 | * Widget Twig Render. 21 | * 22 | * @author Ramazan APAYDIN 23 | */ 24 | class TwigRender implements RenderInterface 25 | { 26 | public function __construct( 27 | private Environment $engine, 28 | private CacheItemPoolInterface $cache, 29 | private TokenStorageInterface $tokenStorage, 30 | private string $baseTemplate) 31 | { 32 | } 33 | 34 | /** 35 | * Render Widgets. 36 | * 37 | * @param $widgets ItemInterface[] 38 | * @param bool $base 39 | * 40 | * @return string 41 | */ 42 | public function render($widgets, bool $base = true): string 43 | { 44 | if (!$widgets) { 45 | return false; 46 | } 47 | 48 | // Output Storage 49 | $output = ''; 50 | 51 | // Get User ID 52 | $userId = $this->tokenStorage->getToken()->getUser()->getId(); 53 | 54 | foreach ($widgets as $widget) { 55 | if ($widget->isActive()) { 56 | $output .= $this->getOutput($widget, $userId); 57 | } 58 | } 59 | 60 | // Render Base 61 | if ($base) { 62 | try { 63 | $output = @$this->engine->render($this->baseTemplate, ['widgets' => $output]); 64 | } catch (\Exception $e) { 65 | } 66 | } 67 | 68 | return $output; 69 | } 70 | 71 | /** 72 | * Get Widget Output for Cache. 73 | */ 74 | public function getOutput(ItemInterface $item, $userId): string 75 | { 76 | if ($item->getCacheTime()) { 77 | // Get Cache Item 78 | $cache = $this->cache->getItem($item->getId() . $userId); 79 | 80 | // Set Cache Expires 81 | $cache->expiresAfter($item->getCacheTime()); 82 | 83 | // Save 84 | if (false === $cache->isHit()) { 85 | $cache->set($item->getTemplate() ? $this->engine->render($item->getTemplate(), ['widget' => $item]) : $item->getContent()); 86 | $this->cache->save($cache); 87 | } 88 | 89 | return $cache->get(); 90 | } 91 | 92 | return $item->getTemplate() ? $this->engine->render($item->getTemplate(), ['widget' => $item]) : $item->getContent(); 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /Builder/ItemInterface.php: -------------------------------------------------------------------------------- 1 | 9 | * @link https://github.com/appaydin/pd-widget 10 | */ 11 | 12 | namespace Pd\WidgetBundle\Builder; 13 | 14 | use Symfony\Component\HttpFoundation\Request; 15 | 16 | /** 17 | * Widget Builder Interface. 18 | * 19 | * @author Ramazan APAYDIN 20 | */ 21 | interface ItemInterface 22 | { 23 | public function getId(); 24 | 25 | /** 26 | * Get Item Name. 27 | */ 28 | public function getName(): string; 29 | 30 | /** 31 | * Set Item Name. 32 | */ 33 | public function setName(string $name): self; 34 | 35 | /** 36 | * Get Item Description. 37 | */ 38 | public function getDescription(): string; 39 | 40 | /** 41 | * Set Item Description. 42 | */ 43 | public function setDescription(string $description): self; 44 | 45 | /** 46 | * Get Item Content. 47 | */ 48 | public function getContent(): string; 49 | 50 | /** 51 | * Set Item Content. 52 | */ 53 | public function setContent(string $content): self; 54 | 55 | /** 56 | * Get Item Template. 57 | */ 58 | public function getTemplate(): string; 59 | 60 | /** 61 | * Set Item Template. 62 | */ 63 | public function setTemplate(string $template): self; 64 | 65 | /** 66 | * Get Content Data. 67 | */ 68 | public function getData(); 69 | 70 | /** 71 | * Set Content Data. 72 | */ 73 | public function setData(callable $data): self; 74 | 75 | public function getConfig(): ?array; 76 | 77 | public function setConfig(array $config): self; 78 | 79 | public function getConfigProcess(Request $request): ?array; 80 | 81 | public function setConfigProcess(callable $process): self; 82 | 83 | /** 84 | * Get Order Number. 85 | */ 86 | public function getOrder(): ?int; 87 | 88 | /** 89 | * Set Order Number. 90 | */ 91 | public function setOrder(int $order): self; 92 | 93 | /** 94 | * Get Access Role. 95 | */ 96 | public function getRole(): array; 97 | 98 | /** 99 | * Set Access Role. 100 | */ 101 | public function setRole(array $role): self; 102 | 103 | /** 104 | * Get Item Status. 105 | */ 106 | public function isActive(): bool; 107 | 108 | /** 109 | * Set Item Status. 110 | */ 111 | public function setActive(bool $status): self; 112 | 113 | /** 114 | * Get Item Group. 115 | */ 116 | public function getGroup(): string; 117 | 118 | /** 119 | * Set Item Group Name. 120 | */ 121 | public function setGroup(string $name): self; 122 | 123 | public function getCacheTime(): int|bool; 124 | 125 | public function setCacheTime(bool|int $time): self; 126 | } 127 | -------------------------------------------------------------------------------- /Widget/Widget.php: -------------------------------------------------------------------------------- 1 | 9 | * @link https://github.com/appaydin/pd-widget 10 | */ 11 | 12 | namespace Pd\WidgetBundle\Widget; 13 | 14 | use Pd\WidgetBundle\Builder\ItemInterface; 15 | use Pd\WidgetBundle\Event\WidgetEvent; 16 | use Psr\Cache\CacheItemPoolInterface; 17 | use Psr\Cache\InvalidArgumentException; 18 | use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; 19 | use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface; 20 | use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; 21 | 22 | /** 23 | * Widget Main. 24 | * 25 | * @author Ramazan APAYDIN 26 | */ 27 | class Widget implements WidgetInterface 28 | { 29 | /** 30 | * Widget Storage. 31 | * 32 | * @var array|ItemInterface[] 33 | */ 34 | private array $widgets = []; 35 | private bool $checkRole; 36 | 37 | public function __construct( 38 | private AuthorizationCheckerInterface $security, 39 | private EventDispatcherInterface $eventDispatcher, 40 | private CacheItemPoolInterface $cache, 41 | private TokenStorageInterface $token) 42 | { 43 | } 44 | 45 | /** 46 | * Get Widgets. 47 | * 48 | * @return ItemInterface[]|null 49 | */ 50 | public function getWidgets($checkRole = true): ?array 51 | { 52 | // Check Role 53 | $this->checkRole = $checkRole; 54 | 55 | // Dispatch Event 56 | if (!$this->widgets) { 57 | $this->eventDispatcher->dispatch(new WidgetEvent($this), WidgetEvent::WIDGET_START); 58 | } 59 | 60 | return $this->widgets; 61 | } 62 | 63 | /** 64 | * Add Widget 65 | */ 66 | public function addWidget(ItemInterface $item): WidgetInterface 67 | { 68 | // Check Security 69 | if ($this->checkRole && $item->getRole()) { 70 | $decide = true; 71 | foreach ($item->getRole() as $role) { 72 | if (!$this->security->isGranted($role)) { 73 | $decide = false; 74 | break; 75 | } 76 | } 77 | 78 | if (!$decide) { 79 | return $this; 80 | } 81 | } 82 | 83 | // Add 84 | $this->widgets[$item->getId()] = $item; 85 | 86 | return $this; 87 | } 88 | 89 | /** 90 | * Remove Widget. 91 | */ 92 | public function removeWidget(string $widgetId): WidgetInterface 93 | { 94 | if (isset($this->widgets[$widgetId])) { 95 | unset($this->widgets[$widgetId]); 96 | } 97 | 98 | return $this; 99 | } 100 | 101 | /** 102 | * Clear current user widget cache. 103 | */ 104 | public function clearWidgetCache(): void 105 | { 106 | // Get Widgets 107 | $widgets = $this->getWidgets(false); 108 | $userId = $this->token->getToken()->getUser()->getId(); 109 | 110 | // Clear Cache 111 | foreach ($widgets as $widget) { 112 | try { 113 | $this->cache->deleteItem($widget->getId() . $userId); 114 | } catch (InvalidArgumentException $e) { 115 | } 116 | } 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /Widget/WidgetBuilder.php: -------------------------------------------------------------------------------- 1 | 9 | * @link https://github.com/appaydin/pd-widget 10 | */ 11 | 12 | namespace Pd\WidgetBundle\Widget; 13 | 14 | use Doctrine\ORM\EntityManagerInterface; 15 | use Pd\WidgetBundle\Builder\ItemInterface; 16 | use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; 17 | 18 | /** 19 | * Widget Builder. 20 | * 21 | * @author Ramazan APAYDIN 22 | */ 23 | class WidgetBuilder implements WidgetBuilderInterface 24 | { 25 | /** 26 | * User Widget Configuration. 27 | */ 28 | private array $widgetConfig = []; 29 | 30 | public function __construct( 31 | private EntityManagerInterface $entityManager, 32 | private TokenStorageInterface $tokenStorage) 33 | { 34 | } 35 | 36 | /** 37 | * Build Widgets. 38 | * 39 | * @param $widgets ItemInterface[] 40 | * @param string $widgetGroup 41 | * @param array $widgetId 42 | * @param bool $render 43 | * 44 | * @return ItemInterface[] 45 | */ 46 | public function build(array $widgets, string $widgetGroup = '', array $widgetId = [], bool $render = false): ?array 47 | { 48 | // Without Widgets 49 | if (!$widgets) { 50 | return $widgets; 51 | } 52 | 53 | // Load User Widget Configuration 54 | $this->loadUserConfig(); 55 | 56 | // Output Widgets 57 | $outputWidget = []; 58 | 59 | // Custom Items 60 | if ($widgetId) { 61 | foreach ($widgetId as $id) { 62 | if (isset($widgets[$id])) { 63 | // Activate 64 | $widgets[$id]->setActive($this->widgetConfig[$id]['status'] ?? false); 65 | 66 | // Set Widget Config 67 | $widgets[$id]->setConfig($this->widgetConfig[$id] ?? []); 68 | 69 | $outputWidget[] = $widgets[$id]; 70 | } 71 | } 72 | 73 | return $outputWidget; 74 | } 75 | 76 | foreach ($widgets as $widget) { 77 | // Activate 78 | $widget->setActive($this->widgetConfig[$widget->getId()]['status'] ?? false); 79 | 80 | // Set Widget Config 81 | $widget->setConfig($this->widgetConfig[$widget->getId()] ?? []); 82 | 83 | // Enable 84 | if (('' !== $widgetGroup && $widget->getGroup() !== $widgetGroup) || ($render && !$widget->isActive())) { 85 | continue; 86 | } 87 | 88 | // Set Custom Order 89 | if (isset($this->widgetConfig[$widget->getId()]['order'])) { 90 | $widget->setOrder($this->widgetConfig[$widget->getId()]['order']); 91 | } 92 | 93 | // Order 94 | if (null !== $widget->getOrder()) { 95 | while (isset($outputWidget[$widget->getOrder()])) { 96 | $widget->setOrder($widget->getOrder() + 1); 97 | } 98 | 99 | $outputWidget[$widget->getOrder()] = $widget; 100 | } else { 101 | $outputWidget[] = $widget; 102 | } 103 | } 104 | 105 | // Sort 106 | ksort($outputWidget); 107 | 108 | return $outputWidget; 109 | } 110 | 111 | /** 112 | * Load User Widget Configuration. 113 | */ 114 | private function loadUserConfig(): void 115 | { 116 | if (!$this->widgetConfig) { 117 | $config = $this->entityManager 118 | ->getRepository('PdWidgetBundle:WidgetUser') 119 | ->findOneBy([ 120 | 'owner' => $this->tokenStorage->getToken()->getUser(), 121 | ]); 122 | 123 | if (null !== $config) { 124 | $this->widgetConfig = $config->getConfig(); 125 | } 126 | } 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /Controller/WidgetController.php: -------------------------------------------------------------------------------- 1 | 9 | * @link https://github.com/appaydin/pd-widget 10 | */ 11 | 12 | namespace Pd\WidgetBundle\Controller; 13 | 14 | use Pd\WidgetBundle\Entity\WidgetUser; 15 | use Pd\WidgetBundle\Repository\WidgetUserRepository; 16 | use Pd\WidgetBundle\Widget\WidgetInterface; 17 | use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; 18 | use Symfony\Component\HttpFoundation\JsonResponse; 19 | use Symfony\Component\HttpFoundation\RedirectResponse; 20 | use Symfony\Component\HttpFoundation\Request; 21 | use Symfony\Contracts\Cache\CacheInterface; 22 | 23 | /** 24 | * Widget Actions. 25 | * 26 | * @author Ramazan APAYDIN 27 | */ 28 | class WidgetController extends AbstractController 29 | { 30 | public function __construct( 31 | private WidgetUserRepository $widgetUserRepo, 32 | private CacheInterface $cache 33 | ) 34 | { 35 | } 36 | 37 | /** 38 | * Change Widget Status. 39 | */ 40 | public function status(Request $request, WidgetInterface $widget, string $widgetId, bool $status = true): RedirectResponse 41 | { 42 | // Build Widget 43 | $widgets = $widget->getWidgets(); 44 | 45 | if (isset($widgets[$widgetId])) { 46 | // Get User Widgets 47 | $widgetConfig = $this->widgetUserRepo->findOneBy(['owner' => $this->getUser()]) ?? (new WidgetUser())->setOwner($this->getUser()); 48 | 49 | // Add Config Parameters 50 | $widgetConfig->addWidgetConfig($widgetId, ['status' => $status]); 51 | 52 | // Save 53 | $em = $this->getDoctrine()->getManager(); 54 | $em->persist($widgetConfig); 55 | $em->flush(); 56 | } 57 | 58 | // Response 59 | return $this->redirect($request->headers->get('referer', $this->generateUrl($this->getParameter('pd_widget.return_route')))); 60 | } 61 | 62 | /** 63 | * Change Widget Configuration. 64 | */ 65 | public function configs(Request $request, WidgetInterface $widget, string $widgetId): RedirectResponse 66 | { 67 | // Build Widget 68 | $widgets = $widget->getWidgets(); 69 | 70 | if (isset($widgets[$widgetId])) { 71 | // Get User Widgets 72 | $widgetConfig = $this->widgetUserRepo->findOneBy(['owner' => $this->getUser()]) ?? (new WidgetUser())->setOwner($this->getUser()); 73 | 74 | // Add or Remove Config Parameters 75 | if ($request->get('remove')) { 76 | $widgetConfig->removeWidgetConfig($widgetId, $widgets[$widgetId]->getConfigProcess($request) ?? []); 77 | } else { 78 | $widgetConfig->addWidgetConfig($widgetId, $widgets[$widgetId]->getConfigProcess($request) ?? []); 79 | } 80 | 81 | // Save 82 | $em = $this->getDoctrine()->getManager(); 83 | $em->persist($widgetConfig); 84 | $em->flush(); 85 | 86 | // Flush Widget Cache 87 | $this->cache->delete($widgetId . $this->getUser()->getId()); 88 | } 89 | 90 | // Response 91 | return $this->redirect($request->headers->get('referer', $this->generateUrl($this->getParameter('pd_widget.return_route')))); 92 | } 93 | 94 | /** 95 | * Change Widget Order. 96 | */ 97 | public function order(WidgetInterface $widget, string $widgetId, int $order): JsonResponse 98 | { 99 | // Build Widget 100 | $widgets = $widget->getWidgets(); 101 | 102 | if (isset($widgets[$widgetId])) { 103 | // Get User Widgets 104 | $widgetConfig = $this->widgetUserRepo->findOneBy(['owner' => $this->getUser()]) ?? (new WidgetUser())->setOwner($this->getUser()); 105 | 106 | // Add Config Parameters 107 | $widgetConfig->addWidgetConfig($widgetId, ['order' => $order]); 108 | 109 | // Save 110 | $em = $this->getDoctrine()->getManager(); 111 | $em->persist($widgetConfig); 112 | $em->flush(); 113 | } 114 | 115 | // Response 116 | return $this->json([ 117 | 'result' => 'success', 118 | ]); 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /Builder/Item.php: -------------------------------------------------------------------------------- 1 | 9 | * @link https://github.com/appaydin/pd-widget 10 | */ 11 | 12 | namespace Pd\WidgetBundle\Builder; 13 | 14 | use Symfony\Component\HttpFoundation\Request; 15 | 16 | /** 17 | * Widget Item Builder. 18 | * 19 | * @author Ramazan APAYDIN 20 | */ 21 | class Item implements ItemInterface 22 | { 23 | private $id; 24 | private string $name = ''; 25 | private string $description = ''; 26 | private string $content = ''; 27 | private string $template = ''; 28 | /** 29 | * @var callable 30 | */ 31 | private $data; 32 | private array $config; 33 | 34 | /** 35 | * @var callable 36 | */ 37 | private $configProcess; 38 | private $order; 39 | private array $role = []; 40 | private string $group = ''; 41 | private bool $active = false; 42 | private int|bool $cacheExpires; 43 | 44 | public function __construct($id, $cacheExpires = 3600) 45 | { 46 | $this->id = $id; 47 | $this->cacheExpires = $cacheExpires; 48 | } 49 | 50 | public function getId() 51 | { 52 | return $this->id; 53 | } 54 | 55 | public function getName(): string 56 | { 57 | return $this->name; 58 | } 59 | 60 | public function setName(string $name): ItemInterface 61 | { 62 | $this->name = $name; 63 | 64 | return $this; 65 | } 66 | 67 | public function getDescription(): string 68 | { 69 | return $this->description; 70 | } 71 | 72 | public function setDescription(string $description): ItemInterface 73 | { 74 | $this->description = $description; 75 | 76 | return $this; 77 | } 78 | 79 | public function getContent(): string 80 | { 81 | return $this->content; 82 | } 83 | 84 | public function setContent(string $content): ItemInterface 85 | { 86 | $this->content = $content; 87 | 88 | return $this; 89 | } 90 | 91 | public function getTemplate(): string 92 | { 93 | return $this->template; 94 | } 95 | 96 | public function setTemplate(string $template): ItemInterface 97 | { 98 | $this->template = $template; 99 | 100 | return $this; 101 | } 102 | 103 | public function getData() 104 | { 105 | if (null !== $this->data) { 106 | $data = $this->data; 107 | 108 | return $data($this->config); 109 | } 110 | 111 | return false; 112 | } 113 | 114 | public function setData(callable $data): ItemInterface 115 | { 116 | $this->data = $data; 117 | 118 | return $this; 119 | } 120 | 121 | public function getConfig(): ?array 122 | { 123 | return $this->config; 124 | } 125 | 126 | public function setConfig(array $config): ItemInterface 127 | { 128 | $this->config = $config; 129 | 130 | return $this; 131 | } 132 | 133 | public function getConfigProcess(Request $request): ?array 134 | { 135 | if (null !== $this->configProcess) { 136 | $data = $this->configProcess; 137 | 138 | return $data($request); 139 | } 140 | 141 | return null; 142 | } 143 | 144 | public function setConfigProcess(callable $process): ItemInterface 145 | { 146 | $this->configProcess = $process; 147 | 148 | return $this; 149 | } 150 | 151 | public function getOrder(): ?int 152 | { 153 | return $this->order; 154 | } 155 | 156 | public function setOrder(int $order): ItemInterface 157 | { 158 | $this->order = $order; 159 | 160 | return $this; 161 | } 162 | 163 | public function getRole(): array 164 | { 165 | return $this->role; 166 | } 167 | 168 | public function setRole(array $role): ItemInterface 169 | { 170 | $this->role = $role; 171 | 172 | return $this; 173 | } 174 | 175 | public function isActive(): bool 176 | { 177 | return $this->active; 178 | } 179 | 180 | public function setActive(bool $status): ItemInterface 181 | { 182 | $this->active = $status; 183 | 184 | return $this; 185 | } 186 | 187 | public function getGroup(): string 188 | { 189 | return $this->group; 190 | } 191 | 192 | public function setGroup(string $name): ItemInterface 193 | { 194 | $this->group = $name; 195 | 196 | return $this; 197 | } 198 | 199 | public function getCacheTime(): bool|int 200 | { 201 | return $this->cacheExpires; 202 | } 203 | 204 | public function setCacheTime(bool|int $time): ItemInterface 205 | { 206 | $this->cacheExpires = $time; 207 | 208 | return $this; 209 | } 210 | } 211 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # pdWidget Bundle 2 | Flexible widget system for Symfony 5. 3 | 4 | [![Packagist](https://img.shields.io/packagist/dt/appaydin/pd-widget.svg)](https://github.com/appaydin/pd-widget) 5 | [![Github Release](https://img.shields.io/github/release/appaydin/pd-widget.svg)](https://github.com/appaydin/pd-widget) 6 | [![license](https://img.shields.io/github/license/appaydin/pd-widget.svg)](https://github.com/appaydin/pd-widget) 7 | [![PHP from Packagist](https://img.shields.io/packagist/php-v/appaydin/pd-widget.svg)](https://github.com/appaydin/pd-widget) 8 | 9 | Installation 10 | --- 11 | 12 | ### Step 1: Download the Bundle 13 | 14 | Open a command console, enter your project directory and execute the 15 | following command to download the latest stable version of this bundle: 16 | 17 | ```console 18 | $ composer require appaydin/pd-widget 19 | ``` 20 | 21 | This command requires you to have Composer installed globally, as explained 22 | in the [installation chapter](https://getcomposer.org/doc/00-intro.md) 23 | of the Composer documentation. 24 | 25 | ### Step 2: Enable the Bundle 26 | 27 | With Symfony 4, the package will be activated automatically. But if something goes wrong, you can install it manually. 28 | 29 | Then, enable the bundle by adding it to the list of registered bundles 30 | in the `config/bundles.php` file of your project: 31 | 32 | ```php 33 | ['all' => true] 39 | ]; 40 | ``` 41 | 42 | Add Widget Routing: 43 | 44 | ```yaml 45 | #config/routes.yaml 46 | 47 | # Widget Routing 48 | widget: 49 | resource: "@PdWidgetBundle/Resources/config/routing.yml" 50 | ``` 51 | 52 | Edit Doctrine Settings (`config/packages/doctrine.yaml`): 53 | 54 | ```yaml 55 | doctrine: 56 | orm: 57 | resolve_target_entities: 58 | Pd\WidgetBundle\Entity\UserInterface: App\Entity\User 59 | ``` 60 | 61 | UserInterface field, enter the class for the existing authorization system. 62 | 63 | ### Step 3: Settings Bundle (Optional) 64 | You can specify the template for the widget container. 65 | ```yaml 66 | # config/packages/framework.yaml 67 | 68 | pd_widget: 69 | base_template: '@PdWidget/widgetBase.html.twig' 70 | return_route: 'admin_dashboard' 71 | ``` 72 | 73 | Create Your First Widget 74 | --- 75 | 76 | #### Step 1: Create Widget Event Listener 77 | 78 | Widgets work with Event. Create Widget with Event Listener 79 | 80 | ```php 81 | getWidgetContainer(); 95 | 96 | // Add Widgets 97 | $widgets 98 | ->addWidget((new Item('user_info', 3600)) // Add Cache Time or Default 3600 Second 99 | ->setGroup('admin') 100 | ->setName('widget_user_info.name') 101 | ->setDescription('widget_user_info.description') 102 | ->setTemplate('widgets/userInfo.html.twig') 103 | //->setContent('pdWidget Text Content') 104 | //->setRole(['USER_INFO_WIDGET']) 105 | ->setData(function () { 106 | return ['userCount' => 5]; 107 | }) 108 | ->setOrder(5) 109 | ); 110 | } 111 | } 112 | ``` 113 | #### Step 2: Create Widget Template 114 | You can create a Twig template for the widget or can only use text content. 115 | ```twig 116 | # templates/userInfo.html.twig 117 | 118 | {% if widget.isActive %} 119 |
120 | 129 |
130 | {% endif %} 131 | ``` 132 | 133 | #### Step 3: Create Widget Services: 134 | ```yaml 135 | # config/services.yaml 136 | 137 | # Load All Widgets 138 | App\Widgets\: 139 | resource: '../src/Widgets/*' 140 | tags: 141 | - { name: kernel.event_listener, event: widget.start, method: builder } 142 | 143 | # Load Single Widget 144 | App\Widgets\DashboardWidget: 145 | tags: 146 | - { name: kernel.event_listener, event: widget.start, method: builder } 147 | ``` 148 | 149 | Rendering Widgets 150 | --- 151 | The creation process is very simple. You should use widget groups for widget creation. 152 | 153 | ```twig 154 | # Render all 'admin' widget groups 155 | {{ pd_widget_render('admin') }} 156 | 157 | # Render selected widgets in 'admin' group 158 | {{ pd_widget_render('admin', ['user_info']) }} 159 | ``` 160 | 161 | 162 | --------------------------------------------------------------------------------