Today, we will see how to add a new admin tab to manage an entity with Sonata. For example you want to manage Animals, you could have this entity :

<?php

namespace My\AppBundle\Entity;

use Doctrine\ORM\Mapping as ORM;

use Doctrine\Common\Collections\ArrayCollection;

/**

* My\AppBundle\Entity\Animal

*

* @ORM\Table()

*/

class Animal

{

/**

* @var integer $id

*

* @ORM\Column(name="id", type="integer")

* @ORM\Id

* @ORM\GeneratedValue(strategy="AUTO")

*/

private $id;

/**

* @var string $name

*

* @ORM\Column(name="name", type="string", length=64)

*/

private $name;

/* Getters and setters */

} 

Now, you have to create an Admin folder in your bundle directory and create AnimalAdmin.php

The AnimalAdmin extends of Sonata Admin main class.

 <?php

namespace My\AppBundle\Admin;

use Sonata\AdminBundle\Form\FormMapper;

use Sonata\AdminBundle\Datagrid\DatagridMapper;

use Sonata\AdminBundle\Datagrid\ListMapper;

use Sonata\AdminBundle\Show\ShowMapper;

use Sonata\AdminBundle\Admin\Admin ;

class AnimalAdmin extends Admin

{

public function getTemplate($name)

{

switch ($name) {

/*case 'edit':

return 'MyAppBundle::my-custom-edit.html.twig';

break; */

default:

return parent::getTemplate($name);

break;

}

}

protected function configureListFields(ListMapper $listMapper)

{

$listMapper

->add('id')

->add('name')

;

}

/**

* {@inheritdoc}

*/

protected function configureShowFields(ShowMapper $showMapper)

{

$showMapper

->with('General')

->add('id')

->add('name')

->end()

;

}

/**

* {@inheritdoc}

*/

// Fields wich are shown in you Animal Admin Tab

protected function configureFormFields(FormMapper $formMapper)

{

$formMapper

->with('General')

->add('id')

->add('name')

->end()

;

}

}

When you select the edit button, you can redirect to your own template, here « my-custom-edit.html.twig », you just have to uncomment the case ‘edit’. Otherwise, the default template from sonataAdmin will be used.

Finally, you have to create the service. To do this, you just have to create services.xml in Ressources/config of your bundle directory.


<?xml version="1.0" ?>

<container xmlns="http://symfony.com/schema/dic/services"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="<a href="http://symfony.com/schema/dic/services">http://symfony.com/schema/dic/services</a> http://symfony.com/schema/dic/services/services-1.0.xsd">

<parameters>

<parameter key="sonata.animal.admin.animal.class">Vet\BlogBundle\Admin\AnimalAdmin</parameter>

<parameter key="sonata.animal.admin.animal.entity">Vet\BlogBundle\Entity\Animal</parameter>

<parameter key="sonata.animal.admin.animal.controller">SonataAdminBundle:CRUD</parameter>

<parameter key="sonata.animal.admin.animal.translation_domain">VetBlogBundle</parameter>

<parameter key="sonata.animal.admin.groupname">animal</parameter>

</parameters>

<services>

<service id="sonata.animal.admin.animal" class="%sonata.animal.admin.animal.class%">

<tag name="sonata.admin" manager_type="orm" group="%sonata.animal.admin.groupname%" label="animals" label_catalogue="VetBlogBundle" label_translator_strategy="sonata.admin.label.strategy.underscore" />

<argument />

<argument>%sonata.animal.admin.animal.entity%</argument>

<argument>%sonata.animal.admin.animal.controller%</argument>

<call method="setTranslationDomain">

<argument>%sonata.animal.admin.animal.translation_domain%</argument>

</call>

</service>

</services>

</container>

You can now manage your Animals entity on your Sonata backend.

| Symfony 2 Utils | Laisser un commentaire

Elasticsearch is built on top of Lucene and indexes data as JSON documents in a similar way to the way MongoDB stores data. This means as with Mongo that it is schemaless and creates fields on the fly. It is queried over HTTP using queries which are themselves defined in JSON. I am not going to go into details about using elasticsearch in this way, there is plenty of information in its online documentation.

