gkratz/adminbundle

A bundle to manage CMS administration for symfony 3

Installs: 16

Dependents: 0

Suggesters: 0

Type:symfony-bundle


README

//-- extraordinary --//

IN ONE COMMAND, YOU WILL GENERATE EVERYTHING FOR YOUR BACK-OFFICE 
INCLUDING USERS AND CONFIGURATION

START A NEW PROJECT AND INSTALL IT TO UNDERSTAND THE POWER OF THIS BUNDLE.

//-- database --//

We recommend to use a mariadb database if you have errors with sql queries.
The bundle use complexes sql queries and MARIADB is better than mysql in this case.

//-- entities --//

Every entity that you will CREATE for your own project
MUST absolutely have a field named "state" type integer.
if you want to use the TRASH PLUGIN and the SEARCH PLUGIN

INSTALLATION

1) //-- add in the "composer.json" of your project --//

"require": {
    // ...
    "gkratz/adminbundle": "dev-master",
    "friendsofsymfony/user-bundle": "dev-master",
    "beberlei/DoctrineExtensions": "dev-master"
}

2) //-- activate the bundle in your "app\AppKernel.php" --//

new Gkratz\AdminBundle\GkratzAdminBundle(),
new Ambta\DoctrineEncryptBundle\AmbtaDoctrineEncryptBundle(),
new Ob\HighchartsBundle\ObHighchartsBundle(),
new Knp\Bundle\PaginatorBundle\KnpPaginatorBundle(),
new Lexik\Bundle\FormFilterBundle\LexikFormFilterBundle(),
new Symfony\Bundle\AsseticBundle\AsseticBundle(),
new FOS\UserBundle\FOSUserBundle(),
new Stof\DoctrineExtensionsBundle\StofDoctrineExtensionsBundle(),
new Ivory\CKEditorBundle\IvoryCKEditorBundle(),
new FM\ElfinderBundle\FMElfinderBundle(),
new SimpleThings\EntityAudit\SimpleThingsEntityAuditBundle(),
new Doctrine\Bundle\MigrationsBundle\DoctrineMigrationsBundle(),

if (in_array($this->getEnvironment(), array('dev', 'test'))) {
    // ...
    $bundles[] = new Doctrine\Bundle\FixturesBundle\DoctrineFixturesBundle();
    // ...
}

3) //-- enter this command in your terminal and don't worry about the "Undefined index: search"db_driver" at path "fos_user" must be configured" alert when installing the vendors. Error will be corrected by other steps of the installation --//

//-- after installation, verify if the gkratz folder is present in the vendor folder. If no, enter this command: "php composer update gkratz/adminbundle" --//

//-- after installation, verify if the beberlei folder is present in the vendor folder. If no, enter this command: "php composer update beberlei/DoctrineExtensions" --//

php composer require doctrine/doctrine-fixtures-bundle

4) //-- update your "app\config\config.yml" file --//

# Twig Configuration
twig:
    # ...
    globals:
        my_site:              '@gkratz_twig_my_site'
    form_themes:
        - LexikFormFilterBundle:Form:form_div_layout.html.twig
        - GkratzAdminBundle:form:form_errors.html.twig

# Doctrine Configuration
doctrine:
    # ...
    orm:
        # ...
        dql:
            string_functions:
                # To use SOUNDEX mysql function with Doctrine
                SOUNDEX:      Gkratz\AdminBundle\Extensions\Doctrine\SoundexFunction
            datetime_functions:
                month:        DoctrineExtensions\Query\Mysql\Month
                year:         DoctrineExtensions\Query\Mysql\Year
                day:          DoctrineExtensions\Query\Mysql\Day
                dayofweek:    DoctrineExtensions\Query\Mysql\DayOfWeek

# Doctrine Migrations Configuration
doctrine_migrations:
    dir_name: "%kernel.root_dir%/Migrations"
    namespace: Application\Migrations
    table_name: migration_versions
    name: Application Migrations
    organize_migrations: false # Version >=1.2 Possible values are: "BY_YEAR", "BY_YEAR_AND_MONTH", false

# KNPPaginator Configuration
knp_paginator:
    default_options:
        distinct:             false
    template:
        pagination:           GkratzAdminBundle:admin/__partial:pagination.html.twig

# Stof Configuration
stof_doctrine_extensions:
    orm:
        default:
            sluggable:        true

