# Toubiz POI package for Neos CMS.

This package provides functionality for integrating POI from toubiz into Neos.

## Integration

### Setup
To integrate this package into your project, simply add it to your **project** `composer.json`. To ensure that loading order (e.g. for configurations) is correct, also add it to your **site package** `composer.json`.

### Routing
This package comes with pre-configured routes. You need to add the following configuration to your **project** `Configuration/Routes.yaml`:

```yaml
-
  name: 'Poi'
  uriPattern: '<PoiSubroutes>'
  subRoutes:
    'PoiSubroutes':
      package: 'Newland.Toubiz.Poi.Neos'
```


### Teaser
The POI teaser is an abstract node type. This allows you to implement custom node types based on it with ease. The following example shows how to integrate a `POI Grid Teaser` node type based on the provided teaser node type from this package.

First, set up a node type configuration referencing the teaser as super type:
```
'VendorName.PackageName:PoiGridTeaser':
  superTypes:
    'Newland.Toubiz.Poi.Neos:Teaser': true

  ui:
    label: 'POI Grid Teaser'
```

As the teaser is a plugin, you need a fusion prototype for your plugin referencing the teaser fusion prototype:
```
prototype(Newland.AutTheme:PoiGridTeaser) >
prototype(Newland.AutTheme:PoiGridTeaser) < prototype(Newland.Toubiz.Poi.Neos:Teaser)
```

Most likely, you want to adapt the rendering to e.g. wrap the teaser output with additional markup. For this, you need to add a few bits of fusion script:
```
prototype(Newland.AutTheme:PoiGridTeaser) < prototype(Newland.Toubiz.Poi.Neos:Teaser) {
  wrapStart = '<div class="o-grid__item">'
  wrapEnd = '</div>'
}
```

If you extend your node type with properties, you can access them as usual inside the fusion script. The following would also be possible:
```
wrapStart = ${'<div class="o-grid__item ' + q(node).property('distribution') + '">'}
```

### List
Links to article detail pages can be rendered using the LinkViewHelper.

This will automatically find the first available list/detail plugin to display the article.

Notice: Plugins with the setting "Use this page as detail view for articles of this type." will be preferred.

### Detail Pages

By default all article detail views are located on `/poi/name-of-article`.
This can be configured on a per-type basis using the configuration `Newland.Toubiz.Poi.Neos.detailPage`.
Additionally breadcrumbs can be defined: This must be an array of fizzle / FlowQuery selectors
for that should be visible in the breadcrumb menu.

All valid article types can be seen in `ArticleConstants`. An example configuration looks as follows:

```yaml
Newland:
  Toubiz:
    Poi:
      Neos:
        detailPage:
          _fallback: 
            uriSegment: poi/detail
          2: 
            uriSegment:
                de: poi/touren
                en: poi/tours
            breadcrumb:
                _fallback:
                  - '/sites/foobar-theme/node-abc123def'
                  - '/sites/foobar-theme/node-abc123def/node-49494949'
                site-foobar:
                  - '/sites/foobar-theme/node-def-456-fgi'
                  - '/sites/foobar-theme/node-node-def456fgi/node-12121212'
```

### Data Sources
### `newland-toubiz-poi-neos-pois`
The `newland-toubiz-poi-neos-pois` data source can be used to select an article.
The articles will be displayed by their name and selected by their identifier.
The selected identifier can be resolved to articles using `ArticleRepository` or
the `Newland.Article.findByIdentifier` fusion helper.

Example Usage:
```yaml
'Foo.Bar:MyCustomArticleNode':
  properties:
   article:
     type: string
     ui:
       inspector:
         editor: Neos.Neos/Inspector/Editors/SelectBoxEditor
         editorOptions:
           allowEmpty: false
           dataSourceIdentifier: 'newland-toubiz-poi-neos-pois'
           dataSourceAdditionalData:
             mainType: '2'
```

### Fusion Helpers
#### `Newland.Toubiz.Article.findByIdentifier(string $identifier): ?Article`
Fetches the article object for the given identifier and returns it.
If no article was found (or if no identifier was given) then `null` is returned.

Example Usage:
```neosfusion
prototype(Foo.Bar:MyCustomArticleNode) {
    article = ${Newland.Toubiz.Article.findByIdentifier(q(node).property('article'))}
}
```

### Client Filters

Some API services support multiple clients (e.g. Tportal) and can be configured within the
`Newland.Toubiz.Sync.Neos` settings. To apply the filter during output, the clients need to be
configured in this package here, under the settings key `Newland.Toubiz.Poi.Neos.clientFilter`.
Then, only records for the specified client are shown on any one domain.

For configuration options check the example at [Configuration\Settings.yaml](Configuration\Settings.yaml).

### Splitscreen map on lists
Splitscreen maps can be enabled on a per-articletype basis in the `Newland.Toubiz.Poi.Neos.splitscreen`
settings. For a list of all article types check out the `ArticleConstants` interface of `Newland.ToubizApi`.