To get started you need to install elasticsearch itself, as well as installing Elastica and the FOQElasticaBundle in the usual way.

As an example of how easy the integration is I will look at a very basic application for bookmarking sites and searching for them. For simplicity’s sake we are just going to have a single entity to model each site, it is just a name, the URL and some keywords stored as a comma separated list. So here it is as a Doctrine entity class:

use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;

/**
 * @ORM\Entity
 * @ORM\Table(name="site")
 */
class Site
{
    /**
     * @ORM\Id
     * @ORM\Column(type="integer")
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

    /**
     * @ORM\Column(type="string", length=255)
     * @Assert\NotBlank()
     * @Assert\MaxLength(255)
     */
    private $name;

    /**
     * @ORM\Column(type="string", length=255)
     * @Assert\NotBlank()
     * @Assert\Url()
     * @Assert\MaxLength(255)
     */
    private $url;

    /**
     * @ORM\Column(type="string", length=255)
     * @Assert\MaxLength(255)
     */
    private $keywords;

    public function getId()
    {
        return $this->id;
    }

    public function setName($name)
    {
        $this->name = $name;
    }

    public function getName()
    {
        return $this->name;
    }

    public function setUrl($url)
    {
        $this->url = $url;
    }

    public function getUrl()
    {
        return $this->url;
    }

    public function setKeywords($keywords)
    {
        $this->keywords = $keywords;
    }

    public function getKeywords()
    {
        return $this->keywords;
    }

}

We can then set up the bundle to index the fields of our entity. By choosing to use the integration with doctrine we can make this very simple:

foq_elastica:
    clients:
        default: { host: localhost, port: 9200 }
    indexes:
        bookmarks:
            client: default
            types:
                site:
                    mappings:
                        name:
                        keywords:
                    doctrine:
                        driver: orm
                        model: XXXBundle\Entity\Site
                        provider:

Whilst there are quite a few settings here it is fairly straight forward. The client just sets the port to use for the http communication. The bookmarks setting under indexes is the name of the index we will create. Within each index you can have types for each of your entity types, we just have the one type (site) here at the moment.

We have specified that we are using the ORM, the entity class and which fields to map, for now just the name and keywords (I will return to indexing in the url in my next post). That is enough to get any existing Sites stored in the database into the search index. Running the following console command will do this:

php app/console foq:elastica:populate

t is as easy as that! All the sites already stored in the database are now indexed without the need for even writing any code, just a small amount of configuration. Great as that is, it would be even better if we could automatically index any new entities, as well as updating and removing entities as they are updated and removed from the database without having to rerun the console command. This is just as easy to achieve with only one extra item added to the configuration:

foq_elastica:
    clients:
        default: { host: localhost, port: 9200 }
    indexes:
        bookmarks:
            client: default
            types:
                site:
                    mappings:
                        name:
                        keywords:
                    doctrine:
                        driver: orm
                        model: XXXBundle\Entity\Site
                        provider:
                        listener:

This enables the bundle’s built in doctrine event listeners which will then do just that, keep the search index up to date with any changes we make to the entities, again without any additional code needed in typical CRUD controllers.

Before looking at searching the index there is one more bit of config which can be added to make integration easy:

foq_elastica:
    clients:
        default: { host: localhost, port: 9200 }
    indexes:
        bookmarks:
            client: default
            types:
                site:
                    mappings:
                        name:
                        keywords:
                    doctrine:
                        driver: orm
                        model: XXXXBundle\Entity\Site
                        provider:
                        listener:
                        finder:

By adding the finder line we activate the support for returning the search results as Doctrine entities, so the bundle will do the work of fetching the relevant entities from the database after querying the elasticsearch index.

So how do we query the index? The bundle dynamically creates a service you can request from the container with the format foq_elastica.finder.index-name.type-name. These match the values in our config, so the service we need is foq_elastica.finder.bookmarks.site. We can now issue queries using this service:

/**
* @Route("/sites/search/", name="site_search")
* @Method({ "head", "get" })
* @Template
*/
public function searchAction(Request $request)
{
    $finder = $this->get('foq_elastica.finder.bookmarks.site');
    $searchTerm = $request->query->get('search');
    $sites = $finder->find($searchTerm);
    return array('sites' => $sites);
}

Elastica provides an OO query builder for creating more complicated queries but I will leave that for another day. Hopefully I have shown just how straightforward it is to get stated using elasticsearch with a Symfony2 app. As always, it is not limited to such simplicity and you can override these built in services to provide your own providers, finders and listeners if you have more complex requirements.

| Symfony 2 Utils | 2 commentaires

This Article is going to explain quickly how to manage an upload file in an Entity in Symfony2
I do not give you the best practice but a simple practice to do it

1)First of All create your entity :

namespace XXX;

use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;

/**
 * @ORM\Table()
 * @ORM\Entity
 */
class Article
{
    /**
     * @var integer $id
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

      /**
     * @var string $image
     * @Assert\File( maxSize = "1024k", mimeTypesMessage = "Please upload a valid Image")
     * @ORM\Column(name="image", type="string", length=255)
     */
    private $image;

    /**
     * Get id
     *
     * @return integer
     */
    public function getId()
    {
        return $this->id;
    }

    /**
     * Set image
     *
     * @param string $image
     */
    public function setImage($image)
    {
        $this->image = $image;
    }

    /**
     * Get image
     *
     * @return string
     */
    public function getImage()
    {
        return $this->image;
    }

}

2) build your form: So we will then create a simple form type for this Article entity in order to fit into the other forms: ArticleType.php


use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilder;

class ArticleType extends AbstractType
{
    public function buildForm(FormBuilder $builder, array $options)
    {
        $builder
            ->add('image')
            ->add('...')

        ;
    }

    public function getName()
    {
        return 'xxx_articletype';
    }
}

3)Create the controller: The controller below shows you how to manage the whole process: ArticleController.php:

use Symfony\Bundle\FrameworkBundle\Controller\Controller;

/**
 * Article controller.
 *
 */
class ArticleController extends Controller
{

    /**
     * Finds and displays a Article entity.
     *
     */
    public function showAction($id)
    {

        $em = $this->getDoctrine()->getEntityManager();
        $entity = $em->getRepository('XXXBundle:Article')->find($id);
        if (!$entity) {
            throw $this->createNotFoundException('Unable to find Article entity.');
        }
    return $this->render('XXXBundle:Article:show.html.twig', array(
            'entity'      => $entity,

        ));
    }
    /**
     * Displays a form to create a new Article entity.
     *
     */
    public function newAction()
    {
        $entity = new Article();
        //$entity = $em->getRepository('CliniqueGynecoBundle:Article');
        $form   = $this->createForm(new ArticleType(), $entity);

        return $this->render('XXXBundle:Article:new.html.twig', array(
            'entity' => $entity,
            'form'   => $form->createView()
        ));
    }

    /**
     * Creates a new Article entity.
     *
     */
    public function createAction()
    {
        $entity  = new Article();
        $request = $this->getRequest();
        $form    = $this->createForm(new ArticleType(), $entity);
        $form->bindRequest($request);

        if ($form->isValid()) {
            $em = $this->getDoctrine()->getEntityManager();
            $em->persist($entity);
            $em->flush();

            return $this->redirect($this->generateUrl('article_show', array('id' => $entity->getId())));

        }

        return $this->render('XXXBundle:Article:new.html.twig', array(
            'entity' => $entity,
            'form'   => $form->createView()
        ));
    }

    private function createDeleteForm($id)
    {
        return $this->createFormBuilder(array('id' => $id))
            ->add('id', 'hidden')
            ->getForm()
        ;
    }
}

4) layout for the upload form: new.html.twig

<form action="{{ path('basearticle_create') }}" method="post" {{ form_enctype(form) }}>
    {{ form_widget(form) }}
    <p>
        <button class="btn btn-primary" type="submit">Create</button>
    </p>
</form>

5) Display layout: show.html.twig