# Assetic Configuration
assetic:
    debug:                    "%kernel.debug%"
    use_controller:           false
    bundles:                  [GkratzAdminBundle, AppBundle, FOSUserBundle]
    filters:
        cssrewrite:           ~
        jsqueeze:             ~
        scssphp:
                                                      # the formatter must be the FQCN (don't use the 'compressed' value)
            formatter:        "Leafo\\ScssPhp\\Formatter\\Compressed"

# FOSUser Configuration
fos_user:
    db_driver:                orm
    firewall_name:            main
    user_class:               AppBundle\Entity\User
    from_email:
        address:              webmaster@example.com
        sender_name:          Admin
    registration:
        confirmation:
            enabled:          true                    # change to false to disable email confirmation
            template:         FOSUserBundle:Registration:email.html.twig
    resetting:
        email:
            template:         FOSUserBundle:Resetting:email.html.twig
    profile:
        form:
            type:             Gkratz\AdminBundle\Form\ProfileType
            name:             gkratz_profile_form
            validation_groups:  [Profile, Default]
    service:
        mailer:               fos_user.mailer.twig_swift

# ELFinder Configuration
fm_elfinder:
    assets_path:              /assets
    instances:
        default:
            locale:           "%locale%"              # defaults to current request locale
            editor:           ckeditor                # other options are tinymce, tinymce4, form, custom and simple
            fullscreen:       true                    # defaults true, applies to simple and ckeditor editors
            theme:            smoothness              # jquery theme
            include_assets:   true                    # disable if you want to handle loading of the javascript and css assets yourself
            connector:
                debug:        true                    # defaults to false
                roots:                                # at least one root must be defined
                    uploads:
                        show_hidden:      false       # defaults to false
                        driver:           LocalFileSystem
                        path:             uploads/img/filemanager
#                        upload_allow:     ['image/png', 'image/jpg', 'image/jpeg', 'application/pdf', 'application/msword', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', 'application/vnd.ms-excel']
                        upload_allow:     ['image/png', 'image/jpg', 'image/jpeg']
                        upload_deny:      ['all']
                        upload_max_size:  8M

# IvoryCKEditor Configuration
ivory_ck_editor:
    default_config:           default
    configs:
        default:
            toolbar:          standard
            locale:           "%locale%"
            filebrowserBrowseRoute: elfinder
            filebrowserBrowseRouteParameters: []

# SimpleThingsEntityAudit Configuration
simple_things_entity_audit:
    audited_entities:
        - AppBundle\Entity\Post

# Monolog Configuration
monolog:
    channels: ["gkadmin"]
    handlers:
        file:
            type:   stream
            path:   "%kernel.logs_dir%/gkadmin.log"
            level:  info
            channels: gkadmin

# Gkratz Admin Configuration
gkratz_admin:
    search:
        allow_approaching:    true                    # may be very slow. set false to only allow perfect match.
        fields:               Post.title, Post.content, User.username, User.lastname, User.firstname, User.address
    notification:
        option:               true                    # set false to @TODO develop notification listeners
        fields:               thing, other, another

5) //-- make sure that this line in "app\config\config.yml" is decommented to enable translations --//

framework:
    translator:      { fallbacks: ["%locale%"] }

6) //-- update your "app\config\routing.yml" file --//

#LINE 1 - this route must ABSOLUTELY be
#at the top of the routing.yml file
GkratzAdmin:
    resource: "@GkratzAdminBundle/Controller/"
    type:     annotation
    prefix:   /admin

fos_user_security:
    resource: "@FOSUserBundle/Resources/config/routing/security.xml"
fos_user_profile:
    resource: "@FOSUserBundle/Resources/config/routing/profile.xml"
    prefix: /profile
fos_user_register:
    resource: "@FOSUserBundle/Resources/config/routing/registration.xml"
    prefix: /register
fos_user_resetting:
    resource: "@FOSUserBundle/Resources/config/routing/resetting.xml"
    prefix: /resetting
fos_user_change_password:
    resource: "@FOSUserBundle/Resources/config/routing/change_password.xml"
    prefix: /profile
    
elfinder:
     resource: "@FMElfinderBundle/Resources/config/routing.yml"

7) //-- update your "src\AppBundle\Controller\DefaultController.php" --// //-- AND EVERY OTHER FRONT CONTROLLER TO USE MAINTENANCE ACTIVATION --// //-- use only render() method to render view, not renderView() --//

<?php
namespace AppBundle\Controller;