```yaml
Newland:
  Toubiz:
    Poi:
      Neos:
        splitscreen:
          mainType:
            # Gastronomy
            1: { enabled: true }
```

#### API Keys
- If the `Newland.Toubiz.Map.Neos` package is installed then the API Keys in the configuration of that
  package are used.
- If the map package is not installed API Keys can be set in `Newland.Toubiz.Poi.Neos.apiKeysForUseWithoutMapsPackage.{nodeName}`
  as the subkeys `googleMaps` and `toursprung`.
  
#### Marker & clustering styles

```yaml
Newland:
  Toubiz:
    Poi:
      Neos:
        splitscreen:
          mainType:
            # Gastronomy
            1:
              enabled: true
              marker:
                icon: marker
                color: tomato
                backgroundColor: '#222'
              clustering:
                color: tomato
                backgroundColor: '#222'
```

### Static Maps on Detail Pages
For static map the setting `Newland.Toubiz.Poi.Neos.staticMap.mapType` can be set to either
`google-maps` or `toursprung` to select the static map provider. In order to use static maps
in your project you have to have the api keys for the selected map type defined.

#### API Keys
- If the `Newland.Toubiz.Map.Neos` package is installed then the API Keys in the configuration of that
  package are used.
- If the map package is not installed API Keys can be set in `Newland.Toubiz.Poi.Neos.apiKeysForUseWithoutMapsPackage.{nodeName}`
  as the subkeys `googleMaps` and `toursprung`.

#### Marker icon
- The static part of the static map does not support customizable marker icons.
- If the  `Newland.Toubiz.Map.Neos` package is installed then the configuration in `Newland.Toubiz.Poi.Neos.staticMap.style`
  is used to dynamically generate an icon url.
- If the map package is not installed the configuration `Newland.Toubiz.Poi.Neos.staticMap.markerIcon` can be
  set to either a resource uri or a fully qualified HTTP URL as a fallback.

```yaml
Newland:
  Toubiz:
    Map:
      Neos:
        staticMap:
          # Dynamic icon generation if map package is installed
          style:
            icon: otter
            color: red
            backgroundColor: white
            
          # Static resource if map package is not installed
          markerIcon: 'resource://Newland.MySitePackage/Public/Icons/marker.svg'
```

## Filtered Lists

### About Topics
In a lot of places inside of the filtered lists node articles should be accessible using
their categories or related attributes. For this reason both of those things have been
unified into a single concept called a 'Topic'. Topic identifiers can be one of the following:

- `category:{UUID}` Topic defined by the category with the given uuid
- `attribute:{NAME}` Topic defined by the existence of a relationship to an attribute with the given name.
  This can be used to filter by boolean attributes. An example for this would be `attribute:publicTransit`
  which would only contain articles that have a relation to an attribute of the name `publicTransit` thereby
  eliminating all that don't have public transit options available.
- `attribute:{NAME}:{VALUE}` Topic defined by the name and concrete value of an attribute. This is an extension
  of the simple name based identifier that also filters for the value. An example would be `attribute:bestSeason:jan`
  and `attribute:properties:suitableForPreambulators`.


#### Defining attributes for topics
  Attribute based topics are very versatile and specific to one site. Not all attributes
  make sense in a topic based filtering system. Therefor attributes that are used for filtered
  lists must be configured on a per-site basis. These attribute based topics will then be available
  alongside category based topics for the following features:
  - Filter
  - Tags
  - Lists

All attribute configuration can be found in `Newland.Toubiz.Poi.Neos.attributes`.
Attributes are configured on a per article-type basis: This means that tours can
have different attributes configured than lodgings.

```
Newland:
  Toubiz:
    Poi:
      Neos:
        attributes:

          # Add a source of language labels for attributes
          i18n:
            - package: 'Newland.MySitePackage'
              source: 'Models/Topic'

          # Attributes for articles with the mainType 2 (tours) - defined
          # using a list of topic identifiers
          mainTypes:
            2:
              - 'attribute:publicTransit'
              - 'attribute:properties:suitableForPreambulators'
              - 'attribute:labels:top'
              - 'attribute:properties:asphalt'
```


### Defining tags for topics
Similar to attributes, tag based topics can be configured on an per article-type basis.
There are many different tags, so not all of the possibilities are pre-defined.

```yaml
Newland:
  Toubiz:
    Poi:
      Neos:
        tags:
          articleTypes:
          
            # Tags for articles with the mainType 0 (attraction)
            # See ArticleConstants for all available types
            0:
              - 'tag:Museum'
              - 'tag:Kultur'
              - 'tag:Musik'
```

#### Language labels for topics
Sources for language labels can be configured in `Newland.Toubiz.Poi.Neos.attributes.i18n`. This configuration must include `package` and `source` of the label. The labels themselves adhere to the following logic:

- `attribute.{NAME}` for boolean attributes (e.g. `attribute.publicTransit`)
- `attribute.{NAME}.{VALUE}` for non-boolean attributes (e.g. `attributes.bestSeason.jan`)