<table>
 <tr>
      <td align="center" valign="top"><img src="{{ asset('upload/' ~ entity.id ~'/' ~ entity.image)}}" alt="" height="525" width="666" /></td>
</tr>
</table>

6) Use the « Lifecycle Callbacks » hooking the entity in a « Lifecycle callbacks »: « @ ORM \ HasLifecycleCallbacks »

/**
 *
 * @ORM\Table()
 * @ORM\HasLifecycleCallbacks
 * @ORM\Entity
 */
class Article
{
....

7) Added methods to download files:

class Article
{
   ....................................

    public function getFullImagePath() {
        return null === $this->image ? null : $this->getUploadRootDir(). $this->image;
    }

    protected function getUploadRootDir() {
        // the absolute directory path where uploaded documents should be saved
        return $this->getTmpUploadRootDir().$this->getId()."/";
    }

    protected function getTmpUploadRootDir() {
        // the absolute directory path where uploaded documents should be saved
        return __DIR__ . '/../../../../web/upload/';
    }

    /**
     * @ORM\PrePersist()
     * @ORM\PreUpdate()
     */
    public function uploadImage() {
        // the file property can be empty if the field is not required
        if (null === $this->image) {
            return;
        }
        if(!$this->id){
            $this->image->move($this->getTmpUploadRootDir(), $this->image->getClientOriginalName());
        }else{
            $this->image->move($this->getUploadRootDir(), $this->image->getClientOriginalName());
        }
        $this->setImage($this->image->getClientOriginalName());
    }

    /**
     * @ORM\PostPersist()
     */
    public function moveImage()
    {
        if (null === $this->image) {
            return;
        }
        if(!is_dir($this->getUploadRootDir())){
            mkdir($this->getUploadRootDir());
        }
        copy($this->getTmpUploadRootDir().$this->image, $this->getFullImagePath());
        unlink($this->getTmpUploadRootDir().$this->image);
    }