use AppBundle\Entity\Post;
use Gkratz\AdminBundle\Controller\GKratzController;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;

class DefaultController extends GKratzController
{
    /**
     * @Route("/", name="homepage")
     */
    public function indexAction(Request $request)
    {
        return $this->render('default/index.html.twig', [
            'base_dir' => realpath($this->getParameter('kernel.project_dir')).DIRECTORY_SEPARATOR,
        ]);
    }

    /**
     * @Route("/{slug}", name="front_routage")
     */
    public function routingAction($slug){
        /** @var \AppBundle\Entity\Post $post */
        $post = $this->getDoctrine()->getRepository(Post::class)->getFrontPage($slug);
        if (null === $post){
            throw new NotFoundHttpException();
        } else {
            return $this->render('@GkratzAdmin/admin/page.html.twig', ['entity' => $post]);
        }
    }
}

8) //-- generate user entity THAT EXTENDS FOSUSER MODEL --//

php bin/console doctrine:generate:entity

//-- shortcut name: AppBundle:User --//

<?php

namespace AppBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use FOS\UserBundle\Model\User as BaseUser;
use Gkratz\AdminBundle\Constants\Constants;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;

/**
 * @ORM\Table(name="user")
 * @ORM\Entity(repositoryClass="AppBundle\Repository\UserRepository")
 * @UniqueEntity("email")
 * @UniqueEntity("username")
 * @ORM\HasLifecycleCallbacks()
 */
class User extends BaseUser
{
    const ROLES_LIST = array(
        'ROLE_ANONYMOUS_USER' => 'ROLE_ANONYMOUS_USER',
        'ROLE_USER' => 'ROLE_USER',
        'ROLE_ADMIN' => 'ROLE_ADMIN',
        'ROLE_SUPER_ADMIN' => 'ROLE_SUPER_ADMIN',
    );

    public function __construct()
    {
        parent::__construct();
        $this->roles = array(self::ROLES_LIST['ROLE_USER'], self::ROLES_LIST['ROLE_ANONYMOUS_USER']);
        $this->state = 0;
    }

    /**
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    protected $id;

    /**
     * @var String
     * @ORM\Column(name="lastname", type="string", length=100, nullable=true)
     */
    private $lastname;

    /**
     * @var String
     * @ORM\Column(name="firstname", type="string", length=100, nullable=true)
     */
    private $firstname;

    /**
     * @var String
     * @ORM\Column(name="address", type="string", length=100, nullable=true)
     * @Assert\Length(min=10, max=190)
     */
    private $address;

    /**
     * @var String
     * @ORM\Column(name="phone_number", type="string", length=100, nullable=true)
     */
    private $phoneNumber;

    /**
     * @var \DateTime
     * @ORM\Column(name="birthdate", type="datetime", length=255, nullable=true)
     */
    private $birthdate;

    /**
     * @var Integer
     * @ORM\Column(name="state", type="integer", nullable=true)
     */
    private $state;

    /**
     * @var String
     * @ORM\Column(name="avatar", type="string", length=255, nullable=true)
     */
    private $avatar;

    /**
     * @Assert\File(
     *     maxSize = "2048k",
     *     mimeTypes = {"image/png", "image/jpg", "image/gif", "image/jpeg"},
     *     mimeTypesMessage = "Please upload a valid image"
     * )
     */
    private $avatarSrc;

    public function getAvatarSrc()
    {
        return $this->avatarSrc;
    }

    public function setAvatarSrc(UploadedFile $avatarSrc = null)
    {
        $this->avatarSrc = $avatarSrc;
    }

    /**
     * @ORM\PreUpdate
     */
    public function upload()
    {
        $this->enabled = ($this->state == Constants::ENTITY_STATE_VALID) ? 1 : 0;

        if (null === $this->avatarSrc) {
            return;
        }

        $name = ''.time().'-'.$this->avatarSrc->getClientOriginalName();
        $this->avatarSrc->move($this->getUploadRootDir(), $name);
        $this->avatar = $name;
    }

    public function getUploadDir()
    {
        return 'uploads/avatar';
    }

    protected function getUploadRootDir()
    {
        return __DIR__.'/../../../web/'.$this->getUploadDir();
    }
}

9) //-- update your "app\config\security.yml" file --//

