DWGingerBundle
=============
 
 The DWGingerBundle provides entities and CRUD to manage basic personal data (like a person´s name, email, address, etc.).
 
 Further more the bundle provides the following features:
 
 * Tagging of personal data
 * Importing persons data from `.csv`-files
 * Anonymization of personalized data
 * Logging of activites related to persons
 * Usermanagement of registered users
 
For more detailed information on the bundle checkout our [Developer Wiki](https://bitbucket.org/dwerk/dwgingerbundle/wiki/Home)...


Installation
============  

### Prerequisites

The following prerequisites must be fulfilled before installing the bundle:

* Make sure you have [setup your SSH-Key](https://confluence.atlassian.com/bitbucket/set-up-an-ssh-key-728138079.html) on Bitbucket.
  To make SSH work out-of-the-box without any extra configuration, your public key should reside in the default directory on your local macchine (e.g: ```~/.ssh/id_rsa.pub``` for MAC OSX).

* In the BitBucket-Settings for your user account, create an [OAuth-Client](https://confluence.atlassian.com/bitbucket/oauth-on-bitbucket-cloud-238027431.html#OAuthonBitbucketCloud-Createaconsumer).
  If composer asks you for OAuth-credentials during the installation process, paste the creds (Key, Secret) for this client into the console and allow composer to store those on your machine.
  
* Your lokal GIT version should be at least 2.10.1 (tested on OS X) or 2.16 (tested on Win).

### Step 1: Create new symfony project

To create a new symfony application from the latest LTS version:

```
symfony new my-sf-ginger-project --full --version=lts 
```
*Note:* Don´t have the `symfony` binary installed on your local machine yet? Time to grab it from [here](https://symfony.com/download), believe me, it´s awesome ;)

### Step 2: Install Ginger

##### Downgrade `twig/twig` et al.:

```
symfony composer req twig/twig:^2.12 doctrine/orm:2.7.* doctrine/common:2.* symfony/templating -W
```

##### Update your project´s `composer.json` by adding:

```
    "repositories": [
        {
            "type": "vcs",
            "url":  "https://bitbucket.org/dwerk/dwgingerbundle.git"
        }
    ],
    "minimum-stability": "dev",
    "prefer-stable": true
```
*Note:* The stabillity settings are currently required due to [#76b3216](https://bitbucket.org/dwerk/dwgingerbundle/commits/76b32165a5d0ab3830673636eb6ceaca02b988b9)

##### Require latest ginger:

```
symfony composer req datenwerk/ginger-bundle
```

*Note:* At this point you´ll get some errors, as you need to configure the bundle manually. Resolve them by following the steps below...

### ~~Step 2,5: Configure supervisor programs~~

~~3 supervisor programs need to run to enable Gingers asynchronous features (Systemd user services work as well).~~
#### ~~Messenger consumer:~~
~~`bin/console messenger:consume --time-limit=3600 -v async_priority_high async async_priority_low`~~

#### ~~JMS job scheduler:~~
~~`bin/console jms-job-queue:schedule --env=prod --verbose`~~

#### ~~JMS job queue:~~
~~`bin/console jms-job-queue:run --env=prod --verbose`~~

### Step 2,5: Systemd Configuration for async messenger
Create a "user service" using `systemd`  
see https://symfony.com/doc/current/messenger.html#systemd-configuration  
Example worker config in [this file](./messenger-worker.service) 


### Step 2,6: Cronjobs
Until a symfony scheduler component is availabe you need to setup some cronjobs that run every hour:  
Garbage collection of upload- and download files:  
`bin/console ginger:storage:clear exports.storage '72 hours ago'`
`bin/console ginger:storage:clear imports.storage '4 weeks ago'`

If eyepin integration is enabled: fetching of Eyepin reports:
`bin/console ginger:eyepin:reports`

### Step 3: Configure Ginger

##### Add `locale`-parameter:

```yaml
# config/services.yaml
parameters:
    locale: 'en'
```
```yaml
# config/packages/translation.yaml
framework:
    default_locale: 'en'
```

##### Add basic ginger config:

```yaml
# config/packages/dw_ginger.yaml
dw_ginger:
    crud:
        person:
            class: App\Entity\Contact
            form: ~
        group:
            class: App\Entity\Group
            form: ~
```

##### Extend Gingers person class and add a repository:

```php
<?php

namespace App\Entity;

use DW\GingerBundle\Entity\Person;
use Doctrine\ORM\Mapping as ORM;

/**
 * Class Contact
 * @ORM\Entity(repositoryClass="App\Repository\ContactRepository")
 * @ORM\Table(name="contact")
 */
class Contact extends Person
{
 // Add your properties / methods here
}
```

```php
<?php

namespace App\Repository;

use App\Entity\Contact;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Common\Persistence\ManagerRegistry;

class ContactRepository extends ServiceEntityRepository
{
    public function __construct(ManagerRegistry $registry)
    {
        parent::__construct($registry, Contact::class);
    }
}
```

##### Configure doctrine to correctly resolve your person class:

Add in your `doctrine.yaml` under `orm` an entry for `resolve_target_entities` with target to concrete person/contact class.

```yaml
# config/packages/doctrine.yaml
parameters:
    # Adds a fallback DATABASE_URL if the env var is not set.
    # This allows you to run cache:warmup even if your
    # environment variables are not available yet.
    # You should not need to change this value.
    env(DATABASE_URL): ''

doctrine:
    dbal:
        # configure these for your database server
        driver: 'pdo_mysql'
        server_version: '5.7'
        charset: utf8mb4

        # With Symfony 3.3, remove the `resolve:` prefix
        url: '%env(resolve:DATABASE_URL)%'
    orm:
        auto_generate_proxy_classes: '%kernel.debug%'
        naming_strategy: doctrine.orm.naming_strategy.underscore
        auto_mapping: true
        mappings:
            App:
                is_bundle: false
                type: annotation
                dir: '%kernel.project_dir%/src/Entity'
                prefix: 'App\Entity'
                alias: App
        # ginger-bundle: add this to resolve relation to contact entity
        resolve_target_entities:
            DW\GingerBundle\Entity\PersonInterface: App\Entity\Contact
            DW\GingerBundle\Entity\GroupInterface: App\Entity\Group
```

#### Do the same for the Group-Entity.


##### Manually enable the exporter library:

###### Problem:

As the auto-enable feature is currently not working for the exporter-library, you will get the following exception after running `composer install` or `php bin/console cache:clear`:
```console

In CheckExceptionOnInvalidReferenceBehaviorPass.php line 32:

The service "dw_ginger.person.controller" has a dependency on a non-existent service "sonata.exporter.exporter".

```

###### Solution:
Manually enable the library by adding it to the `config/bundles.php`-file of your project (see [here](https://bitbucket.org/dwerk/akginger/src/f1027f33560ac1260b73c87484876f59fe0c701d/config/bundles.php#lines-27) for an example).


##### Configure your application's security.yml
Ginger has its own User class and user authenticator that should be used.

Ginger provides a lot of granular roles that can be inherited by your custom user roles
For a full list of available roles and examples see [Ginger-Roles](Resources/doc/ROLES.md)

Although some defaults have already been set by the DWGingerBundle during the installation process, you still need to configure 
your firewall settings in your `security.yaml` as follows:

```yaml
# config/packages/security.yaml
security:
    encoders:
        # use your user class name here
        DW\GingerBundle\Entity\User:
            # Use native password encoder
            # This value auto-selects the best possible hashing algorithm
            # (i.e. Sodium when available).
            algorithm: auto
    # https://symfony.com/doc/current/security.html#where-do-users-come-from-user-providers
    providers:
        # used to reload user from session & other features (e.g. switch_user)
        app_user_provider:
            entity:
                class: DW\GingerBundle\Entity\User
                property: username

    role_hierarchy:
        ROLE_USER:
            - ROLE_GINGER_USER_EDIT_MYPROFILE
        ROLE_CUSTOM_EDITOR:
            - ROLE_USER
            - ROLE_GINGER_BEUSER
            - ROLE_GINGER_CONTACT_CREATE
            - ROLE_GINGER_CONTACT_EDIT
            - ROLE_GINGER_CONTACT_DELETE
            - ROLE_GINGER_CONTACT_ANONYMIZE
            - ROLE_GINGER_CONTACT_DUPLICATE_RESOLVE
            - ROLE_GINGER_JOBS_LIST
            - ROLE_GINGER_ADMINLOG_LIST
        ROLE_CUSTOM_CHIEF:
            - ROLE_CUSTOM_EDITOR
            - ROLE_GINGER_TAG_CREATE
            - ROLE_GINGER_TAG_EDIT
            - ROLE_GINGER_TAG_DELETE
            - ROLE_GINGER_TAG_ATTRIBUTE_ASSIGN
            - ROLE_GINGER_TAG_TYPE_ASSIGN
            - ROLE_GINGER_ATTRIBUTE_LIST
            - ROLE_GINGER_ATTRIBUTE_CREATE
            - ROLE_GINGER_ATTRIBUTE_EDIT
            - ROLE_GINGER_ATTRIBUTE_DELETE
        ROLE_CUSTOM_ADMIN:
            - ROLE_CUSTOM_CHIEF
            - ROLE_ALLOWED_TO_SWITCH
            - ROLE_GINGER_USER_CREATE
            - ROLE_GINGER_USER_EDIT
            - ROLE_GINGER_USER_DELETE
            - ROLE_GINGER_USER_PROMOTE
            - ROLE_GINGER_GROUP_CREATE
            - ROLE_GINGER_USER_GROUP_ASSIGN
            - ROLE_GINGER_GROUP_CREATE
            - ROLE_GINGER_GROUP_EDIT
            - ROLE_GINGER_GROUP_DELETE

    firewalls:
        dev:
            pattern: ^/(_(profiler|wdt)|css|images|js)/
            security: false
        main:
            pattern: ^/
            anonymous: true
            provider: app_user_provider
            switch_user: true
            guard:
                authenticators:
                    - dw_ginger.userAuthenticator
            form_login:
                csrf_token_generator: security.csrf.token_manager
                always_use_default_target_path: false
                default_target_path: /
                check_path: /login_check
                failure_path: /login
                login_path: /login

            logout:
                path: /logout
                target: /login
                invalidate_session: true

        #############################################
        ##### START CONFIG FROM oAuth & GraphQL #####
        #############################################
        oauth_token:
            pattern: ^/api/oauth/v2/token
            security: false
        api:
            pattern: ^/api/graphql
            fos_oauth: true
            stateless: true
            anonymous: false
        ###########################################
        ##### END CONFIG FROM oAuth & GraphQL #####
        ###########################################

    # Easy way to control access for large sections of your site
    # Note: Only the *first* access control that matches will be used
    access_control:
        - { path: ^/login$, role: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/reset-password, role: IS_AUTHENTICATED_ANONYMOUSLY }
        # Ginger paths with prefix are all behind firewall:
        - { path: ^/, role: ROLE_GINGER_BEUSER }

        #############################################
        ##### START CONFIG FROM oAuth & GraphQL #####
        #############################################
        - { path: ^/api/oauth, roles: [ IS_AUTHENTICATED_ANONYMOUSLY ] }
        - { path: ^/api/graphql, roles: [ ROLE_USER ] }
        ###########################################
        ##### END CONFIG FROM oAuth & GraphQL #####
        ###########################################
```

##### Import Ginger´s routing:

```yaml
# config/routes.yaml
ginger:
    resource: '@DWGingerBundle/Resources/config/routing.yaml'
```

##### Add required `env`-vars:

```yaml
# .env.local
ES_POSTFIX=some-elastic-search-index-postfix
```

##### Create an admin user
At this point the application should already be fully functional, but you will be pointed to the login page when accessing it from your browser.

After creating your local database, running your migrations etc (yu know the drill), create a new admin user with the proper role:
```console
symfony console ginger:user:create dwadmin my@email.com mypassword ROLE_CUSTOMER_ADMIN
```

### Congratz!
Now you should be able to sign in to Ginger!


##### Upgrading applications to Ginger 2.*
If you are running an application using versions of Ginger < 2.0 that already contains real data and want to upgrade to Ginger 2.*, head over to our [Upgrade-Guide](https://bitbucket.org/dwerk/dwgingerbundle/src/master/Resources/doc/).