### Glossary

Because there are a lot of similar sounding concepts in the 'filtered lists' feature
it is important that everyone uses the same words for the same concept.

- **List:** A swiper based list on the first page of the plugin that is defined by a topic.
  It contains up to 11 Articles relating to that tour and a last slide with a 'show all' link.
  ![](Documentation/Images/list.png)

- **Topic:** Tags on top of the page suggesting certain predefined filters. A topic can be
  backed by an attribute or a category. Read more on that in the previous section of this
  Document.
  **NOTE:** The name 'topic' is also used outside of the topics-tag-bar context to refer
  to a way of selecting and grouping articles.
  ![](Documentation/Images/topic.png)

- **Filter:** The bar at the very top of the page containing concrete filterable items
  that the user can use to filter all articles. A filter is defined by multiple sections.
  ![](Documentation/Images/filter.png)

- **Filter Section:** The coarsest subdivision of filters - currently displayed as a list
  of buttons with dropdowns. A filter section can contain multiple field sets.
  ![](Documentation/Images/filter-section.jpg)

- **Filter Field Set:** Fields that share a behaviour inside of a section. A field set is
  usually concerned with the value of a single attribute or topic
  ![](Documentation/Images/filter-fieldset.jpg)

### Defining filters

The settings in `Newland.Toubiz.Poi.Neos.filter` can be used to configured the filter
in the following way:

```YAML
Newland:
  Toubiz:
    Poi:
      Neos:
        filter:
          sections:
            $sectionName: <SECTION-DEF>
            $sectionName: <SECTION-DEF>
            $sectionName: <SECTION-DEF>
```

Where `$sectionName` is a unique identifier for the section and the following
definitions:

```YAML
<SECTION-DEF>:
  # Translations are optional
  i18n:
    id: 'section.{sectionName}'
    source: 'Views/Filter'
    package: 'Newland.Toubiz.Poi.Neos'

  fieldSets:
    $fieldSetName: <FIELDSET-DEF>
    $fieldSetName: <FIELDSET-DEF>
    $fieldSetName: <FIELDSET-DEF>

<FIELDSET-DEF>:
  # translations are optional
  i18n:
    id: 'fieldSet.{fieldSetName}'
    source: 'Views/Filter'
    package: 'Newland.Toubiz.Poi.Neos'

  # type can be one of `range`, `checkboxes`, `categories` and defines additional
  # configuration options.
  type: '...'

  # `type: range` displays a range slider that allows the user to pick a
  # range of allowed numeral values.
  type: range
  min: 0
  max: 100

  # `type: checkboxes` displays a row of checkboxes that can be activated.
  # The checkboxes themselves are idetified by their topic identifier.
  # `searchable: true` will enable a search field to quickly find the checkbox
  # you are searching for.
  type: checkboxes
  searchable: false
  items:
    - 'attribute:properties:suitableforperambulators'
    - 'attribute:properties:geology'
    - 'attribute:properties:dining'
    - 'attribute:properties:scenic'

  # `type: categories` is the same as `type: checkboxes` with `searchable: true`
  # and items automatically filled with all categories for the given mainType.
  type: categories
  mainType: 2
```

To remove default filter sections or field sets unset them in your project configuration:
```yaml
// hide a section
Newland:
  Toubiz:
    Poi:
      Neos:
        filter:
          sections:
            0:
              sections:
                categories: ~

// hide a field set
Newland:
  Toubiz:
    Poi:
      Neos:
        filter:
          sections:
            2:
              sections:
                difficulty:
                  fieldSets:
                    ascentElevation: ~
```


## City pages

### Link list

The link list is a row of buttons that link to various list views. By default this includes:
restaurants, tours, events, lodgings, attractions.

List views can be pages from inside Neos or listings from external sources (tportal).

#### Use Neos page

To use a Neos page, a page must be created anywhere in the Neos backend.
The list view plugin must be created on that page. All POI links need the `list` plugin by default.
Events use the `full` plugin.


Set the page identifier in the link data configuration in the project `Settings.yaml` beginning with
the node protocol `node://`:
```yaml
Newland:
  Toubiz:
    Poi:
      Neos:
        cityDetails:
          links:
            data:
              restaurants:
                target: 'node://4d8c8785-eae1-474a-9ecb-1775360246ab'
```

The page identifier can be found under `Page properties -> Metadata -> Additional info -> Identifier`

#### Use external page

Any URL can be used as a link target.

To use a tportal list page, the URL can include one of these placeholders: `{idToubiz}`, `{idTomas}`:

```yaml
Newland:
  Toubiz:
    Poi:
      Neos:
        cityDetails:
          links:
            data:
              restaurants:
                target: '/my-portal/restaurants/{idToubiz}'

```

### Spa towns

Spa towns can have lots of information, stored in attributes.

Use the display options of the detail view to
hide these if necessary.

Check `Newland.Toubiz.Poi.Neos.displayOptions` in [Configuration\Settings.yaml](Configuration\Settings.yaml) for
configuration options. 