security:
    encoders:
        AppBundle\Entity\User: sha512

    providers:
        main:
            id: fos_user.user_provider.username_email

    firewalls:
        main:
            pattern: ^/
            form_login:
                login_path: fos_user_security_login
                check_path: fos_user_security_check
                provider: main
                csrf_token_generator: security.csrf.token_manager
            anonymous: true
            logout:
                path: fos_user_security_logout
            remember_me:
                secret:     "%secret%"

    role_hierarchy:
            ROLE_USER: [ROLE_ANONYMOUS_USER]
            ROLE_ADMIN: [ROLE_USER, ROLE_ALLOWED_TO_SWITCH]
            ROLE_SUPER_ADMIN: [ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH]

    access_control:
        - { path: ^/login$, role: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/register, role: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/resetting, role: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/admin/, role: ROLE_ADMIN }
        - { path: ^/efconnect, role: ROLE_ADMIN }
        - { path: ^/elfinder, role: ROLE_ADMIN }

10) //-- enter this command in your terminal WARNING! THE DATABASE MUST BE CREATED BEFORE LAUNCHING COMMAND --//

//-- the command install folders. in case of error during the command, see the "gkratzadmin\Command folder --//
//-- IN PRODUCTION MODE, USE: "php bin/console gkratz:generate:all --env=prod"

php bin/console gkratz:generate:all

11) //-- load example datas. In case of error, truncate the tables and enter again the command --//

php bin/console doctrine:fixtures:load --append

HOW TO USE

- visit the complete admin at yoursite/admin (username/password: user/user or admin/admin)

- to use the softDelete filter, every entity with a state field must be filtered in front-end:
    ie: $this->getDoctrine()->getRepository(Class:class)->findBy(array('state' => 0, 'otherField' => 'otherValue'));
    
- to use the search plugin, you need to activate the fields for the search in the config.yml and 
    your entity must have a state integer field
    # config.yml
    # GkratzAdminBundle Configuration
    gkratz_admin:
        search:
            allow_approaching:    true                    # may be very slow. set false to only allow perfect match.
            fields:               Post.title, Post.content, User.username, User.lastname, User.firstname, User.locality, User.country
        # ...  

- you can use database strings encryption with the @Encrypted() annotation
    don't forget the use statement: use Ambta\DoctrineEncryptBundle\Configuration\Encrypted;

- you can use shortcuts:
    * ctrl + h: navigate from profile or back-office to the homepage
    * ctrl + p: navigate from back-office to profile
        (only for users with role 'ROLE_ADMIN')
    * ctrl + b: navigate from profile to back-office
        (only for users with role 'ROLE_ADMIN')
    * ctrl + click: select  elements in lists of entities or tables sortable
        (only for users with role 'ROLE_ADMIN')
    * shift + click: select  elements in lists of entities or tables sortable
        (only for users with role 'ROLE_ADMIN')
    * ctrl + a: select/deselect all elements in lists of entities or tables sortable
        (only for users with role 'ROLE_ADMIN')
    * ctrl + s: save and submit forms
        (only for users with role 'ROLE_ADMIN')
        
- to make a CRUD for a new entity named "Barfoo", you just need:
    * a "state" integer field to your entity "Barfoo"
    * an entry in the menu with this path: {{ path('gkratz_admin_admin_barfoo_index') }}
    * a BarfooType and a BarfooFilterType
    * a folder "barfoo" in Gkartz\AdminBundle\Resources\views\admin folder with 2 views: index and form, 
    * a BarfooController in Gkartz\AdminBundle\Controller\Admin with:
        <?php
        
        namespace Gkratz\AdminBundle\Controller\Admin;
        
        use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
        
        /**
         * @Route("/user")
         * Class BarfooController
         * @package Gkratz\AdminBundle\Controller\Admin
         */
        class BarfooController extends AdminController
        {
            protected function getClassFilterForm(){
                return \Gkratz\AdminBundle\FormFilter\BarfooFilterType::class;
            }
        
            protected function getClassForm(){
                return \Gkratz\AdminBundle\Form\BarfooType::class;
            }

            protected function getClass(){
                return 'AppBundle\Entity\Barfoo';
            }
        
            protected function getClassName(){
                return \AppBundle\Entity\Barfoo::class;
            }
        
            protected function getName(){
                return 'barfoo';
            }
        
            protected function getLabel(){
                return 'Barfoo';
            }
        }

OVERRIDE

- all the views and controllers are override ready. Like any bundle.

- take your time to understand the power of the bundle: backup, maintenance, messaging, notifying, UX and UI, data modeling in viewing, ...

- you can easily modify the layout use for the fosuser views