    /**
     * @ORM\PreRemove()
     */
    public function removeImage()
    {
        unlink($this->getFullImagePath());
        rmdir($this->getUploadRootDir());
    }

The class is now all you need: it generates a unique file name before persistent, move the file after persistence, and deletes the file if the entity is already deleted.

The @ ORM \ PrePersist () and @ ORM \ PostPersist () event reminders are triggered before and after the entity is stored in the database. On the other hand, the ORM @ \ PreUpdate () and @ ORM \ PostUpdate () event callbacks are called when the entity is updated.

The PreUpdate and reminders are triggered PostUpdate if there is a change in one of the fields of the entity that are retained. This means that, by default, only if you change the file ownership, these events will not be triggered, as the property itself is not directly persisted via Doctrine. One solution would be to use an updated field that has persisted to the doctrine, and change manually when you edit the file.

| Symfony 2 Utils | 2 commentaires

When we call the method isGranted in Symfony2, it checks if the current user can access to a page or an object with a mask defined. (ACL/ROLE)
These checking are made by Voter class implemented by the VoterInterface of Symfony2.
All we need to do is to define a new voter class to do some other checkings like some treatments when the user is granted or not, blocks.authorizes a user according his IP….
At first we need to declare a new Voter class:


class MVMVoter implements VoterInterface

{
/**

* Checks if the voter supports the given attribute.

* @param string $attribute An attribute

* @return Boolean true if this Voter supports the attribute, false otherwise

*/

function supportsAttribute($attribute)

{

return true;

}

&nbsp;

/**

* Checks if the voter supports the given class.

* @param string $class A class name

* @return true if this Voter can process the class

*/

function supportsClass($class)

{

return true;

}

&nbsp;

/**

* Returns the vote for the given parameters.

*

* This method must return one of the following constants:

* ACCESS_GRANTED, ACCESS_DENIED, or ACCESS_ABSTAIN.

*

* @param TokenInterface $token      A TokenInterface instance

* @param object         $object     The object to secure

* @param array          $attributes An array of attributes associated with the method being invoked

* @return integer either ACCESS_GRANTED, ACCESS_ABSTAIN, or ACCESS_DENIED

*/

function vote(TokenInterface $token, $object, array $attributes)

{

return self::ACCESS_ABSTAIN;

}

Then, we need to create a new service to tag this class as a voter security class

<parameter   key="mvm.voter.class">MVM\MainBundle\Acl\MVMVoter</parameter>

<service id="mvm.voter"  >

<tag name="security.voter"></tag>

</service>

As you can see the VoterClass has a method named vote

This method will be called each time there is a check acl/permission.

| Symfony 2 Utils | 2 commentaires

To restore a single database from a database dump which contains multiple databases:

mysql -u username -p --one-database data_base_to_restore < backup.sql

or if you have a bz2 file

mysql -u username -p --one-database data_base_to_restore < bzcat backup.sql.bz2
| Symfony 2 Utils | Laisser un commentaire

If you need to test your code against the same version of PHP that you use in production, or if you want to test out the cutting edge features of PHP then you will probably need to compile php from source.

This is easier than it sounds, and I know it is easy with debian or ubuntu (and hence aptitude)

First, download the source from php.net then unpack it somewhere in your home directory:

cd /home/daniel/source
tar -xvzf php-5.3.10.tar.gz

Then you can try and configure it as follows

./configure

It will probably fail with a message stating that some library was not found. It is necessary to install all the dependent libraries before you can compile from source, fortunately this is very easy in debian/ubuntu

apt-get build-dep php5

Hopefully you now have all the dependencies required and you can configure again:

./configure

OK? ok. However this will configure a very minimal build. In general you will want to compile many modules (e.g. mysql, gd, xml, curl, etc). This is a typical configuration that I use:

'./configure'  '--with-apxs2=/usr/bin/apxs2' '--with-curl' '--with-gd' '--with-mcrypt' '--with-mhash' '--with-mysql' '--with-pdo-mysql' '--with-snmp' '--enable-soap' '--with-openssl' '--with-xsl' '--with-config-file-path=/etc/php5' '--enable-bcmath' '--with-zlib' '--enable-sysvsem' '--with-gd' '--with-jpeg-dir=/usr/lib' '--with-png-dir=/usr/lib' '--with-readline' '--enable-mbstring' '--enable-intl' '--prefix=/opt/php-5.3.10'

Note the prefix option. This is important as it is the directory where the code will be installed. By default it will install to usr/lib but this might become confusing later on. So I put all my cusom code in /opt.

OK. Now we can compile the code

make

Hit return and you will see many lines of as yet incomprehensible messages. This will continue for a while depending on the speed of your computer, it can take 10-15 minutes on my laptop.

When it has finished you should not see any errors, and the final lines should look as follows:

Generating phar.php
Generating phar.phar
PEAR package PHP_Archive not installed: generated phar will require PHP's phar extension be enabled.
invertedregexiterator.inc
pharcommand.inc
directorygraphiterator.inc
directorytreeiterator.inc
clicommand.inc
phar.inc

Build complete.
Don't forget to run 'make test'.

Now you can install with

make install

This will configure apache automatically (at least it seems to that for me) but if you want to use php from the command line you will need to symlink the php binary in your bin path (or add /opt/php-blah to you environment path).

cd /usr/bin
ln -s /opt/php-x.x.x/bin/php php

Et voila:


root@dan-x61:/usr/bin# php --version
PHP 5.3.10 (cli) (built: Feb 15 2012 09:30:11)
Copyright (c) 1997-2012 The PHP Group
Zend Engine v2.3.0, Copyright (c) 1998-2012 Zend Technologies
| Symfony 2 Utils | Laisser un commentaire

This article is going to explain /how to/ correctly create a backoffice
(admin panel + crud) in Symfony 2.

Generally, for any web project, you need a back office to manager
entities, data, users and groups and, fortunately today there are a
number of bundles for Symfony 2 which allow you to create an admin panel
quickly.

(.. because it is boring to redevelop phpmyadmin we can save ourselves
the effort and reutilise existing Symfony2 bundles)

What bundles do you need to install?

[FOSUserBundle]
    git=git://github.com/FriendsOfSymfony/FOSUserBundle.git
    target=bundles/FOS/UserBundle

[SonatajQueryBundle]
    git=https://github.com/sonata-project/SonatajQueryBundle.git
    target=/bundles/Sonata/jQueryBundle

[SonataAdminBundle]
    git=https://github.com/sonata-project/SonataAdminBundle.git
    target=/bundles/Sonata/AdminBundle

[MenuBundle]
    git=https://github.com/KnpLabs/KnpMenuBundle.git
    target=/bundles/Knp/Bundle/MenuBundle

[KnpMenu]
    git=https://github.com/KnpLabs/KnpMenu.git
    target=/knp/menu

[SonataUserBundle]
    git=git://github.com/sonata-project/SonataUserBundle.git
    target=/bundles/Sonata/UserBundle

[SonataEasyExtendsBundle]
    git=git://github.com/sonata-project/SonataEasyExtendsBundle.git
    target=/bundles/Sonata/EasyExtendsBundle

[SonataDoctrineORMAdminBundle]
    git=https://github.com/sonata-project/SonataDoctrineORMAdminBundle.git
    target=/bundles/Sonata/DoctrineORMAdminBundle

Add the namespaces to app/autoload.php

// app/autoload.php
$loader->registerNamespaces(array(
    // ...
    'FOS'              => __DIR__.'/../vendor/bundles',
    'Sonata'           => __DIR__.'/../vendor/bundles',
    'Application'      => __DIR__,
    'Knp'              => array(
                          __DIR__.'/../vendor/bundles',
                          __DIR__.'/../vendor/knp/menu/src',
                          ),
    // ...
));

Enable the bundles in app/AppKernel.php

// app/AppKernel.php
    public function registerBundles()
    {
        $bundles = array(
            // ...
            new FOS\UserBundle\FOSUserBundle(),
            new Sonata\jQueryBundle\SonatajQueryBundle(),
            new Sonata\AdminBundle\SonataAdminBundle(),
            new Sonata\DoctrineORMAdminBundle\SonataDoctrineORMAdminBundle(),
            new Knp\Bundle\MenuBundle\KnpMenuBundle(),
            new Sonata\UserBundle\SonataUserBundle('FOSUserBundle'),
            new Sonata\EasyExtendsBundle\SonataEasyExtendsBundle(),
            // ...
        );
        // ...
    }

Add

# app/config/config.yml
fos_user:
    db_driver: orm
    firewall_name: main
    user_class: Application\Sonata\UserBundle\Entity\User

Run

php app/console sonata:easy-extends:generate SonataUserBundle

It is going to generate an Application UserBundle, but you can use your own.
To develop your understanding, do this by default and try after to readapt the code with your UserBundle

Add the new Bundle to app/AppKernel.php

// app/AppKernel.php
    public function registerbundles()
    {
        $bundles = array(
            // Application Bundles
            // ...
            new Application\Sonata\UserBundle\ApplicationSonataUserBundle(),
            // ...
        );
        // ...
    }

Add routing

# app/config/routing.yml
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: /change-password

admin:
    resource: '@SonataAdminBundle/Resources/config/routing/sonata_admin.xml'
    prefix: /admin

_sonata_admin:
    resource: .
    type: sonata_admin
    prefix: /admin

soanata_user:
    resource: '@SonataUserBundle/Resources/config/routing/admin_security.xml'
    prefix: /admin

sonata_user_impersonating:
    pattern: /
    defaults: { _controller: SonataPageBundle:Page:catchAll }

Add the following to app/config/security.yml

# app/config/security.yml
security:
    encoders:
        FOS\UserBundle\Model\UserInterface: sha512

