//
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations
// under the License.
//
=== Customization

[CAUTION]
Only Maven projects can be customized: if using Standalone, Debian packages or the GUI installer, none of the
customizations discussed below can be applied.

Apache Syncope is designed to be as flexible as possible, to best suit the various environments
in which it can be deployed. Besides other aspects, this means that every feature and component can be extended or
replaced.

Once the project has been created from the provided Maven archetype, the generated source tree is available for either
adding new features or replacing existing components.

[[override-behavior]]
.Override behavior
****
As a rule of thumb, any file of the local project will take precedence over a file with the same name in the same
package directory of the standard Apache Syncope release.

For example, if you place

 core/src/main/java/org/apache/syncope/core/spring/security/SyncopeAuthenticationProvider.java

in the local project, this file will be picked up instead of
ifeval::["{snapshotOrRelease}" == "release"]
https://github.com/apache/syncope/blob/syncope-{docVersion}/core/spring/src/main/java/org/apache/syncope/core/spring/security/SyncopeAuthenticationProvider.java[SyncopeAuthenticationProvider^].
endif::[]
ifeval::["{snapshotOrRelease}" == "snapshot"]
https://github.com/apache/syncope/blob/2_1_X/core/spring/src/main/java/org/apache/syncope/core/spring/security/SyncopeAuthenticationProvider.java[SyncopeAuthenticationProvider^].
endif::[]

The same happens with resources as images or HTML files; if you place

 console/src/main/resources/org/apache/syncope/client/console/pages/BasePage.html

in the local project, this file will be picked up instead of
ifeval::["{snapshotOrRelease}" == "release"]
https://github.com/apache/syncope/blob/syncope-{docVersion}/client/console/src/main/resources/org/apache/syncope/client/console/pages/BasePage.html[BasePage.html^].
endif::[]
ifeval::["{snapshotOrRelease}" == "snapshot"]
https://github.com/apache/syncope/blob/2_1_X/client/console/src/main/resources/org/apache/syncope/client/console/pages/BasePage.html[BasePage.html^].
endif::[]

This general behavior might have exceptions: when you need to customize one of the Spring context definitions, say
ifeval::["{snapshotOrRelease}" == "release"]
https://github.com/apache/syncope/blob/syncope-{docVersion}/core/spring/src/main/resources/securityContext.xml[securityContext.xml^]
endif::[]
ifeval::["{snapshotOrRelease}" == "snapshot"]
https://github.com/apache/syncope/blob/2_1_X/core/spring/src/main/resources/securityContext.xml[securityContext.xml^]
endif::[]
for example, you will also need to replace the following text in `core/src/main/webapp/WEB-INF/web.xml`,

....
classpath*:/coreContext.xml
classpath*:/securityContext.xml
classpath*:/logicContext.xml
classpath*:/restCXFContext.xml
classpath*:/persistenceContext.xml
classpath*:/provisioning*Context.xml
classpath*:/workflow*Context.xml
....

with

....
classpath*:/coreContext.xml
classpath:/securityContext.xml
classpath*:/logicContext.xml
classpath*:/restCXFContext.xml
classpath*:/persistenceContext.xml
classpath*:/provisioning*Context.xml
classpath*:/workflow*Context.xml
....

to be sure that `core/src/main/resources/securityContext.xml` is picked up. +
Please also note that the actual list of Spring context files to include might depend on the configured extensions.
****

In general, the Embedded Mode (see the
ifeval::["{backend}" == "html5"]
http://syncope.apache.org/docs/getting-started.html[Apache Syncope Getting Started Guide]
endif::[]
ifeval::["{backend}" == "pdf"]
http://syncope.apache.org/docs/getting-started.pdf[Apache Syncope Getting Started Guide]
endif::[]
for details) allows the user to work comfortably from a single workstation, with no need of additional setup; it is
effectively implemented as the `all`
https://maven.apache.org/guides/introduction/introduction-to-profiles.html[Maven profile^], where the available optional
components and extensions are enabled. +
When deploying the generated WAR artifacts into an external <<javaee-container>> however, the required components and
extensions need to be explicitly selected and enabled, as shown in the following text.

[[deployment-directories]]
.Deployment directories
****
Apache Syncope needs three base directories to be defined:

* bundles - where the <<connector-bundles,connector bundles>> are stored;
* log - where all the system logs are written;
* conf (optional) - where configuration files are located, if overriding the default values is needed.

[WARNING]
The `bundles` directory should only contain connector bundle JAR files. +
The presence of any other file might cause the unavailability of any connector bundle in Apache Syncope.

For reference, the suggested directory layout can be created as follows:

....
$ mkdir /opt/syncope
$ mkdir /opt/syncope/bundles
$ mkdir /opt/syncope/log
$ mkdir /opt/syncope/conf
....
****

The WAR artifacts are generated by running the Maven command (with reference to the suggested directory layout):

....
$ mvn clean verify \
   -Dconf.directory=/opt/syncope/conf \
   -Dbundles.directory=/opt/syncope/bundles \
   -Dlog.directory=/opt/syncope/log
$ cp core/target/classes/*properties /opt/syncope/conf
$ cp console/target/classes/*properties /opt/syncope/conf
$ cp enduser/target/classes/*properties /opt/syncope/conf
$ cp enduser/target/classes/customFormAttributes.json /opt/syncope/conf
$ cp enduser/target/classes/customTemplate.json /opt/syncope/conf
....

After downloading all of the dependencies that are needed, three WAR files will be produced:

. `core/target/syncope.war`
. `console/target/syncope-console.war`
. `enduser/target/syncope-enduser.war`

If no failures are encountered, your basic Apache Syncope project is now ready to be deployed.

[[embedded-debug]]
[TIP]
.JPDA Debug in Embedded Mode
====
The Java™ Platform Debugger Architecture (http://docs.oracle.com/javase/8/docs/technotes/guides/jpda/index.html[JPDA^])
is a collection of APIs aimed to help with debugging Java code.

Enhancing the `embedded` profile of the `enduser` module to enable the JPDA socket is quite
straightforward: just add the `<profile>` below to `enduser/pom.xml`:

[source,xml,subs="verbatim,attributes"]
----
<profile>
  <id>debug</id>

  <build>
    <plugins>
      <plugin>
        <groupId>org.codehaus.cargo</groupId>
        <artifactId>cargo-maven2-plugin</artifactId>
        <inherited>true</inherited>
        <configuration>
          <configuration>
            <properties>
              <cargo.jvmargs>
              -Xdebug
              -Xrunjdwp:transport=dt_socket,address=8000,server=y,suspend=n
              -noverify -XX:+CMSClassUnloadingEnabled
              -XX:+UseConcMarkSweepGC -Xmx1024m -Xms512m
              </cargo.jvmargs>
            </properties>
          </configuration>
        </configuration>
      </plugin>
    </plugins>
  </build>
</profile>
----

Now, from the `enduser` subdirectory, execute:

[source,bash]
$ mvn -P embedded,debug

At this point your favourite IDE can be attached to the port `8000`.
====

[[customization-core]]
==== Core

[CAUTION]
When providing custom Java classes implementing the defined interfaces or extending the existing
implementations, their package *must* be rooted under `org.apache.syncope.core`, otherwise they will not be available
at runtime.

Besides replacing existing classes as explained <<override-behavior,above>>, new <<implementations,implementations>> can
be provided - in the source tree under `core/src/main/java` when Java or via REST services if Groovy - for the following
components:

* <<propagationactions,propagation>>, <<pushactions,push>>, <<pullactions,pull>> and <<logicactions,logic>> actions
* <<push-correlation-rules,push>> / <<pull-correlation-rules,pull>> correlation rules
* <<pull-mode,reconciliation filter builders>>
* <<tasks-custom,custom tasks>>
* <<reportlets,reportlets>>
* <<account-rules,account>> and <<password-rules,password>> rules for policies
* <<plain,plain schema validators>>
* <<mapping,mapping item transformers>>
* <<virtual-attribute-cache,virtual attribute cache>>
* <<workflow-adapters,workflow adapters>>
* <<provisioning-managers,provisioning managers>>
* <<notifications,notification recipient providers>>
* <<jwtssoprovider,JWT SSO providers>>
* <<audit-appenders, audit appenders>>

[discrete]
===== Customize OpenJPA settings

Apache OpenJPA is at the core of the <<persistence,persistence>> layer; its configuration can be tweaked under several
aspects - including http://openjpa.apache.org/builds/3.0.0/apache-openjpa/docs/ref_guide_caching.html[caching^] for
example, to best suit the various environments.

In case you need to alter the standard settings provided, proceed as follows.

Replace

....
classpath*:/coreContext.xml
classpath*:/securityContext.xml
classpath*:/logicContext.xml
classpath*:/restCXFContext.xml
classpath*:/persistenceContext.xml
classpath*:/provisioning*Context.xml
classpath*:/workflow*Context.xml
....

with

....
classpath*:/coreContext.xml
classpath*:/securityContext.xml
classpath*:/logicContext.xml
classpath:/restCXFContext.xml
classpath:/persistenceContext.xml
classpath*:/provisioning*Context.xml
classpath*:/workflow*Context.xml
....

in `core/src/main/webapp/WEB-INF/web.xml`.

Download
ifeval::["{snapshotOrRelease}" == "release"]
https://github.com/apache/syncope/blob/syncope-{docVersion}/core/persistence-jpa/src/main/resources/persistenceContext.xml[persistenceContext.xml^]
endif::[]
ifeval::["{snapshotOrRelease}" == "snapshot"]
https://github.com/apache/syncope/blob/2_1_X/core/persistence-jpa/src/main/resources/persistenceContext.xml[persistenceContext.xml^]
endif::[]
and save it under `core/src/main/resources/`.

Download
ifeval::["{snapshotOrRelease}" == "release"]
https://github.com/apache/syncope/blob/syncope-{docVersion}/core/persistence-jpa/src/main/resources/domains.xml
endif::[]
ifeval::["{snapshotOrRelease}" == "snapshot"]
https://github.com/apache/syncope/blob/2_1_X/core/persistence-jpa/src/main/resources/domains.xml[domains.xml^]
endif::[]
and save it under `core/src/main/resources/`.

Now you can add any OpenJPA configuration property in one of domains configuration files as
`core/src/main/resources/domains/MasterDomain.xml` as follows, for the `EntityManagerFactory` bean:

[source,xml]
....
<property name="jpaPropertyMap">
  <map>
    <entry key="openjpa.DataCache" value="true(Lru=true)"/>
    <entry key="openjpa.QueryCache" value="true(Lru=true)"/>
  </map>
</property>
....

[WARNING]
====
The OpenJPA documentation's XML snippets refer to a different configuration style; for example, when used in
one of domains configuration files as `MasterDomain.xml`, this:

[source,xml]
....
<property name="openjpa.DataCache" value="true"/>
....

becomes:

[source,xml]
....
<entry key="openjpa.DataCache" value="true"/>
....
====

[discrete]
===== Enable the <<flowable-user-workflow-adapter>>

Add the following dependency to `core/pom.xml`:

[source,xml,subs="verbatim,attributes"]
----
<dependency>
  <groupId>org.apache.syncope.ext.flowable</groupId>
  <artifactId>syncope-ext-flowable-rest-cxf</artifactId>
  <version>${syncope.version}</version>
</dependency>
----

Copy `core/src/main/resources/all/workflow.properties` to `core/src/main/resources/workflow.properties`.

[discrete]
===== Enable the <<apache-camel-provisioning-manager>>

Add the following dependencies to `core/pom.xml`:

[source,xml,subs="verbatim,attributes"]
----
<dependency>
  <groupId>org.apache.syncope.ext.camel</groupId>
  <artifactId>syncope-ext-camel-rest-cxf</artifactId>
  <version>${syncope.version}</version>
</dependency>
<dependency>
  <groupId>org.apache.syncope.ext.camel</groupId>
  <artifactId>syncope-ext-camel-persistence-jpa</artifactId>
  <version>${syncope.version}</version>
</dependency>
<dependency>
  <groupId>org.apache.syncope.ext.camel</groupId>
  <artifactId>syncope-ext-camel-provisioning</artifactId>
  <version>${syncope.version}</version>
</dependency>
----

Copy `core/src/main/resources/all/provisioning.properties` to `core/src/main/resources/provisioning.properties`.

[discrete]
===== Enable the <<swagger>> extension

Add the following dependency to `core/pom.xml`:

[source,xml,subs="verbatim,attributes"]
----
<dependency>
  <groupId>org.apache.syncope.ext</groupId>
  <artifactId>syncope-ext-swagger-ui</artifactId>
  <version>${syncope.version}</version>
</dependency>
----

[discrete]
===== Enable the <<saml-2-0-service-provider>> extension

Add the following dependencies to `core/pom.xml`:

[source,xml,subs="verbatim,attributes"]
----
<dependency>
  <groupId>org.apache.syncope.ext.saml2sp</groupId>
  <artifactId>syncope-ext-saml2sp-rest-cxf</artifactId>
  <version>${syncope.version}</version>
</dependency>
<dependency>
  <groupId>org.apache.syncope.ext.saml2sp</groupId>
  <artifactId>syncope-ext-saml2sp-persistence-jpa</artifactId>
  <version>${syncope.version}</version>
</dependency>
----

Copy `core/src/main/resources/all/saml2sp-logic.properties` to `core/src/main/resources/saml2sp-logic.properties`.

Setup a <<keystore,keystore>> and place it under the <<properties-files-location,configuration directory>>, then review
the content of `core/src/main/resources/saml2sp-logic.properties` accordingly.

[discrete]
===== Enable the <<openid-connect-client>> extension

Add the following dependencies to `core/pom.xml`:

[source,xml,subs="verbatim,attributes"]
----
<dependency>
  <groupId>org.apache.syncope.ext.oidcclient</groupId>
  <artifactId>syncope-ext-oidcclient-rest-cxf</artifactId>
  <version>${syncope.version}</version>
</dependency>
<dependency>
  <groupId>org.apache.syncope.ext.oidcclient</groupId>
  <artifactId>syncope-ext-oidcclient-persistence-jpa</artifactId>
  <version>${syncope.version}</version>
</dependency>
----

[discrete]
===== Enable the <<elasticsearch>> extension

Add the following dependencies to `core/pom.xml`:

[source,xml,subs="verbatim,attributes"]
----
<dependency>
  <groupId>org.apache.syncope.ext.elasticsearch</groupId>
  <artifactId>syncope-ext-elasticsearch-logic</artifactId>
  <version>${syncope.version}</version>
</dependency>
<dependency>
  <groupId>org.apache.syncope.ext.elasticsearch</groupId>
  <artifactId>syncope-ext-elasticsearch-provisioning-java</artifactId>
  <version>${syncope.version}</version>
</dependency>
<dependency>
  <groupId>org.apache.syncope.ext.elasticsearch</groupId>
  <artifactId>syncope-ext-elasticsearch-persistence-jpa</artifactId>
  <version>${syncope.version}</version>
</dependency>
----

Replace

....
default.audit.appender=org.apache.syncope.core.logic.audit.JdbcAuditAppender
....

with

....
default.audit.appender=org.apache.syncope.core.logic.audit.ElasticsearchAuditAppender
....

in `core/src/main/resources/logic.properties`.

Download 

ifeval::["{snapshotOrRelease}" == "release"]
https://github.com/apache/syncope/blob/syncope-{docVersion}/ext/elasticsearch/persistence-jpa/src/main/resources/persistence.properties[persistence.properties^]
endif::[]
ifeval::["{snapshotOrRelease}" == "snapshot"]
https://github.com/apache/syncope/blob/2_1_X/ext/elasticsearch/persistence-jpa/src/main/resources/persistence.properties[persistence.properties^]
endif::[]

and

ifeval::["{snapshotOrRelease}" == "release"]
https://github.com/apache/syncope/blob/syncope-{docVersion}/ext/elasticsearch/client-elasticsearch/src/main/resources/elasticsearchClientContext.xml[elasticsearchClientContext.xml^]
endif::[]
ifeval::["{snapshotOrRelease}" == "snapshot"]
https://github.com/apache/syncope/blob/2_1_X/ext/elasticsearch/client-elasticsearch/src/main/resources/elasticsearchClientContext.xml[elasticsearchClientContext.xml^]
endif::[]

then save both under `core/src/main/resources`.

Now, adjust the parameters in `core/src/main/resources/elasticsearchClientContext.xml` to match your
Elasticsearch deployment.

Finally, replace the following text in `core/src/main/webapp/WEB-INF/web.xml`:

....
classpath*:/coreContext.xml
classpath*:/securityContext.xml
classpath*:/logicContext.xml
classpath*:/restCXFContext.xml
classpath*:/persistenceContext.xml
classpath*:/provisioning*Context.xml
classpath*:/workflow*Context.xml
....

with

....
classpath*:/coreContext.xml
classpath:/elasticsearchClientContext.xml
classpath*:/securityContext.xml
classpath*:/logicContext.xml
classpath*:/restCXFContext.xml
classpath*:/persistenceContext.xml
classpath*:/provisioning*Context.xml
classpath*:/workflow*Context.xml
....

It is also required to initialize the Elasticsearch indexes: add a new Java <<implementations,implementation>> for
`TASKJOB_DELEGATE` and use `org.apache.syncope.core.provisioning.java.job.ElasticsearchReindex` as class. +
Then, create a new <<tasks-custom, custom task>>, select the implementation just created as job delegate and execute it.

[TIP]
The `org.apache.syncope.core.provisioning.java.job.ElasticsearchReindex` custom task created above is not meant for
scheduled execution; rather, it can be run every time you want to blank and re-create the Elasticsearch indexes
starting from Syncope's internal storage.

[discrete]
===== Enable the <<SCIM>> extension

Add the following dependencies to `core/pom.xml`:

[source,xml,subs="verbatim,attributes"]
----
<dependency>
  <groupId>org.apache.syncope.ext.scimv2</groupId>
  <artifactId>syncope-ext-scimv2-rest-cxf</artifactId>
  <version>${syncope.version}</version>
</dependency>
<dependency>
  <groupId>org.apache.syncope.ext.scimv2</groupId>
  <artifactId>syncope-ext-scimv2-scim-rest-cxf</artifactId>
  <version>${syncope.version}</version>
</dependency>
----

[discrete]
===== New REST endpoints
Adding a new REST endpoint involves several operations:

. create - in an extension's `rest-api` module or under `common` otherwise - a Java interface with package
`org.apache.syncope.common.rest.api.service` and proper JAX-RS annotations; check
ifeval::["{snapshotOrRelease}" == "release"]
https://github.com/apache/syncope/blob/syncope-{docVersion}/ext/camel/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/CamelRouteService.java[CamelRouteService^]
endif::[]
ifeval::["{snapshotOrRelease}" == "snapshot"]
https://github.com/apache/syncope/blob/2_1_X/ext/camel/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/CamelRouteService.java[CamelRouteService^]
endif::[]
for reference;
. if needed, define supporting payload objects - in an extension's `common-lib` module or under `common` otherwise;
check
ifeval::["{snapshotOrRelease}" == "release"]
https://github.com/apache/syncope/blob/syncope-{docVersion}/ext/camel/common-lib/src/main/java/org/apache/syncope/common/lib/to/CamelRouteTO.java[CamelRouteTO^]
endif::[]
ifeval::["{snapshotOrRelease}" == "snapshot"]
https://github.com/apache/syncope/blob/2_1_X/ext/camel/common-lib/src/main/java/org/apache/syncope/common/lib/to/CamelRouteTO.java[CamelRouteTO^]
endif::[]
for reference;
. implement - in an extension's `rest-cxf` module or under `core` otherwise -  the interface defined above in a Java
class with package `org.apache.syncope.core.rest.cxf.service`; check
ifeval::["{snapshotOrRelease}" == "release"]
https://github.com/apache/syncope/blob/syncope-{docVersion}/ext/camel/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/CamelRouteServiceImpl.java[CamelRouteServiceImpl^]
endif::[]
ifeval::["{snapshotOrRelease}" == "snapshot"]
https://github.com/apache/syncope/blob/2_1_X/ext/camel/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/CamelRouteServiceImpl.java[CamelRouteServiceImpl^]
endif::[]
for reference.

By following such conventions, the new REST endpoint will be automatically picked up alongside the default services.

[[customization-console]]
==== Console

[CAUTION]
When providing custom Java classes implementing the defined interfaces or extending the existing
implementations, their package *must* be rooted under `org.apache.syncope.client.console`, otherwise they will not be
available at runtime.

[discrete]
===== Enable the <<flowable-user-workflow-adapter>>

Add the following dependency to `console/pom.xml`:

[source,xml,subs="verbatim,attributes"]
----
<dependency>
  <groupId>org.apache.syncope.ext.flowable</groupId>
  <artifactId>syncope-ext-flowable-client-console</artifactId>
  <version>${syncope.version}</version>
</dependency>
----

[discrete]
===== Enable the <<apache-camel-provisioning-manager>>

Add the following dependency to `console/pom.xml`:

[source,xml,subs="verbatim,attributes"]
----
<dependency>
  <groupId>org.apache.syncope.ext.camel</groupId>
  <artifactId>syncope-ext-camel-client-console</artifactId>
  <version>${syncope.version}</version>
</dependency> 
----

[discrete]
===== Enable the <<saml-2-0-service-provider>> extension

Add the following dependencies to `console/pom.xml`:

[source,xml,subs="verbatim,attributes"]
----
<dependency>
  <groupId>org.apache.syncope.ext.saml2sp</groupId>
  <artifactId>syncope-ext-saml2sp-client-console</artifactId>
  <version>${syncope.version}</version>
</dependency>
----

Copy `console/src/main/resources/all/saml2sp-agent.properties` to `console/src/main/resources/saml2sp-agent.properties`.

[discrete]
===== Enable the <<openid-connect-client>> extension

Add the following dependencies to `console/pom.xml`:

[source,xml,subs="verbatim,attributes"]
----
<dependency>
  <groupId>org.apache.syncope.ext.oidcclient</groupId>
  <artifactId>syncope-ext-oidcclient-client-console</artifactId>
  <version>${syncope.version}</version>
</dependency>
----

Copy `console/src/main/resources/all/oidcclient-agent.properties` to `console/src/main/resources/oidcclient-agent.properties`.


[discrete]
===== Enable the <<SCIM>> extension

Add the following dependencies to `console/pom.xml`:

[source,xml,subs="verbatim,attributes"]
----
<dependency>
  <groupId>org.apache.syncope.ext.scimv2</groupId>
  <artifactId>syncope-ext-scimv2-client-console</artifactId>
  <version>${syncope.version}</version>
</dependency>
----

[[customization-enduser]]
==== Enduser

Given the nature of the <<enduser-application>>, all the files required by the AngularJS-based frontend to run are
generated under the local project's `enduser/src/main/webapp/app/` directory and are available for full customization.

The files in use by the Apache Wicket-based backend are still subject to the general
<<override-behavior,override behavior>>, instead.

[discrete]
===== Enable the <<flowable-user-workflow-adapter>>

Add the following dependency to `enduser/pom.xml`:

[source,xml,subs="verbatim,attributes"]
----
<dependency>
  <groupId>org.apache.syncope.ext.flowable</groupId>
  <artifactId>syncope-ext-flowable-client-enduser</artifactId>
  <version>${syncope.version}</version>
</dependency>
----

[discrete]
===== Enable the <<saml-2-0-service-provider>> extension

Add the following dependencies to `enduser/pom.xml`:

[source,xml,subs="verbatim,attributes"]
----
<dependency>
  <groupId>org.apache.syncope.ext.saml2sp</groupId>
  <artifactId>syncope-ext-saml2sp-client-enduser</artifactId>
  <version>${syncope.version}</version>
</dependency>
----

Copy `enduser/src/main/resources/all/saml2sp-agent.properties` to `enduser/src/main/resources/saml2sp-agent.properties`.

[discrete]
===== Enable the <<openid-connect-client>> extension

Add the following dependencies to `enduser/pom.xml`:

[source,xml,subs="verbatim,attributes"]
----
<dependency>
  <groupId>org.apache.syncope.ext.oidcclient</groupId>
  <artifactId>syncope-ext-oidcclient-client-enduser</artifactId>
  <version>${syncope.version}</version>
</dependency>
----

Copy `enduser/src/main/resources/all/oidcclient-agent.properties` to `enduser/src/main/resources/oidcclient-agent.properties`.

[[customization-enduser-i18n]]
===== i18n 

The <<enduser-application>> comes with a native internationalization mechanism.

Under the `enduser/src/main/webapp/app/languages/` directory, a sub-directory for each supported language is available;
each language sub-directory contains two JSON files:

* `static.json` for application messages;
* `dynamic.json` for labels (including attributes).

Changing the content of these files will result in updating the Enduser messages accordingly.

[TIP]
====
In order to add support for a new language (taking French as reference):

* add the support for the new language by updating `index.html`:
```
  <script src="../webjars/kendo-ui-core/${kendo-ui-core.version}/js/cultures/kendo.culture.it.js"></script>
  <script src="../webjars/kendo-ui-core/${kendo-ui-core.version}/js/cultures/kendo.culture.en.js"></script>
  <script src="../webjars/kendo-ui-core/${kendo-ui-core.version}/js/cultures/kendo.culture.de.js"></script>
```
in
```
  <script src="../webjars/kendo-ui-core/${kendo-ui-core.version}/js/cultures/kendo.culture.it.js"></script>
  <script src="../webjars/kendo-ui-core/${kendo-ui-core.version}/js/cultures/kendo.culture.en.js"></script>
  <script src="../webjars/kendo-ui-core/${kendo-ui-core.version}/js/cultures/kendo.culture.de.js"></script>
  <script src="../webjars/kendo-ui-core/${kendo-ui-core.version}/js/cultures/kendo.culture.fr.js"></script>
```
* add the new language entry in `js/app.js` under `availableLanguages`, by updating
```
    $rootScope.languages = {
      availableLanguages: [
        {id: '1', name: 'Italiano', code: 'it', format: 'dd/MM/yyyy HH:mm'},
        {id: '2', name: 'English', code: 'en', format: 'MM/dd/yyyy HH:mm'},
        {id: '3', name: 'Deutsch', code: 'de', format: 'dd/MM/yyyy HH:mm'}
      ]
    };
```
as
```
    $rootScope.languages = {
      availableLanguages: [
        {id: '1', name: 'Italiano', code: 'it', format: 'dd/MM/yyyy HH:mm'},
        {id: '2', name: 'English', code: 'en', format: 'MM/dd/yyyy HH:mm'},
        {id: '3', name: 'Deutsch', code: 'de', format: 'dd/MM/yyyy HH:mm'}
        {id: '4', name: 'Français', code: 'fr', format: 'dd/MM/yyyy HH:mm'}
      ]
    };
```
* copy the `enduser/src/main/webapp/app/languages/en/` directory into `enduser/src/main/webapp/app/languages/fr/`
and modify the JSON files under the new directory
====

[[customization-enduser-form]]
===== Form customization

The <<enduser-application>> allows to customize the form in order to:

* hide / show attributes
* set attributes read-only for users
* provide default value(s)

Under the `enduser/src/main/resources` directory, the `customFormAttributes.json` file is available, allowing to configure form
customization.

[NOTE]
.Hot deploy
====
The `customFormAttributes.json` could be edited and reloaded without the need of re-starting the Java EE container.
====

[TIP]
The `customFormAttributes.json` default content is just an empty object `{}`: if such file is missing, empty or not valid,
form customization will be simply disabled and all attributes will be shown.

.Sample form customization
====
[source,json]
----
{
  "PLAIN":
          {
            "attributes": {
              "firstname": {
                "readonly": true,
                "defaultValues": ["defaultFirstname1", "defaultFirstname2"]
              },
              "surname": {
                "readonly": false,
                "defaultValues": []
              },
              "fullname": {
                "readonly": false
              },
              "email": {
                "readonly": false,
                "defaultValues": ["test@apache.org"]
              },
              "userId": {
                "readonly": false
              },
              "cool": {
                "readonly": true,
                "defaultValues": ["true"]
              },
              "additional#loginDate": {
                "readonly": false
              },
              "additional#cool": {
                "readonly": false,
                "defaultValues": ["true"]
              }
            }
          },
  "VIRTUAL":
          {
            "attributes": {
              "virtualdata": {
                "readonly": true,
                "defaultValues": ["defaultVirtualData"]
              }
            }
          }
}
----
====

The `customFormAttributes.json` file has two main levels:

. Schema type, e.g. `PLAIN`, `DERIVED`, `VIRTUAL`;
. Attributes: list of attributes (by schema type) to be shown on the form.

[discrete]
====== Schema type

The schema type level allows to define customization of the three sub-forms available in the Enduser Application's form.

Only one, two or all three sections can be specified, in order to customize only what is really needed.

[discrete]
====== Attributes

The attributes level contains a map of attributes to show.

Each attribute has:

* a name, e.g. the name of the <<schema,Schema>> from which the attribute is generated
* a body, that specifies if the attribute should be readonly, and possibly its default values

.Form attribute specification
====
[source,json]
----
              "firstname": {
                "readonly": true,
                "defaultValues": ["defaultFirstname1", "defaultFirstname2"]
              },
----
Here, `firstname` is readonly and has two default values `defaultFirstname1` and `defaultFirstname2`.
====

[TIP]
====
An empty `attributes` field translates to skip filtering and show all attributes; for example:

```
{
  "PLAIN": 
          {
            "attributes": {}
          }
}
```
shows all `PLAIN` attributes.

If all attributes are to be hidden, please use <<customization-enduser-template>> to hide the full wizard step, instead.
====

[NOTE]
====
The `readonly` field should not be confused with the read-only flag available for <<plain,Plain>> and
<<virtual,Virtual>> schema. +
Within Enduser form customization, `readonly` prevents the user's browser to modify the value of a given attribute.
====

[TIP]
====
`defaultValues` is a string array: this means, in particular, that date values should be specified as strings
(timestamps). +
Moreover, `defaultValues` do not overwrite any existing value.
====

[[customization-enduser-template]]
===== Dynamic Templating

It is possible to customize the <<enduser-application>> using the "Dynamic Templating" feature. 
It is a simple and fast way to customize structure and style of the whole Enduser.

Under the `enduser/src/main/resources` directory, the `customTemplate.json` file is available, allowing to configure application
style and structure.

[NOTE]
.Hot deploy
====
The `customTemplate.json` could be edited and reloaded without the need of re-starting the Java EE container.
====

[TIP]
====
The `customTemplate.json` default content is:
```
{
  "templates": 
          {
            "login":
                    {
                      "templateUrl": "views/templates/selfTemplate.html",
                      "css": [
                        "css/login.css"
                      ]
                    },

            "edit_user":
                    {
                      "templateUrl": "views/templates/editUserTemplate.html",
                      "css": [
                        "css/editUser.css"
                      ]
                    },

            "password_reset":
                    {
                      "templateUrl": "views/templates/passwordresetTemplate.html",
                      "css": [
                        "css/editUser.css",
                        "css/passwordReset.css"
                      ]
                    },
            "must_change_password":
                    {
                      "templateUrl": "views/templates/mustChangePasswordTemplate.html",
                      "css": [
                        "css/editUser.css",
                        "css/passwordReset.css"
                      ]
                    },
            "confirm_password_reset":
                    {
                      "templateUrl": "views/templates/confirmPasswordResetTemplate.html",
                      "css": [
                        "css/editUser.css",
                        "css/passwordReset.css"
                      ]
                    }
          },

  "generalAssets": 
          {
            "css": [
              "css/notification.css",
              "css/app.css"
            ]
          },

  "wizard": 
          {
            "firstStep": "credentials",
            "steps": {
              "credentials": {
                "url": "/credentials"
              },
              "groups": {
                "url": "/groups"
              },
              "plainSchemas": {
                "url": "/plainSchemas"
              },
              "derivedSchemas": {
                "url": "/derivedSchemas"
              },
              "virtualSchemas": {
                "url": "/virtualSchemas"
              },
              "resources": {
                "url": "/resources"
              },
              "finish": {
                "url": "/finish"
              }
            }
          }
}
```
if such file is missing, empty or not valid, Enduser structure will not be valid and won't work as expected.
====

[discrete]
====== Editing default template

As sample, Syncope provides a configuration to apply a dark theme to Enduser and edit the "User Edit / Create Wizard" 
to only display 3 steps, like:

* PlainSchemas;
* Details (Username, Password, Security question etc…);
* Finish.

The configurations for that sample are in
ifeval::["{snapshotOrRelease}" == "release"]
https://github.com/apache/syncope/blob/syncope-{docVersion}/client/enduser/src/test/resources/customTemplate.json[customTemplate.json^].
endif::[]
ifeval::["{snapshotOrRelease}" == "snapshot"]
https://github.com/apache/syncope/blob/2_1_X/client/enduser/src/test/resources/customTemplate.json[customTemplate.json^].
endif::[]
In order to apply them, that content must be copied to your `customTemplate.json` file.

[discrete]
====== Template styling assets

In case you need, place your static `.css` resources somewhere (e.g. `enduser/src/main/resources/META-INF/resources/app/css/templates/`)
and refer them in `customTemplate.json` file.

[discrete]
====== Wizard steps

It is also possible to customize the list of wizard steps to be shown in "User Edit / Create Wizard".
E.g., only 3 steps are displayed in the dark theme template sample configuration:

```
"wizard": 
        {
          "steps": {
            "plainSchemas": {
              "url": "/plainSchemas"
            },
            "credentials": {
              "url": "/credentials"
            },
            "finish": {
              "url": "/finish"
            }
          }
        }
```

Use the `"firstStep"` property to decide which step must be set as first one in the wizard:

```
"wizard": 
        {
          "firstStep": "plainSchemas",
        }
```

[discrete]
====== Template structure

The structure of the template sections is defined by `templateUrl` property, for each main section:

```
"templates": 
        {
         "login":
                {
                  "templateUrl": "views/templates/selfTemplate.html",
                },

        "edit_user":
                {
                  "templateUrl": "views/templates/onlyPlainAttrsDetails/editUserTemplate.html",
                },

        "password_reset":
                {
                  "templateUrl": "views/templates/passwordresetTemplate.html",
                },

        "must_change_password":
                {
                  "templateUrl": "views/templates/mustChangePasswordTemplate.html",
                },

        "confirm_password_reset":
                {
                  "templateUrl": "views/templates/confirmPasswordResetTemplate.html",
                }
        },
```

E.g., `enduser/src/main/resources/META-INF/resources/app/views/templates/onlyPlainAttrsDetails/editUserTemplate.html` is 
equals to the default
`enduser/src/main/resources/META-INF/resources/app/views/templates/editUserTemplate.html`
template file. 
Anyway, it is just helpful to show that it is possible to customize all the application structure in a very simple way.

It could also be useful to edit the specific parts of a single wizard step or view; all of those are placed in 
`enduser/src/main/resources/META-INF/resources/app/views/` folder.

[WARNING]
Please do not change the main object keys in `customTemplate.json` file (e.g. `templates`, `edit_user`, `login`, `generalAssets`, etc...); 
they are used to identify the specific customizable template parts of the Enduser application.
In case it is necessary to change those identifiers, remember to edit all references in other parts of the application.

[[customization-extensions]]
==== Extensions

<<extensions>> can be part of a local project, to encapsulate special features which are specific to a given deployment.

For example, the http://www.chorevolution.eu/[CHOReVOLUTION^] IdM - based on Apache Syncope - provides
https://gitlab.ow2.org/chorevolution/syncope/tree/master/ext/choreography[an extension^]
for managing via the <<core>> and visualizing via the <<admin-console-component>> the running choreography instances.