    role_hierarchy:
        ROLE_ADMIN:       ROLE_USER
        ROLE_SUPER_ADMIN: [ROLE_USER, ROLE_SONATA_ADMIN, ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH]
        SONATA:
            - ROLE_SONATA_PAGE_ADMIN_PAGE_EDIT  # if you are not using acl then this line must be uncommented

    providers:
        fos_userbundle:
            id: fos_user.user_manager

    firewalls:

        # -> custom firewall for the admin area of the URL
        admin:
            pattern:      /admin(.*)
            form_login:
                provider:       fos_userbundle
                login_path:     /admin/login
                use_forward:    false
                check_path:     /admin/login_check
                failure_path:   null
            logout:
                path:           /admin/logout
            anonymous:    true
        # -> end custom configuration

        # defaut login area for standard users
        main:
            pattern:      .*
            form_login:
                provider:       fos_userbundle
                login_path:     /login
                use_forward:    false
                check_path:     /login_check
                failure_path:   null
            logout:       true
            anonymous:    true

# ...

    access_control:
        # URL of FOSUserBundle which need to be available to anonymous users
        - { path: ^/_wdt, role: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/_profiler, role: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/login$, role: IS_AUTHENTICATED_ANONYMOUSLY }

        # -> custom access control for the admin area of the URL
        - { path: ^/admin/login$, role: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/admin/logout$, role: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/admin/login-check$, role: IS_AUTHENTICATED_ANONYMOUSLY }
        # -> end

        - { path: ^/register, role: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/resetting, role: IS_AUTHENTICATED_ANONYMOUSLY }

        # Secured part of the site
        # This config requires being logged for the whole site and having the admin role for the admin part.
        # Change these rules to adapt them to your needs
        - { path: ^/admin, role: [ROLE_ADMIN, ROLE_SONATA_ADMIN] }
        - { path: ^/.*, role: IS_AUTHENTICATED_ANONYMOUSLY }
# ...

Do theses commands for the end

php app/console doctrine:schema:update --force
php app/console assets:install web
php app/console cache:clear
php app/console fos:user:create admin admin@example.com password --super-admin

You need to enable your translator in /app/config.yml

framework:
    translator: ~

That’s it!!!
As you can see, a lotof things must be done for it to
work

But after this initial step you will only need to modify the XML files
and the Admin class ((plural?)) of your new bundles.

With this demo you should have a functioning admin interface for « user »
and « group » entities. This is accomplished only with the following 2
files:
- Setup UserAdmin Class (found in AdminFolder in the bundle SonataUserBundle) (see more info in SonataAdminBundle Doc)
- Setup admin_orm.xml (found in Ressources/coinfig in the bundle SonataUserBundle) (see more info in SonataAdminBundle Doc)

When you need to administer more entities in your application, editing
these two files is all you will need to do.

For more information, see: http://sonata-project.org/bundles/admin/master/doc/index.html

| Symfony 2 Utils | 9 commentaires

I have been using my server recently to maintain my TODO lists, but I have just moved into a new apartment and I do not yet have the internet, so… I could copy the files to my laptop, go home, then copy them back to the server when I am at work, but this is tedious and I risk losing my data everytime, so I thought it would be nice to syncronize my laptop with the folders on the server.

Dropbox does this, but I don’t like dropbox, I guess I don’t like the idea of putting my data in somebody else hands. So about 2 months ago I had a go at writing my own sync daemon. Using Python with GIT and pyinotify. I soon lost interest however so it doesnt work.

More recently I re-googled and found SparkleShare and DVCS-Autosync.

SparkleShare

SparkleShare is quite user friendly, using a GTK interface, I use « quite » because it still required some technical knowledge for me to get it working. Unfortunately it hijacked my .ssh configuration and left me scratching my head when pushing changes to my GiTHub repository. (It was necessary to remove the .ssh/config file which sparkle share added).

DVCS-Autosync

Install with:

apt-get install dvcs-autosync

The package is not available in squeeze so you will need to upgrade to wheezy.

DVCS (Distributed Version Control System) autosync is very simple and it works.

The only catch is that it requires a Jabber account, I chose to install a jabber server on my web server — but you can alternatively just create an account at jabber.org.

Installing a jabber server was easier than I imagined:

apt-get install prosody

Then after following a few simple steps it was up and running.

Now my files are synconized between my two laptops, my desktop machine and my servers shell account.

(original article here)

| Symfony 2 Utils | Laisser un commentaire